1use crate::request::Request;
2use crate::websocket::Websocket;
3use crate::Protocol::HTTP2;
4use crate::Status;
5use crate::{ContentType, Encoding, Handler, HttpError, Method, Protocol, Upgrade};
6use chrono::{DateTime, Duration as dur, Utc};
7use hpack::Encoder;
8use json::{object, JsonValue};
9use log::{error, info};
10use std::io::Error;
11use std::path::{Path, PathBuf};
12use std::{fs, io, thread};
13const FLAG_END_STREAM: u8 = 0x01;
14const FLAG_END_HEADERS: u8 = 0x04;
15const DEFAULT_CACHE_MAX_AGE: u64 = 86400;
16#[derive(Clone, Debug)]
18pub struct Response {
19 pub request: Request,
20 pub status: Status,
21 pub headers: JsonValue,
22 pub cookies: JsonValue,
23 pub body: Vec<u8>,
25 pub stream_id: u32,
27 pub key: String,
29 pub version: String,
31 pub factory: fn(out: Websocket) -> Box<dyn Handler>,
32 pub allow_origins: Vec<&'static str>,
34 pub allow_methods: Vec<&'static str>,
36 pub allow_headers: Vec<&'static str>,
38 pub content_type: ContentType,
40}
41
42fn sanitize_header_value(value: &str) -> String {
43 value.chars().filter(|c| *c != '\r' && *c != '\n').collect()
44}
45
46impl Response {
47 pub fn new(request: &Request, factory: fn(out: Websocket) -> Box<dyn Handler>) -> Self {
48 Self {
49 request: request.clone(),
50 status: Status::default(),
51 headers: object! {},
52 cookies: object! {},
53 body: vec![],
54 stream_id: 0,
55 key: String::new(),
56 version: String::new(),
57 factory,
58 allow_origins: vec![],
59 allow_methods: vec![],
60 allow_headers: vec![],
61 content_type: ContentType::Other(String::new()),
62 }
63 }
64 pub fn handle(mut self) -> io::Result<()> {
65 match self.request.upgrade {
66 Upgrade::Websocket => {
67 self.handle_protocol_ws()?;
68 return Ok(());
69 }
70 Upgrade::Http | Upgrade::Other(_) => {}
71 Upgrade::H2c => {
72 let _ = self.handle_protocol_h2c();
73 }
74 }
75
76 match self.request.protocol {
77 Protocol::HTTP1_0 => self.handle_protocol_http0()?,
78 Protocol::HTTP1_1 => self.handle_protocol_http1()?,
79 Protocol::HTTP2 => self.handle_protocol_http2()?,
80 Protocol::HTTP3 | Protocol::Other(_) => {}
81 }
82 Ok(())
83 }
84 fn handle_protocol_http0(&mut self) -> io::Result<()> {
85 let websocket = Websocket::http(self.request.clone(), self.clone());
86 let mut factory = (self.factory)(websocket);
87 match self.request.method {
88 Method::OPTIONS => {
89 factory.on_options(self);
90 match self.on_options() {
91 Ok(()) => match self.status(200).send() {
92 Ok(()) => {}
93 Err(e) => {
94 return Err(Error::other(format!(
95 "1004: {} {} {}",
96 self.request.uri.path, e.code, e.body
97 )))
98 }
99 },
100 Err(e) => {
101 return match self.status(e.code).txt(e.body.as_str()).send() {
102 Ok(()) => Ok(()),
103 Err(e2) => Err(Error::other(format!(
104 "1005: {} {} {}",
105 self.request.uri.path, e2.code, e2.body
106 ))),
107 }
108 }
109 }
110 }
111 Method::GET => {
112 if let Ok(e) = self.read_resource() {
113 if self.request.header.has_key("range") {
114 return match self.status(206).range(&e).send() {
115 Ok(()) => Ok(()),
116 Err(e) => Err(Error::other(format!(
117 "1003: {} {} {}",
118 self.request.uri.path, e.code, e.body
119 ))),
120 };
121 }
122 return match self.status(200).file(&e).send() {
123 Ok(_) => Ok(()),
124 Err(e) => Err(Error::other(format!(
125 "1003: {} {} {}",
126 self.request.uri.path, e.code, e.body
127 ))),
128 };
129 }
130 factory.on_request(self.request.clone(), self);
131 factory.on_response(self);
132 match self.send() {
133 Ok(()) => {}
134 Err(e) => {
135 return Err(Error::other(format!(
136 "1002: {} {} {}",
137 self.request.uri.path, e.code, e.body
138 )))
139 }
140 }
141 }
142 _ => {
143 factory.on_request(self.request.clone(), self);
144 factory.on_response(self);
145 match self.send() {
146 Ok(_) => {}
147 Err(e) => return Err(Error::other(format!("1001: {} {}", e.code, e.body))),
148 }
149 }
150 }
151 Ok(())
152 }
153 fn handle_protocol_http1(&mut self) -> io::Result<()> {
154 self.handle_protocol_http0()
155 }
156 fn handle_protocol_http2(&mut self) -> io::Result<()> {
157 let websocket = Websocket::http(self.request.clone(), self.clone());
158 let mut factory = (self.factory)(websocket);
159
160 match self.request.method {
161 Method::OPTIONS => {
162 factory.on_options(self);
163 match self.send() {
164 Ok(_) => {}
165 Err(e) => return Err(Error::other(format!("2001: {} {}", e.code, e.body))),
166 };
167 }
168 Method::GET => {
169 if let Ok(e) = self.read_resource() {
170 return match self.status(200).file(&e).send() {
171 Ok(_) => Ok(()),
172 Err(e) => Err(Error::other(e.body)),
173 };
174 }
175 factory.on_request(self.request.clone(), self);
176 factory.on_response(self);
177 match self.send() {
178 Ok(()) => {}
179 Err(e) => return Err(Error::other(format!("2002: {} {}", e.code, e.body))),
180 }
181 }
182 _ => {
183 factory.on_request(self.request.clone(), self);
184 factory.on_response(self);
185 match self.send() {
186 Ok(_) => {}
187 Err(e) => return Err(Error::other(format!("2003: {} {}", e.code, e.body))),
188 }
189 }
190 }
191 Ok(())
192 }
193 fn handle_protocol_ws(&mut self) -> io::Result<()> {
194 let mut websocket = Websocket::new(self.request.clone(), self.clone());
195 let _ = websocket.handle();
196 Ok(())
197 }
198 fn handle_protocol_h2c(&mut self) -> Result<(), HttpError> {
199 self.header("Upgrade", "h2c");
200 self.header("Connection", "Upgrade");
201 match self.status(101).send() {
202 Ok(()) => self.headers.clear(),
203 Err(e) => return Err(e),
204 };
205 self.request.protocol = HTTP2;
206 self.request
207 .scheme
208 .lock()
209 .map_err(|e| HttpError::new(500, &format!("lock: {}", e)))?
210 .http2_send_server_settings()?;
211 let mut data = vec![];
212 self.request
213 .scheme
214 .lock()
215 .map_err(|e| HttpError::new(500, &format!("lock: {}", e)))?
216 .read(&mut data)?;
217
218 let t = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
219 if let Some(pos) = data.windows(t.len()).position(|window| window == t) {
220 let _ = data.drain(..pos).collect::<Vec<u8>>();
221 data.drain(..t.len());
222 } else {
223 return Err(HttpError::new(400, "请求行错误"));
224 }
225 let (_payload, _frame_type, flags, _stream_id) = self
226 .request
227 .scheme
228 .lock()
229 .map_err(|e| HttpError::new(500, &format!("lock: {}", e)))?
230 .http2_packet(&mut data)?;
231 if flags & 0x01 == 0 {
232 self.request
233 .scheme
234 .lock()
235 .map_err(|e| HttpError::new(500, &format!("lock: {}", e)))?
236 .http2_settings_ack()?;
237 }
238 let (_payload, _frame_type, _flags, _stream_id) = self
239 .request
240 .scheme
241 .lock()
242 .map_err(|e| HttpError::new(500, &format!("lock: {}", e)))?
243 .http2_packet(&mut data)?;
244 let (payload, _frame_type, _flags, _stream_id) = self
245 .request
246 .scheme
247 .lock()
248 .map_err(|e| HttpError::new(500, &format!("lock: {}", e)))?
249 .http2_packet(&mut data)?;
250 if payload.len() == 4 {
251 let raw = u32::from_be_bytes(
252 <[u8; 4]>::try_from(&payload[..4])
253 .map_err(|_| HttpError::new(400, "Invalid WindowUpdate frame data"))?,
254 );
255 let increment = raw & 0x7FFF_FFFF; if self.request.config.debug {
257 info!("WindowUpdate: increment = {} {:?}", increment, payload);
258 }
259 } else {
260 return Err(HttpError::new(
261 400,
262 format!("Invalid WindowUpdate frame length: {}", payload.len()).as_str(),
263 ));
264 }
265 Ok(())
266 }
267 fn read_resource(&mut self) -> io::Result<PathBuf> {
270 let sanitized_path = self.request.uri.path.trim_start_matches('/');
272 if sanitized_path.contains("..") {
273 return Err(Error::other(
274 "Invalid path: directory traversal not allowed",
275 ));
276 }
277
278 if self.request.uri.path != "/" {
279 let base_dir = self
280 .request
281 .config
282 .root_path
283 .join(self.request.config.public.clone());
284 let file = base_dir.join(sanitized_path);
285 if let (Ok(canonical_base), Ok(canonical_file)) =
287 (base_dir.canonicalize(), file.canonicalize())
288 {
289 if canonical_file.starts_with(&canonical_base) && file.is_file() {
290 return Ok(file);
291 }
292 }
293 }
294 if self.request.uri.path == "/" {
295 let file = self
296 .request
297 .config
298 .root_path
299 .join("webpage")
300 .join(self.request.config.webpage.clone())
301 .join("index.html");
302 if file.is_file() {
303 return Ok(file);
304 }
305 } else {
306 let base_dir = self
307 .request
308 .config
309 .root_path
310 .join("webpage")
311 .join(self.request.config.webpage.clone());
312 let file = base_dir.join(sanitized_path);
313 if let (Ok(canonical_base), Ok(canonical_file)) =
315 (base_dir.canonicalize(), file.canonicalize())
316 {
317 if canonical_file.starts_with(&canonical_base) && file.is_file() {
318 return Ok(file);
319 }
320 }
321 }
322 Err(Error::other("Not a file"))
323 }
324 pub fn status(&mut self, code: u16) -> &mut Self {
325 self.status.set_code(code);
326 self
327 }
328 pub fn location(&mut self, uri: &str) -> &mut Self {
330 self.header("Location", uri);
331 self
332 }
333 pub fn set_host(&mut self, host: &str) -> &mut Self {
335 self.header("Host", host);
336 self
337 }
338
339 fn sanitize_header_value(value: &str) -> String {
341 sanitize_header_value(value)
342 }
343
344 pub fn header(&mut self, key: &str, value: &str) -> &mut Self {
345 self.headers[key] = Self::sanitize_header_value(value).into();
346 self
347 }
348 pub fn cookie(&mut self, key: &str, value: &str) -> &mut Self {
349 self.cookies[key] = value.into();
350 self
351 }
352 pub fn cookie_with_options(&mut self, options: CookieOptions) -> &mut Self {
353 self.cookies[options.name.as_str()] = options.to_header_value().into();
354 self
355 }
356 fn get_date(&self, s: i64) -> String {
357 let utc: DateTime<Utc> = Utc::now();
358 let future = utc + dur::seconds(s); future.format("%a, %d %b %Y %H:%M:%S GMT").to_string()
360 }
361 pub fn html(&mut self, value: &str) -> &mut Self {
363 if self.request.config.charset.is_empty() {
364 self.header(
365 "Content-Type",
366 format!("{};", Extension::form("html").as_str()).as_str(),
367 );
368 } else {
369 self.header(
370 "Content-Type",
371 format!(
372 "{}; charset={}",
373 Extension::form("html").as_str(),
374 self.request.config.charset
375 )
376 .as_str(),
377 );
378 }
379 self.content_type = ContentType::Html;
380 self.body = value.as_bytes().to_vec();
381 self
382 }
383 pub fn txt(&mut self, value: &str) -> &mut Self {
385 if self.request.config.charset.is_empty() {
386 self.header(
387 "Content-Type",
388 Extension::form("txt").as_str().to_string().as_str(),
389 );
390 } else {
391 self.header(
392 "Content-Type",
393 format!(
394 "{}; charset={}",
395 Extension::form("txt").as_str(),
396 self.request.config.charset
397 )
398 .as_str(),
399 );
400 }
401 self.content_type = ContentType::Text;
402 self.body = value.as_bytes().to_vec();
403 self
404 }
405 pub fn json(&mut self, value: JsonValue) -> &mut Self {
407 if self.request.config.charset.is_empty() {
408 self.header(
409 "Content-Type",
410 Extension::form("json").as_str().to_string().as_str(),
411 );
412 } else {
413 self.header(
414 "Content-Type",
415 format!(
416 "{}; charset={}",
417 Extension::form("json").as_str(),
418 self.request.config.charset
419 )
420 .as_str(),
421 );
422 }
423 self.content_type = ContentType::Json;
424 self.body = value.to_string().into_bytes();
425 self
426 }
427 pub fn download(&mut self, filename: &Path) -> &mut Self {
429 let Ok(file) = fs::read(filename) else {
430 self.status(404);
431 return self;
432 };
433 let extension = filename
434 .extension()
435 .and_then(|e| e.to_str())
436 .unwrap_or("")
437 .to_lowercase();
438
439 if self.request.config.charset.is_empty() {
440 self.header(
441 "Content-Type",
442 Extension::form(extension.as_str())
443 .as_str()
444 .to_string()
445 .as_str(),
446 );
447 } else {
448 self.header(
449 "Content-Type",
450 format!(
451 "{}; charset={}",
452 Extension::form(extension.as_str()).as_str(),
453 self.request.config.charset
454 )
455 .as_str(),
456 );
457 }
458
459 let encoded_file_name = br_crypto::encoding::urlencoding_encode(
460 filename
461 .file_name()
462 .and_then(|n| n.to_str())
463 .unwrap_or("download"),
464 );
465 self.header(
466 "Content-Disposition",
467 format!(r"attachment; filename={encoded_file_name}").as_str(),
468 );
469 self.header("Cache-Control", "no-cache");
470 self.header("ETag", br_crypto::md5::encrypt_hex(&file).as_str());
471 self.body = file;
472 self
473 }
474 pub fn range(&mut self, filename: &Path) -> &mut Self {
476 let Ok(file) = fs::read(filename) else {
477 self.status(404);
478 return self;
479 };
480 let range = self.request.header["range"].to_string();
481 let range = range.trim_start_matches("bytes=");
482 let parts: Vec<&str> = range.split('-').collect();
483
484 let range_start = match parts.first().and_then(|s| s.parse::<usize>().ok()) {
485 Some(v) => v,
486 None => {
487 self.status(416);
488 self.header("Content-Range", format!("bytes */{}", file.len()).as_str());
489 return self;
490 }
491 };
492
493 let range_end = if parts.len() > 1 && !parts[1].is_empty() {
494 match parts[1].parse::<usize>() {
495 Ok(v) => v,
496 Err(_) => {
497 self.status(416);
498 self.header("Content-Range", format!("bytes */{}", file.len()).as_str());
499 return self;
500 }
501 }
502 } else {
503 file.len().saturating_sub(1)
504 };
505
506 if file.len() < range_end {
507 self.status(416);
508 self.header("Content-Range", format!("bytes */{}", file.len()).as_str());
509 return self;
510 }
511
512 let extension = filename
513 .extension()
514 .and_then(|e| e.to_str())
515 .unwrap_or("")
516 .to_lowercase();
517
518 if self.request.config.charset.is_empty() {
519 self.header(
520 "Content-Type",
521 Extension::form(extension.as_str())
522 .as_str()
523 .to_string()
524 .as_str(),
525 );
526 } else {
527 self.header(
528 "Content-Type",
529 format!(
530 "{}; charset={}",
531 Extension::form(extension.as_str()).as_str(),
532 self.request.config.charset
533 )
534 .as_str(),
535 );
536 }
537
538 self.header("Accept-Ranges", "bytes");
539 self.header(
540 "content-length",
541 (range_end - range_start + 1).to_string().as_str(),
542 );
543 self.header(
544 "Content-Range",
545 format!("bytes {}-{}/{}", range_start, range_end, file.len()).as_str(),
546 );
547 self.body = file[range_start..=range_end].to_vec();
548 self
549 }
550 pub fn file(&mut self, filename: &Path) -> &mut Self {
557 let Ok(file) = fs::read(filename) else {
558 self.status(404);
559 return self;
560 };
561
562 self.header("Access-Control-Expose-Headers", "Content-Disposition");
563 let extension = match filename.extension() {
564 None => String::new(),
565 Some(e) => e.to_str().unwrap_or("").to_lowercase(),
566 };
567 if self.request.config.charset.is_empty() {
568 self.header(
569 "Content-Type",
570 Extension::form(extension.as_str())
571 .as_str()
572 .to_string()
573 .as_str(),
574 );
575 } else {
576 self.header(
577 "Content-Type",
578 format!(
579 "{}; charset={}",
580 Extension::form(extension.as_str()).as_str(),
581 self.request.config.charset
582 )
583 .as_str(),
584 );
585 }
586
587 self.header(
588 "Content-Disposition",
589 format!(
590 r#"inline; filename="{}""#,
591 filename
592 .file_name()
593 .and_then(|n| n.to_str())
594 .unwrap_or("download")
595 )
596 .as_str(),
597 );
598
599 self.header("Accept-Ranges", "bytes");
600
601 self.header(
602 "Cache-Control",
603 format!("public, max-age={}", DEFAULT_CACHE_MAX_AGE).as_str(),
604 );
605 self.header("Expires", &self.get_date(DEFAULT_CACHE_MAX_AGE as i64));
606 self.body = file;
607 self
608 }
609 pub fn send(&mut self) -> Result<(), HttpError> {
615 match &self.request.protocol {
616 Protocol::HTTP1_0 | Protocol::HTTP1_1 => Ok(self.send_http1()?),
617 Protocol::HTTP2 => Ok(self.send_http2()?),
618 Protocol::HTTP3 => {
619 error!("Other1:{:?} {:?}", thread::current().id(), self.request);
620 Err(HttpError::new(500, "暂未实现HTTP3"))
621 }
622 Protocol::Other(e) => {
623 error!("Other:{:?} {:?}", thread::current().id(), self.request);
624 Err(HttpError::new(
625 500,
626 format!(
627 "暂未实现Other: {} {} {:?}",
628 e,
629 self.request.uri.path,
630 thread::current().id()
631 )
632 .as_str(),
633 ))
634 }
635 }
636 }
637 fn send_http1(&mut self) -> Result<(), HttpError> {
638 let mut header = vec![];
639 header.push(format!(
640 "{} {} {}",
641 self.request.protocol.str(),
642 self.status.code,
643 self.status.reason
644 ));
645 self.header("Date", &self.get_date(0));
646
647 if !self.headers.has_key("X-Content-Type-Options") {
648 self.header("X-Content-Type-Options", "nosniff");
649 }
650 if !self.headers.has_key("X-Frame-Options") {
651 self.header("X-Frame-Options", "SAMEORIGIN");
652 }
653 if !self.headers.has_key("X-XSS-Protection") {
654 self.header("X-XSS-Protection", "1; mode=block");
655 }
656
657 match self.request.method {
658 Method::HEAD => {
659 self.header("Content-Length", self.body.len().to_string().as_str());
660 self.body = vec![];
661 }
662 Method::OPTIONS => {
663 self.body = vec![];
664 }
665 _ => {
666 if self.status.code == 101 {
667 } else {
669 match self.request.accept_encoding {
670 Encoding::Gzip => {
671 self.header(
672 "Content-Encoding",
673 self.request.accept_encoding.clone().str(),
674 );
675 if let Ok(e) = self.request.accept_encoding.compress(&self.body) {
676 self.body = e
677 };
678 self.header("Content-Length", self.body.len().to_string().as_str());
679 }
680 _ => {
681 self.header("Content-Length", self.body.len().to_string().as_str());
682 }
683 }
684 }
685 }
686 };
687
688 for (key, value) in self.headers.entries() {
689 header.push(format!("{key}: {value}"));
690 }
691
692 for (key, value) in self.cookies.entries() {
693 header.push(format!(
694 "Set-Cookie: {key}={value}; Path=/; HttpOnly; SameSite=Lax"
695 ));
696 }
697
698 if self.request.config.debug {
699 info!("\r\n=================响应信息 {:?}=================\r\n{}\r\n===========================================",thread::current().id(),header.join("\r\n"));
700 match self.request.accept_encoding {
701 Encoding::Gzip => {}
702 _ => {
703 match self.content_type {
704 ContentType::Text
705 | ContentType::Html
706 | ContentType::Json
707 | ContentType::FormUrlencoded => {
708 info!("\r\n=================响应体 {:?}=================\r\n{}\r\n===========================================",thread::current().id(),String::from_utf8_lossy(self.body.as_slice()));
709 }
710 _ => {}
711 };
712 }
713 }
714 }
715 let mut headers = format!("{}\r\n\r\n", header.join("\r\n")).into_bytes();
716 headers.extend(self.body.clone());
717 self.request
718 .scheme
719 .lock()
720 .map_err(|e| HttpError::new(500, &format!("lock: {}", e)))?
721 .write_all(headers.as_slice())?;
722 Ok(())
723 }
724 fn send_http2(&mut self) -> Result<(), HttpError> {
725 let max_frame_size: usize = 16_384;
726 self.stream_id += 1;
727 let header_frames = self.http2_header(self.stream_id, max_frame_size);
729 let data_frames = self.http2_body(self.stream_id, true, max_frame_size);
730
731 let mut writer = self
733 .request
734 .scheme
735 .lock()
736 .map_err(|e| HttpError::new(500, &format!("lock: {}", e)))?;
737 for f in header_frames.clone() {
738 writer.write_all(&f)?;
739 }
740 for f in data_frames.clone() {
741 writer.write_all(&f)?;
742 }
743 Ok(())
744 }
745
746 fn http2_header(&mut self, stream_id: u32, max_frame_size: usize) -> Vec<Vec<u8>> {
747 let mut headers: Vec<(Vec<u8>, Vec<u8>)> = vec![
748 (
749 b":status".to_vec(),
750 self.status.code.to_string().into_bytes(),
751 ),
752 (b"date".to_vec(), self.get_date(0).into_bytes()),
753 ];
754
755 match self.request.method {
756 Method::HEAD => {
757 self.header("content-length", self.body.len().to_string().as_str());
758 self.body.clear(); }
760 Method::OPTIONS => {
761 self.body.clear();
762 self.header("content-length", "0");
763 }
764 _ => {
765 self.header("content-length", self.body.len().to_string().as_str());
766 }
767 }
768
769 headers.extend(self.headers.entries().map(|(k, v)| {
770 (
771 k.to_string().to_lowercase().into_bytes(),
772 v.to_string().into_bytes(),
773 )
774 }));
775 headers.extend(self.cookies.entries().map(|(k, v)| {
776 (
777 b"set-cookie".to_vec(),
778 format!("{k}={v}; Path=/; HttpOnly; SameSite=Lax").into_bytes(),
779 )
780 }));
781
782 if self.request.config.debug {
783 let dbg = headers
784 .iter()
785 .map(|(n, v)| {
786 format!(
787 "{}: {}",
788 String::from_utf8_lossy(n),
789 String::from_utf8_lossy(v)
790 )
791 })
792 .collect::<Vec<_>>()
793 .join("\r\n");
794 info!("\n=================响应信息 {:?}=================\n{}\n===========================================",
795 std::thread::current().id(), dbg);
796 }
797
798 let mut encoder = Encoder::new();
799 let block = encoder.encode(headers.iter().map(|h| (&h.0[..], &h.1[..])));
801
802 let end_stream = self.body.is_empty();
803 let mut frames = Vec::new();
804 let mut remaining = block.as_slice();
805 let mut first = true;
806
807 while !remaining.is_empty() {
808 let take = remaining.len().min(max_frame_size);
809 let (chunk, rest) = remaining.split_at(take);
810 remaining = rest;
811
812 let is_last = remaining.is_empty();
813 let mut flags = 0u8;
814 if is_last {
815 flags |= FLAG_END_HEADERS;
816 }
817 if is_last && end_stream {
818 flags |= FLAG_END_STREAM;
819 }
820
821 let mut f = Vec::with_capacity(9 + chunk.len());
823 let len = chunk.len();
824 f.push(((len >> 16) & 0xFF) as u8);
825 f.push(((len >> 8) & 0xFF) as u8);
826 f.push((len & 0xFF) as u8);
827 f.push(if first { 0x01 } else { 0x09 }); f.push(flags);
829 f.extend_from_slice(&(stream_id & 0x7FFF_FFFF).to_be_bytes());
830 f.extend_from_slice(chunk);
831
832 frames.push(f);
833 first = false;
834 }
835
836 if frames.is_empty() {
838 let mut f = vec![
839 0,
840 0,
841 0,
842 0x01,
843 FLAG_END_HEADERS | if end_stream { FLAG_END_STREAM } else { 0 },
844 0,
845 0,
846 0,
847 0,
848 ];
849 f[5..9].copy_from_slice(&(stream_id & 0x7FFF_FFFF).to_be_bytes());
850 frames.push(f);
851 }
852
853 frames
854 }
855 fn http2_body(
856 &mut self,
857 stream_id: u32,
858 end_stream: bool,
859 max_frame_size: usize,
860 ) -> Vec<Vec<u8>> {
861 if self.body.is_empty() {
862 return Vec::new();
863 }
864 let mut frames = Vec::new();
865 let mut off = 0usize;
866 let total = self.body.len();
867
868 while off < total {
869 let take = (total - off).min(max_frame_size);
870 let chunk = &self.body[off..off + take];
871 off += take;
872
873 let last = off == total;
874 frames.push(self.build_data_frame(stream_id, chunk, last && end_stream));
875 }
876 frames
877 }
878 fn build_data_frame(&self, stream_id: u32, payload: &[u8], end_stream: bool) -> Vec<u8> {
879 let len = payload.len();
880 let mut f = Vec::with_capacity(9 + len);
881 f.push(((len >> 16) & 0xFF) as u8);
882 f.push(((len >> 8) & 0xFF) as u8);
883 f.push((len & 0xFF) as u8);
884 f.push(0x00); f.push(if end_stream { FLAG_END_STREAM } else { 0 });
886 f.extend_from_slice(&(stream_id & 0x7FFF_FFFF).to_be_bytes());
887 if !payload.is_empty() {
888 f.extend_from_slice(payload);
889 }
890 f
891 }
892 fn on_options(&mut self) -> Result<(), HttpError> {
893 if self.allow_origins.is_empty() {
894 self.header("Access-Control-Allow-Origin", "*");
895 } else if self.request.header.has_key("origin") {
896 if self.allow_origins.contains(&self.request.origin.as_str()) {
897 self.header(
898 "Access-Control-Allow-Origin",
899 self.request.origin.to_string().as_str(),
900 );
901 } else {
902 return Err(HttpError::new(403, "Origin not allowed"));
903 }
904 } else {
905 return Err(HttpError::new(403, "Origin not allowed"));
906 }
907
908 if self.allow_headers.is_empty() {
909 if !self
910 .request
911 .header
912 .has_key("access-control-request-headers")
913 {
914 return Err(HttpError::new(403, "headers not allowed"));
915 }
916 self.header(
917 "Access-Control-Allow-Headers",
918 self.request.header["access-control-request-headers"]
919 .to_string()
920 .as_str(),
921 );
922 } else if !self
923 .request
924 .header
925 .has_key("access-control-request-headers")
926 {
927 let headers = self.allow_headers.join(",");
928 self.header("Access-Control-Allow-Headers", headers.to_string().as_str());
929 } else {
930 let headers = self.allow_headers.join(",");
931 self.header(
932 "Access-Control-Allow-Headers",
933 format!(
934 "{},{}",
935 self.request.header["access-control-request-headers"], headers
936 )
937 .as_str(),
938 );
939 }
940
941 if self.allow_methods.is_empty() {
942 if !self.request.header.has_key("access-control-request-method") {
943 return Err(HttpError::new(403, "methods not allowed"));
944 }
945 self.header(
946 "Access-Control-Allow-Methods",
947 self.request.header["access-control-request-method"]
948 .to_string()
949 .as_str(),
950 );
951 } else {
952 let methods = self.allow_methods.join(",");
953 self.header("Access-Control-Allow-Methods", methods.to_string().as_str());
954 }
955 self.header(
956 "Vary",
957 "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
958 );
959 Ok(())
960 }
961}
962
963enum Extension {}
965impl Extension {
966 pub fn form(extension: &str) -> String {
967 match extension.to_lowercase().as_str() {
968 "html" | "htm" => "text/html",
969 "css" => "text/css",
970 "js" | "mjs" => "application/javascript",
971 "json" => "application/json",
972 "xml" => "application/xml",
973 "txt" => "text/plain",
974 "csv" => "text/csv",
975 "md" => "text/markdown",
976
977 "png" => "image/png",
978 "jpg" | "jpeg" => "image/jpeg",
979 "gif" => "image/gif",
980 "svg" => "image/svg+xml",
981 "webp" => "image/webp",
982 "ico" => "image/x-icon",
983 "bmp" => "image/bmp",
984 "tiff" | "tif" => "image/tiff",
985 "avif" => "image/avif",
986
987 "mp4" => "video/mp4",
988 "webm" => "video/webm",
989 "avi" => "video/x-msvideo",
990 "mov" => "video/quicktime",
991 "mkv" => "video/x-matroska",
992
993 "mp3" => "audio/mpeg",
994 "wav" => "audio/wav",
995 "ogg" => "audio/ogg",
996 "flac" => "audio/flac",
997 "aac" => "audio/aac",
998
999 "woff" => "font/woff",
1000 "woff2" => "font/woff2",
1001 "ttf" => "font/ttf",
1002 "otf" => "font/otf",
1003 "eot" => "application/vnd.ms-fontobject",
1004
1005 "pdf" => "application/pdf",
1006 "doc" => "application/msword",
1007 "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1008 "xls" => "application/vnd.ms-excel",
1009 "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1010 "ppt" => "application/vnd.ms-powerpoint",
1011 "pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
1012
1013 "zip" => "application/zip",
1014 "rar" => "application/vnd.rar",
1015 "7z" => "application/x-7z-compressed",
1016 "tar" => "application/x-tar",
1017 "gz" => "application/gzip",
1018
1019 "wasm" => "application/wasm",
1020 "map" => "application/json",
1021 _ => "application/octet-stream",
1022 }
1023 .to_string()
1024 }
1025}
1026
1027#[derive(Clone, Debug)]
1028pub struct CookieOptions {
1029 pub name: String,
1030 pub value: String,
1031 pub path: Option<String>,
1032 pub domain: Option<String>,
1033 pub max_age: Option<i64>,
1034 pub expires: Option<String>,
1035 pub secure: bool,
1036 pub http_only: bool,
1037 pub same_site: SameSite,
1038}
1039
1040impl Default for CookieOptions {
1041 fn default() -> Self {
1042 Self {
1043 name: String::new(),
1044 value: String::new(),
1045 path: None,
1046 domain: None,
1047 max_age: None,
1048 expires: None,
1049 secure: true,
1050 http_only: false,
1051 same_site: SameSite::default(),
1052 }
1053 }
1054}
1055
1056#[derive(Clone, Debug, Default)]
1057pub enum SameSite {
1058 Strict,
1059 #[default]
1060 Lax,
1061 None,
1062}
1063
1064impl CookieOptions {
1065 #[must_use]
1066 pub fn new(name: &str, value: &str) -> Self {
1067 Self {
1068 name: name.to_string(),
1069 value: value.to_string(),
1070 path: Some("/".to_string()),
1071 http_only: true,
1072 same_site: SameSite::Lax,
1073 ..Default::default()
1074 }
1075 }
1076
1077 pub fn path(mut self, path: &str) -> Self {
1078 self.path = Some(path.to_string());
1079 self
1080 }
1081
1082 pub fn domain(mut self, domain: &str) -> Self {
1083 self.domain = Some(domain.to_string());
1084 self
1085 }
1086
1087 pub fn max_age(mut self, seconds: i64) -> Self {
1088 self.max_age = Some(seconds);
1089 self
1090 }
1091
1092 pub fn expires(mut self, date: &str) -> Self {
1093 self.expires = Some(date.to_string());
1094 self
1095 }
1096
1097 pub fn secure(mut self, secure: bool) -> Self {
1098 self.secure = secure;
1099 self
1100 }
1101
1102 pub fn http_only(mut self, http_only: bool) -> Self {
1103 self.http_only = http_only;
1104 self
1105 }
1106
1107 pub fn same_site(mut self, same_site: SameSite) -> Self {
1108 self.same_site = same_site;
1109 self
1110 }
1111
1112 #[must_use]
1113 pub fn to_header_value(&self) -> String {
1114 let mut parts = vec![format!(
1115 "{}={}",
1116 sanitize_header_value(&self.name),
1117 sanitize_header_value(&self.value)
1118 )];
1119
1120 if let Some(ref path) = self.path {
1121 parts.push(format!("Path={path}"));
1122 }
1123 if let Some(ref domain) = self.domain {
1124 parts.push(format!("Domain={domain}"));
1125 }
1126 if let Some(max_age) = self.max_age {
1127 parts.push(format!("Max-Age={max_age}"));
1128 }
1129 if let Some(ref expires) = self.expires {
1130 parts.push(format!("Expires={expires}"));
1131 }
1132 if self.secure {
1133 parts.push("Secure".to_string());
1134 }
1135 if self.http_only {
1136 parts.push("HttpOnly".to_string());
1137 }
1138 match self.same_site {
1139 SameSite::Strict => parts.push("SameSite=Strict".to_string()),
1140 SameSite::Lax => parts.push("SameSite=Lax".to_string()),
1141 SameSite::None => parts.push("SameSite=None".to_string()),
1142 }
1143
1144 parts.join("; ")
1145 }
1146}