8f6f15ea23a380e8c3853df4559fac9f05422d62
[litoprism.git] / src / main.rs
1 use std::f32::consts::PI;
2 use std::convert::From;
3 use std::env;
4 use std::io::Cursor;
5 use std::fmt::Display;
6 use std::fmt;
7 use std::borrow::BorrowMut;
8 use std::sync::{Arc, Mutex};
9 use log::{debug, LevelFilter};
10 use num_traits::Zero;
11 use num_traits::cast::AsPrimitive;
12 use percent_encoding::percent_decode_str;
13 use actix_web::{middleware::Logger, get, web, App, HttpServer, HttpRequest, HttpResponse, Responder, ResponseError, Result};
14 use actix_web::body::BoxBody;
15 use actix_web::http::StatusCode;
16 use actix_web::cookie::time::OffsetDateTime;
17 use image::{ImageBuffer, ColorType, Rgb, RgbImage, write_buffer_with_format};
18 use image::ImageOutputFormat::Png;
19 use image::imageops::{FilterType, overlay, resize};
20 use image::error::{LimitError, LimitErrorKind};
21
22
23 const TIMEAVATAR_SIZE_U32: u32 = 6;
24 const TIMEAVATAR_SIZE_I64: i64 = TIMEAVATAR_SIZE_U32 as i64;
25
26 macro_rules! response_image {
27 ($cursor:expr) => {
28 HttpResponse::build(StatusCode::OK)
29 .content_type("image/png")
30 .body($cursor.into_inner())
31 }
32 }
33
34 macro_rules! extract_data {
35 ($req:expr,$n:expr) => {
36 $req.uri().path().split('/').nth($n).unwrap()
37 }
38 }
39
40 macro_rules! extract_color {
41 ($req:expr,$n:expr) => {
42 {
43 let data_raw = extract_data!($req, $n);
44 let mut data_decoded = percent_decode_str(data_raw);
45 let mut rgb = ToRgbIter::new(&mut data_decoded);
46 let color = rgb.next().ok_or(ColorError(data_raw.to_owned()))?;
47 match rgb.next() {
48 Some(_) => Err(ColorError(data_raw.to_owned())),
49 _ => Ok(color)
50 }
51 }
52 }
53 }
54
55 macro_rules! extract_header {
56 ($req:expr,$header:expr) => {
57 match $req.headers().get($header) {
58 Some(header) => header.to_str().unwrap_or(""),
59 _ => ""
60 }
61 }
62 }
63
64 #[derive(Debug)]
65 struct ColorError(String);
66
67 impl Display for ColorError {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 write!(f, "Invalid color specification: {}", &self.0)
70 }
71 }
72
73 impl ResponseError for ColorError {
74 fn status_code(&self) -> StatusCode {
75 StatusCode::INTERNAL_SERVER_ERROR
76 }
77
78 fn error_response(&self) -> HttpResponse<BoxBody> {
79 HttpResponse::InternalServerError().body(format!("{}\n", &self))
80 }
81 }
82
83 #[derive(Debug)]
84 struct ImageError(image::ImageError);
85
86 impl ImageError {
87 fn dimension_error() -> ImageError {
88 ImageError(image::ImageError::Limits(LimitError::from_kind(LimitErrorKind::DimensionError)))
89 }
90 }
91
92 impl Display for ImageError {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "{}", &self.0)
95 }
96 }
97
98 impl ResponseError for ImageError {
99 fn status_code(&self) -> StatusCode {
100 StatusCode::INTERNAL_SERVER_ERROR
101 }
102
103 fn error_response(&self) -> HttpResponse<BoxBody> {
104 HttpResponse::InternalServerError().body(format!("{}\n", &self))
105 }
106 }
107
108 impl From<image::ImageError> for ImageError {
109 fn from(e: image::ImageError) -> Self {
110 Self(e)
111 }
112 }
113
114 #[derive(Debug)]
115 enum CanvasError {
116 NotExists,
117 DimensionMismatch((u32, u32)),
118 ImageError(ImageError)
119 }
120
121 impl Display for CanvasError {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 write!(f, "Canvas Error: see error_response() for more details")
124 }
125 }
126
127 impl ResponseError for CanvasError {
128 fn status_code(&self) -> StatusCode {
129 match &self {
130 CanvasError::NotExists => StatusCode::NOT_FOUND,
131 CanvasError::DimensionMismatch(_) => StatusCode::BAD_REQUEST,
132 CanvasError::ImageError(e) => e.status_code()
133 }
134 }
135
136 fn error_response(&self) -> HttpResponse<BoxBody> {
137 match &self {
138 CanvasError::NotExists => HttpResponse::NotFound().body("Canvas does not exist."),
139 CanvasError::DimensionMismatch(dim) =>
140 HttpResponse::BadRequest().body(format!("Supplied dimensions don't match the dimensions of existing canvas {:?}.", dim)),
141 CanvasError::ImageError(e) => e.error_response()
142 }
143 }
144 }
145
146 impl From<ImageError> for CanvasError {
147 fn from(e: ImageError) -> CanvasError {
148 CanvasError::ImageError(e)
149 }
150 }
151
152 struct ToRgbIter<'a, T> where T: AsPrimitive<u8> {
153 iter: &'a mut dyn Iterator<Item = T>
154 }
155
156 impl<'a, T: AsPrimitive<u8>> ToRgbIter<'a, T> {
157 fn new(iter: &'a mut (dyn Iterator<Item = T>)) -> ToRgbIter<T> {
158 ToRgbIter { iter }
159 }
160 }
161
162 impl<T: AsPrimitive<u8> + Zero> Iterator for ToRgbIter<'_, T> {
163 type Item = Rgb<u8>;
164 fn next(&mut self) -> Option<Rgb<u8>> {
165 if let Some(r) = self.iter.next() {
166 let g = self.iter.next().unwrap_or_else(T::zero);
167 let b = self.iter.next().unwrap_or_else(T::zero);
168 Some(Rgb([r.as_(), g.as_(), b.as_()]))
169 }
170 else {
171 None
172 }
173 }
174 }
175
176 #[derive(Debug)]
177 struct Canvas {
178 pen: usize,
179 img: RgbImage
180 }
181
182 impl Canvas {
183 fn new(width: u32, height: u32) -> Result<Canvas, CanvasError> {
184 // Image must not be larger than 1 megapixel
185 if width * height > 1048576 {
186 Err(ImageError::dimension_error().into())
187 }
188 else {
189 Ok(Canvas { pen: 0, img: ImageBuffer::new(width, height) })
190 }
191 }
192
193 /*fn from_image(img: RgbImage) -> Canvas {
194 Canvas { pen: 0, img }
195 }
196
197 fn replace(&self, img: RgbImage, pen: usize) -> Canvas {
198 self.img = img;
199 self.pen = pen;
200 self
201 }*/
202
203 fn advance_pen(mut self, offset: usize) -> Canvas {
204 //Canvas { pen: self.pen + offset, img: self.img }
205 self.pen += offset;
206 self.pen %= self.img.width() as usize * self.img.height() as usize;
207 self
208 }
209 }
210
211 struct Canvas0(Option<Canvas>);
212 struct Canvas1(Option<Canvas>);
213 struct Canvas2(Option<Canvas>);
214
215 #[get("/hello/{name}")]
216 async fn greet(name: web::Path<String>, req: HttpRequest) -> impl Responder {
217 println!("{:?}", req);
218 format!("Hello {name}!\n{:?}", req.headers().get("user-agent"))
219 }
220
221 fn rgb_encode_with_pen(img: &mut RgbImage, pen: usize, data: &mut dyn Iterator<Item = u8>) -> Result<usize, ImageError> {
222 let mut pixels = img.pixels_mut().skip(pen);
223 let mut counter = 0;
224
225 for sp in ToRgbIter::new(data) {
226 let mut dp = match pixels.next() {
227 Some(pixel) => pixel,
228 None => {
229 pixels = img.pixels_mut().skip(0);
230 pixels.next().ok_or_else(ImageError::dimension_error)?
231 }
232 };
233 dp.0 = sp.0;
234 counter += 1;
235 }
236
237 Ok(counter)
238 }
239
240 fn rgb_encode_to_canvas(mut canvas: Canvas, data: &mut dyn Iterator<Item = u8>) -> Result<Canvas, CanvasError> {
241 let counter = rgb_encode_with_pen(&mut canvas.img, canvas.pen, data)?;
242 Ok(canvas.advance_pen(counter))
243 }
244
245 fn rgb_encode(mut img: RgbImage, data: &mut dyn Iterator<Item = u8>) -> Result<RgbImage, ImageError> {
246 rgb_encode_with_pen(&mut img, 0, data)?;
247 Ok(img)
248 }
249
250 fn sine_encode(mut img: RgbImage, wave_offset: (u32, u32), amplitude: f32, freq_base: f32, run: u32, wave_color: Rgb<u8>,
251 data: &mut dyn Iterator<Item = u8>) -> Result<RgbImage, ImageError>
252 {
253 let run: f32 = run as f32;
254 let (wave_offset_x, wave_offset_y) = wave_offset;
255 let (wave_offset_x, wave_offset_y) = (wave_offset_x as f32, wave_offset_y as f32);
256 let signal = &mut data.peekable();
257 let mut x: f32 = wave_offset_x as f32;
258 let max_x: f32 = (img.width() - 1) as f32;
259 let max_y: f32 = img.height() as f32;
260 while signal.peek().is_some() && (x < max_x) {
261 let px = x;
262 let s = signal.next().unwrap_or(0);
263 while ((x - px) < run) && (x < max_x) {
264 let y = if s != 0 {
265 (2.0 * PI * freq_base * x * s as f32).sin() * amplitude + wave_offset_y
266 }
267 else { wave_offset_y };
268 x += 0.001;
269 if (y > 0.0) && (y < max_y) {
270 img.put_pixel(x as u32, y as u32, wave_color);
271 }
272 }
273 }
274 Ok(img)
275 }
276
277 fn offset_encode(mut img: RgbImage, offset: (u32, u32), run: u32, color: Rgb<u8>, data: &mut dyn Iterator<Item = u8>) -> Result<RgbImage, ImageError> {
278 let (offset_x, offset_y) = offset;
279 let mut x = offset_x;
280 let mut data = data.peekable();
281 while (x < img.width()) && (data.peek().is_some()) {
282 let v = data.next().unwrap_or(0);
283 let y = offset_y + v as u32;
284 let max_x = if (x + run) < img.width() { x + run } else { img.width() };
285 while x < max_x {
286 if y < img.height() {
287 img.put_pixel(x, offset_y + v as u32, color);
288 }
289 x += 1;
290 }
291 }
292 Ok(img)
293 }
294
295 fn apply_to_canvas<'a, F>(f: F, canvas_option: &'a mut Option<Canvas>, dimensions: Option<(u32, u32)>, data: &str) -> Result<&'a mut Canvas, CanvasError>
296 where F: Fn(Canvas, &mut dyn Iterator<Item = u8>) -> Result<Canvas, CanvasError>
297 {
298 let canvas = match canvas_option.take() {
299 Some(canvas) => match dimensions {
300 Some(dimensions) => {
301 if dimensions == canvas.img.dimensions() {
302 Ok(canvas)
303 }
304 else {
305 Err(CanvasError::DimensionMismatch(canvas_option.insert(canvas).img.dimensions()))
306 }
307 },
308 None =>
309 Ok(canvas)
310 },
311 None => match dimensions {
312 Some((dim_x, dim_y)) =>
313 Ok(Canvas::new(dim_x, dim_y)?),
314 None =>
315 Err(CanvasError::NotExists)
316 }
317 }?;
318
319 Ok(canvas_option.insert(f(canvas, percent_decode_str(data).into_iter().borrow_mut())?))
320 }
321
322 fn make_png<F>(dim_x: u32, dim_y: u32, scale: u32, data: &mut dyn Iterator<Item = u8>, encode: F) -> Result<Cursor<Vec<u8>>, ImageError>
323 where F: Fn(RgbImage, &mut dyn Iterator<Item = u8>) -> Result<RgbImage, ImageError>
324 {
325 // Image must not be larger than 1 megapixel
326 if dim_x * dim_y * scale > 1048576 {
327 return Err(ImageError::dimension_error())
328 }
329
330 let img: RgbImage = encode(ImageBuffer::new(dim_x, dim_y), data)?;
331 let cursor = image_to_cursor(img, scale)?;
332 Ok(cursor)
333 }
334
335 fn image_to_cursor(img: RgbImage, scale: u32) -> Result<Cursor<Vec<u8>>, ImageError> {
336 let (dim_x, dim_y) = img.dimensions();
337 let (dim_x, dim_y) = (dim_x * scale, dim_y * scale);
338
339 // Image must not be larger than 1 megapixel
340 if dim_x * dim_y > 1048576 {
341 return Err(ImageError::dimension_error())
342 }
343
344 let mut cursor = Cursor::new(Vec::new());
345 let img = resize(&img, dim_x, dim_y, FilterType::Nearest);
346 write_buffer_with_format(&mut cursor, &img, dim_x, dim_y, ColorType::Rgb8, Png)?;
347 Ok(cursor)
348 }
349
350
351 #[get("/gen/0/{data}")]
352 async fn img_gen0(req: HttpRequest) -> Result<impl Responder> {
353 let data = extract_data!(req, 3);
354 let cursor = make_png(32, 32, 16, percent_decode_str(data).into_iter().borrow_mut(), rgb_encode)?;
355 Ok(response_image!(cursor))
356 }
357
358 #[get("/gen/1/{dim_x}/{dim_y}/{scale}/{data}")]
359 async fn img_gen1(req: HttpRequest, path: web::Path<(u32, u32, u32)>) -> Result<impl Responder> {
360 let (dim_x, dim_y, scale) = path.into_inner();
361 let data = extract_data!(req, 6);
362 let cursor = make_png(dim_x, dim_y, scale, percent_decode_str(data).into_iter().borrow_mut(), rgb_encode)?;
363 Ok(response_image!(cursor))
364 }
365
366 #[get("/gen/2/{dim_x}/{dim_y}/{scale}")]
367 async fn img_gen2(req: HttpRequest, path: web::Path<(u32, u32, u32)>) -> Result<impl Responder> {
368 let (dim_x, dim_y, scale) = path.into_inner();
369 let data = extract_header!(req, "user-agent");
370 let cursor = make_png(dim_x, dim_y, scale, &mut data.bytes(), rgb_encode)?;
371 Ok(response_image!(cursor))
372 }
373
374 #[get("/gen/3/{scale}")]
375 async fn img_gen3(req: HttpRequest, path: web::Path<u32>) -> Result<impl Responder> {
376 let scale = path.into_inner();
377 let data = extract_header!(req, "user-agent");
378 let rgbimg: RgbImage = rgb_encode(ImageBuffer::new(TIMEAVATAR_SIZE_U32, TIMEAVATAR_SIZE_U32), &mut data.bytes())?;
379 let mut resimg: RgbImage = ImageBuffer::new(60 * TIMEAVATAR_SIZE_U32, 24 * TIMEAVATAR_SIZE_U32);
380 let time = OffsetDateTime::now_utc().time();
381 let (hour, minute): (i64, i64) = (time.hour().into(), time.minute().into());
382 overlay(&mut resimg, &rgbimg, minute * TIMEAVATAR_SIZE_I64, hour * TIMEAVATAR_SIZE_I64);
383 let cursor = image_to_cursor(resimg, scale)?;
384 Ok(response_image!(cursor))
385 }
386
387 #[get("/gen/4/{dim_x}/{dim_y}/{scale}/{wave_offset_x}/{wave_offset_y}/{amplitude}/{freq}/{run}/{wave_color}/{data}")]
388 #[allow(clippy::type_complexity)]
389 async fn img_gen4(req: HttpRequest, path: web::Path<(u32, u32, u32, u32, u32, f32, f32, u32)>) -> Result<impl Responder> {
390 let (dim_x, dim_y, scale, wave_offset_x, wave_offset_y, amplitude, freq_base, run) = path.into_inner();
391 let freq_base = freq_base / 1000.0;
392 let wave_color = extract_color!(req, 11)?;
393 let signal = &mut percent_decode_str(extract_data!(req, 12));
394 let cursor = make_png(dim_x, dim_y, scale, signal,
395 |img, signal| sine_encode(img, (wave_offset_x, wave_offset_y), amplitude, freq_base, run, wave_color, signal))?;
396 Ok(response_image!(cursor))
397 }
398
399 #[get("/gen/5/{dim_x}/{dim_y}/{scale}/{offset_x}/{offset_y}/{run}/{color}/{data}")]
400 async fn img_gen5(req: HttpRequest, path: web::Path<(u32, u32, u32, u32, u32, u32)>) -> Result<impl Responder> {
401 let (dim_x, dim_y, scale, offset_x, offset_y, run) = path.into_inner();
402 let color = extract_color!(req, 9)?;
403 let data = &mut percent_decode_str(extract_data!(req, 10));
404 let cursor = make_png(dim_x, dim_y, scale, data,
405 |img, data| offset_encode(img, (offset_x, offset_y), run, color, data))?;
406 Ok(response_image!(cursor))
407 }
408
409 #[get("/pgen/0/{data}")]
410 async fn img_pgen0(req: HttpRequest, canvas0: web::Data<Arc<Mutex<Canvas0>>>) -> Result<impl Responder> {
411 let data = extract_data!(req, 3);
412 let scale = 16;
413 let img: RgbImage = ({
414 let canvas_option = &mut *canvas0.lock().unwrap();
415 Ok(apply_to_canvas(rgb_encode_to_canvas, &mut canvas_option.0, Some((32, 32)), data)?.img.clone())
416 } as Result<RgbImage, CanvasError>)?;
417 let cursor = image_to_cursor(img, scale)?;
418 Ok(response_image!(cursor))
419 }
420
421 #[get("/pgen/1/{dim_x}/{dim_y}/{scale}/{data}")]
422 async fn img_pgen1(req: HttpRequest, path: web::Path<(u32, u32, u32)>, canvas1: web::Data<Arc<Mutex<Canvas1>>>) -> Result<impl Responder> {
423 let (dim_x, dim_y, scale) = path.into_inner();
424 let data = extract_data!(req, 6);
425 let img: RgbImage = ({
426 let canvas_option = &mut *canvas1.lock().unwrap();
427 Ok(apply_to_canvas(rgb_encode_to_canvas, &mut canvas_option.0, Some((dim_x, dim_y)), data)?.img.clone())
428 } as Result<RgbImage, CanvasError>)?;
429 let cursor = image_to_cursor(img, scale)?;
430 Ok(response_image!(cursor))
431 }
432
433 #[get("/pgen/2/{dim_x}/{dim_y}/{scale}")]
434 async fn img_pgen2(req: HttpRequest, path: web::Path<(u32, u32, u32)>, canvas1: web::Data<Arc<Mutex<Canvas1>>>) -> Result<impl Responder> {
435 let (dim_x, dim_y, scale) = path.into_inner();
436 let data = extract_header!(req, "user-agent");
437 let img: RgbImage = ({
438 let canvas_option = &mut *canvas1.lock().unwrap();
439 Ok(apply_to_canvas(rgb_encode_to_canvas, &mut canvas_option.0, Some((dim_x, dim_y)), data)?.img.clone())
440 } as Result<RgbImage, CanvasError>)?;
441 let cursor = image_to_cursor(img, scale)?;
442 Ok(response_image!(cursor))
443 }
444
445 #[get("/pgen/3/{scale}")]
446 async fn img_pgen3(req: HttpRequest, path: web::Path<u32>, canvas2: web::Data<Arc<Mutex<Canvas2>>>) -> Result<impl Responder> {
447 let scale = path.into_inner();
448 let data = extract_header!(req, "user-agent");
449 let time = OffsetDateTime::now_utc().time();
450 let (hour, minute): (i64, i64) = (time.hour().into(), time.minute().into());
451 let rgbimg: RgbImage = rgb_encode(ImageBuffer::new(TIMEAVATAR_SIZE_U32, TIMEAVATAR_SIZE_U32), &mut data.bytes())?;
452 let resimg: RgbImage = ({
453 let canvas_option = &mut *canvas2.lock().unwrap();
454 let mut canvas = match canvas_option.0.take() {
455 Some(canvas) => canvas,
456 None => Canvas::new(60 * TIMEAVATAR_SIZE_U32, 24 * TIMEAVATAR_SIZE_U32)?
457 };
458 overlay(&mut canvas.img, &rgbimg, minute * TIMEAVATAR_SIZE_I64, hour * TIMEAVATAR_SIZE_I64);
459 Ok(canvas_option.0.insert(canvas).img.clone())
460 } as Result<RgbImage, CanvasError>)?;
461 let cursor = image_to_cursor(resimg, scale)?;
462 Ok(response_image!(cursor))
463 }
464
465 #[get("/pgen/4/{dim_x}/{dim_y}/{scale}/{wave_offset_x}/{wave_offset_y}/{amplitude}/{freq}/{run}/{wave_color}/{data}")]
466 #[allow(clippy::type_complexity)]
467 async fn img_pgen4(req: HttpRequest, path: web::Path<(u32, u32, u32, u32, u32, f32, f32, u32)>,
468 canvas1: web::Data<Arc<Mutex<Canvas1>>>) -> Result<impl Responder>
469 {
470 let (dim_x, dim_y, scale, wave_offset_x, wave_offset_y, amplitude, freq_base, run) = path.into_inner();
471 let freq_base = freq_base / 1000.0;
472 let wave_color = extract_color!(req, 11)?;
473 let data = extract_data!(req, 12);
474 let img: RgbImage = ({
475 let canvas_option = &mut *canvas1.lock().unwrap();
476 Ok(apply_to_canvas(
477 |mut canvas, signal| {
478 canvas.img = sine_encode(canvas.img, (wave_offset_x, wave_offset_y), amplitude, freq_base, run, wave_color, signal)?;
479 Ok(canvas)
480 },
481 &mut canvas_option.0, Some((dim_x, dim_y)), data)?.img.clone())
482 } as Result<RgbImage, CanvasError>)?;
483 let cursor = image_to_cursor(img, scale)?;
484 Ok(response_image!(cursor))
485 }
486
487 #[get("/pgen/5/{dim_x}/{dim_y}/{scale}/{offset_x}/{offset_y}/{run}/{color}/{data}")]
488 async fn img_pgen5(req: HttpRequest, path: web::Path<(u32, u32, u32, u32, u32, u32)>,
489 canvas1: web::Data<Arc<Mutex<Canvas1>>>) -> Result<impl Responder>
490 {
491 let (dim_x, dim_y, scale, offset_x, offset_y, run) = path.into_inner();
492 let color = extract_color!(req, 9)?;
493 let data = extract_data!(req, 10);
494 let img: RgbImage = ({
495 let canvas_option = &mut *canvas1.lock().unwrap();
496 Ok(apply_to_canvas(
497 |mut canvas, data| {
498 canvas.img = offset_encode(canvas.img, (offset_x, offset_y), run, color, data)?;
499 Ok(canvas)
500 },
501 &mut canvas_option.0, Some((dim_x, dim_y)), data)?.img.clone())
502 } as Result<RgbImage, CanvasError>)?;
503 let cursor = image_to_cursor(img, scale)?;
504 Ok(response_image!(cursor))
505 }
506
507 #[get("/pdrop/{canvas_id}")]
508 async fn pdrop(canvas_id: web::Path<u8>,
509 canvas0: web::Data<Arc<Mutex<Canvas0>>>,
510 canvas1: web::Data<Arc<Mutex<Canvas1>>>,
511 canvas2: web::Data<Arc<Mutex<Canvas2>>>) -> Result<impl Responder> {
512
513 let canvas_id = canvas_id.into_inner();
514 match canvas_id {
515 0 => canvas0.lock().unwrap().0.take(),
516 1 => canvas1.lock().unwrap().0.take(),
517 2 => canvas2.lock().unwrap().0.take(),
518 _ => None
519 }.ok_or(CanvasError::NotExists)?;
520 Ok(HttpResponse::build(StatusCode::OK)
521 .body(format!("Canvas {} dropped.", canvas_id)))
522 }
523
524
525 #[actix_web::main] // or #[tokio::main]
526 async fn main() -> std::io::Result<()> {
527 env_logger::builder()
528 .filter_module("actix_web", LevelFilter::Info)
529 .parse_default_env()
530 .init();
531
532 let listenaddress = env::args().nth(1).unwrap_or_else(|| "127.0.0.1:8080".to_string());
533 let canvas0 = Arc::new(Mutex::new(Canvas0(None)));
534 let canvas1 = Arc::new(Mutex::new(Canvas1(None)));
535 let canvas2 = Arc::new(Mutex::new(Canvas2(None)));
536
537 HttpServer::new(move || {
538 App::new()
539 .wrap(Logger::default())
540 .service(greet)
541 .service(img_gen0)
542 .service(img_gen1)
543 .service(img_gen2)
544 .service(img_gen3)
545 .service(img_gen4)
546 .service(img_gen5)
547 .service(img_pgen0)
548 .service(img_pgen1)
549 .service(img_pgen2)
550 .service(img_pgen3)
551 .service(img_pgen4)
552 .service(img_pgen5)
553 .service(pdrop)
554 .app_data(web::Data::new(canvas0.clone()))
555 .app_data(web::Data::new(canvas1.clone()))
556 .app_data(web::Data::new(canvas2.clone()))
557 })
558 .bind(listenaddress)?
559 .run()
560 .await
561 }