├── .gitignore ├── files ├── .gitignore ├── html │ ├── image.jpg │ ├── favicon.ico │ ├── foyue │ │ ├── css │ │ │ └── styles.css │ │ ├── js │ │ │ ├── main.js │ │ │ ├── popper.min.js │ │ │ └── crypto-js.min.js │ │ └── foyue.html │ ├── video.html │ ├── php │ │ └── time.php │ └── index.html ├── config.toml └── get.txt ├── .idea ├── vcs.xml ├── .gitignore ├── modules.xml └── webserver.iml ├── Cargo.toml ├── src ├── exception.rs ├── cache.rs ├── config.rs ├── request.rs ├── util.rs ├── param.rs ├── main.rs └── response.rs ├── LICENSE ├── log4rs.yaml ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /files/.gitignore: -------------------------------------------------------------------------------- 1 | log 2 | video.mkv -------------------------------------------------------------------------------- /files/html/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eslzzyl/webserver/HEAD/files/html/image.jpg -------------------------------------------------------------------------------- /files/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eslzzyl/webserver/HEAD/files/html/favicon.ico -------------------------------------------------------------------------------- /files/config.toml: -------------------------------------------------------------------------------- 1 | www_root = "./files/html/" 2 | port = 7878 3 | worker_threads = 0 4 | cache_size = 10 5 | local = true 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /files/html/foyue/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | padding-top: 50px; 4 | } 5 | 6 | .button-cen { 7 | text-align: center; 8 | padding-top: 5px; 9 | padding-bottom: 5px; 10 | } 11 | 12 | .container{ 13 | text-align: center; 14 | } 15 | 16 | .footer { 17 | background-color: #f5f5f5; 18 | } 19 | 20 | .lowroe{ 21 | padding-bottom: 10px; 22 | } 23 | 24 | .leftb{ 25 | text-align: left; 26 | } 27 | 28 | .rightb{ 29 | text-align: right; 30 | } -------------------------------------------------------------------------------- /.idea/webserver.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "webserver" 3 | version = "0.1.0" 4 | authors = ["Eslzzyl "] 5 | edition = "2021" 6 | description = "A toy webserver based on Rust" 7 | repository = "https://github.com/Eslzzyl/webserver" 8 | license = "MIT" 9 | publish = false # 这个程序过于简陋,不应被发布到crates.io 10 | 11 | [profile.release] 12 | lto = true 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | brotli = "3.5.0" 18 | bytes = "1.6.0" 19 | chrono = "0.4.35" 20 | flate2 = "1.0.28" 21 | lazy_static = "1.4.0" 22 | log = "0.4.21" 23 | log4rs = "1.3.0" 24 | num_cpus = "1.16.0" 25 | regex = "1.10.4" 26 | serde = "1.0.197" 27 | serde_derive = "1.0.197" 28 | tokio = { version = "1.36.0", features = ["full"] } 29 | toml = "0.8.12" 30 | -------------------------------------------------------------------------------- /files/html/video.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Welcome to Eslzzyl's webserver! 8 | 21 | 22 | 23 | 24 |
25 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /src/exception.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Debug, Copy, Clone)] 4 | pub enum Exception { 5 | RequestIsNotUtf8, 6 | UnSupportedRequestMethod, 7 | UnsupportedHttpVersion, 8 | FileNotFound, 9 | PHPExecuteFailed, 10 | PHPCodeError, 11 | } 12 | 13 | use Exception::*; 14 | 15 | impl fmt::Display for Exception { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | match self { 18 | RequestIsNotUtf8 => write!(f, "Request bytes can't be parsed in UTF-8"), 19 | UnSupportedRequestMethod => write!(f, "Unsupported request method"), 20 | UnsupportedHttpVersion => write!(f, "Unsupported HTTP version"), 21 | FileNotFound => write!(f, "File not found (404)"), 22 | PHPExecuteFailed => write!(f, "Couldn't invoke PHP interpreter"), 23 | PHPCodeError => write!(f, "An error happened in php code"), 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /files/html/php/time.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PHP基本功能示例 5 | 20 | 21 | 22 |

PHP基本功能示例

23 | 24 |
25 | 当前日期和时间:$currentDateTime

"; 29 | phpinfo(); // 写在报告里 30 | 31 | // 计算两个数字的和 32 | $num1 = 10; 33 | $num2 = 5; 34 | $sum = $num1 + $num2; 35 | echo "

两个数字的和:$sum

"; 36 | ?> 37 |
38 | 39 | -------------------------------------------------------------------------------- /files/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Welcome to Eslzzyl's webserver! 8 | 15 | 16 | 17 | 18 |

Welcome to Eslzzyl's webserver!

19 | 20 |

If you see this page, the webserver is successfully setup and 21 | working.

22 | 23 |

Use 与佛论禅 at here.

24 | 25 |

View an image at here.

26 | 27 |

Watch a short video at here.

28 | 29 |

View a PHP test page at here.

30 | 31 |

Thank you for using this server.

32 | 33 | 34 | -------------------------------------------------------------------------------- /files/get.txt: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host: localhost 3 | Proxy-Connection: Keep-Alive 4 | X-Request-ID: cd4ff5fbc1bdb64c7cbfca2c787e123f 5 | X-Real-IP: 155.94.194.41 6 | X-Forwarded-For: 155.94.194.41 7 | X-Forwarded-Host: eslzzyl-opulent-bassoon-q6rqqxv7g47399jv-7878.preview.app.github.dev 8 | X-Forwarded-Port: 443 9 | X-Forwarded-Proto: https 10 | X-Original-URI: / 11 | X-Scheme: https 12 | sec-ch-ua: "Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111" 13 | sec-ch-ua-mobile: ?0 14 | sec-ch-ua-platform: "Windows" 15 | upgrade-insecure-requests: 1 16 | user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.62 17 | accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 18 | sec-fetch-site: same-site 19 | sec-fetch-mode: navigate 20 | sec-fetch-user: ?1 21 | sec-fetch-dest: document 22 | referer: https://eslzzyl-opulent-bassoon-q6rqqxv7g47399jv.github.dev/ 23 | accept-encoding: gzip, deflate, br 24 | accept-language: zh-CN,zh;q=0.9,en;q=0 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Eslzzyl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /log4rs.yaml: -------------------------------------------------------------------------------- 1 | # 检查配置文件变动的时间间隔 2 | refresh_rate: 30 seconds 3 | # appender 负责将日志收集到控制台或文件, 可配置多个 4 | appenders: 5 | stdout: 6 | kind: console 7 | encoder: 8 | # log 信息模式,pattern指的是纯文本输出 9 | # https://docs.rs/log4rs/latest/log4rs/encode/pattern/index.html 10 | # d表示时间,下面这种时间设置可以产生类似2016-03-20 14:22:20的时间,时区为本地时区。 11 | # t表示这条日志的target 12 | # m表示日志信息 13 | # n表示平台无关的换行 14 | pattern: "{d(%Y-%m-%d %H:%M:%S)} {h([{l}])} {t} - {m}{n}" 15 | log_file: 16 | kind: rolling_file # 循环写入配置文件 17 | policy: 18 | kind: compound # 默认值, 即使用所有 policy 19 | trigger: # 当文件超过1MB 时触发 rotate 20 | kind: size 21 | limit: 1mb 22 | roller: # rotate 类型 23 | kind: delete # 直接原有文件 24 | path: "files/log/app.log" 25 | append: true # 追加模式, 即每次在已有文件末尾添加日志, 默认为 true 26 | encoder: 27 | pattern: "{d(%Y-%m-%d %H:%M:%S)} {l} {t} - {m}{n}" 28 | # 对全局 log 进行配置 29 | root: 30 | level: info 31 | appenders: 32 | - stdout 33 | - log_file -------------------------------------------------------------------------------- /src/cache.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use bytes::Bytes; 4 | 5 | /// ### 文件缓存 FileCache 6 | /// 7 | /// 能够容纳一定量的文件的缓存,供Response使用 8 | pub struct FileCache { 9 | cache: HashMap, 10 | capacity: usize, // 最大容纳的缓存数 11 | size: usize, // 当前缓存数 12 | first: String, 13 | } 14 | 15 | impl FileCache { 16 | /// 通过指定缓存大小来创建一个新的缓存 17 | pub fn from_capacity(capacity: usize) -> Self { 18 | if capacity == 0 { 19 | panic!("调用from_capacity时指定的大小是0。如果需要自动设置大小,请在调用处进行处理,而不是传入0"); 20 | } 21 | Self { 22 | cache: HashMap::new(), 23 | capacity, 24 | size: 0, 25 | first: String::new(), 26 | } 27 | } 28 | 29 | /// 将一段数据放入缓存。 30 | /// 31 | /// - 如果缓存已满,则替换掉最早进入缓存的数据 32 | /// - 如果缓存未满,则直接放入 33 | pub fn push(&mut self, filename: &str, bytes: Bytes) { 34 | let filename_str = filename.to_string(); 35 | // 已达到最大容量,替换掉最旧的缓存记录 36 | if self.size == self.capacity { 37 | self.cache.remove(&self.first); 38 | self.first = filename_str.clone(); 39 | } else { 40 | self.size += 1; 41 | } 42 | self.cache.insert(filename_str, bytes); 43 | } 44 | 45 | /// 在缓存中查找数据 46 | /// 47 | /// ## 参数: 48 | /// - `filename`:文件名,也是缓存的key。 49 | pub fn find(&self, filename: &str) -> Option<&Bytes> { 50 | self.cache.get(filename) 51 | } 52 | } -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use serde_derive::Deserialize; 2 | use serde_derive::Serialize; 3 | use num_cpus; 4 | 5 | use std::fs::File; 6 | use std::io::prelude::*; 7 | use core::str; 8 | use log::{error, warn}; 9 | 10 | /// Config 11 | /// 12 | /// 储存服务器需要的配置信息 13 | /// 14 | /// - `www_root`: 服务器的Web根路径 15 | /// - `port`: 要绑定的本机端口 16 | /// - `worker_threads`: Tokio的工作线程数量。设置为`0`以使程序自动确定工作线程数量。默认值为CPU的核心数。 17 | /// - `cache_size`: 文件缓存的大小,即能够容纳多少个文件。 18 | /// - `local`: 是否工作在内网。 19 | /// - 如果设置为`true`,则监听IP是`127.0.0.1` 20 | /// - 如果设置为`false`,则监听IP是`0.0.0.0` 21 | #[derive(Serialize, Deserialize, Debug, Clone)] 22 | pub struct Config { 23 | www_root: String, 24 | port: u16, 25 | worker_threads: usize, 26 | cache_size: usize, 27 | local: bool, 28 | } 29 | 30 | impl Config { 31 | /// 产生一个默认的`Config`对象 32 | pub fn new() -> Self { 33 | Self { 34 | www_root: ".".to_string(), 35 | port: 7878, 36 | worker_threads: 0, 37 | cache_size: 5, 38 | local: true, 39 | } 40 | } 41 | 42 | /// 通过TOML文件产生配置 43 | /// 44 | /// ## 参数: 45 | /// - `filename`: TOML文件的路径 46 | pub fn from_toml(filename: &str) -> Self { 47 | // 打开文件 48 | let mut file = match File::open(filename) { 49 | Ok(f) => f, 50 | Err(e) => panic!("no such file {} exception:{}", filename, e) 51 | }; 52 | // 读文件到str 53 | let mut str_val = String::new(); 54 | match file.read_to_string(&mut str_val) { 55 | Ok(s) => s, 56 | Err(e) => panic!("Error Reading file: {}", e) 57 | }; 58 | 59 | // 尝试读配置文件,若成功则返回,若失败则返回默认值 60 | let mut raw_config = match toml::from_str(&str_val) { 61 | Ok(t) => t, 62 | Err(_) => { 63 | error!("无法成功从配置文件构建配置对象,使用默认配置"); 64 | Config::new() 65 | } 66 | }; 67 | // config要求自动确定worker threads数量,使用当前cpu核心数量 68 | if raw_config.worker_threads == 0 { 69 | raw_config.worker_threads = num_cpus::get(); 70 | } 71 | if raw_config.cache_size == 0 { 72 | warn!("cache_size被设置为0,但目前尚不支持禁用缓存,因此该值将被改为5。"); 73 | raw_config.cache_size = 5; 74 | } 75 | raw_config 76 | } 77 | } 78 | 79 | impl Config { 80 | /// 获取 WWW root 81 | pub fn www_root(&self) -> &str { 82 | &self.www_root 83 | } 84 | 85 | /// 获取监听端口号 86 | pub fn port(&self) -> u16 { 87 | self.port 88 | } 89 | 90 | /// 获取工作线程数量 91 | pub fn worker_threads(&self) -> usize { 92 | self.worker_threads 93 | } 94 | 95 | /// 获取缓存大小 96 | pub fn cache_size(&self) -> usize { 97 | self.cache_size 98 | } 99 | 100 | /// 检查服务器是否工作在内网 101 | pub fn local(&self) -> bool { 102 | self.local 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/request.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | exception::Exception, 3 | param::*, 4 | }; 5 | 6 | use log::error; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct Request { 10 | method: HttpRequestMethod, 11 | path: String, 12 | version: HttpVersion, 13 | user_agent: String, 14 | accept_encoding: Vec, // 压缩编码,可以支持多种编码,如果该vec为空说明不支持压缩 15 | } 16 | 17 | impl Request { 18 | /// 尝试通过字节流解析Request 19 | /// 20 | /// ## 参数: 21 | /// - `buffer`: 来自客户浏览器的请求报文,用字节流表示 22 | pub fn try_from(buffer: &Vec, id: u128) -> Result { 23 | let request_string = match String::from_utf8(buffer.to_vec()) { 24 | Ok(string) => string, 25 | Err(_) => { 26 | error!("[ID{}]无法解析HTTP请求", id); 27 | return Err(Exception::RequestIsNotUtf8); 28 | } 29 | }; 30 | 31 | // 以CRLF为边界分割字符串 32 | let request_lines: Vec<&str> = request_string.split(CRLF).collect(); 33 | 34 | // 然后再以空格分割首行 35 | let first_line: Vec<&str> = request_lines[0].split(" ").collect(); 36 | let method_str = first_line[0].to_uppercase(); 37 | let method = match method_str.as_str() { 38 | "GET" => HttpRequestMethod::Get, 39 | "HEAD" => HttpRequestMethod::Head, 40 | "OPTIONS" => HttpRequestMethod::Options, 41 | "POST" => HttpRequestMethod::Post, 42 | _ => { 43 | error!("[ID{}]不支持的HTTP请求方法:{}", id, &method_str); 44 | return Err(Exception::UnSupportedRequestMethod); 45 | } 46 | }; 47 | let path = first_line[1].to_string(); 48 | let version_str = first_line[2].to_uppercase(); 49 | let version = match version_str.as_str() { 50 | // 当前只支持1.1 51 | r"HTTP/1.1" => HttpVersion::V1_1, 52 | _ => { 53 | error!("[ID{}]不支持的HTTP协议版本:{}", id, &version_str); 54 | return Err(Exception::UnsupportedHttpVersion); 55 | } 56 | }; 57 | 58 | // 确定剩余字段 59 | let mut user_agent = "".to_string(); 60 | let mut accept_encoding = vec!(); 61 | for line in &request_lines { 62 | // 确定user-agent,注意 HTTP请求头大小写不敏感 63 | if line.starts_with("user-agent") || line.starts_with("User-Agent") { 64 | user_agent = line.split(": ").collect::>()[1].to_string(); 65 | break; 66 | } 67 | } 68 | 69 | for line in &request_lines { 70 | // 确定accept-encoding,即浏览器能接受的压缩编码 71 | if line.starts_with("accept-encoding") || line.starts_with("Accept-Encoding") { 72 | let encoding = line.split(": ").collect::>()[1]; 73 | if encoding.contains("gzip") { 74 | accept_encoding.push(HttpEncoding::Gzip); 75 | } 76 | if encoding.contains("deflate") { 77 | accept_encoding.push(HttpEncoding::Deflate); 78 | } 79 | if encoding.contains("br") { 80 | accept_encoding.push(HttpEncoding::Br); 81 | } 82 | break; 83 | } 84 | } 85 | 86 | Ok(Self { 87 | method, 88 | path, 89 | version, 90 | user_agent, 91 | accept_encoding, 92 | }) 93 | } 94 | } 95 | 96 | impl Request { 97 | /// 返回请求的HTTP协议版本 98 | pub fn version(&self) -> &HttpVersion { 99 | &self.version 100 | } 101 | 102 | /// 返回当前Request的请求路径 103 | pub fn path(&self) -> &str { 104 | &self.path 105 | } 106 | 107 | /// 返回请求的方法 108 | pub fn method(&self) -> HttpRequestMethod { 109 | self.method 110 | } 111 | 112 | /// 返回当前Request的User-Agent 113 | pub fn user_agent(&self) -> &str { 114 | &self.user_agent 115 | } 116 | 117 | /// 返回当前浏览器接受的压缩编码 118 | pub fn accept_encoding(&self) -> &Vec { 119 | &self.accept_encoding 120 | } 121 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 基于 Rust 的 Web 服务器 2 | 3 | 这是合肥工业大学宣城校区 2023 年《计算机网络课程设计》项目。题目如下: 4 | 5 | > ### 设计目的 6 | > 7 | > 1. 熟悉开发工具 (Visual Studio、C/C++、Java 等) 的基本操作; 8 | > 2. 掌握 http 协议的工作原理; 9 | > 3. 掌握多线程编程; 10 | > 4. 对于 Socket 编程建立初步的概念。 11 | > 5. 掌握对文件的网络传输操作; 12 | > 13 | > ### 设计要求 14 | > 15 | > 1. 不限平台,熟悉 Socket API 主要函数的使用; 16 | > 2. 实现一个简单的基于 http 协议的 WEB 服务器; 17 | > 3. 实现对服务器运行状态的监控; 18 | > 19 | > ### 设计内容 20 | > 21 | > 请注意: 22 | > 23 | > 1. 此处 Web 服务器,只是对 HTTP 请求予以应答;IE 浏览器访问本服务器,请求当前服务器中的某静态网页文件(html 或 htm 文件等),服务器端查询服务器端相应的路径下该网页是否存在,如存在,则利用当前的 TCP 连接传递该网页文件,如果不存在,则返回 404 错误提示。 24 | > 2. 不涉及动态网页的解析,如 `asp`、`aspx`、`php`、`jsp` 等; 25 | > 3. 应考虑服务器的多客户端访问问题,参见:多线程机制、异步的套接字I/O机制或套接字链表等等; 26 | > 27 | > ### 思考题 28 | > 29 | > 1. 该服务器的工作模式是什么? 30 | > 2. 如何对其进行测试,还有哪些可以完善的功能? 31 | > 3. 有什么办法可以提高它的性能? 32 | 33 | **本设计已在课程设计验收中获评“优秀”。** 34 | 35 | 课程设计报告可以在 [此处](https://eslzzyl.lanzoum.com/iJquZ10ksheb) 下载。有问题可以发 issue。 36 | 37 | ## 功能 / Features 38 | 39 | - 基于 [Tokio](https://tokio.rs/) 实现 TCP 连接的异步并发处理 40 | - 手动解析 HTTP 请求,手动构造 HTTP 响应 41 | - 支持 HTTP 的 GET、HEAD 请求,部分地支持 OPTIONS 请求(不支持CORS的预检请求) 42 | - 支持 HTTP 1.1 43 | - 支持 HTTP 压缩,支持的编码有 Brotli, Gzip, Deflate 44 | - 通过 MIME 表支持常见的 Web 格式 45 | - 支持简单的命令行控制 46 | - 支持通过配置文件修改服务器参数 47 | - 通过 [log4rs](https://github.com/estk/log4rs) 支持简单的日志系统,支持记录到文件或标准输出 48 | - 通过一个 FIFO 的文件缓存减少磁盘 I/O 的次数 49 | - 支持文件列表模式(课程设计加分点) 50 | - 支持超链接跳转 51 | - 文件列表自动排序 52 | - 表格排版,清晰易读 53 | - 状态码页面动态生成 54 | - 简单的 PHP 页面支持(课程设计主要加分点) 55 | 56 | 各种请求方法的测试: 57 | - GET:使用浏览器测试即可 58 | - HEAD 59 | ```bash 60 | eslzzyl:~$ curl --head 127.0.0.1:7878/ -i 61 | HTTP/1.1 200 OK 62 | Content-Length: 858 63 | Date: Mon, 19 Jun 2023 09:38:16 +0000 64 | Server: eslzzyl-webserver 65 | ``` 66 | - OPTIONS 67 | ```bash 68 | eslzzyl:~$ curl -X OPTIONS 127.0.0.1:7878 -i 69 | HTTP/1.1 204 No Content 70 | Content-Length: 0 71 | Date: Mon, 19 Jun 2023 09:22:51 +0000 72 | Server: eslzzyl-webserver 73 | Allow: GET, HEAD, OPTIONS 74 | ``` 75 | 76 | ### 构建 / Build 77 | 78 | 安装最新的 Rust stable 工具链:[此处](https://www.rust-lang.org/learn/get-started)。我在编写代码时使用的版本是`1.69.0`。 79 | 80 | Clone 本仓库,然后执行 81 | 82 | ```bash 83 | cargo build --release 84 | ``` 85 | 86 | 在构建之前,如果需要,可以修改 crates.io 的索引以加快依赖下载。见[此处](https://mirrors.tuna.tsinghua.edu.cn/help/crates.io-index/)。 87 | 88 | ### 运行 / Run 89 | 90 | 1. 安装 PHP 环境。在 Ubuntu 下,执行 91 | 92 | ```bash 93 | sudo apt install php 94 | ``` 95 | 96 | - 在其他系统(如 Windows)中,可能需要手动配置环境变量。 97 | - PHP 不是必要的,但是没有 PHP 环境则无法使用 PHP 扩展,服务器将返回 500 状态码。 98 | 99 | 2. 启动服务器 100 | 101 | ```bash 102 | cargo run --release 103 | ``` 104 | 105 | 服务器默认在 `127.0.0.1` 监听,默认的端口是 `7878`,但可以在配置文件 `files/config.toml` 中更改。 106 | 107 | 如果要在云服务器上运行: 108 | - 将 `files/config.toml` 中的 `local` 项改为 `false` 109 | - 在云服务器防火墙或安全组管理中放通指定的 **TCP 入站端口**(默认端口为 7878) 110 | - 这个玩具服务器**很不安全**,特别是 PHP 支持的部分,没有任何安全防护措施。**一定不要**让它在公网环境长期运行,否则你的服务器可能会被入侵! 111 | 112 | 程序启动后,打开浏览器,访问 `127.0.0.1:7878`。如果运行在公网,则将 IP 替换为对应的公网 IP。 113 | 114 | 默认的 Web 根文件夹是 `./files/html/`,但是可以在配置文件中修改。 115 | - 浏览器尝试请求 `/` 时,服务器将返回根文件夹下的 `index.html`。 116 | - 浏览器尝试请求文件夹时,服务器将返回该文件夹下的文件列表。 117 | 118 | 程序在 Ubuntu 22.04、Arch Linux、Windows 11 平台测试过,可以正常运行。 119 | 120 | ### 性能测试 / Benchmark 121 | 122 | 目前的性能测试结果都是在无优化的 Debug 模式下得出的。 123 | 124 | 可能我代码有点问题,在高并发时会一次性打开大量 socket 连接,占用很多 socket file discriptor,一旦超过操作系统设定的 `nlimit` 限制(默认为 1024),就会 panic。 125 | 126 | #### 暂时提升 `nlimit` 限制 127 | 128 | `cargo run --release` 启动之后,查找进程PID 129 | ```bash 130 | ps -e | grep webserver 131 | ``` 132 | 133 | 然后暂时提升限制 134 | ```bash 135 | sudo prlimit --pid [PID] --nofile=32768:32768 136 | ``` 137 | 138 | 之后再进行测试。 139 | 140 | #### 测试结果 141 | 142 | - 本机: 143 | 144 | ```bash 145 | eslzzyl:~/W/c/webbench-1.5 $ ./webbench -c 10000 -t 10 --get --http11 http://127.0.0.1:7878/ 146 | Webbench - Simple Web Benchmark 1.5 147 | Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software. 148 | 149 | Benchmarking: GET http://127.0.0.1:7878/ (using HTTP/1.1) 150 | 10000 clients, running 10 sec. 151 | 152 | Speed=2991498 pages/min, 44423480 bytes/sec. 153 | Requests: 498583 susceed, 0 failed. 154 | ``` 155 | 156 | 测试 20000 并发时,端口号不够用了。肯定是我代码的问题,正常不应该是这样的。总之 10000 并发肯定是有的。 157 | 158 | 测试机器:AMD Ryzen 5 4600U, 16G DDR4, Ubuntu 22.04 159 | 160 | - 远程服务器: 161 | 162 | ```bash 163 | eslzzyl:~/W/c/webbench-1.5 $ ./webbench -c 10000 -t 10 --get --http11 http://xx.xx.xx.xx:7878/ 164 | Webbench - Simple Web Benchmark 1.5 165 | Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software. 166 | 167 | Benchmarking: GET http://xx.xx.xx.xx:7878/ (using HTTP/1.1) 168 | 10000 clients, running 10 sec. 169 | 170 | Speed=6864 pages/min, 101930 bytes/sec. 171 | Requests: 1144 susceed, 0 failed. 172 | ``` 173 | 174 | ```bash 175 | eslzzyl:~/W/c/webbench-1.5 $ ./webbench -c 12000 -t 10 --get --http11 http://xx.xx.xx.xx:7878/ 176 | Webbench - Simple Web Benchmark 1.5 177 | Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software. 178 | 179 | Benchmarking: GET http://xx.xx.xx.xx:7878/ (using HTTP/1.1) 180 | 12000 clients, running 10 sec. 181 | 182 | Speed=5430 pages/min, 81993 bytes/sec. 183 | Requests: 905 susceed, 0 failed. 184 | ``` 185 | 186 | 测试 20000 并发时同样出现问题。 187 | 188 | 测试机器:腾讯云上海 1核 2G 1M, Ubuntu 22.04 189 | 190 | ### 参考文献 191 | 192 | - 《HTTP权威指南》 David Gourley 等著 (2002),陈涓 等译(人民邮电出版社 2012 年版) 193 | - https://developer.mozilla.org/zh-CN/docs/Web/HTTP 194 | - [RFC 2616 Hypertext Transfer Protocol -- HTTP/1.1](https://www.rfc-editor.org/rfc/rfc2616) 195 | - RFC [7230](https://www.rfc-editor.org/rfc/rfc7230) - [7235](https://www.rfc-editor.org/rfc/rfc7235) 196 | - [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.htm) (即现行的 HTTP Specification) 197 | - [Tokio 学习笔记](https://skyao.io/learning-tokio/) 198 | 199 | 特别鸣谢:[ChatGPT](http://chat.openai.com/),在本项目的编写过程中提供了极大的帮助。 200 | 201 | ### 开发 202 | 203 | 由于课程设计已经结束,因此这里记录的一些内容可能不会很快实现/修复。 204 | 205 | #### 功能添加和调整 206 | 207 | - ~实现 LRU 缓存:已放弃。因为不得不用 `unsafe`,而 `unsafe` 结构在线程之间传递极难实现。~ 208 | - 看一看 PHP 的安全性方面有没有能挖掘的地方 209 | 210 | 找个机会精简一下依赖,目前依赖快 100 个,编译太慢了,很多依赖只是用到一个简单的功能,没必要用库。尤其是 `Config` 的读取那部分,`serde` 的依赖有很多 211 | 212 | #### 待修复的问题 213 | 214 | - `route` 找不到 `index.html` 时,应当返回根路径,以便`Response` 列出根文件夹下的文件列表。目前是默认 `index.html` 一定存在了,会 panic。 215 | - 文件缓存应当同时保存文件的修改时间,再次请求同一缓存块时比对时间,如果修改时间发生了变化,说明文件在缓存期间发生了变化,此时不应该返回缓存中的结果,而是应该重新读取文件。文件列表模式可以存储文件夹的修改时间。 216 | - **【严重问题】【存疑】** 在低速网络上传送稍大的二进制文件会被异常中断 217 | 218 | #### 注意事项 219 | 220 | 不能让 URI 退到 wwwroot 之外,如 `www.example.com/../`。对于这种请求,应该给一个拒绝访问的 Response。 221 | 222 | #### HTTP压缩 223 | 224 | 压缩已经实现,但 brotli 非常慢,因此默认启用 gzip,无论浏览器是否支持 brotli。该逻辑可以在 `response.rs` 文件的 `decide_encoding` 函数中更改。 225 | -------------------------------------------------------------------------------- /files/html/foyue/js/main.js: -------------------------------------------------------------------------------- 1 | // JavaScript Document 2 | 3 | $("#error-alert").hide(); 4 | $("#copy-alert").hide(); 5 | 6 | var password = "TakuronDotTop"; 7 | 8 | String.prototype.replaceAll = function (s1, s2) { 9 | var reg = new RegExp(s1, "g"); 10 | return this.replace(reg, s2); 11 | } 12 | 13 | function encrypt() { 14 | var msg = $("#text-decryped").val(); 15 | var key = $("#text-key").val(); 16 | 17 | if (msg.length < 1) { 18 | $("#error-alert").show(); 19 | $("#copy-alert").hide(); 20 | $("#error-alert").text("无言者,纵真神再临,亦不可渡。(请输入待加密的明文)"); 21 | } else { 22 | if (key.length < 1) { 23 | key = password; 24 | } 25 | 26 | $("#text-encryped").val(togod(msg, key)); 27 | $("#error-alert").hide(); 28 | $("#copy-alert").hide(); 29 | } 30 | 31 | } 32 | 33 | function decrypt() { 34 | var msg = $("#text-decryped").val(); 35 | var key = $("#text-key").val(); 36 | 37 | if (msg.length < 1) { 38 | $("#error-alert").show(); 39 | $("#copy-alert").hide(); 40 | $("#error-alert").text("无言者,纵真神再临,亦不可渡。(请输入待解密的密文)"); 41 | } else { 42 | if (msg.substring(0, 3) != "佛曰:") { 43 | $("#error-alert").show(); 44 | $("#copy-alert").hide(); 45 | $("#error-alert").text("施主可曾记得此为何高僧所言?(不是佛语,请确定密文来源本网站并且密文以“佛曰:开头”)"); 46 | } else { 47 | if (key.length < 1) { 48 | key = password; 49 | } 50 | 51 | try { 52 | $("#error-alert").hide(); 53 | var str = toman(msg, key); 54 | } catch (err) { 55 | $("#error-alert").show(); 56 | $("#copy-alert").hide(); 57 | $("#error-alert").text("施主可曾记得此为何高僧所言?(佛语有误,请确定密钥正确并未被篡改)"); 58 | } finally { 59 | $("#text-encryped").val(str); 60 | 61 | } 62 | 63 | 64 | 65 | } 66 | 67 | 68 | } 69 | 70 | } 71 | 72 | 73 | function copyUrl2() { 74 | var Url2 = document.getElementById("text-encryped"); 75 | Url2.select(); 76 | document.execCommand("Copy"); 77 | $("#copy-alert").show(); 78 | $("#error-alert").hide(); 79 | } 80 | 81 | function togod(msg, key) { 82 | var str = CryptoJS.AES.encrypt(msg, key).toString(); 83 | 84 | str = str.substring(10); 85 | 86 | str = str.replaceAll("e", "啰"); 87 | str = str.replaceAll("E", "羯"); 88 | str = str.replaceAll("t", "婆"); 89 | str = str.replaceAll("T", "提"); 90 | str = str.replaceAll("a", "摩"); 91 | str = str.replaceAll("A", "埵"); 92 | str = str.replaceAll("o", "诃"); 93 | str = str.replaceAll("O", "迦"); 94 | str = str.replaceAll("i", "耶"); 95 | str = str.replaceAll("I", "吉"); 96 | str = str.replaceAll("n", "娑"); 97 | str = str.replaceAll("N", "佛"); 98 | str = str.replaceAll("s", "夜"); 99 | str = str.replaceAll("S", "驮"); 100 | str = str.replaceAll("h", "那"); 101 | str = str.replaceAll("H", "谨"); 102 | str = str.replaceAll("r", "悉"); 103 | str = str.replaceAll("R", "墀"); 104 | str = str.replaceAll("d", "阿"); 105 | str = str.replaceAll("D", "呼"); 106 | str = str.replaceAll("l", "萨"); 107 | str = str.replaceAll("L", "尼"); 108 | str = str.replaceAll("c", "陀"); 109 | str = str.replaceAll("C", "唵"); 110 | str = str.replaceAll("u", "唎"); 111 | str = str.replaceAll("U", "伊"); 112 | str = str.replaceAll("m", "卢"); 113 | str = str.replaceAll("M", "喝"); 114 | str = str.replaceAll("w", "帝"); 115 | str = str.replaceAll("W", "烁"); 116 | str = str.replaceAll("f", "醯"); 117 | str = str.replaceAll("F", "蒙"); 118 | str = str.replaceAll("g", "罚"); 119 | str = str.replaceAll("G", "沙"); 120 | str = str.replaceAll("y", "嚧"); 121 | str = str.replaceAll("Y", "他"); 122 | str = str.replaceAll("p", "南"); 123 | str = str.replaceAll("P", "豆"); 124 | str = str.replaceAll("b", "无"); 125 | str = str.replaceAll("B", "孕"); 126 | str = str.replaceAll("v", "菩"); 127 | str = str.replaceAll("V", "伽"); 128 | str = str.replaceAll("k", "怛"); 129 | str = str.replaceAll("K", "俱"); 130 | str = str.replaceAll("j", "哆"); 131 | str = str.replaceAll("J", "度"); 132 | str = str.replaceAll("x", "皤"); 133 | str = str.replaceAll("X", "阇"); 134 | str = str.replaceAll("q", "室"); 135 | str = str.replaceAll("Q", "地"); 136 | str = str.replaceAll("z", "利"); 137 | str = str.replaceAll("Z", "遮"); 138 | str = str.replaceAll("0", "穆"); 139 | str = str.replaceAll("1", "参"); 140 | str = str.replaceAll("2", "舍"); 141 | str = str.replaceAll("3", "苏"); 142 | str = str.replaceAll("4", "钵"); 143 | str = str.replaceAll("5", "曳"); 144 | str = str.replaceAll("6", "数"); 145 | str = str.replaceAll("7", "写"); 146 | str = str.replaceAll("8", "栗"); 147 | str = str.replaceAll("9", "楞"); 148 | str = str.replaceAll("\\+", "咩"); 149 | str = str.replaceAll("/", "输"); 150 | str = str.replaceAll("=", "漫"); 151 | 152 | return "佛曰:" + str; 153 | } 154 | 155 | function toman(msg, key) { 156 | 157 | str = msg.substring(3); 158 | 159 | str = str.replaceAll("啰", "e"); 160 | str = str.replaceAll("羯", "E"); 161 | str = str.replaceAll("婆", "t"); 162 | str = str.replaceAll("提", "T"); 163 | str = str.replaceAll("摩", "a"); 164 | str = str.replaceAll("埵", "A"); 165 | str = str.replaceAll("诃", "o"); 166 | str = str.replaceAll("迦", "O"); 167 | str = str.replaceAll("耶", "i"); 168 | str = str.replaceAll("吉", "I"); 169 | str = str.replaceAll("娑", "n"); 170 | str = str.replaceAll("佛", "N"); 171 | str = str.replaceAll("夜", "s"); 172 | str = str.replaceAll("驮", "S"); 173 | str = str.replaceAll("那", "h"); 174 | str = str.replaceAll("谨", "H"); 175 | str = str.replaceAll("悉", "r"); 176 | str = str.replaceAll("墀", "R"); 177 | str = str.replaceAll("阿", "d"); 178 | str = str.replaceAll("呼", "D"); 179 | str = str.replaceAll("萨", "l"); 180 | str = str.replaceAll("尼", "L"); 181 | str = str.replaceAll("陀", "c"); 182 | str = str.replaceAll("唵", "C"); 183 | str = str.replaceAll("唎", "u"); 184 | str = str.replaceAll("伊", "U"); 185 | str = str.replaceAll("卢", "m"); 186 | str = str.replaceAll("喝", "M"); 187 | str = str.replaceAll("帝", "w"); 188 | str = str.replaceAll("烁", "W"); 189 | str = str.replaceAll("醯", "f"); 190 | str = str.replaceAll("蒙", "F"); 191 | str = str.replaceAll("罚", "g"); 192 | str = str.replaceAll("沙", "G"); 193 | str = str.replaceAll("嚧", "y"); 194 | str = str.replaceAll("他", "Y"); 195 | str = str.replaceAll("南", "p"); 196 | str = str.replaceAll("豆", "P"); 197 | str = str.replaceAll("无", "b"); 198 | str = str.replaceAll("孕", "B"); 199 | str = str.replaceAll("菩", "v"); 200 | str = str.replaceAll("伽", "V"); 201 | str = str.replaceAll("怛", "k"); 202 | str = str.replaceAll("俱", "K"); 203 | str = str.replaceAll("哆", "j"); 204 | str = str.replaceAll("度", "J"); 205 | str = str.replaceAll("皤", "x"); 206 | str = str.replaceAll("阇", "X"); 207 | str = str.replaceAll("室", "q"); 208 | str = str.replaceAll("地", "Q"); 209 | str = str.replaceAll("利", "z"); 210 | str = str.replaceAll("遮", "Z"); 211 | str = str.replaceAll("穆", "0"); 212 | str = str.replaceAll("参", "1"); 213 | str = str.replaceAll("舍", "2"); 214 | str = str.replaceAll("苏", "3"); 215 | str = str.replaceAll("钵", "4"); 216 | str = str.replaceAll("曳", "5"); 217 | str = str.replaceAll("数", "6"); 218 | str = str.replaceAll("写", "7"); 219 | str = str.replaceAll("栗", "8"); 220 | str = str.replaceAll("楞", "9"); 221 | str = str.replaceAll("咩", "+"); 222 | str = str.replaceAll("输", "/"); 223 | str = str.replaceAll("漫", "="); 224 | 225 | var st = CryptoJS.AES.decrypt("U2FsdGVkX1" + str, key).toString(CryptoJS.enc.Utf8); 226 | 227 | 228 | return st; 229 | } 230 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | path::PathBuf, 3 | process::Command, 4 | }; 5 | 6 | use chrono::{DateTime, Local}; 7 | use log::error; 8 | 9 | use crate::{ 10 | param::STATUS_CODES, 11 | exception::Exception, 12 | }; 13 | 14 | /// `HtmlBuilder` 15 | /// 16 | /// 用于动态构建HTML页面的结构 17 | pub struct HtmlBuilder { 18 | title: String, 19 | css: String, 20 | script: String, 21 | body: String, 22 | } 23 | 24 | impl HtmlBuilder { 25 | /// 通过状态码创建一个`HtmlBuilder` 26 | /// 27 | /// ## 参数: 28 | /// - `code`: HTML状态码 29 | /// - `note`: 自定义的说明文字。可以没有。 30 | /// 31 | /// ## 返回 32 | /// - 一个`HtmlBuilder`对象。要构建它,使用`build()`。 33 | /// 34 | /// ## 示例 35 | /// ```rust 36 | /// // 带有自定义说明文字的HTML 37 | /// let html404: String = HtmlBuilder::from_status_code(404, Some("无法找到网页")).build(); 38 | /// // 不指定说明文字,此时使用默认的说明(“I'm a teapot”) 39 | /// let html418: String = HtmlBuilder::from_status_code(418, None).build(); 40 | /// ``` 41 | pub fn from_status_code(code: u16, note: Option<&str>) -> Self { 42 | let title = format!("{}", code); 43 | let css = r" 44 | body { 45 | width: 35em; 46 | margin: 0 auto; 47 | font-family: Tahoma, Verdana, Arial, sans-serif; 48 | } 49 | ".to_string(); 50 | let description = match note { 51 | // 如果有自定义说明,则使用自定义说明 52 | Some(n) => n, 53 | // 否则,使用预定义的状态码说明 54 | None => match STATUS_CODES.get(&code) { 55 | Some(d) => *d, 56 | None => { 57 | panic!("非法的状态码:{}", code); 58 | } 59 | } 60 | }; 61 | let body = format!( 62 | r" 63 |

{}

64 |

{}

65 | ", code, description 66 | ); 67 | Self { 68 | title, 69 | css, 70 | script: "".to_string(), 71 | body, 72 | } 73 | } 74 | 75 | /// 通过文件列表创建一个`HtmlBuilder` 76 | /// 77 | /// ## 参数 78 | /// - `path`: 路径名 79 | /// - `dir_vec`: 文件列表 80 | /// 81 | /// ## 返回 82 | /// - 一个`HtmlBuilder`对象。要构建它,使用`build()`。 83 | pub fn from_dir(path: &str, dir_vec: &mut Vec) -> Self { 84 | let mut body = String::new(); 85 | sort_dir_entries(dir_vec); 86 | 87 | let mut path_mut = path; 88 | // 如果path是以"/"结尾的,就移除它 89 | if path_mut.ends_with("/") { 90 | let len = path_mut.len(); 91 | path_mut = &path_mut[..(len-1)]; 92 | } 93 | // 下面的`
`添加了一条水平分割线 94 | body.push_str(&format!("

{}的文件列表


", path_mut)); 95 | body.push_str(""); 96 | body.push_str(r#" 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | "# 108 | ); 109 | for entry in dir_vec { 110 | let metadata = entry.metadata().unwrap(); 111 | // 使用本地时区格式化为当前本地时间 112 | let local_time: DateTime = metadata.modified().unwrap().into(); 113 | let formatted_time = local_time.format("%Y-%m-%d %H:%M:%S %Z").to_string(); 114 | 115 | let filename = entry.file_name().unwrap().to_string_lossy(); 116 | 117 | if entry.is_file() { 118 | let size = metadata.len(); 119 | let formatted_size = format_file_size(size); 120 | body.push_str(&format!( 121 | r#" 122 | 123 | 124 | 125 | 126 | 127 | "#, 128 | &filename, 129 | &filename, 130 | &formatted_size, 131 | &formatted_time 132 | )); 133 | } else if entry.is_dir() { 134 | let filename = [&filename, "/"].concat(); 135 | body.push_str(&format!( 136 | r#" 137 | 138 | 139 | 140 | 141 | 142 | "#, 143 | &filename, 144 | &filename, 145 | &formatted_time 146 | )); 147 | } else { 148 | // 虽然我觉得这个条件永远不会被访问到。 149 | panic!(); 150 | } 151 | } 152 | body.push_str("
文件名大小修改时间
..
{}{}{}
{}文件夹{}
"); 153 | let title = format!("{}的文件列表", path); 154 | let css = r" 155 | table { 156 | border-collapse: collapse; 157 | width: 100%; 158 | } 159 | 160 | td { 161 | padding: 8px; 162 | white-space: pre-wrap; /* 保留换行符和空格 */ 163 | border: none; /* 隐藏单元格边框 */ 164 | } 165 | 166 | th { 167 | padding: 8px; 168 | border: none; /* 隐藏表头边框 */ 169 | }".to_string(); 170 | HtmlBuilder { 171 | title, 172 | css, 173 | script: "".to_string(), 174 | body, 175 | } 176 | } 177 | 178 | /// 构建一个`HtmlBuilder` 179 | pub fn build(&self) -> String { 180 | format!(r##" 181 | 182 | 183 | 184 | 185 | 186 | {} 187 | 188 | 189 | 190 | {} 191 | 192 | "##, 193 | self.script, self.title, self.css, self.body 194 | ) 195 | } 196 | } 197 | 198 | /// 格式化文件大小 199 | /// 200 | /// ## 参数 201 | /// - `size`: 以字节为单位的文件大小 202 | /// 203 | /// ## 返回 204 | /// - 格式化后的文件大小,原始大小的单位将被动态地调整到`B`、`KB`、`MB`、`GB`、`TB`等单位,并保留1位小数。 205 | fn format_file_size(size: u64) -> String { 206 | let units = ["B", "KB", "MB", "GB", "TB"]; 207 | let mut size = size as f64; 208 | let mut unit_index = 0; 209 | 210 | while size >= 1024.0 && unit_index < units.len() - 1 { 211 | size /= 1024.0; 212 | unit_index += 1; 213 | } 214 | 215 | format!("{:.1} {}", size, units[unit_index]) 216 | } 217 | 218 | /// 对文件列表进行排序,使满足: 219 | /// 220 | /// - 文件夹在前面 221 | /// - 文件在后面 222 | /// - 文件夹和文件按照各自的顺序排列 223 | fn sort_dir_entries(vec: &mut Vec) { 224 | vec.sort_by(|a, b| { 225 | let a_is_dir = a.is_dir(); 226 | let b_is_dir = b.is_dir(); 227 | 228 | if a_is_dir && !b_is_dir { 229 | std::cmp::Ordering::Less 230 | } else if !a_is_dir && b_is_dir { 231 | std::cmp::Ordering::Greater 232 | } else { 233 | a.cmp(b) 234 | } 235 | }); 236 | } 237 | 238 | /// 处理对PHP文件的请求 239 | pub fn handle_php(path: &str, id: u128) -> Result { 240 | let result = Command::new("php") 241 | .arg(path) // PHP文件路径 242 | .output(); 243 | let output = match result { 244 | Ok(o) => o, 245 | Err(_) => return Err(Exception::PHPExecuteFailed) 246 | }; 247 | 248 | if output.status.success() { // 执行完毕 249 | let stdout = String::from_utf8_lossy(&output.stdout); 250 | Ok(String::from(stdout)) 251 | } else { // 解释器出错 252 | let stderr = String::from_utf8_lossy(&output.stderr); 253 | error!("[ID{}]PHP解释器出错:{}", id, stderr); 254 | Err(Exception::PHPCodeError) 255 | } 256 | } 257 | 258 | #[cfg(test)] 259 | mod tests { 260 | use crate::util::format_file_size; 261 | 262 | #[test] 263 | fn test_file_size() { 264 | let a = 9926; 265 | let b = 51800; 266 | assert_eq!(format_file_size(a), "9.7 KB".to_string()); 267 | assert_eq!(format_file_size(b), "50.6 KB".to_string()); 268 | } 269 | } -------------------------------------------------------------------------------- /src/param.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use lazy_static::lazy_static; 4 | 5 | pub const HTML_INDEX: &str = r"files/html/index.html"; 6 | 7 | pub const SERVER_NAME: &str = "eslzzyl-webserver"; 8 | 9 | pub const CRLF: &str = "\r\n"; 10 | 11 | lazy_static! { 12 | pub static ref ALLOWED_METHODS: Vec = { 13 | vec![ 14 | HttpRequestMethod::Get, 15 | HttpRequestMethod::Head, 16 | HttpRequestMethod::Options, 17 | ] 18 | }; 19 | } 20 | 21 | lazy_static! { 22 | pub static ref STATUS_CODES: HashMap = { 23 | let mut map = HashMap::new(); 24 | map.insert(100, "Continue"); 25 | map.insert(101, "Switching Protocols"); 26 | // 2xx: Successful 27 | map.insert(200, "OK"); 28 | map.insert(201, "Created"); 29 | map.insert(202, "Accepted"); 30 | map.insert(203, "Non-Authoritative Information"); 31 | map.insert(204, "No Content"); 32 | map.insert(205, "Reset Content"); 33 | map.insert(206, "Partial Content"); 34 | // 3xx: Redirection 35 | map.insert(300, "Multiple Choices"); 36 | map.insert(301, "Moved Permanently"); 37 | map.insert(302, "Found"); 38 | map.insert(303, "See Other"); 39 | map.insert(304, "Not Modified"); 40 | map.insert(305, "Use Proxy"); 41 | // 306 已弃用 42 | map.insert(307, "Temporary Redirect"); 43 | map.insert(308, "Permanent Redirect"); 44 | // 4xx: Client Error 45 | map.insert(400, "Bad Request"); 46 | map.insert(401, "Unauthorized"); 47 | map.insert(402, "Payment Required"); // 保留,当前不使用 48 | map.insert(403, "Forbidden"); 49 | map.insert(404, "Not Found"); 50 | map.insert(405, "Method Not Allowed"); 51 | map.insert(406, "Not Acceptable"); 52 | map.insert(407, "Proxy Authentication Required"); 53 | map.insert(408, "Request Timeout"); 54 | map.insert(409, "Conflict"); 55 | map.insert(410, "Gone"); 56 | map.insert(411, "Length Required"); 57 | map.insert(412, "Precondition Failed"); 58 | map.insert(413, "Content Too Large"); 59 | map.insert(414, "URI Too Long"); 60 | map.insert(415, "Unsupported Media Type"); 61 | map.insert(416, "Range Not Satisfiable"); 62 | map.insert(417, "Expectation Failed"); 63 | map.insert(418, "I'm a teapot"); // 愚人节玩笑,见RFC2324,该状态码不应被使用 64 | map.insert(421, "Misdirected Request"); 65 | map.insert(422, "Unprocessable Content"); 66 | map.insert(426, "Upgrade Required"); 67 | // 5xx: Server Error 68 | map.insert(500, "Internal Server Error"); 69 | map.insert(501, "Not Implemented"); 70 | map.insert(502, "Bad Gateway"); 71 | map.insert(503, "Service Unavailable"); 72 | map.insert(504, "Gateway Timeout"); 73 | map.insert(505, "HTTP Version Not Supported"); 74 | map 75 | }; 76 | } 77 | 78 | lazy_static! { 79 | pub static ref MIME_TYPES: HashMap<&'static str, &'static str> = { 80 | let mut map = HashMap::new(); 81 | map.insert("aac", "audio/aac"); 82 | map.insert("abw", "application/x-abiword"); 83 | map.insert("apk", "application/vnd.android.package-archive"); 84 | map.insert("arc", "application/x-freearc"); 85 | map.insert("avi", "video/x-msvideo"); 86 | map.insert("avif", "image/avif"); 87 | map.insert("azw", "application/vnd.amazon.ebook"); 88 | map.insert("bin", "application/octet-stream"); 89 | map.insert("bmp", "image/bmp"); 90 | map.insert("bz", "application/x-bzip"); 91 | map.insert("bz2", "application/x-bzip2"); 92 | map.insert("cab", "application/vnd.ms-cab-compressed"); 93 | map.insert("cda", "application/x-cdf"); 94 | map.insert("csh", "application/x-csh"); 95 | map.insert("css", "text/css;charset=utf-8"); 96 | map.insert("csv", "text/csv"); 97 | map.insert("crx", "application/x-chrome-extension"); 98 | map.insert("deb", "application/x-deb"); 99 | map.insert("doc", "application/msword"); 100 | map.insert("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); 101 | map.insert("eot", "application/vnd.ms-fontobject"); 102 | map.insert("epub", "application/epub+zip"); 103 | map.insert("exe", "application/x-msdownload"); 104 | map.insert("gif", "image/gif"); 105 | map.insert("gz", "application/gzip"); 106 | map.insert("htm", "text/html;charset=utf-8"); 107 | map.insert("html", "text/html;charset=utf-8"); 108 | map.insert("img", "application/x-iso9660-image"); 109 | map.insert("ico", "image/x-icon"); 110 | map.insert("ics", "text/calendar"); 111 | map.insert("iso", "application/x-iso9660-image"); 112 | map.insert("jar", "application/java-archive"); 113 | map.insert("js", "text/javascript;charset=utf-8"); 114 | map.insert("json", "application/json"); 115 | map.insert("jsonld", "application/ld+json"); 116 | map.insert("jpg", "image/jpeg"); 117 | map.insert("jpeg", "image/jpeg"); 118 | map.insert("mid", "audio/x-midi"); 119 | map.insert("midi", "audio/x-midi"); 120 | map.insert("mjs", "text/javascript"); 121 | map.insert("mkv", "video/x-matroska"); 122 | map.insert("mp3", "audio/mpeg"); 123 | map.insert("mp4", "video/mp4"); 124 | map.insert("mpeg", "video/mpeg"); 125 | map.insert("mpkg", "application/vnd.apple.installer+xml"); 126 | map.insert("msi", "application/x-msdownload"); 127 | map.insert("odp", "application/vnd.oasis.opendocument.presentation"); 128 | map.insert("ods", "application/vnd.oasis.opendocument.spreadsheet"); 129 | map.insert("odt", "application/vnd.oasis.opendocument.text"); 130 | map.insert("oga", "audio/ogg"); 131 | map.insert("ogv", "video/ogg"); 132 | map.insert("ogx", "application/ogg"); 133 | map.insert("opus", "audio/opus"); 134 | map.insert("otf", "font/otf"); 135 | map.insert("pdf", "application/pdf"); 136 | map.insert("png", "image/png"); 137 | map.insert("php", "application/x-httpd-php"); 138 | map.insert("ppt", "application/vnd.ms-powerpoint"); 139 | map.insert("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"); 140 | map.insert("rar", "application/x-rar-compressed"); 141 | map.insert("rtf", "application/rtf"); 142 | map.insert("rpm", "application/x-rpm"); 143 | map.insert("sh", "application/x-sh"); 144 | map.insert("svg", "image/svg+xml"); 145 | map.insert("swf", "application/x-shockwave-flash"); 146 | map.insert("tar", "application/x-tar"); 147 | map.insert("tif", "image/tiff"); 148 | map.insert("tiff", "image/tiff"); 149 | map.insert("ts", "video/mp2t"); 150 | map.insert("txt", "text/plain"); 151 | map.insert("ttf", "font/ttf"); 152 | map.insert("vsd", "application/vnd.visio"); 153 | map.insert("wav", "audio/wav"); 154 | map.insert("wasm", "application/wasm"); 155 | map.insert("weba", "audio/webm"); 156 | map.insert("webm", "video/webm"); 157 | map.insert("webp", "image/webp"); 158 | map.insert("woff", "font/woff"); 159 | map.insert("woff2", "font/woff2"); 160 | map.insert("xhtml", "application/xhtml+xml"); 161 | map.insert("xls", "application/vnd.ms-excel"); 162 | map.insert("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); 163 | map.insert("xml", "text/xml"); 164 | map.insert("xpi", "application/x-xpinstall"); 165 | map.insert("xul", "application/vnd.mozilla.xul+xml"); 166 | map.insert("zip", "application/zip"); 167 | map.insert("7z", "application/x-7z-compressed"); 168 | map.insert("_", "application/octet-stream"); 169 | map 170 | }; 171 | } 172 | 173 | #[derive(Debug, Clone, Copy)] 174 | pub enum HttpVersion { 175 | V1_1, 176 | } 177 | 178 | #[derive(Debug, Clone, Copy, PartialEq)] 179 | pub enum HttpRequestMethod { 180 | Get, 181 | Head, 182 | Options, 183 | Post, 184 | } 185 | 186 | #[derive(Debug, Clone, Copy, PartialEq)] 187 | pub enum HttpEncoding { 188 | Gzip, 189 | Deflate, 190 | Br, 191 | } 192 | 193 | use std::fmt; 194 | 195 | impl fmt::Display for HttpVersion { 196 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 197 | match *self { 198 | HttpVersion::V1_1 => write!(f, "1.1"), 199 | } 200 | } 201 | } 202 | 203 | impl fmt::Display for HttpRequestMethod { 204 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 205 | match *self { 206 | HttpRequestMethod::Get => write!(f, "GET"), 207 | HttpRequestMethod::Head => write!(f, "HEAD"), 208 | HttpRequestMethod::Options => write!(f, "OPTIONS"), 209 | HttpRequestMethod::Post => write!(f, "POST"), 210 | } 211 | } 212 | } 213 | 214 | impl fmt::Display for HttpEncoding { 215 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 216 | match *self { 217 | HttpEncoding::Gzip => write!(f, "gzip"), 218 | HttpEncoding::Deflate => write!(f, "deflate"), 219 | HttpEncoding::Br => write!(f, "br"), 220 | } 221 | } 222 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unused_io_amount)] 2 | 3 | mod exception; 4 | mod param; 5 | mod config; 6 | mod request; 7 | mod response; 8 | mod cache; 9 | mod util; 10 | 11 | use request::Request; 12 | use config::Config; 13 | use response::Response; 14 | use cache::FileCache; 15 | 16 | use tokio::{ 17 | net::{TcpListener, TcpStream}, 18 | io::{ 19 | AsyncWriteExt, 20 | AsyncBufReadExt, 21 | BufReader 22 | }, 23 | runtime::Builder, 24 | }; 25 | use log::{error, warn, info, debug}; 26 | use log4rs; 27 | use regex::Regex; 28 | 29 | use std::{ 30 | net::{Ipv4Addr, SocketAddrV4}, 31 | path::{Path, PathBuf}, 32 | time::Instant, 33 | sync::{Arc, Mutex}, 34 | process::Command, 35 | }; 36 | 37 | use crate::{ 38 | param::HTML_INDEX, 39 | exception::Exception, 40 | }; 41 | 42 | #[tokio::main] 43 | async fn main() { 44 | // 初始化日志系统 45 | log4rs::init_file("log4rs.yaml", Default::default()).unwrap(); 46 | 47 | // 加载配置文件 48 | let config = Config::from_toml("files/config.toml"); 49 | info!("配置文件已载入"); 50 | let root = config.www_root().to_string(); 51 | info!("www root: {}", &root); 52 | 53 | // 设置工作线程数量 54 | let worker_threads = config.worker_threads(); 55 | let runtime = Builder::new_multi_thread() 56 | .worker_threads(worker_threads) 57 | .build() 58 | .unwrap(); 59 | 60 | // 初始化文件缓存 61 | let cache_size = config.cache_size(); 62 | let cache = Arc::new( 63 | Mutex::new(FileCache::from_capacity(cache_size)) 64 | ); 65 | 66 | // 检测PHP环境 67 | let php_result = Command::new("php") 68 | .arg("-v") 69 | .output(); 70 | match php_result { 71 | Ok(o) => { 72 | if o.status.success() { 73 | let output = String::from_utf8_lossy(&o.stdout); 74 | // 使用正则表达式捕获版本号 75 | let re = Regex::new(r"PHP (\d+\.\d+\.\d+-\dubuntu\d+\.\d+)").unwrap(); 76 | if let Some(capture) = re.captures(&output) { 77 | if let Some(version) = capture.get(1) { 78 | info!("找到PHP解释器,版本:{}", version.as_str()); 79 | } 80 | } 81 | } else { 82 | // 执行php -v的status应该总是success的 83 | panic!("在查找PHP解释器时遇到未知错误"); 84 | } 85 | } 86 | Err(_) => { 87 | warn!("无法找到PHP解释器。服务器将继续运行,但将无法处理PHP请求。"); 88 | } 89 | }; 90 | 91 | // 监听端口 92 | let port: u16 = config.port(); 93 | info!("服务端将在{}端口上监听Socket连接", port); 94 | // 地址,本地调试用127.0.0.1 95 | let address = match config.local() { 96 | true => Ipv4Addr::new(127, 0, 0, 1), 97 | false => Ipv4Addr::new(0, 0, 0, 0) 98 | }; 99 | info!("服务端将在{}地址上监听Socket连接", address); 100 | // 拼接socket 101 | let socket = SocketAddrV4::new(address, port); 102 | 103 | // 执行bind 104 | let listener = match TcpListener::bind(socket).await { 105 | Ok(listener) => listener, 106 | Err(e) => { 107 | error!("无法绑定端口:{},错误:{}", port, e); 108 | panic!("无法绑定端口:{},错误:{}", port, e); 109 | } 110 | }; 111 | info!("端口{}绑定完成", port); 112 | 113 | // 停机命令标志 114 | let shutdown_flag = Arc::new(Mutex::new(false)); 115 | // 活跃连接计数 116 | let active_connection = Arc::new(Mutex::new(0u32)); 117 | 118 | // 启动异步命令处理任务 119 | runtime.spawn({ 120 | let shutdown_flag = Arc::clone(&shutdown_flag); 121 | let active_connection = Arc::clone(&active_connection); 122 | async move { 123 | let stdin = tokio::io::stdin(); 124 | let mut reader = BufReader::new(stdin); 125 | let mut input = String::new(); 126 | loop { 127 | input.clear(); 128 | // 在这里处理命令,可以调用服务器的相关函数或执行其他操作 129 | if let Ok(_) = reader.read_line(&mut input).await { 130 | let cmd = input.trim(); 131 | match cmd { 132 | "stop" => { 133 | // 如果收到 "stop" 命令,则设置停机标志 134 | let mut flag = shutdown_flag.lock().unwrap(); 135 | *flag = true; 136 | break; 137 | }, 138 | "help" => { 139 | println!("== Webserver Help =="); 140 | println!("输入stop并再发出一次连接请求以停机"); 141 | println!("输入status以查看当前服务器状态"); 142 | println!("===================="); 143 | }, 144 | "status" => { 145 | let active_count = *active_connection.lock().unwrap(); 146 | println!("== Webserver 状态 ==="); 147 | println!("当前连接数: {}", active_count); 148 | println!("===================="); 149 | }, 150 | _ => { 151 | println!("无效的命令:{}", cmd); 152 | } 153 | } 154 | } else { 155 | break; 156 | } 157 | } 158 | } 159 | }); 160 | 161 | let mut id: u128 = 0; 162 | 163 | loop { 164 | // 检查停机标志,如果设置了停机标志,退出循环 165 | if *shutdown_flag.lock().unwrap() { 166 | break; 167 | } 168 | let (mut stream, addr) = listener.accept().await.unwrap(); 169 | debug!("新的连接:{}", addr); 170 | 171 | let active_connection_arc = Arc::clone(&active_connection); 172 | let root_clone = root.clone(); 173 | let cache_arc = Arc::clone(&cache); 174 | debug!("[ID{}]TCP连接已建立", id); 175 | tokio::spawn(async move { 176 | { 177 | let mut lock = active_connection_arc.lock().unwrap(); 178 | *lock += 1; 179 | } 180 | handle_connection(&mut stream, id, &root_clone, cache_arc).await; 181 | { 182 | let mut lock = active_connection_arc.lock().unwrap(); 183 | *lock -= 1; 184 | } 185 | }); 186 | id += 1; 187 | } 188 | } 189 | 190 | /// 处理TCP连接 191 | /// 192 | /// 参数: 193 | /// - `stream`: 建立好的`TcpStream` 194 | /// - `config`: Web服务器配置类型,在当前子线程建立时使用`Arc`共享 195 | /// - `id`: 当前TCP连接的ID 196 | async fn handle_connection(stream: &mut TcpStream, id: u128, root: &str, cache: Arc>) { 197 | let mut buffer = vec![0; 1024]; 198 | 199 | // 等待tcpstream变得可读 200 | stream.readable().await.unwrap(); 201 | 202 | match stream.try_read(&mut buffer) { 203 | Ok(0) => return, 204 | Err(e) => { 205 | error!("[ID{}]读取TCPStream时遇到错误: {}", id, e); 206 | panic!(); 207 | }, 208 | _ => {}, 209 | } 210 | debug!("[ID{}]HTTP请求接收完毕", id); 211 | 212 | // 启动timer 213 | let start_time = Instant::now(); 214 | 215 | let request = Request::try_from(&buffer, id).unwrap(); 216 | debug!("[ID{}]成功解析HTTP请求", id); 217 | 218 | let result = route(&request.path(), id, root).await; 219 | debug!("[ID{}]HTTP路由解析完毕", id); 220 | 221 | // 如果path不存在,就返回404。使用Response::response_404 222 | let response = match result { 223 | Ok(path) => { 224 | let path_str = match path.to_str() { 225 | Some(s) => s, 226 | None => { 227 | let path_str = path.to_str().unwrap(); 228 | error!("[ID{}]无法将路径{}转换为str", id, path_str); 229 | return; 230 | }, 231 | }; 232 | Response::from(path_str, &request, id, &cache) 233 | }, 234 | Err(Exception::FileNotFound) => { 235 | warn!("[ID{}]请求的路径:{} 不存在,返回404响应", id, &request.path()); 236 | Response::response_404(&request, id) 237 | }, 238 | Err(e) => { 239 | panic!("非法的错误类型:{}", e); 240 | } 241 | }; 242 | 243 | debug!("[ID{}]HTTP响应构建完成,服务端用时{}ms。", 244 | id, 245 | start_time.elapsed().as_millis() 246 | ); 247 | 248 | info!("[ID{}] {}, {}, {}, {}, {}, {}, ", id, 249 | request.version(), 250 | request.path(), 251 | request.method(), 252 | response.status_code(), 253 | response.information(), 254 | request.user_agent(), 255 | ); 256 | 257 | stream.write(&response.as_bytes()).await.unwrap(); 258 | stream.flush().await.unwrap(); 259 | debug!("[ID{}]HTTP响应已写回", id); 260 | } 261 | 262 | /// 路由解析函数 263 | /// 264 | /// ## 参数: 265 | /// - `path`:请求路径 266 | /// - `config`:Web服务器配置类型 267 | /// - `id`: 当前TCP连接的ID 268 | /// 269 | /// ## 返回: 270 | /// - `u8`: 状态码。0为正常,1为404 271 | /// - `PathBuf`: 文件的完整路径 272 | /// - `String`: MIME类型 273 | async fn route(path: &str, id: u128, root: &str) -> Result { 274 | if path == "/" { 275 | debug!("[ID{}]请求路径为根目录,返回index", id); 276 | let path = PathBuf::from(HTML_INDEX); 277 | return Ok(path); 278 | } else if path == "*" { // 常见于OPTIONS方法 279 | debug!("[ID{}]请求路径为*", id); 280 | let path = PathBuf::from("*"); 281 | return Ok(path); 282 | } 283 | let mut path_str = path.to_string(); 284 | path_str.remove(0); 285 | let path = Path::new(&path_str); 286 | // 将路径和config.wwwroot拼接 287 | let root = Path::new(root); 288 | let path = root.join(path); 289 | debug!("[ID{}]请求文件路径:{}", id, path.to_str().unwrap()); 290 | match path.exists() { 291 | true => Ok(path), 292 | false => Err(Exception::FileNotFound), 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /files/html/foyue/foyue.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 与佛论禅加密版 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 |
30 |
31 |

32 | 与佛论禅 33 | 34 |

35 | 36 | 43 | 44 |
45 |
46 |
47 | 48 |
49 |
50 |
51 | 52 |
53 |
54 |
55 |
56 | 57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | 74 | 75 |
76 |      77 | 78 |
79 | 80 | 81 |
82 | 83 | 84 |
85 |
86 | 87 |
88 | 91 | 92 | 93 | 94 |
95 |
96 |
97 |
98 |
99 |
100 | 101 |
102 |
103 |
104 |
105 |
106 |
107 | 110 | 111 | 112 | 113 |
114 |
115 | 116 |
117 |
118 |
119 | 120 | 121 |
122 | 123 |
124 |
125 | 126 | 127 | 128 | 152 | 153 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 303 | 308 | 309 | 338 | 359 |
360 |

361 | 与佛论禅帮助 362 |

363 |

364 | 听佛讲经(加密):将原文填写在上方输入框,输入箴言(即密码,可选)即可得到佛语(密文); 365 |

366 |

367 | 听佛解惑(解密):将佛经(密文)填写在上方,输入箴言(如有)即可得到原文。 368 |

369 |

370 | 箴言(可选)是解密所需的密码。 371 |

372 |

 

373 | 376 | 377 |
378 |
379 | 380 | 381 | -------------------------------------------------------------------------------- /files/html/foyue/js/popper.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Federico Zivolo 2019 3 | Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). 4 | */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function i(e){return e&&e.referenceNode?e.referenceNode:e}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f],10),E=parseFloat(w['border'+f+'Width'],10),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.rightwindow.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge}); 5 | //# sourceMappingURL=popper.min.js.map 6 | -------------------------------------------------------------------------------- /src/response.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | param::*, 3 | request::Request, 4 | cache::FileCache, 5 | util::{HtmlBuilder, handle_php}, 6 | }; 7 | 8 | use chrono::prelude::*; 9 | use bytes::Bytes; 10 | use flate2::{ 11 | write::{DeflateEncoder, GzEncoder}, 12 | Compression, 13 | }; 14 | use brotli::enc::{self, backward_references::BrotliEncoderParams}; 15 | use log::{error, warn, debug}; 16 | 17 | use std::{ 18 | io::{self, Read, Write}, 19 | sync::{Arc, Mutex}, 20 | fs::{self, File, metadata}, 21 | ffi::OsStr, 22 | path::{Path, PathBuf}, 23 | str, 24 | }; 25 | 26 | /// HTTP 响应 27 | /// 28 | /// - `version`: 使用的HTTP版本。目前仅支持1.1 29 | /// - `status_code`: HTTP状态码 30 | /// - `information`: 对状态码的说明文字 31 | /// - `content_type`: MIME 32 | /// - `content_length`: 响应**体**的长度。不包含响应头。 33 | /// - `date`: 发送响应时的时间 34 | /// - `content_encoding`: 指定响应体应当以何种算法进行压缩 35 | /// - `server_name`: 服务器名 36 | /// - `allow`: 服务器允许的HTTP请求方法 37 | /// - `content`: 响应体本身 38 | #[derive(Debug, Clone)] 39 | pub struct Response { 40 | version: HttpVersion, 41 | status_code: u16, 42 | information: String, 43 | content_type: Option, 44 | content_length: u64, 45 | date: DateTime, 46 | content_encoding: Option, 47 | server_name: String, 48 | allow: Option>, 49 | content: Option, 50 | } 51 | 52 | impl Response { 53 | /// 生成一个空的Response对象,各成员默认值为: 54 | /// 55 | /// - HTTP版本:`1.1` 56 | /// - 状态码:200 57 | /// - 响应信息:`OK` 58 | /// - Content-Type:None 59 | /// - Content-Length:`0` 60 | /// - Date:当前的UTC时间 61 | /// - Content-Encoding:明文(无压缩) 62 | /// - Server: `SERVER_NAME` 63 | /// - Allow: GET、HEAD、OPTIONS 64 | /// - Content:None 65 | pub fn new() -> Self { 66 | Self { 67 | version: HttpVersion::V1_1, 68 | status_code: 200, 69 | information: "OK".to_string(), 70 | content_type: None, 71 | content_length: 0, 72 | date: Utc::now(), 73 | content_encoding: None, 74 | server_name: SERVER_NAME.to_string(), 75 | allow: Some(ALLOWED_METHODS.to_vec()), 76 | content: None, 77 | } 78 | } 79 | 80 | /// 通过指定的文件构建content域,文件内容是以无压缩字节流的形式写入的 81 | /// 82 | /// ## 参数 83 | /// - `path`: 文件的完整路径 84 | /// - `accept_encoding`: 浏览器能够接受的压缩编码,需要根据该参数确定压缩编码 85 | /// - `id`: 用于日志的TCP连接编号 86 | /// - `cache`: 共享的文件缓存指针 87 | /// 88 | /// ## 返回 89 | /// - 一个新的 Response 对象,不完整,还需要进一步处理才能发回浏览器 90 | fn from_file(path: &str, accept_encoding: Vec, id: u128, cache: &Arc>, headonly: bool, mime: &str) -> Self { 91 | let mut response = Self::new(); 92 | response.allow = None; 93 | response.content_encoding = match headonly { 94 | true => None, 95 | false => decide_encoding(&accept_encoding), 96 | }; 97 | match response.content_encoding { 98 | Some(HttpEncoding::Gzip) => debug!("[ID{}]使用Gzip压缩编码", id), 99 | Some(HttpEncoding::Br) => debug!("[ID{}]使用Brotli压缩编码", id), 100 | Some(HttpEncoding::Deflate) => debug!("[ID{}]使用Deflate压缩编码", id), 101 | None => debug!("[ID{}]不进行压缩", id), 102 | }; 103 | 104 | // 查找缓存 105 | let mut cache_lock = cache.lock().unwrap(); 106 | match cache_lock.find(path) { 107 | Some(bytes) => { 108 | debug!("[ID{}]缓存命中", id); 109 | // 这里其实是有个潜在问题的。理论上不同客户端要求的encoding可能会不同,但是缓存却是共享的,导致encoding是相同的。 110 | // 但是单客户端情况下可以忽略。而且目前所有主流浏览器也都支持gzip了。 111 | response.content_length = bytes.len() as u64; 112 | response.content = match headonly { 113 | // headonly时,不填入content(但正常设置length),否则填入找到的bytes 114 | true => None, 115 | false => Some(bytes.clone()), 116 | }; 117 | }, 118 | None => { 119 | debug!("[ID{}]缓存未命中", id); 120 | if headonly { 121 | // 如果headonly为true,则通过Metadata直接获取文件大小,而不是真的读取文件,从而提高性能 122 | let path = Path::new(path); 123 | let metadata = metadata(path).unwrap(); 124 | response.content_type = None; 125 | response.content = None; 126 | response.content_length = metadata.len(); 127 | } else { 128 | // 如果为false,又缓存不命中,就只好读取文件 129 | let mut file = match File::open(path) { 130 | Ok(f) => f, 131 | Err(e) => { 132 | error!("[ID{}]无法打开路径{}指定的文件。错误:{}", id, path, e); 133 | panic!(); 134 | }, 135 | }; 136 | let mut contents = Vec::new(); 137 | match file.read_to_end(&mut contents) { 138 | Ok(_) => {}, 139 | Err(e) => { 140 | error!("[ID{}]无法读取文件{}。错误:{}", id, path, e); 141 | panic!(); 142 | } 143 | } 144 | contents = compress(contents, response.content_encoding).unwrap(); 145 | 146 | response.content_length = contents.len() as u64; 147 | debug!("[ID{}]Content-Length: {}", id, response.content_length); 148 | 149 | let content_type_str = mime.to_string(); 150 | debug!("[ID{}]Content-Type: {}", id, &content_type_str); 151 | response.content_type = Some(content_type_str); 152 | 153 | response.content = Some(Bytes::from(contents.clone())); 154 | cache_lock.push(path, Bytes::from(contents)); 155 | } 156 | } 157 | } 158 | response 159 | } 160 | 161 | /// 通过状态码创建response对象。content部分由`HtmlBuilder`生成。 162 | /// 163 | /// ## 参数 164 | /// - `code`: 状态码 165 | /// - `accept_encoding`: 浏览器能够接受的压缩编码,需要根据该参数确定压缩编码 166 | /// - `id`: 用于日志的TCP连接编号 167 | /// 168 | /// ## 返回 169 | /// - 一个新的 Response 对象,不完整,还需要进一步处理才能发回浏览器 170 | fn from_status_code(code: u16, accept_encoding: Vec, id: u128) -> Self { 171 | let mut response = Self::new(); 172 | response.content_encoding = decide_encoding(&accept_encoding); 173 | // 204响应不包含响应体,因此encoding和type也不需要 174 | if code == 204 { 175 | response.content = None; 176 | response.content_encoding = None; 177 | response.content_type = None; 178 | response.allow = Some(ALLOWED_METHODS.to_vec()); 179 | response.set_code(code); 180 | return response; 181 | } 182 | response.allow = None; 183 | match response.content_encoding { 184 | Some(HttpEncoding::Gzip) => debug!("[ID{}]使用Gzip压缩编码", id), 185 | Some(HttpEncoding::Br) => debug!("[ID{}]使用Brotli压缩编码", id), 186 | Some(HttpEncoding::Deflate) => debug!("[ID{}]使用Deflate压缩编码", id), 187 | None => debug!("[ID{}]不进行压缩", id), 188 | }; 189 | let content = match code { 190 | 404 => HtmlBuilder::from_status_code(404, Some( 191 | r"

噢!

你指定的网页无法找到。

" 192 | )), 193 | 405 => HtmlBuilder::from_status_code(405, Some( 194 | r"

噢!

你的浏览器发出了一个非GET方法的HTTP请求。本服务器目前仅支持GET方法。

" 195 | )), 196 | 500 => HtmlBuilder::from_status_code(500, Some( 197 | r"

噢!

服务器出现了一个内部错误。

" 198 | )), 199 | _ => HtmlBuilder::from_status_code(code, None), 200 | }.build(); 201 | let content_compressed = compress(content.into_bytes(), response.content_encoding).unwrap(); 202 | let bytes = Bytes::from(content_compressed); 203 | response.content_length = bytes.len() as u64; 204 | response.content = Some(bytes); 205 | response.content_type = Some("text/html;charset=utf-8".to_string()); 206 | response.set_code(code); 207 | response 208 | } 209 | 210 | /// 通过目录来生成一个 `Response`,该 `Response` 应当列出目录的所有文件。 211 | /// 212 | /// ## 参数 213 | /// - `path`: 文件的完整路径 214 | /// - `accept_encoding`: 浏览器能够接受的压缩编码,需要根据该参数确定压缩编码 215 | /// - `id`: 用于日志的TCP连接编号 216 | /// - `cache`: 共享的文件缓存指针 217 | /// 218 | /// ## 返回 219 | /// - 一个新的 Response 对象,不完整,还需要进一步处理才能发回浏览器 220 | fn from_dir(path: &str, accept_encoding: Vec, id: u128, cache: &Arc>, headonly: bool) -> Self { 221 | let mut response = Self::new(); 222 | response.allow = None; 223 | response.content_encoding = match headonly { 224 | true => None, 225 | false => decide_encoding(&accept_encoding), 226 | }; 227 | match response.content_encoding { 228 | Some(HttpEncoding::Gzip) => debug!("[ID{}]使用Gzip压缩编码", id), 229 | Some(HttpEncoding::Br) => debug!("[ID{}]使用Brotli压缩编码", id), 230 | Some(HttpEncoding::Deflate) => debug!("[ID{}]使用Deflate压缩编码", id), 231 | None => debug!("[ID{}]不进行压缩", id), 232 | }; 233 | 234 | // 仅在有响应体时才设置content-type 235 | if !headonly { 236 | response.content_type = Some("text/html;charset=utf-8".to_string()); 237 | } else { 238 | response.content_type = None; 239 | } 240 | 241 | // 查找缓存 242 | let mut cache_lock = cache.lock().unwrap(); 243 | match cache_lock.find(path) { 244 | Some(bytes) => { 245 | debug!("[ID{}]缓存命中", id); 246 | // 这里其实是有个潜在问题的。理论上不同客户端要求的encoding可能会不同,但是缓存却是共享的,导致encoding是相同的。 247 | // 但是单客户端情况下可以忽略。而且目前所有主流浏览器也都支持gzip了。 248 | response.content = match headonly { 249 | // headonly时,填入一个空字符串,否则填入找到的bytes 250 | true => None, 251 | false => Some(bytes.clone()), 252 | }; 253 | response.content_length = bytes.len() as u64; 254 | }, 255 | None => { // 缓存未命中,生成目录列表 256 | debug!("[ID{}]缓存未命中", id); 257 | let mut dir_vec = Vec::::new(); 258 | let entries = fs::read_dir(path).unwrap(); 259 | for entry in entries.into_iter() { 260 | dir_vec.push(entry.unwrap().path()); 261 | } 262 | let content = HtmlBuilder::from_dir(path, &mut dir_vec).build(); 263 | let content_compressed = compress(content.into_bytes(), response.content_encoding).unwrap(); 264 | response.content_length = content_compressed.len() as u64; 265 | // headonly时,填入一个空字符串,否则填入压缩好的content 266 | response.content = match headonly { 267 | true => None, 268 | false => Some(Bytes::from(content_compressed.clone())), 269 | }; 270 | // 无论是否是HEAD请求,都要写缓存 271 | cache_lock.push(path, Bytes::from(content_compressed)); 272 | } 273 | } 274 | response 275 | } 276 | 277 | /// 通过HTML代码生成一个`Response` 278 | /// 279 | /// ## 参数 280 | /// - `html`: HTML代码 281 | /// - `accept_encoding`: 浏览器能够接受的压缩编码,需要根据该参数确定压缩编码 282 | /// - `id`: 用于日志的TCP连接编号 283 | /// 284 | /// ## 返回 285 | /// - 一个新的 Response 对象,不完整,还需要进一步处理才能发回浏览器 286 | /// 287 | /// 本函数不涉及对文件缓存的访问,因为本函数被设计用来进行PHP的处理,而PHP往往是动态页面。 288 | fn from_html(html: &str, accept_encoding: Vec, id: u128, headonly: bool) -> Response { 289 | let mut response = Self::new(); 290 | response.allow = None; 291 | if headonly { 292 | response.content_encoding = None; 293 | response.content_type = None; 294 | response.content = None; 295 | return response; 296 | } 297 | response.content_encoding = decide_encoding(&accept_encoding); 298 | match response.content_encoding { 299 | Some(HttpEncoding::Gzip) => debug!("[ID{}]使用Gzip压缩编码", id), 300 | Some(HttpEncoding::Br) => debug!("[ID{}]使用Brotli压缩编码", id), 301 | Some(HttpEncoding::Deflate) => debug!("[ID{}]使用Deflate压缩编码", id), 302 | None => debug!("[ID{}]不进行压缩", id), 303 | }; 304 | let content_compressed = compress(Vec::from(html), response.content_encoding).unwrap(); 305 | response.content_length = content_compressed.len() as u64; 306 | response.content_type = Some("text/html;charset=utf-8".to_string()); 307 | response.content = Some(Bytes::from(content_compressed)); 308 | response 309 | } 310 | 311 | /// 设定时间为当前时刻 312 | fn set_date(&mut self) -> &mut Self { 313 | self.date = Utc::now(); 314 | self 315 | } 316 | 317 | /// 设置响应协议版本,当前固定为HTTP1.1 318 | fn set_version(&mut self) -> &mut Self { 319 | self.version = HttpVersion::V1_1; 320 | self 321 | } 322 | 323 | /// 设置服务器名 324 | fn set_server_name(&mut self) -> &mut Self { 325 | self.server_name = SERVER_NAME.to_string(); 326 | self 327 | } 328 | 329 | /// 本函数根据传入的`code`参数设置`self`对象的状态码字段和HTTP信息字段。状态码和信息是一一对应的。 330 | /// 331 | /// 现行HTTP协议的状态码由[RFC9110#15](https://www.rfc-editor.org/rfc/rfc9110#section-15)规定。 332 | /// 333 | /// ## 参数: 334 | /// - `code`: 状态码。实际上HTTP状态码最大的也就是500多,因此采用`u16` 335 | fn set_code(&mut self, code: u16) -> &mut Self { 336 | self.status_code = code; 337 | self.information = match STATUS_CODES.get(&code) { 338 | Some(&debug) => debug.to_string(), 339 | None => { 340 | error!("非法的状态码:{}。这条错误说明代码编写出现了错误。", code); 341 | panic!(); 342 | } 343 | }; 344 | self 345 | } 346 | 347 | /// 预设的404 Response 348 | pub fn response_404(request: &Request, id: u128) -> Self { 349 | let accept_encoding = request.accept_encoding().to_vec(); 350 | Self::from_status_code(404, accept_encoding, id) 351 | .set_date() 352 | .set_code(404) 353 | .set_version() 354 | .to_owned() 355 | } 356 | 357 | /// 预设的500 Response 358 | pub fn response_500(request: &Request, id: u128) -> Self { 359 | let accept_encoding = request.accept_encoding().to_vec(); 360 | Self::from_status_code(500, accept_encoding, id) 361 | .set_date() 362 | .set_code(500) 363 | .set_version() 364 | .to_owned() 365 | } 366 | 367 | /// 通过指定的路径创建一个`response`对象 368 | /// 369 | /// ## 参数 370 | /// - `path`: 文件的完整路径 371 | /// - `request`: 来自浏览器的`request` 372 | /// - `id`: 用于日志的TCP连接编号 373 | /// - `cache`: 共享的文件缓存指针 374 | /// 375 | /// ## 返回 376 | /// - HTTP响应 377 | pub fn from(path: &str, request: &Request, id: u128, cache: &Arc>) -> Response { 378 | let accept_encoding = request.accept_encoding().to_vec(); 379 | let method = request.method(); 380 | let metadata_result = fs::metadata(path); 381 | 382 | // 仅有下列方法得到支持,其他方法一律返回405 383 | if method != HttpRequestMethod::Get 384 | && method != HttpRequestMethod::Head 385 | && method != HttpRequestMethod::Options { 386 | return Self::from_status_code(405, accept_encoding, id) 387 | .set_date() 388 | .set_version() 389 | .set_server_name() 390 | .to_owned(); 391 | } 392 | 393 | // 对于OPTIONS方法的处理 394 | // OPTIONS允许指定明确的请求路径,或者请求*。服务器目前对所有的请求资源均使用相同的请求方法,因此无需特别处理路径问题。 395 | if method == HttpRequestMethod::Options { 396 | debug!("[ID{}]请求方法为OPTIONS", id); 397 | return Self::from_status_code(204, accept_encoding, id) 398 | .set_date() 399 | .set_version() 400 | .set_server_name() 401 | .to_owned(); 402 | } 403 | 404 | // 若请求方法为HEAD,则设置headonly为true 405 | let headonly = match method { 406 | HttpRequestMethod::Head => { 407 | debug!("[ID{}]请求方法为HEAD", id); 408 | true 409 | }, 410 | _ => false, 411 | }; 412 | 413 | match metadata_result { 414 | Ok(metadata) => { 415 | if metadata.is_dir() { // path是目录 416 | debug!("[ID{}]请求的路径是目录", id); 417 | Self::from_dir(path, accept_encoding, id, cache, headonly) 418 | .set_date() 419 | .set_code(200) 420 | .set_version() 421 | .set_server_name() 422 | .to_owned() 423 | } else { // path是文件 424 | debug!("[ID{}]请求的路径是文件", id); 425 | let extention = match Path::new(path).extension() { 426 | Some(e) => e, 427 | None => { 428 | error!("[ID{}]无法确定请求路径{}的文件扩展名", id, path); 429 | return Self::response_404(request, id); 430 | } 431 | }; 432 | debug!("[ID{}]文件扩展名: {}", id, extention.to_str().unwrap()); 433 | // 特殊情况:文件扩展名是PHP 434 | if extention == "php" { 435 | debug!("[ID{}]请求的文件是PHP,启用PHP处理", id); 436 | let html = match handle_php(path, id) { 437 | Ok(html) => html, 438 | Err(e) => { 439 | error!("[ID{}]解析PHP文件{}时出错:{}", id, path, e); 440 | return Self::response_500(request, id); 441 | } 442 | }; 443 | return Self::from_html(&html, accept_encoding, id, headonly) 444 | .set_date() 445 | .set_code(200) 446 | .set_version() 447 | .set_server_name() 448 | .to_owned(); 449 | } 450 | let mime = get_mime(extention); 451 | debug!("[ID{}]MIME类型: {}", id, mime); 452 | Self::from_file(path, accept_encoding, id, cache, headonly, mime) 453 | .set_date() 454 | .set_code(200) 455 | .set_version() 456 | .set_server_name() 457 | .to_owned() 458 | } 459 | } 460 | Err(_) => { 461 | warn!("[ID{}]无法获取{}的元数据,产生500 response", id, path); 462 | Self::response_500(request, id) 463 | } 464 | } 465 | } 466 | 467 | /// 将一个 `Response` 对象转换为字节流 468 | pub fn as_bytes(&self) -> Vec { 469 | // 如果content字段是None,那么content-type和content-encoding也必须是None 470 | if self.content == None { 471 | assert_eq!(self.content_encoding, None); 472 | assert_eq!(self.content_type, None); 473 | } 474 | // 获取各字段的&str 475 | let version: &str = match self.version { 476 | HttpVersion::V1_1 => "HTTP/1.1", 477 | }; 478 | let status_code: &str = &self.status_code.to_string(); 479 | let information: &str = &self.information; 480 | let content_length: &str = &self.content_length.to_string(); 481 | let date: &str = &format_date(&self.date); 482 | let server: &str = &self.server_name; 483 | 484 | // 拼接响应 485 | let header = [ 486 | version, " ", status_code, " ", information, CRLF, 487 | // 选择性地填入content_type 488 | match &self.content_type { 489 | Some(t) => { 490 | ["Content-Type: ", &t, CRLF].concat() 491 | }, 492 | None => "".to_string(), 493 | }.as_str(), 494 | // 选择性地填入content_encoding 495 | match self.content_encoding { 496 | Some(e) => { 497 | [ 498 | "Content-encoding: ", 499 | match e { 500 | HttpEncoding::Gzip => "gzip", 501 | HttpEncoding::Deflate => "deflate", 502 | HttpEncoding::Br => "br", 503 | }, 504 | CRLF, 505 | ].concat().to_string() 506 | }, 507 | None => "".to_string(), 508 | }.as_str(), 509 | "Content-Length: ", content_length, CRLF, 510 | "Date: ", date, CRLF, 511 | "Server: ", server, CRLF, 512 | // 选择性地填入allow 513 | match &self.allow { 514 | Some(a) => { 515 | let mut allow_str = String::new(); 516 | for (index, method) in a.iter().enumerate() { 517 | allow_str.push_str(&format!("{}", method)); 518 | // 如果后面还有,就加一个逗号分隔 519 | if index < a.len() - 1 { 520 | allow_str.push_str(", "); 521 | } 522 | } 523 | ["Allow: ", &allow_str, CRLF].concat() 524 | }, 525 | None => "".to_string(), 526 | }.as_str(), 527 | CRLF, // 分隔响应头和响应体的空行 528 | ].concat(); 529 | // 然后拼接响应体,注意响应体可能有压缩,因此需要以Vec格式拼接,而不是上面的String。 530 | [ 531 | header.as_bytes(), 532 | // 选择性地填入响应体 533 | match &self.content { 534 | Some(c) => &c, 535 | None => b"", 536 | }, 537 | ].concat() 538 | } 539 | } 540 | 541 | impl Response { 542 | pub fn status_code(&self) -> u16 { 543 | self.status_code 544 | } 545 | 546 | pub fn information(&self) -> &str { 547 | &self.information 548 | } 549 | } 550 | 551 | /// 格式化时间,使用`chrono` crate自带的`to_rfc2822`方法 552 | fn format_date(date: &DateTime) -> String { 553 | date.to_rfc2822() 554 | } 555 | 556 | /// 压缩响应体 557 | /// 558 | /// ## 参数: 559 | /// - `data`:响应体数据,以字节流形式给出 560 | /// - `mode`:指定的压缩格式,见[HttpEncoding] 561 | /// 562 | /// ## 返回: 563 | /// - 压缩后的响应体数据,以字节流形式给出 564 | fn compress(data: Vec, mode: Option) -> io::Result> { 565 | match mode { 566 | Some(HttpEncoding::Gzip) => { 567 | let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); 568 | encoder.write_all(&data)?; 569 | encoder.finish() 570 | }, 571 | Some(HttpEncoding::Deflate) => { 572 | let mut encoder = DeflateEncoder::new(Vec::new(), Compression::default()); 573 | encoder.write_all(&data)?; 574 | encoder.finish() 575 | }, 576 | Some(HttpEncoding::Br) => { 577 | let params = BrotliEncoderParams::default(); 578 | let mut output = Vec::new(); 579 | enc::BrotliCompress(&mut io::Cursor::new(data), &mut output, ¶ms)?; 580 | Ok(output) 581 | }, 582 | None => { 583 | // 无压缩方式,直接返回原文 584 | Ok(data) 585 | } 586 | } 587 | } 588 | 589 | /// 确定响应体压缩编码的逻辑: 590 | /// 1. 如果浏览器支持Brotli,则使用Brotli。 591 | /// 2. 否则,如果浏览器支持Gzip,则使用Gzip。 592 | /// 3. 否则,如果浏览器支持Deflate,则使用Deflate。 593 | /// 4. 再否则,就只好不压缩了。 594 | /// 595 | /// 实测Brotli太慢,因此优先用Gzip。考虑后期换一个brotli库。 596 | fn decide_encoding(accept_encoding: &Vec) -> Option { 597 | if accept_encoding.contains(&HttpEncoding::Gzip) { 598 | Some(HttpEncoding::Gzip) 599 | } else if accept_encoding.contains(&HttpEncoding::Deflate) { 600 | Some(HttpEncoding::Deflate) 601 | } else { 602 | None 603 | } 604 | } 605 | 606 | /// MIME 607 | /// 608 | /// 保存了常见文件类型的映射关系 609 | /// 610 | /// 611 | fn get_mime(extension: &OsStr) -> &str { 612 | let extension = match extension.to_str() { 613 | Some(e) => e, 614 | None => { 615 | error!("无法将&OsStr转换为&str类型"); 616 | return "application/octet-stream"; 617 | } 618 | }; 619 | match MIME_TYPES.get(extension) { 620 | Some(v) => v, 621 | None => "application/octet-stream", 622 | } 623 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.0.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "alloc-no-stdlib" 31 | version = "2.0.4" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 34 | 35 | [[package]] 36 | name = "alloc-stdlib" 37 | version = "0.2.2" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 40 | dependencies = [ 41 | "alloc-no-stdlib", 42 | ] 43 | 44 | [[package]] 45 | name = "android-tzdata" 46 | version = "0.1.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 49 | 50 | [[package]] 51 | name = "android_system_properties" 52 | version = "0.1.5" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 55 | dependencies = [ 56 | "libc", 57 | ] 58 | 59 | [[package]] 60 | name = "anyhow" 61 | version = "1.0.71" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" 64 | 65 | [[package]] 66 | name = "arc-swap" 67 | version = "1.6.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" 70 | 71 | [[package]] 72 | name = "autocfg" 73 | version = "1.1.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 76 | 77 | [[package]] 78 | name = "backtrace" 79 | version = "0.3.71" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 82 | dependencies = [ 83 | "addr2line", 84 | "cc", 85 | "cfg-if", 86 | "libc", 87 | "miniz_oxide", 88 | "object", 89 | "rustc-demangle", 90 | ] 91 | 92 | [[package]] 93 | name = "bitflags" 94 | version = "1.3.2" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 97 | 98 | [[package]] 99 | name = "brotli" 100 | version = "3.5.0" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" 103 | dependencies = [ 104 | "alloc-no-stdlib", 105 | "alloc-stdlib", 106 | "brotli-decompressor", 107 | ] 108 | 109 | [[package]] 110 | name = "brotli-decompressor" 111 | version = "2.5.1" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" 114 | dependencies = [ 115 | "alloc-no-stdlib", 116 | "alloc-stdlib", 117 | ] 118 | 119 | [[package]] 120 | name = "bumpalo" 121 | version = "3.12.2" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" 124 | 125 | [[package]] 126 | name = "bytes" 127 | version = "1.6.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 130 | 131 | [[package]] 132 | name = "cc" 133 | version = "1.0.90" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" 136 | 137 | [[package]] 138 | name = "cfg-if" 139 | version = "1.0.0" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 142 | 143 | [[package]] 144 | name = "chrono" 145 | version = "0.4.35" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" 148 | dependencies = [ 149 | "android-tzdata", 150 | "iana-time-zone", 151 | "js-sys", 152 | "num-traits", 153 | "wasm-bindgen", 154 | "windows-targets 0.52.4", 155 | ] 156 | 157 | [[package]] 158 | name = "core-foundation-sys" 159 | version = "0.8.4" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" 162 | 163 | [[package]] 164 | name = "crc32fast" 165 | version = "1.3.2" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 168 | dependencies = [ 169 | "cfg-if", 170 | ] 171 | 172 | [[package]] 173 | name = "derivative" 174 | version = "2.2.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 177 | dependencies = [ 178 | "proc-macro2", 179 | "quote", 180 | "syn 1.0.109", 181 | ] 182 | 183 | [[package]] 184 | name = "destructure_traitobject" 185 | version = "0.2.0" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" 188 | 189 | [[package]] 190 | name = "equivalent" 191 | version = "1.0.1" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 194 | 195 | [[package]] 196 | name = "flate2" 197 | version = "1.0.28" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" 200 | dependencies = [ 201 | "crc32fast", 202 | "miniz_oxide", 203 | ] 204 | 205 | [[package]] 206 | name = "fnv" 207 | version = "1.0.7" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 210 | 211 | [[package]] 212 | name = "getrandom" 213 | version = "0.2.12" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 216 | dependencies = [ 217 | "cfg-if", 218 | "libc", 219 | "wasi", 220 | ] 221 | 222 | [[package]] 223 | name = "gimli" 224 | version = "0.28.1" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 227 | 228 | [[package]] 229 | name = "hashbrown" 230 | version = "0.14.3" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 233 | 234 | [[package]] 235 | name = "hermit-abi" 236 | version = "0.3.9" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 239 | 240 | [[package]] 241 | name = "humantime" 242 | version = "2.1.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 245 | 246 | [[package]] 247 | name = "iana-time-zone" 248 | version = "0.1.56" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" 251 | dependencies = [ 252 | "android_system_properties", 253 | "core-foundation-sys", 254 | "iana-time-zone-haiku", 255 | "js-sys", 256 | "wasm-bindgen", 257 | "windows", 258 | ] 259 | 260 | [[package]] 261 | name = "iana-time-zone-haiku" 262 | version = "0.1.2" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 265 | dependencies = [ 266 | "cc", 267 | ] 268 | 269 | [[package]] 270 | name = "indexmap" 271 | version = "2.2.6" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 274 | dependencies = [ 275 | "equivalent", 276 | "hashbrown", 277 | ] 278 | 279 | [[package]] 280 | name = "itoa" 281 | version = "1.0.6" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 284 | 285 | [[package]] 286 | name = "js-sys" 287 | version = "0.3.63" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" 290 | dependencies = [ 291 | "wasm-bindgen", 292 | ] 293 | 294 | [[package]] 295 | name = "lazy_static" 296 | version = "1.4.0" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 299 | 300 | [[package]] 301 | name = "libc" 302 | version = "0.2.153" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 305 | 306 | [[package]] 307 | name = "lock_api" 308 | version = "0.4.9" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 311 | dependencies = [ 312 | "autocfg", 313 | "scopeguard", 314 | ] 315 | 316 | [[package]] 317 | name = "log" 318 | version = "0.4.21" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 321 | dependencies = [ 322 | "serde", 323 | ] 324 | 325 | [[package]] 326 | name = "log-mdc" 327 | version = "0.1.0" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" 330 | 331 | [[package]] 332 | name = "log4rs" 333 | version = "1.3.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6" 336 | dependencies = [ 337 | "anyhow", 338 | "arc-swap", 339 | "chrono", 340 | "derivative", 341 | "fnv", 342 | "humantime", 343 | "libc", 344 | "log", 345 | "log-mdc", 346 | "once_cell", 347 | "parking_lot", 348 | "rand", 349 | "serde", 350 | "serde-value", 351 | "serde_json", 352 | "serde_yaml", 353 | "thiserror", 354 | "thread-id", 355 | "typemap-ors", 356 | "winapi", 357 | ] 358 | 359 | [[package]] 360 | name = "memchr" 361 | version = "2.7.1" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 364 | 365 | [[package]] 366 | name = "miniz_oxide" 367 | version = "0.7.1" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 370 | dependencies = [ 371 | "adler", 372 | ] 373 | 374 | [[package]] 375 | name = "mio" 376 | version = "0.8.11" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 379 | dependencies = [ 380 | "libc", 381 | "wasi", 382 | "windows-sys 0.48.0", 383 | ] 384 | 385 | [[package]] 386 | name = "num-traits" 387 | version = "0.2.15" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 390 | dependencies = [ 391 | "autocfg", 392 | ] 393 | 394 | [[package]] 395 | name = "num_cpus" 396 | version = "1.16.0" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 399 | dependencies = [ 400 | "hermit-abi", 401 | "libc", 402 | ] 403 | 404 | [[package]] 405 | name = "object" 406 | version = "0.32.2" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 409 | dependencies = [ 410 | "memchr", 411 | ] 412 | 413 | [[package]] 414 | name = "once_cell" 415 | version = "1.17.1" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 418 | 419 | [[package]] 420 | name = "ordered-float" 421 | version = "2.10.0" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" 424 | dependencies = [ 425 | "num-traits", 426 | ] 427 | 428 | [[package]] 429 | name = "parking_lot" 430 | version = "0.12.1" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 433 | dependencies = [ 434 | "lock_api", 435 | "parking_lot_core", 436 | ] 437 | 438 | [[package]] 439 | name = "parking_lot_core" 440 | version = "0.9.7" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" 443 | dependencies = [ 444 | "cfg-if", 445 | "libc", 446 | "redox_syscall", 447 | "smallvec", 448 | "windows-sys 0.45.0", 449 | ] 450 | 451 | [[package]] 452 | name = "pin-project-lite" 453 | version = "0.2.13" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 456 | 457 | [[package]] 458 | name = "ppv-lite86" 459 | version = "0.2.17" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 462 | 463 | [[package]] 464 | name = "proc-macro2" 465 | version = "1.0.79" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" 468 | dependencies = [ 469 | "unicode-ident", 470 | ] 471 | 472 | [[package]] 473 | name = "quote" 474 | version = "1.0.35" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 477 | dependencies = [ 478 | "proc-macro2", 479 | ] 480 | 481 | [[package]] 482 | name = "rand" 483 | version = "0.8.5" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 486 | dependencies = [ 487 | "libc", 488 | "rand_chacha", 489 | "rand_core", 490 | ] 491 | 492 | [[package]] 493 | name = "rand_chacha" 494 | version = "0.3.1" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 497 | dependencies = [ 498 | "ppv-lite86", 499 | "rand_core", 500 | ] 501 | 502 | [[package]] 503 | name = "rand_core" 504 | version = "0.6.4" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 507 | dependencies = [ 508 | "getrandom", 509 | ] 510 | 511 | [[package]] 512 | name = "redox_syscall" 513 | version = "0.2.16" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 516 | dependencies = [ 517 | "bitflags", 518 | ] 519 | 520 | [[package]] 521 | name = "regex" 522 | version = "1.10.4" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" 525 | dependencies = [ 526 | "aho-corasick", 527 | "memchr", 528 | "regex-automata", 529 | "regex-syntax", 530 | ] 531 | 532 | [[package]] 533 | name = "regex-automata" 534 | version = "0.4.6" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 537 | dependencies = [ 538 | "aho-corasick", 539 | "memchr", 540 | "regex-syntax", 541 | ] 542 | 543 | [[package]] 544 | name = "regex-syntax" 545 | version = "0.8.2" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 548 | 549 | [[package]] 550 | name = "rustc-demangle" 551 | version = "0.1.23" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 554 | 555 | [[package]] 556 | name = "ryu" 557 | version = "1.0.13" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 560 | 561 | [[package]] 562 | name = "scopeguard" 563 | version = "1.1.0" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 566 | 567 | [[package]] 568 | name = "serde" 569 | version = "1.0.197" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" 572 | dependencies = [ 573 | "serde_derive", 574 | ] 575 | 576 | [[package]] 577 | name = "serde-value" 578 | version = "0.7.0" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" 581 | dependencies = [ 582 | "ordered-float", 583 | "serde", 584 | ] 585 | 586 | [[package]] 587 | name = "serde_derive" 588 | version = "1.0.197" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" 591 | dependencies = [ 592 | "proc-macro2", 593 | "quote", 594 | "syn 2.0.55", 595 | ] 596 | 597 | [[package]] 598 | name = "serde_json" 599 | version = "1.0.96" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" 602 | dependencies = [ 603 | "itoa", 604 | "ryu", 605 | "serde", 606 | ] 607 | 608 | [[package]] 609 | name = "serde_spanned" 610 | version = "0.6.5" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" 613 | dependencies = [ 614 | "serde", 615 | ] 616 | 617 | [[package]] 618 | name = "serde_yaml" 619 | version = "0.9.34+deprecated" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 622 | dependencies = [ 623 | "indexmap", 624 | "itoa", 625 | "ryu", 626 | "serde", 627 | "unsafe-libyaml", 628 | ] 629 | 630 | [[package]] 631 | name = "signal-hook-registry" 632 | version = "1.4.1" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 635 | dependencies = [ 636 | "libc", 637 | ] 638 | 639 | [[package]] 640 | name = "smallvec" 641 | version = "1.10.0" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 644 | 645 | [[package]] 646 | name = "socket2" 647 | version = "0.5.6" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" 650 | dependencies = [ 651 | "libc", 652 | "windows-sys 0.52.0", 653 | ] 654 | 655 | [[package]] 656 | name = "syn" 657 | version = "1.0.109" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 660 | dependencies = [ 661 | "proc-macro2", 662 | "quote", 663 | "unicode-ident", 664 | ] 665 | 666 | [[package]] 667 | name = "syn" 668 | version = "2.0.55" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" 671 | dependencies = [ 672 | "proc-macro2", 673 | "quote", 674 | "unicode-ident", 675 | ] 676 | 677 | [[package]] 678 | name = "thiserror" 679 | version = "1.0.40" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" 682 | dependencies = [ 683 | "thiserror-impl", 684 | ] 685 | 686 | [[package]] 687 | name = "thiserror-impl" 688 | version = "1.0.40" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" 691 | dependencies = [ 692 | "proc-macro2", 693 | "quote", 694 | "syn 2.0.55", 695 | ] 696 | 697 | [[package]] 698 | name = "thread-id" 699 | version = "4.1.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "3ee93aa2b8331c0fec9091548843f2c90019571814057da3b783f9de09349d73" 702 | dependencies = [ 703 | "libc", 704 | "redox_syscall", 705 | "winapi", 706 | ] 707 | 708 | [[package]] 709 | name = "tokio" 710 | version = "1.36.0" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" 713 | dependencies = [ 714 | "backtrace", 715 | "bytes", 716 | "libc", 717 | "mio", 718 | "num_cpus", 719 | "parking_lot", 720 | "pin-project-lite", 721 | "signal-hook-registry", 722 | "socket2", 723 | "tokio-macros", 724 | "windows-sys 0.48.0", 725 | ] 726 | 727 | [[package]] 728 | name = "tokio-macros" 729 | version = "2.2.0" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" 732 | dependencies = [ 733 | "proc-macro2", 734 | "quote", 735 | "syn 2.0.55", 736 | ] 737 | 738 | [[package]] 739 | name = "toml" 740 | version = "0.8.12" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" 743 | dependencies = [ 744 | "serde", 745 | "serde_spanned", 746 | "toml_datetime", 747 | "toml_edit", 748 | ] 749 | 750 | [[package]] 751 | name = "toml_datetime" 752 | version = "0.6.5" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 755 | dependencies = [ 756 | "serde", 757 | ] 758 | 759 | [[package]] 760 | name = "toml_edit" 761 | version = "0.22.9" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" 764 | dependencies = [ 765 | "indexmap", 766 | "serde", 767 | "serde_spanned", 768 | "toml_datetime", 769 | "winnow", 770 | ] 771 | 772 | [[package]] 773 | name = "typemap-ors" 774 | version = "1.0.0" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" 777 | dependencies = [ 778 | "unsafe-any-ors", 779 | ] 780 | 781 | [[package]] 782 | name = "unicode-ident" 783 | version = "1.0.8" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 786 | 787 | [[package]] 788 | name = "unsafe-any-ors" 789 | version = "1.0.0" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" 792 | dependencies = [ 793 | "destructure_traitobject", 794 | ] 795 | 796 | [[package]] 797 | name = "unsafe-libyaml" 798 | version = "0.2.11" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 801 | 802 | [[package]] 803 | name = "wasi" 804 | version = "0.11.0+wasi-snapshot-preview1" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 807 | 808 | [[package]] 809 | name = "wasm-bindgen" 810 | version = "0.2.86" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" 813 | dependencies = [ 814 | "cfg-if", 815 | "wasm-bindgen-macro", 816 | ] 817 | 818 | [[package]] 819 | name = "wasm-bindgen-backend" 820 | version = "0.2.86" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" 823 | dependencies = [ 824 | "bumpalo", 825 | "log", 826 | "once_cell", 827 | "proc-macro2", 828 | "quote", 829 | "syn 2.0.55", 830 | "wasm-bindgen-shared", 831 | ] 832 | 833 | [[package]] 834 | name = "wasm-bindgen-macro" 835 | version = "0.2.86" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" 838 | dependencies = [ 839 | "quote", 840 | "wasm-bindgen-macro-support", 841 | ] 842 | 843 | [[package]] 844 | name = "wasm-bindgen-macro-support" 845 | version = "0.2.86" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" 848 | dependencies = [ 849 | "proc-macro2", 850 | "quote", 851 | "syn 2.0.55", 852 | "wasm-bindgen-backend", 853 | "wasm-bindgen-shared", 854 | ] 855 | 856 | [[package]] 857 | name = "wasm-bindgen-shared" 858 | version = "0.2.86" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" 861 | 862 | [[package]] 863 | name = "webserver" 864 | version = "0.1.0" 865 | dependencies = [ 866 | "brotli", 867 | "bytes", 868 | "chrono", 869 | "flate2", 870 | "lazy_static", 871 | "log", 872 | "log4rs", 873 | "num_cpus", 874 | "regex", 875 | "serde", 876 | "serde_derive", 877 | "tokio", 878 | "toml", 879 | ] 880 | 881 | [[package]] 882 | name = "winapi" 883 | version = "0.3.9" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 886 | dependencies = [ 887 | "winapi-i686-pc-windows-gnu", 888 | "winapi-x86_64-pc-windows-gnu", 889 | ] 890 | 891 | [[package]] 892 | name = "winapi-i686-pc-windows-gnu" 893 | version = "0.4.0" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 896 | 897 | [[package]] 898 | name = "winapi-x86_64-pc-windows-gnu" 899 | version = "0.4.0" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 902 | 903 | [[package]] 904 | name = "windows" 905 | version = "0.48.0" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" 908 | dependencies = [ 909 | "windows-targets 0.48.0", 910 | ] 911 | 912 | [[package]] 913 | name = "windows-sys" 914 | version = "0.45.0" 915 | source = "registry+https://github.com/rust-lang/crates.io-index" 916 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 917 | dependencies = [ 918 | "windows-targets 0.42.2", 919 | ] 920 | 921 | [[package]] 922 | name = "windows-sys" 923 | version = "0.48.0" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 926 | dependencies = [ 927 | "windows-targets 0.48.0", 928 | ] 929 | 930 | [[package]] 931 | name = "windows-sys" 932 | version = "0.52.0" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 935 | dependencies = [ 936 | "windows-targets 0.52.4", 937 | ] 938 | 939 | [[package]] 940 | name = "windows-targets" 941 | version = "0.42.2" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 944 | dependencies = [ 945 | "windows_aarch64_gnullvm 0.42.2", 946 | "windows_aarch64_msvc 0.42.2", 947 | "windows_i686_gnu 0.42.2", 948 | "windows_i686_msvc 0.42.2", 949 | "windows_x86_64_gnu 0.42.2", 950 | "windows_x86_64_gnullvm 0.42.2", 951 | "windows_x86_64_msvc 0.42.2", 952 | ] 953 | 954 | [[package]] 955 | name = "windows-targets" 956 | version = "0.48.0" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 959 | dependencies = [ 960 | "windows_aarch64_gnullvm 0.48.0", 961 | "windows_aarch64_msvc 0.48.0", 962 | "windows_i686_gnu 0.48.0", 963 | "windows_i686_msvc 0.48.0", 964 | "windows_x86_64_gnu 0.48.0", 965 | "windows_x86_64_gnullvm 0.48.0", 966 | "windows_x86_64_msvc 0.48.0", 967 | ] 968 | 969 | [[package]] 970 | name = "windows-targets" 971 | version = "0.52.4" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" 974 | dependencies = [ 975 | "windows_aarch64_gnullvm 0.52.4", 976 | "windows_aarch64_msvc 0.52.4", 977 | "windows_i686_gnu 0.52.4", 978 | "windows_i686_msvc 0.52.4", 979 | "windows_x86_64_gnu 0.52.4", 980 | "windows_x86_64_gnullvm 0.52.4", 981 | "windows_x86_64_msvc 0.52.4", 982 | ] 983 | 984 | [[package]] 985 | name = "windows_aarch64_gnullvm" 986 | version = "0.42.2" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 989 | 990 | [[package]] 991 | name = "windows_aarch64_gnullvm" 992 | version = "0.48.0" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 995 | 996 | [[package]] 997 | name = "windows_aarch64_gnullvm" 998 | version = "0.52.4" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" 1001 | 1002 | [[package]] 1003 | name = "windows_aarch64_msvc" 1004 | version = "0.42.2" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 1007 | 1008 | [[package]] 1009 | name = "windows_aarch64_msvc" 1010 | version = "0.48.0" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 1013 | 1014 | [[package]] 1015 | name = "windows_aarch64_msvc" 1016 | version = "0.52.4" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" 1019 | 1020 | [[package]] 1021 | name = "windows_i686_gnu" 1022 | version = "0.42.2" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 1025 | 1026 | [[package]] 1027 | name = "windows_i686_gnu" 1028 | version = "0.48.0" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 1031 | 1032 | [[package]] 1033 | name = "windows_i686_gnu" 1034 | version = "0.52.4" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" 1037 | 1038 | [[package]] 1039 | name = "windows_i686_msvc" 1040 | version = "0.42.2" 1041 | source = "registry+https://github.com/rust-lang/crates.io-index" 1042 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 1043 | 1044 | [[package]] 1045 | name = "windows_i686_msvc" 1046 | version = "0.48.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 1049 | 1050 | [[package]] 1051 | name = "windows_i686_msvc" 1052 | version = "0.52.4" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" 1055 | 1056 | [[package]] 1057 | name = "windows_x86_64_gnu" 1058 | version = "0.42.2" 1059 | source = "registry+https://github.com/rust-lang/crates.io-index" 1060 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 1061 | 1062 | [[package]] 1063 | name = "windows_x86_64_gnu" 1064 | version = "0.48.0" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 1067 | 1068 | [[package]] 1069 | name = "windows_x86_64_gnu" 1070 | version = "0.52.4" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" 1073 | 1074 | [[package]] 1075 | name = "windows_x86_64_gnullvm" 1076 | version = "0.42.2" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 1079 | 1080 | [[package]] 1081 | name = "windows_x86_64_gnullvm" 1082 | version = "0.48.0" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 1085 | 1086 | [[package]] 1087 | name = "windows_x86_64_gnullvm" 1088 | version = "0.52.4" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" 1091 | 1092 | [[package]] 1093 | name = "windows_x86_64_msvc" 1094 | version = "0.42.2" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 1097 | 1098 | [[package]] 1099 | name = "windows_x86_64_msvc" 1100 | version = "0.48.0" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 1103 | 1104 | [[package]] 1105 | name = "windows_x86_64_msvc" 1106 | version = "0.52.4" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" 1109 | 1110 | [[package]] 1111 | name = "winnow" 1112 | version = "0.6.5" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" 1115 | dependencies = [ 1116 | "memchr", 1117 | ] 1118 | -------------------------------------------------------------------------------- /files/html/foyue/js/crypto-js.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports?module.exports=exports=e():"function"==typeof define&&define.amd?define([],e):t.CryptoJS=e()}(this,function(){var h,t,e,r,i,n,f,o,s,c,a,l,d,m,x,b,H,z,A,u,p,_,v,y,g,B,w,k,S,C,D,E,R,M,F,P,W,O,I,U,K,X,L,j,N,T,q,Z,V,G,J,$,Q,Y,tt,et,rt,it,nt,ot,st,ct,at,ht,lt,ft,dt,ut,pt,_t,vt,yt,gt,Bt,wt,kt,St,bt=bt||function(l){var t;if("undefined"!=typeof window&&window.crypto&&(t=window.crypto),!t&&"undefined"!=typeof window&&window.msCrypto&&(t=window.msCrypto),!t&&"undefined"!=typeof global&&global.crypto&&(t=global.crypto),!t&&"function"==typeof require)try{t=require("crypto")}catch(t){}function i(){if(t){if("function"==typeof t.getRandomValues)try{return t.getRandomValues(new Uint32Array(1))[0]}catch(t){}if("function"==typeof t.randomBytes)try{return t.randomBytes(4).readInt32LE()}catch(t){}}throw new Error("Native crypto module could not be used to get secure random number.")}var r=Object.create||function(t){var e;return n.prototype=t,e=new n,n.prototype=null,e};function n(){}var e={},o=e.lib={},s=o.Base={extend:function(t){var e=r(this);return t&&e.mixIn(t),e.hasOwnProperty("init")&&this.init!==e.init||(e.init=function(){e.$super.init.apply(this,arguments)}),(e.init.prototype=e).$super=this,e},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}},f=o.WordArray=s.extend({init:function(t,e){t=this.words=t||[],this.sigBytes=null!=e?e:4*t.length},toString:function(t){return(t||a).stringify(this)},concat:function(t){var e=this.words,r=t.words,i=this.sigBytes,n=t.sigBytes;if(this.clamp(),i%4)for(var o=0;o>>2]>>>24-o%4*8&255;e[i+o>>>2]|=s<<24-(i+o)%4*8}else for(o=0;o>>2]=r[o>>>2];return this.sigBytes+=n,this},clamp:function(){var t=this.words,e=this.sigBytes;t[e>>>2]&=4294967295<<32-e%4*8,t.length=l.ceil(e/4)},clone:function(){var t=s.clone.call(this);return t.words=this.words.slice(0),t},random:function(t){for(var e=[],r=0;r>>2]>>>24-n%4*8&255;i.push((o>>>4).toString(16)),i.push((15&o).toString(16))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i>>3]|=parseInt(t.substr(i,2),16)<<24-i%8*4;return new f.init(r,e/2)}},h=c.Latin1={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],n=0;n>>2]>>>24-n%4*8&255;i.push(String.fromCharCode(o))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i>>2]|=(255&t.charCodeAt(i))<<24-i%4*8;return new f.init(r,e)}},d=c.Utf8={stringify:function(t){try{return decodeURIComponent(escape(h.stringify(t)))}catch(t){throw new Error("Malformed UTF-8 data")}},parse:function(t){return h.parse(unescape(encodeURIComponent(t)))}},u=o.BufferedBlockAlgorithm=s.extend({reset:function(){this._data=new f.init,this._nDataBytes=0},_append:function(t){"string"==typeof t&&(t=d.parse(t)),this._data.concat(t),this._nDataBytes+=t.sigBytes},_process:function(t){var e,r=this._data,i=r.words,n=r.sigBytes,o=this.blockSize,s=n/(4*o),c=(s=t?l.ceil(s):l.max((0|s)-this._minBufferSize,0))*o,a=l.min(4*c,n);if(c){for(var h=0;h>>32-e}function Dt(t,e,r,i){var n,o=this._iv;o?(n=o.slice(0),this._iv=void 0):n=this._prevBlock,i.encryptBlock(n,0);for(var s=0;s>24&255)){var e=t>>16&255,r=t>>8&255,i=255&t;255===e?(e=0,255===r?(r=0,255===i?i=0:++i):++r):++e,t=0,t+=e<<16,t+=r<<8,t+=i}else t+=1<<24;return t}function Rt(){for(var t=this._X,e=this._C,r=0;r<8;r++)ft[r]=e[r];e[0]=e[0]+1295307597+this._b|0,e[1]=e[1]+3545052371+(e[0]>>>0>>0?1:0)|0,e[2]=e[2]+886263092+(e[1]>>>0>>0?1:0)|0,e[3]=e[3]+1295307597+(e[2]>>>0>>0?1:0)|0,e[4]=e[4]+3545052371+(e[3]>>>0>>0?1:0)|0,e[5]=e[5]+886263092+(e[4]>>>0>>0?1:0)|0,e[6]=e[6]+1295307597+(e[5]>>>0>>0?1:0)|0,e[7]=e[7]+3545052371+(e[6]>>>0>>0?1:0)|0,this._b=e[7]>>>0>>0?1:0;for(r=0;r<8;r++){var i=t[r]+e[r],n=65535&i,o=i>>>16,s=((n*n>>>17)+n*o>>>15)+o*o,c=((4294901760&i)*i|0)+((65535&i)*i|0);dt[r]=s^c}t[0]=dt[0]+(dt[7]<<16|dt[7]>>>16)+(dt[6]<<16|dt[6]>>>16)|0,t[1]=dt[1]+(dt[0]<<8|dt[0]>>>24)+dt[7]|0,t[2]=dt[2]+(dt[1]<<16|dt[1]>>>16)+(dt[0]<<16|dt[0]>>>16)|0,t[3]=dt[3]+(dt[2]<<8|dt[2]>>>24)+dt[1]|0,t[4]=dt[4]+(dt[3]<<16|dt[3]>>>16)+(dt[2]<<16|dt[2]>>>16)|0,t[5]=dt[5]+(dt[4]<<8|dt[4]>>>24)+dt[3]|0,t[6]=dt[6]+(dt[5]<<16|dt[5]>>>16)+(dt[4]<<16|dt[4]>>>16)|0,t[7]=dt[7]+(dt[6]<<8|dt[6]>>>24)+dt[5]|0}function Mt(){for(var t=this._X,e=this._C,r=0;r<8;r++)wt[r]=e[r];e[0]=e[0]+1295307597+this._b|0,e[1]=e[1]+3545052371+(e[0]>>>0>>0?1:0)|0,e[2]=e[2]+886263092+(e[1]>>>0>>0?1:0)|0,e[3]=e[3]+1295307597+(e[2]>>>0>>0?1:0)|0,e[4]=e[4]+3545052371+(e[3]>>>0>>0?1:0)|0,e[5]=e[5]+886263092+(e[4]>>>0>>0?1:0)|0,e[6]=e[6]+1295307597+(e[5]>>>0>>0?1:0)|0,e[7]=e[7]+3545052371+(e[6]>>>0>>0?1:0)|0,this._b=e[7]>>>0>>0?1:0;for(r=0;r<8;r++){var i=t[r]+e[r],n=65535&i,o=i>>>16,s=((n*n>>>17)+n*o>>>15)+o*o,c=((4294901760&i)*i|0)+((65535&i)*i|0);kt[r]=s^c}t[0]=kt[0]+(kt[7]<<16|kt[7]>>>16)+(kt[6]<<16|kt[6]>>>16)|0,t[1]=kt[1]+(kt[0]<<8|kt[0]>>>24)+kt[7]|0,t[2]=kt[2]+(kt[1]<<16|kt[1]>>>16)+(kt[0]<<16|kt[0]>>>16)|0,t[3]=kt[3]+(kt[2]<<8|kt[2]>>>24)+kt[1]|0,t[4]=kt[4]+(kt[3]<<16|kt[3]>>>16)+(kt[2]<<16|kt[2]>>>16)|0,t[5]=kt[5]+(kt[4]<<8|kt[4]>>>24)+kt[3]|0,t[6]=kt[6]+(kt[5]<<16|kt[5]>>>16)+(kt[4]<<16|kt[4]>>>16)|0,t[7]=kt[7]+(kt[6]<<8|kt[6]>>>24)+kt[5]|0}return h=bt.lib.WordArray,bt.enc.Base64={stringify:function(t){var e=t.words,r=t.sigBytes,i=this._map;t.clamp();for(var n=[],o=0;o>>2]>>>24-o%4*8&255)<<16|(e[o+1>>>2]>>>24-(o+1)%4*8&255)<<8|e[o+2>>>2]>>>24-(o+2)%4*8&255,c=0;c<4&&o+.75*c>>6*(3-c)&63));var a=i.charAt(64);if(a)for(;n.length%4;)n.push(a);return n.join("")},parse:function(t){var e=t.length,r=this._map,i=this._reverseMap;if(!i){i=this._reverseMap=[];for(var n=0;n>>6-o%4*2,a=s|c;i[n>>>2]|=a<<24-n%4*8,n++}return h.create(i,n)}(t,e,i)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="},function(l){var t=bt,e=t.lib,r=e.WordArray,i=e.Hasher,n=t.algo,H=[];!function(){for(var t=0;t<64;t++)H[t]=4294967296*l.abs(l.sin(t+1))|0}();var o=n.MD5=i.extend({_doReset:function(){this._hash=new r.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(t,e){for(var r=0;r<16;r++){var i=e+r,n=t[i];t[i]=16711935&(n<<8|n>>>24)|4278255360&(n<<24|n>>>8)}var o=this._hash.words,s=t[e+0],c=t[e+1],a=t[e+2],h=t[e+3],l=t[e+4],f=t[e+5],d=t[e+6],u=t[e+7],p=t[e+8],_=t[e+9],v=t[e+10],y=t[e+11],g=t[e+12],B=t[e+13],w=t[e+14],k=t[e+15],S=o[0],m=o[1],x=o[2],b=o[3];S=z(S,m,x,b,s,7,H[0]),b=z(b,S,m,x,c,12,H[1]),x=z(x,b,S,m,a,17,H[2]),m=z(m,x,b,S,h,22,H[3]),S=z(S,m,x,b,l,7,H[4]),b=z(b,S,m,x,f,12,H[5]),x=z(x,b,S,m,d,17,H[6]),m=z(m,x,b,S,u,22,H[7]),S=z(S,m,x,b,p,7,H[8]),b=z(b,S,m,x,_,12,H[9]),x=z(x,b,S,m,v,17,H[10]),m=z(m,x,b,S,y,22,H[11]),S=z(S,m,x,b,g,7,H[12]),b=z(b,S,m,x,B,12,H[13]),x=z(x,b,S,m,w,17,H[14]),S=A(S,m=z(m,x,b,S,k,22,H[15]),x,b,c,5,H[16]),b=A(b,S,m,x,d,9,H[17]),x=A(x,b,S,m,y,14,H[18]),m=A(m,x,b,S,s,20,H[19]),S=A(S,m,x,b,f,5,H[20]),b=A(b,S,m,x,v,9,H[21]),x=A(x,b,S,m,k,14,H[22]),m=A(m,x,b,S,l,20,H[23]),S=A(S,m,x,b,_,5,H[24]),b=A(b,S,m,x,w,9,H[25]),x=A(x,b,S,m,h,14,H[26]),m=A(m,x,b,S,p,20,H[27]),S=A(S,m,x,b,B,5,H[28]),b=A(b,S,m,x,a,9,H[29]),x=A(x,b,S,m,u,14,H[30]),S=C(S,m=A(m,x,b,S,g,20,H[31]),x,b,f,4,H[32]),b=C(b,S,m,x,p,11,H[33]),x=C(x,b,S,m,y,16,H[34]),m=C(m,x,b,S,w,23,H[35]),S=C(S,m,x,b,c,4,H[36]),b=C(b,S,m,x,l,11,H[37]),x=C(x,b,S,m,u,16,H[38]),m=C(m,x,b,S,v,23,H[39]),S=C(S,m,x,b,B,4,H[40]),b=C(b,S,m,x,s,11,H[41]),x=C(x,b,S,m,h,16,H[42]),m=C(m,x,b,S,d,23,H[43]),S=C(S,m,x,b,_,4,H[44]),b=C(b,S,m,x,g,11,H[45]),x=C(x,b,S,m,k,16,H[46]),S=D(S,m=C(m,x,b,S,a,23,H[47]),x,b,s,6,H[48]),b=D(b,S,m,x,u,10,H[49]),x=D(x,b,S,m,w,15,H[50]),m=D(m,x,b,S,f,21,H[51]),S=D(S,m,x,b,g,6,H[52]),b=D(b,S,m,x,h,10,H[53]),x=D(x,b,S,m,v,15,H[54]),m=D(m,x,b,S,c,21,H[55]),S=D(S,m,x,b,p,6,H[56]),b=D(b,S,m,x,k,10,H[57]),x=D(x,b,S,m,d,15,H[58]),m=D(m,x,b,S,B,21,H[59]),S=D(S,m,x,b,l,6,H[60]),b=D(b,S,m,x,y,10,H[61]),x=D(x,b,S,m,a,15,H[62]),m=D(m,x,b,S,_,21,H[63]),o[0]=o[0]+S|0,o[1]=o[1]+m|0,o[2]=o[2]+x|0,o[3]=o[3]+b|0},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;e[i>>>5]|=128<<24-i%32;var n=l.floor(r/4294967296),o=r;e[15+(64+i>>>9<<4)]=16711935&(n<<8|n>>>24)|4278255360&(n<<24|n>>>8),e[14+(64+i>>>9<<4)]=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8),t.sigBytes=4*(e.length+1),this._process();for(var s=this._hash,c=s.words,a=0;a<4;a++){var h=c[a];c[a]=16711935&(h<<8|h>>>24)|4278255360&(h<<24|h>>>8)}return s},clone:function(){var t=i.clone.call(this);return t._hash=this._hash.clone(),t}});function z(t,e,r,i,n,o,s){var c=t+(e&r|~e&i)+n+s;return(c<>>32-o)+e}function A(t,e,r,i,n,o,s){var c=t+(e&i|r&~i)+n+s;return(c<>>32-o)+e}function C(t,e,r,i,n,o,s){var c=t+(e^r^i)+n+s;return(c<>>32-o)+e}function D(t,e,r,i,n,o,s){var c=t+(r^(e|~i))+n+s;return(c<>>32-o)+e}t.MD5=i._createHelper(o),t.HmacMD5=i._createHmacHelper(o)}(Math),e=(t=bt).lib,r=e.WordArray,i=e.Hasher,n=t.algo,f=[],o=n.SHA1=i.extend({_doReset:function(){this._hash=new r.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],n=r[1],o=r[2],s=r[3],c=r[4],a=0;a<80;a++){if(a<16)f[a]=0|t[e+a];else{var h=f[a-3]^f[a-8]^f[a-14]^f[a-16];f[a]=h<<1|h>>>31}var l=(i<<5|i>>>27)+c+f[a];l+=a<20?1518500249+(n&o|~n&s):a<40?1859775393+(n^o^s):a<60?(n&o|n&s|o&s)-1894007588:(n^o^s)-899497514,c=s,s=o,o=n<<30|n>>>2,n=i,i=l}r[0]=r[0]+i|0,r[1]=r[1]+n|0,r[2]=r[2]+o|0,r[3]=r[3]+s|0,r[4]=r[4]+c|0},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return e[i>>>5]|=128<<24-i%32,e[14+(64+i>>>9<<4)]=Math.floor(r/4294967296),e[15+(64+i>>>9<<4)]=r,t.sigBytes=4*e.length,this._process(),this._hash},clone:function(){var t=i.clone.call(this);return t._hash=this._hash.clone(),t}}),t.SHA1=i._createHelper(o),t.HmacSHA1=i._createHmacHelper(o),function(n){var t=bt,e=t.lib,r=e.WordArray,i=e.Hasher,o=t.algo,s=[],B=[];!function(){function t(t){for(var e=n.sqrt(t),r=2;r<=e;r++)if(!(t%r))return;return 1}function e(t){return 4294967296*(t-(0|t))|0}for(var r=2,i=0;i<64;)t(r)&&(i<8&&(s[i]=e(n.pow(r,.5))),B[i]=e(n.pow(r,1/3)),i++),r++}();var w=[],c=o.SHA256=i.extend({_doReset:function(){this._hash=new r.init(s.slice(0))},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],n=r[1],o=r[2],s=r[3],c=r[4],a=r[5],h=r[6],l=r[7],f=0;f<64;f++){if(f<16)w[f]=0|t[e+f];else{var d=w[f-15],u=(d<<25|d>>>7)^(d<<14|d>>>18)^d>>>3,p=w[f-2],_=(p<<15|p>>>17)^(p<<13|p>>>19)^p>>>10;w[f]=u+w[f-7]+_+w[f-16]}var v=i&n^i&o^n&o,y=(i<<30|i>>>2)^(i<<19|i>>>13)^(i<<10|i>>>22),g=l+((c<<26|c>>>6)^(c<<21|c>>>11)^(c<<7|c>>>25))+(c&a^~c&h)+B[f]+w[f];l=h,h=a,a=c,c=s+g|0,s=o,o=n,n=i,i=g+(y+v)|0}r[0]=r[0]+i|0,r[1]=r[1]+n|0,r[2]=r[2]+o|0,r[3]=r[3]+s|0,r[4]=r[4]+c|0,r[5]=r[5]+a|0,r[6]=r[6]+h|0,r[7]=r[7]+l|0},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return e[i>>>5]|=128<<24-i%32,e[14+(64+i>>>9<<4)]=n.floor(r/4294967296),e[15+(64+i>>>9<<4)]=r,t.sigBytes=4*e.length,this._process(),this._hash},clone:function(){var t=i.clone.call(this);return t._hash=this._hash.clone(),t}});t.SHA256=i._createHelper(c),t.HmacSHA256=i._createHmacHelper(c)}(Math),function(){var n=bt.lib.WordArray,t=bt.enc;t.Utf16=t.Utf16BE={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],n=0;n>>2]>>>16-n%4*8&65535;i.push(String.fromCharCode(o))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i>>1]|=t.charCodeAt(i)<<16-i%2*16;return n.create(r,2*e)}};function s(t){return t<<8&4278255360|t>>>8&16711935}t.Utf16LE={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],n=0;n>>2]>>>16-n%4*8&65535);i.push(String.fromCharCode(o))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i>>1]|=s(t.charCodeAt(i)<<16-i%2*16);return n.create(r,2*e)}}}(),function(){if("function"==typeof ArrayBuffer){var t=bt.lib.WordArray,n=t.init;(t.init=function(t){if(t instanceof ArrayBuffer&&(t=new Uint8Array(t)),(t instanceof Int8Array||"undefined"!=typeof Uint8ClampedArray&&t instanceof Uint8ClampedArray||t instanceof Int16Array||t instanceof Uint16Array||t instanceof Int32Array||t instanceof Uint32Array||t instanceof Float32Array||t instanceof Float64Array)&&(t=new Uint8Array(t.buffer,t.byteOffset,t.byteLength)),t instanceof Uint8Array){for(var e=t.byteLength,r=[],i=0;i>>2]|=t[i]<<24-i%4*8;n.call(this,r,e)}else n.apply(this,arguments)}).prototype=t}}(),Math,c=(s=bt).lib,a=c.WordArray,l=c.Hasher,d=s.algo,m=a.create([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]),x=a.create([5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]),b=a.create([11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]),H=a.create([8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]),z=a.create([0,1518500249,1859775393,2400959708,2840853838]),A=a.create([1352829926,1548603684,1836072691,2053994217,0]),u=d.RIPEMD160=l.extend({_doReset:function(){this._hash=a.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(t,e){for(var r=0;r<16;r++){var i=e+r,n=t[i];t[i]=16711935&(n<<8|n>>>24)|4278255360&(n<<24|n>>>8)}var o,s,c,a,h,l,f,d,u,p,_,v=this._hash.words,y=z.words,g=A.words,B=m.words,w=x.words,k=b.words,S=H.words;l=o=v[0],f=s=v[1],d=c=v[2],u=a=v[3],p=h=v[4];for(r=0;r<80;r+=1)_=o+t[e+B[r]]|0,_+=r<16?mt(s,c,a)+y[0]:r<32?xt(s,c,a)+y[1]:r<48?Ht(s,c,a)+y[2]:r<64?zt(s,c,a)+y[3]:At(s,c,a)+y[4],_=(_=Ct(_|=0,k[r]))+h|0,o=h,h=a,a=Ct(c,10),c=s,s=_,_=l+t[e+w[r]]|0,_+=r<16?At(f,d,u)+g[0]:r<32?zt(f,d,u)+g[1]:r<48?Ht(f,d,u)+g[2]:r<64?xt(f,d,u)+g[3]:mt(f,d,u)+g[4],_=(_=Ct(_|=0,S[r]))+p|0,l=p,p=u,u=Ct(d,10),d=f,f=_;_=v[1]+c+u|0,v[1]=v[2]+a+p|0,v[2]=v[3]+h+l|0,v[3]=v[4]+o+f|0,v[4]=v[0]+s+d|0,v[0]=_},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;e[i>>>5]|=128<<24-i%32,e[14+(64+i>>>9<<4)]=16711935&(r<<8|r>>>24)|4278255360&(r<<24|r>>>8),t.sigBytes=4*(e.length+1),this._process();for(var n=this._hash,o=n.words,s=0;s<5;s++){var c=o[s];o[s]=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8)}return n},clone:function(){var t=l.clone.call(this);return t._hash=this._hash.clone(),t}}),s.RIPEMD160=l._createHelper(u),s.HmacRIPEMD160=l._createHmacHelper(u),p=bt.lib.Base,_=bt.enc.Utf8,bt.algo.HMAC=p.extend({init:function(t,e){t=this._hasher=new t.init,"string"==typeof e&&(e=_.parse(e));var r=t.blockSize,i=4*r;e.sigBytes>i&&(e=t.finalize(e)),e.clamp();for(var n=this._oKey=e.clone(),o=this._iKey=e.clone(),s=n.words,c=o.words,a=0;a>>24)|4278255360&(o<<24|o>>>8),s=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8),(x=r[n]).high^=s,x.low^=o}for(var c=0;c<24;c++){for(var a=0;a<5;a++){for(var h=0,l=0,f=0;f<5;f++){h^=(x=r[a+5*f]).high,l^=x.low}var d=R[a];d.high=h,d.low=l}for(a=0;a<5;a++){var u=R[(a+4)%5],p=R[(a+1)%5],_=p.high,v=p.low;for(h=u.high^(_<<1|v>>>31),l=u.low^(v<<1|_>>>31),f=0;f<5;f++){(x=r[a+5*f]).high^=h,x.low^=l}}for(var y=1;y<25;y++){var g=(x=r[y]).high,B=x.low,w=C[y];l=w<32?(h=g<>>32-w,B<>>32-w):(h=B<>>64-w,g<>>64-w);var k=R[D[y]];k.high=h,k.low=l}var S=R[0],m=r[0];S.high=m.high,S.low=m.low;for(a=0;a<5;a++)for(f=0;f<5;f++){var x=r[y=a+5*f],b=R[y],H=R[(a+1)%5+5*f],z=R[(a+2)%5+5*f];x.high=b.high^~H.high&z.high,x.low=b.low^~H.low&z.low}x=r[0];var A=E[c];x.high^=A.high,x.low^=A.low}},_doFinalize:function(){var t=this._data,e=t.words,r=(this._nDataBytes,8*t.sigBytes),i=32*this.blockSize;e[r>>>5]|=1<<24-r%32,e[(d.ceil((1+r)/i)*i>>>5)-1]|=128,t.sigBytes=4*e.length,this._process();for(var n=this._state,o=this.cfg.outputLength/8,s=o/8,c=[],a=0;a>>24)|4278255360&(l<<24|l>>>8),f=16711935&(f<<8|f>>>24)|4278255360&(f<<24|f>>>8),c.push(f),c.push(l)}return new u.init(c,o)},clone:function(){for(var t=i.clone.call(this),e=t._state=this._state.slice(0),r=0;r<25;r++)e[r]=e[r].clone();return t}});t.SHA3=i._createHelper(n),t.HmacSHA3=i._createHmacHelper(n)}(Math),function(){var t=bt,e=t.lib.Hasher,r=t.x64,i=r.Word,n=r.WordArray,o=t.algo;function s(){return i.create.apply(i,arguments)}var mt=[s(1116352408,3609767458),s(1899447441,602891725),s(3049323471,3964484399),s(3921009573,2173295548),s(961987163,4081628472),s(1508970993,3053834265),s(2453635748,2937671579),s(2870763221,3664609560),s(3624381080,2734883394),s(310598401,1164996542),s(607225278,1323610764),s(1426881987,3590304994),s(1925078388,4068182383),s(2162078206,991336113),s(2614888103,633803317),s(3248222580,3479774868),s(3835390401,2666613458),s(4022224774,944711139),s(264347078,2341262773),s(604807628,2007800933),s(770255983,1495990901),s(1249150122,1856431235),s(1555081692,3175218132),s(1996064986,2198950837),s(2554220882,3999719339),s(2821834349,766784016),s(2952996808,2566594879),s(3210313671,3203337956),s(3336571891,1034457026),s(3584528711,2466948901),s(113926993,3758326383),s(338241895,168717936),s(666307205,1188179964),s(773529912,1546045734),s(1294757372,1522805485),s(1396182291,2643833823),s(1695183700,2343527390),s(1986661051,1014477480),s(2177026350,1206759142),s(2456956037,344077627),s(2730485921,1290863460),s(2820302411,3158454273),s(3259730800,3505952657),s(3345764771,106217008),s(3516065817,3606008344),s(3600352804,1432725776),s(4094571909,1467031594),s(275423344,851169720),s(430227734,3100823752),s(506948616,1363258195),s(659060556,3750685593),s(883997877,3785050280),s(958139571,3318307427),s(1322822218,3812723403),s(1537002063,2003034995),s(1747873779,3602036899),s(1955562222,1575990012),s(2024104815,1125592928),s(2227730452,2716904306),s(2361852424,442776044),s(2428436474,593698344),s(2756734187,3733110249),s(3204031479,2999351573),s(3329325298,3815920427),s(3391569614,3928383900),s(3515267271,566280711),s(3940187606,3454069534),s(4118630271,4000239992),s(116418474,1914138554),s(174292421,2731055270),s(289380356,3203993006),s(460393269,320620315),s(685471733,587496836),s(852142971,1086792851),s(1017036298,365543100),s(1126000580,2618297676),s(1288033470,3409855158),s(1501505948,4234509866),s(1607167915,987167468),s(1816402316,1246189591)],xt=[];!function(){for(var t=0;t<80;t++)xt[t]=s()}();var c=o.SHA512=e.extend({_doReset:function(){this._hash=new n.init([new i.init(1779033703,4089235720),new i.init(3144134277,2227873595),new i.init(1013904242,4271175723),new i.init(2773480762,1595750129),new i.init(1359893119,2917565137),new i.init(2600822924,725511199),new i.init(528734635,4215389547),new i.init(1541459225,327033209)])},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],n=r[1],o=r[2],s=r[3],c=r[4],a=r[5],h=r[6],l=r[7],f=i.high,d=i.low,u=n.high,p=n.low,_=o.high,v=o.low,y=s.high,g=s.low,B=c.high,w=c.low,k=a.high,S=a.low,m=h.high,x=h.low,b=l.high,H=l.low,z=f,A=d,C=u,D=p,E=_,R=v,M=y,F=g,P=B,W=w,O=k,I=S,U=m,K=x,X=b,L=H,j=0;j<80;j++){var N,T,q=xt[j];if(j<16)T=q.high=0|t[e+2*j],N=q.low=0|t[e+2*j+1];else{var Z=xt[j-15],V=Z.high,G=Z.low,J=(V>>>1|G<<31)^(V>>>8|G<<24)^V>>>7,$=(G>>>1|V<<31)^(G>>>8|V<<24)^(G>>>7|V<<25),Q=xt[j-2],Y=Q.high,tt=Q.low,et=(Y>>>19|tt<<13)^(Y<<3|tt>>>29)^Y>>>6,rt=(tt>>>19|Y<<13)^(tt<<3|Y>>>29)^(tt>>>6|Y<<26),it=xt[j-7],nt=it.high,ot=it.low,st=xt[j-16],ct=st.high,at=st.low;T=(T=(T=J+nt+((N=$+ot)>>>0<$>>>0?1:0))+et+((N+=rt)>>>0>>0?1:0))+ct+((N+=at)>>>0>>0?1:0),q.high=T,q.low=N}var ht,lt=P&O^~P&U,ft=W&I^~W&K,dt=z&C^z&E^C&E,ut=A&D^A&R^D&R,pt=(z>>>28|A<<4)^(z<<30|A>>>2)^(z<<25|A>>>7),_t=(A>>>28|z<<4)^(A<<30|z>>>2)^(A<<25|z>>>7),vt=(P>>>14|W<<18)^(P>>>18|W<<14)^(P<<23|W>>>9),yt=(W>>>14|P<<18)^(W>>>18|P<<14)^(W<<23|P>>>9),gt=mt[j],Bt=gt.high,wt=gt.low,kt=X+vt+((ht=L+yt)>>>0>>0?1:0),St=_t+ut;X=U,L=K,U=O,K=I,O=P,I=W,P=M+(kt=(kt=(kt=kt+lt+((ht=ht+ft)>>>0>>0?1:0))+Bt+((ht=ht+wt)>>>0>>0?1:0))+T+((ht=ht+N)>>>0>>0?1:0))+((W=F+ht|0)>>>0>>0?1:0)|0,M=E,F=R,E=C,R=D,C=z,D=A,z=kt+(pt+dt+(St>>>0<_t>>>0?1:0))+((A=ht+St|0)>>>0>>0?1:0)|0}d=i.low=d+A,i.high=f+z+(d>>>0>>0?1:0),p=n.low=p+D,n.high=u+C+(p>>>0>>0?1:0),v=o.low=v+R,o.high=_+E+(v>>>0>>0?1:0),g=s.low=g+F,s.high=y+M+(g>>>0>>0?1:0),w=c.low=w+W,c.high=B+P+(w>>>0>>0?1:0),S=a.low=S+I,a.high=k+O+(S>>>0>>0?1:0),x=h.low=x+K,h.high=m+U+(x>>>0>>0?1:0),H=l.low=H+L,l.high=b+X+(H>>>0>>0?1:0)},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return e[i>>>5]|=128<<24-i%32,e[30+(128+i>>>10<<5)]=Math.floor(r/4294967296),e[31+(128+i>>>10<<5)]=r,t.sigBytes=4*e.length,this._process(),this._hash.toX32()},clone:function(){var t=e.clone.call(this);return t._hash=this._hash.clone(),t},blockSize:32});t.SHA512=e._createHelper(c),t.HmacSHA512=e._createHmacHelper(c)}(),Z=(q=bt).x64,V=Z.Word,G=Z.WordArray,J=q.algo,$=J.SHA512,Q=J.SHA384=$.extend({_doReset:function(){this._hash=new G.init([new V.init(3418070365,3238371032),new V.init(1654270250,914150663),new V.init(2438529370,812702999),new V.init(355462360,4144912697),new V.init(1731405415,4290775857),new V.init(2394180231,1750603025),new V.init(3675008525,1694076839),new V.init(1203062813,3204075428)])},_doFinalize:function(){var t=$._doFinalize.call(this);return t.sigBytes-=16,t}}),q.SHA384=$._createHelper(Q),q.HmacSHA384=$._createHmacHelper(Q),bt.lib.Cipher||function(){var t=bt,e=t.lib,r=e.Base,a=e.WordArray,i=e.BufferedBlockAlgorithm,n=t.enc,o=(n.Utf8,n.Base64),s=t.algo.EvpKDF,c=e.Cipher=i.extend({cfg:r.extend(),createEncryptor:function(t,e){return this.create(this._ENC_XFORM_MODE,t,e)},createDecryptor:function(t,e){return this.create(this._DEC_XFORM_MODE,t,e)},init:function(t,e,r){this.cfg=this.cfg.extend(r),this._xformMode=t,this._key=e,this.reset()},reset:function(){i.reset.call(this),this._doReset()},process:function(t){return this._append(t),this._process()},finalize:function(t){return t&&this._append(t),this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(i){return{encrypt:function(t,e,r){return h(e).encrypt(i,t,e,r)},decrypt:function(t,e,r){return h(e).decrypt(i,t,e,r)}}}});function h(t){return"string"==typeof t?w:g}e.StreamCipher=c.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var l,f=t.mode={},d=e.BlockCipherMode=r.extend({createEncryptor:function(t,e){return this.Encryptor.create(t,e)},createDecryptor:function(t,e){return this.Decryptor.create(t,e)},init:function(t,e){this._cipher=t,this._iv=e}}),u=f.CBC=((l=d.extend()).Encryptor=l.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize;p.call(this,t,e,i),r.encryptBlock(t,e),this._prevBlock=t.slice(e,e+i)}}),l.Decryptor=l.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,n=t.slice(e,e+i);r.decryptBlock(t,e),p.call(this,t,e,i),this._prevBlock=n}}),l);function p(t,e,r){var i,n=this._iv;n?(i=n,this._iv=void 0):i=this._prevBlock;for(var o=0;o>>2];t.sigBytes-=e}},v=(e.BlockCipher=c.extend({cfg:c.cfg.extend({mode:u,padding:_}),reset:function(){var t;c.reset.call(this);var e=this.cfg,r=e.iv,i=e.mode;this._xformMode==this._ENC_XFORM_MODE?t=i.createEncryptor:(t=i.createDecryptor,this._minBufferSize=1),this._mode&&this._mode.__creator==t?this._mode.init(this,r&&r.words):(this._mode=t.call(i,this,r&&r.words),this._mode.__creator=t)},_doProcessBlock:function(t,e){this._mode.processBlock(t,e)},_doFinalize:function(){var t,e=this.cfg.padding;return this._xformMode==this._ENC_XFORM_MODE?(e.pad(this._data,this.blockSize),t=this._process(!0)):(t=this._process(!0),e.unpad(t)),t},blockSize:4}),e.CipherParams=r.extend({init:function(t){this.mixIn(t)},toString:function(t){return(t||this.formatter).stringify(this)}})),y=(t.format={}).OpenSSL={stringify:function(t){var e=t.ciphertext,r=t.salt;return(r?a.create([1398893684,1701076831]).concat(r).concat(e):e).toString(o)},parse:function(t){var e,r=o.parse(t),i=r.words;return 1398893684==i[0]&&1701076831==i[1]&&(e=a.create(i.slice(2,4)),i.splice(0,4),r.sigBytes-=16),v.create({ciphertext:r,salt:e})}},g=e.SerializableCipher=r.extend({cfg:r.extend({format:y}),encrypt:function(t,e,r,i){i=this.cfg.extend(i);var n=t.createEncryptor(r,i),o=n.finalize(e),s=n.cfg;return v.create({ciphertext:o,key:r,iv:s.iv,algorithm:t,mode:s.mode,padding:s.padding,blockSize:t.blockSize,formatter:i.format})},decrypt:function(t,e,r,i){return i=this.cfg.extend(i),e=this._parse(e,i.format),t.createDecryptor(r,i).finalize(e.ciphertext)},_parse:function(t,e){return"string"==typeof t?e.parse(t,this):t}}),B=(t.kdf={}).OpenSSL={execute:function(t,e,r,i){i=i||a.random(8);var n=s.create({keySize:e+r}).compute(t,i),o=a.create(n.words.slice(e),4*r);return n.sigBytes=4*e,v.create({key:n,iv:o,salt:i})}},w=e.PasswordBasedCipher=g.extend({cfg:g.cfg.extend({kdf:B}),encrypt:function(t,e,r,i){var n=(i=this.cfg.extend(i)).kdf.execute(r,t.keySize,t.ivSize);i.iv=n.iv;var o=g.encrypt.call(this,t,e,n.key,i);return o.mixIn(n),o},decrypt:function(t,e,r,i){i=this.cfg.extend(i),e=this._parse(e,i.format);var n=i.kdf.execute(r,t.keySize,t.ivSize,e.salt);return i.iv=n.iv,g.decrypt.call(this,t,e,n.key,i)}})}(),bt.mode.CFB=((Y=bt.lib.BlockCipherMode.extend()).Encryptor=Y.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize;Dt.call(this,t,e,i,r),this._prevBlock=t.slice(e,e+i)}}),Y.Decryptor=Y.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,n=t.slice(e,e+i);Dt.call(this,t,e,i,r),this._prevBlock=n}}),Y),bt.mode.ECB=((tt=bt.lib.BlockCipherMode.extend()).Encryptor=tt.extend({processBlock:function(t,e){this._cipher.encryptBlock(t,e)}}),tt.Decryptor=tt.extend({processBlock:function(t,e){this._cipher.decryptBlock(t,e)}}),tt),bt.pad.AnsiX923={pad:function(t,e){var r=t.sigBytes,i=4*e,n=i-r%i,o=r+n-1;t.clamp(),t.words[o>>>2]|=n<<24-o%4*8,t.sigBytes+=n},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},bt.pad.Iso10126={pad:function(t,e){var r=4*e,i=r-t.sigBytes%r;t.concat(bt.lib.WordArray.random(i-1)).concat(bt.lib.WordArray.create([i<<24],1))},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},bt.pad.Iso97971={pad:function(t,e){t.concat(bt.lib.WordArray.create([2147483648],1)),bt.pad.ZeroPadding.pad(t,e)},unpad:function(t){bt.pad.ZeroPadding.unpad(t),t.sigBytes--}},bt.mode.OFB=(et=bt.lib.BlockCipherMode.extend(),rt=et.Encryptor=et.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,n=this._iv,o=this._keystream;n&&(o=this._keystream=n.slice(0),this._iv=void 0),r.encryptBlock(o,0);for(var s=0;s>>8^255&n^99,h[r]=n;var o=t[l[n]=r],s=t[o],c=t[s],a=257*t[n]^16843008*n;f[r]=a<<24|a>>>8,d[r]=a<<16|a>>>16,u[r]=a<<8|a>>>24,p[r]=a;a=16843009*c^65537*s^257*o^16843008*r;_[n]=a<<24|a>>>8,v[n]=a<<16|a>>>16,y[n]=a<<8|a>>>24,g[n]=a,r?(r=o^t[t[t[c^o]]],i^=t[t[i]]):r=i=1}}();var B=[0,1,2,4,8,16,32,64,128,27,54],i=r.AES=e.extend({_doReset:function(){if(!this._nRounds||this._keyPriorReset!==this._key){for(var t=this._keyPriorReset=this._key,e=t.words,r=t.sigBytes/4,i=4*(1+(this._nRounds=6+r)),n=this._keySchedule=[],o=0;o>>24]<<24|h[a>>>16&255]<<16|h[a>>>8&255]<<8|h[255&a]):(a=h[(a=a<<8|a>>>24)>>>24]<<24|h[a>>>16&255]<<16|h[a>>>8&255]<<8|h[255&a],a^=B[o/r|0]<<24),n[o]=n[o-r]^a);for(var s=this._invKeySchedule=[],c=0;c>>24]]^v[h[a>>>16&255]]^y[h[a>>>8&255]]^g[h[255&a]]}}},encryptBlock:function(t,e){this._doCryptBlock(t,e,this._keySchedule,f,d,u,p,h)},decryptBlock:function(t,e){var r=t[e+1];t[e+1]=t[e+3],t[e+3]=r,this._doCryptBlock(t,e,this._invKeySchedule,_,v,y,g,l);r=t[e+1];t[e+1]=t[e+3],t[e+3]=r},_doCryptBlock:function(t,e,r,i,n,o,s,c){for(var a=this._nRounds,h=t[e]^r[0],l=t[e+1]^r[1],f=t[e+2]^r[2],d=t[e+3]^r[3],u=4,p=1;p>>24]^n[l>>>16&255]^o[f>>>8&255]^s[255&d]^r[u++],v=i[l>>>24]^n[f>>>16&255]^o[d>>>8&255]^s[255&h]^r[u++],y=i[f>>>24]^n[d>>>16&255]^o[h>>>8&255]^s[255&l]^r[u++],g=i[d>>>24]^n[h>>>16&255]^o[l>>>8&255]^s[255&f]^r[u++];h=_,l=v,f=y,d=g}_=(c[h>>>24]<<24|c[l>>>16&255]<<16|c[f>>>8&255]<<8|c[255&d])^r[u++],v=(c[l>>>24]<<24|c[f>>>16&255]<<16|c[d>>>8&255]<<8|c[255&h])^r[u++],y=(c[f>>>24]<<24|c[d>>>16&255]<<16|c[h>>>8&255]<<8|c[255&l])^r[u++],g=(c[d>>>24]<<24|c[h>>>16&255]<<16|c[l>>>8&255]<<8|c[255&f])^r[u++];t[e]=_,t[e+1]=v,t[e+2]=y,t[e+3]=g},keySize:8});t.AES=e._createHelper(i)}(),function(){var t=bt,e=t.lib,n=e.WordArray,r=e.BlockCipher,i=t.algo,h=[57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4],l=[14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32],f=[1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28],d=[{0:8421888,268435456:32768,536870912:8421378,805306368:2,1073741824:512,1342177280:8421890,1610612736:8389122,1879048192:8388608,2147483648:514,2415919104:8389120,2684354560:33280,2952790016:8421376,3221225472:32770,3489660928:8388610,3758096384:0,4026531840:33282,134217728:0,402653184:8421890,671088640:33282,939524096:32768,1207959552:8421888,1476395008:512,1744830464:8421378,2013265920:2,2281701376:8389120,2550136832:33280,2818572288:8421376,3087007744:8389122,3355443200:8388610,3623878656:32770,3892314112:514,4160749568:8388608,1:32768,268435457:2,536870913:8421888,805306369:8388608,1073741825:8421378,1342177281:33280,1610612737:512,1879048193:8389122,2147483649:8421890,2415919105:8421376,2684354561:8388610,2952790017:33282,3221225473:514,3489660929:8389120,3758096385:32770,4026531841:0,134217729:8421890,402653185:8421376,671088641:8388608,939524097:512,1207959553:32768,1476395009:8388610,1744830465:2,2013265921:33282,2281701377:32770,2550136833:8389122,2818572289:514,3087007745:8421888,3355443201:8389120,3623878657:0,3892314113:33280,4160749569:8421378},{0:1074282512,16777216:16384,33554432:524288,50331648:1074266128,67108864:1073741840,83886080:1074282496,100663296:1073758208,117440512:16,134217728:540672,150994944:1073758224,167772160:1073741824,184549376:540688,201326592:524304,218103808:0,234881024:16400,251658240:1074266112,8388608:1073758208,25165824:540688,41943040:16,58720256:1073758224,75497472:1074282512,92274688:1073741824,109051904:524288,125829120:1074266128,142606336:524304,159383552:0,176160768:16384,192937984:1074266112,209715200:1073741840,226492416:540672,243269632:1074282496,260046848:16400,268435456:0,285212672:1074266128,301989888:1073758224,318767104:1074282496,335544320:1074266112,352321536:16,369098752:540688,385875968:16384,402653184:16400,419430400:524288,436207616:524304,452984832:1073741840,469762048:540672,486539264:1073758208,503316480:1073741824,520093696:1074282512,276824064:540688,293601280:524288,310378496:1074266112,327155712:16384,343932928:1073758208,360710144:1074282512,377487360:16,394264576:1073741824,411041792:1074282496,427819008:1073741840,444596224:1073758224,461373440:524304,478150656:0,494927872:16400,511705088:1074266128,528482304:540672},{0:260,1048576:0,2097152:67109120,3145728:65796,4194304:65540,5242880:67108868,6291456:67174660,7340032:67174400,8388608:67108864,9437184:67174656,10485760:65792,11534336:67174404,12582912:67109124,13631488:65536,14680064:4,15728640:256,524288:67174656,1572864:67174404,2621440:0,3670016:67109120,4718592:67108868,5767168:65536,6815744:65540,7864320:260,8912896:4,9961472:256,11010048:67174400,12058624:65796,13107200:65792,14155776:67109124,15204352:67174660,16252928:67108864,16777216:67174656,17825792:65540,18874368:65536,19922944:67109120,20971520:256,22020096:67174660,23068672:67108868,24117248:0,25165824:67109124,26214400:67108864,27262976:4,28311552:65792,29360128:67174400,30408704:260,31457280:65796,32505856:67174404,17301504:67108864,18350080:260,19398656:67174656,20447232:0,21495808:65540,22544384:67109120,23592960:256,24641536:67174404,25690112:65536,26738688:67174660,27787264:65796,28835840:67108868,29884416:67109124,30932992:67174400,31981568:4,33030144:65792},{0:2151682048,65536:2147487808,131072:4198464,196608:2151677952,262144:0,327680:4198400,393216:2147483712,458752:4194368,524288:2147483648,589824:4194304,655360:64,720896:2147487744,786432:2151678016,851968:4160,917504:4096,983040:2151682112,32768:2147487808,98304:64,163840:2151678016,229376:2147487744,294912:4198400,360448:2151682112,425984:0,491520:2151677952,557056:4096,622592:2151682048,688128:4194304,753664:4160,819200:2147483648,884736:4194368,950272:4198464,1015808:2147483712,1048576:4194368,1114112:4198400,1179648:2147483712,1245184:0,1310720:4160,1376256:2151678016,1441792:2151682048,1507328:2147487808,1572864:2151682112,1638400:2147483648,1703936:2151677952,1769472:4198464,1835008:2147487744,1900544:4194304,1966080:64,2031616:4096,1081344:2151677952,1146880:2151682112,1212416:0,1277952:4198400,1343488:4194368,1409024:2147483648,1474560:2147487808,1540096:64,1605632:2147483712,1671168:4096,1736704:2147487744,1802240:2151678016,1867776:4160,1933312:2151682048,1998848:4194304,2064384:4198464},{0:128,4096:17039360,8192:262144,12288:536870912,16384:537133184,20480:16777344,24576:553648256,28672:262272,32768:16777216,36864:537133056,40960:536871040,45056:553910400,49152:553910272,53248:0,57344:17039488,61440:553648128,2048:17039488,6144:553648256,10240:128,14336:17039360,18432:262144,22528:537133184,26624:553910272,30720:536870912,34816:537133056,38912:0,43008:553910400,47104:16777344,51200:536871040,55296:553648128,59392:16777216,63488:262272,65536:262144,69632:128,73728:536870912,77824:553648256,81920:16777344,86016:553910272,90112:537133184,94208:16777216,98304:553910400,102400:553648128,106496:17039360,110592:537133056,114688:262272,118784:536871040,122880:0,126976:17039488,67584:553648256,71680:16777216,75776:17039360,79872:537133184,83968:536870912,88064:17039488,92160:128,96256:553910272,100352:262272,104448:553910400,108544:0,112640:553648128,116736:16777344,120832:262144,124928:537133056,129024:536871040},{0:268435464,256:8192,512:270532608,768:270540808,1024:268443648,1280:2097152,1536:2097160,1792:268435456,2048:0,2304:268443656,2560:2105344,2816:8,3072:270532616,3328:2105352,3584:8200,3840:270540800,128:270532608,384:270540808,640:8,896:2097152,1152:2105352,1408:268435464,1664:268443648,1920:8200,2176:2097160,2432:8192,2688:268443656,2944:270532616,3200:0,3456:270540800,3712:2105344,3968:268435456,4096:268443648,4352:270532616,4608:270540808,4864:8200,5120:2097152,5376:268435456,5632:268435464,5888:2105344,6144:2105352,6400:0,6656:8,6912:270532608,7168:8192,7424:268443656,7680:270540800,7936:2097160,4224:8,4480:2105344,4736:2097152,4992:268435464,5248:268443648,5504:8200,5760:270540808,6016:270532608,6272:270540800,6528:270532616,6784:8192,7040:2105352,7296:2097160,7552:0,7808:268435456,8064:268443656},{0:1048576,16:33555457,32:1024,48:1049601,64:34604033,80:0,96:1,112:34603009,128:33555456,144:1048577,160:33554433,176:34604032,192:34603008,208:1025,224:1049600,240:33554432,8:34603009,24:0,40:33555457,56:34604032,72:1048576,88:33554433,104:33554432,120:1025,136:1049601,152:33555456,168:34603008,184:1048577,200:1024,216:34604033,232:1,248:1049600,256:33554432,272:1048576,288:33555457,304:34603009,320:1048577,336:33555456,352:34604032,368:1049601,384:1025,400:34604033,416:1049600,432:1,448:0,464:34603008,480:33554433,496:1024,264:1049600,280:33555457,296:34603009,312:1,328:33554432,344:1048576,360:1025,376:34604032,392:33554433,408:34603008,424:0,440:34604033,456:1049601,472:1024,488:33555456,504:1048577},{0:134219808,1:131072,2:134217728,3:32,4:131104,5:134350880,6:134350848,7:2048,8:134348800,9:134219776,10:133120,11:134348832,12:2080,13:0,14:134217760,15:133152,2147483648:2048,2147483649:134350880,2147483650:134219808,2147483651:134217728,2147483652:134348800,2147483653:133120,2147483654:133152,2147483655:32,2147483656:134217760,2147483657:2080,2147483658:131104,2147483659:134350848,2147483660:0,2147483661:134348832,2147483662:134219776,2147483663:131072,16:133152,17:134350848,18:32,19:2048,20:134219776,21:134217760,22:134348832,23:131072,24:0,25:131104,26:134348800,27:134219808,28:134350880,29:133120,30:2080,31:134217728,2147483664:131072,2147483665:2048,2147483666:134348832,2147483667:133152,2147483668:32,2147483669:134348800,2147483670:134217728,2147483671:134219808,2147483672:134350880,2147483673:134217760,2147483674:134219776,2147483675:0,2147483676:133120,2147483677:2080,2147483678:131104,2147483679:134350848}],u=[4160749569,528482304,33030144,2064384,129024,8064,504,2147483679],o=i.DES=r.extend({_doReset:function(){for(var t=this._key.words,e=[],r=0;r<56;r++){var i=h[r]-1;e[r]=t[i>>>5]>>>31-i%32&1}for(var n=this._subKeys=[],o=0;o<16;o++){var s=n[o]=[],c=f[o];for(r=0;r<24;r++)s[r/6|0]|=e[(l[r]-1+c)%28]<<31-r%6,s[4+(r/6|0)]|=e[28+(l[r+24]-1+c)%28]<<31-r%6;s[0]=s[0]<<1|s[0]>>>31;for(r=1;r<7;r++)s[r]=s[r]>>>4*(r-1)+3;s[7]=s[7]<<5|s[7]>>>27}var a=this._invSubKeys=[];for(r=0;r<16;r++)a[r]=n[15-r]},encryptBlock:function(t,e){this._doCryptBlock(t,e,this._subKeys)},decryptBlock:function(t,e){this._doCryptBlock(t,e,this._invSubKeys)},_doCryptBlock:function(t,e,r){this._lBlock=t[e],this._rBlock=t[e+1],p.call(this,4,252645135),p.call(this,16,65535),_.call(this,2,858993459),_.call(this,8,16711935),p.call(this,1,1431655765);for(var i=0;i<16;i++){for(var n=r[i],o=this._lBlock,s=this._rBlock,c=0,a=0;a<8;a++)c|=d[a][((s^n[a])&u[a])>>>0];this._lBlock=s,this._rBlock=o^c}var h=this._lBlock;this._lBlock=this._rBlock,this._rBlock=h,p.call(this,1,1431655765),_.call(this,8,16711935),_.call(this,2,858993459),p.call(this,16,65535),p.call(this,4,252645135),t[e]=this._lBlock,t[e+1]=this._rBlock},keySize:2,ivSize:2,blockSize:2});function p(t,e){var r=(this._lBlock>>>t^this._rBlock)&e;this._rBlock^=r,this._lBlock^=r<>>t^this._lBlock)&e;this._lBlock^=r,this._rBlock^=r<192.");var e=t.slice(0,2),r=t.length<4?t.slice(0,2):t.slice(2,4),i=t.length<6?t.slice(0,2):t.slice(4,6);this._des1=o.createEncryptor(n.create(e)),this._des2=o.createEncryptor(n.create(r)),this._des3=o.createEncryptor(n.create(i))},encryptBlock:function(t,e){this._des1.encryptBlock(t,e),this._des2.decryptBlock(t,e),this._des3.encryptBlock(t,e)},decryptBlock:function(t,e){this._des3.decryptBlock(t,e),this._des2.encryptBlock(t,e),this._des1.decryptBlock(t,e)},keySize:6,ivSize:2,blockSize:2});t.TripleDES=r._createHelper(s)}(),function(){var t=bt,e=t.lib.StreamCipher,r=t.algo,i=r.RC4=e.extend({_doReset:function(){for(var t=this._key,e=t.words,r=t.sigBytes,i=this._S=[],n=0;n<256;n++)i[n]=n;n=0;for(var o=0;n<256;n++){var s=n%r,c=e[s>>>2]>>>24-s%4*8&255;o=(o+i[n]+c)%256;var a=i[n];i[n]=i[o],i[o]=a}this._i=this._j=0},_doProcessBlock:function(t,e){t[e]^=n.call(this)},keySize:8,ivSize:0});function n(){for(var t=this._S,e=this._i,r=this._j,i=0,n=0;n<4;n++){r=(r+t[e=(e+1)%256])%256;var o=t[e];t[e]=t[r],t[r]=o,i|=t[(t[e]+t[r])%256]<<24-8*n}return this._i=e,this._j=r,i}t.RC4=e._createHelper(i);var o=r.RC4Drop=i.extend({cfg:i.cfg.extend({drop:192}),_doReset:function(){i._doReset.call(this);for(var t=this.cfg.drop;0>>24)|4278255360&(t[r]<<24|t[r]>>>8);var i=this._X=[t[0],t[3]<<16|t[2]>>>16,t[1],t[0]<<16|t[3]>>>16,t[2],t[1]<<16|t[0]>>>16,t[3],t[2]<<16|t[1]>>>16],n=this._C=[t[2]<<16|t[2]>>>16,4294901760&t[0]|65535&t[1],t[3]<<16|t[3]>>>16,4294901760&t[1]|65535&t[2],t[0]<<16|t[0]>>>16,4294901760&t[2]|65535&t[3],t[1]<<16|t[1]>>>16,4294901760&t[3]|65535&t[0]];for(r=this._b=0;r<4;r++)Rt.call(this);for(r=0;r<8;r++)n[r]^=i[r+4&7];if(e){var o=e.words,s=o[0],c=o[1],a=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8),h=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8),l=a>>>16|4294901760&h,f=h<<16|65535&a;n[0]^=a,n[1]^=l,n[2]^=h,n[3]^=f,n[4]^=a,n[5]^=l,n[6]^=h,n[7]^=f;for(r=0;r<4;r++)Rt.call(this)}},_doProcessBlock:function(t,e){var r=this._X;Rt.call(this),lt[0]=r[0]^r[5]>>>16^r[3]<<16,lt[1]=r[2]^r[7]>>>16^r[5]<<16,lt[2]=r[4]^r[1]>>>16^r[7]<<16,lt[3]=r[6]^r[3]>>>16^r[1]<<16;for(var i=0;i<4;i++)lt[i]=16711935&(lt[i]<<8|lt[i]>>>24)|4278255360&(lt[i]<<24|lt[i]>>>8),t[e+i]^=lt[i]},blockSize:4,ivSize:2}),ct.Rabbit=at._createHelper(ut),bt.mode.CTR=(pt=bt.lib.BlockCipherMode.extend(),_t=pt.Encryptor=pt.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,n=this._iv,o=this._counter;n&&(o=this._counter=n.slice(0),this._iv=void 0);var s=o.slice(0);r.encryptBlock(s,0),o[i-1]=o[i-1]+1|0;for(var c=0;c>>16,t[1],t[0]<<16|t[3]>>>16,t[2],t[1]<<16|t[0]>>>16,t[3],t[2]<<16|t[1]>>>16],i=this._C=[t[2]<<16|t[2]>>>16,4294901760&t[0]|65535&t[1],t[3]<<16|t[3]>>>16,4294901760&t[1]|65535&t[2],t[0]<<16|t[0]>>>16,4294901760&t[2]|65535&t[3],t[1]<<16|t[1]>>>16,4294901760&t[3]|65535&t[0]],n=this._b=0;n<4;n++)Mt.call(this);for(n=0;n<8;n++)i[n]^=r[n+4&7];if(e){var o=e.words,s=o[0],c=o[1],a=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8),h=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8),l=a>>>16|4294901760&h,f=h<<16|65535&a;i[0]^=a,i[1]^=l,i[2]^=h,i[3]^=f,i[4]^=a,i[5]^=l,i[6]^=h,i[7]^=f;for(n=0;n<4;n++)Mt.call(this)}},_doProcessBlock:function(t,e){var r=this._X;Mt.call(this),Bt[0]=r[0]^r[5]>>>16^r[3]<<16,Bt[1]=r[2]^r[7]>>>16^r[5]<<16,Bt[2]=r[4]^r[1]>>>16^r[7]<<16,Bt[3]=r[6]^r[3]>>>16^r[1]<<16;for(var i=0;i<4;i++)Bt[i]=16711935&(Bt[i]<<8|Bt[i]>>>24)|4278255360&(Bt[i]<<24|Bt[i]>>>8),t[e+i]^=Bt[i]},blockSize:4,ivSize:2}),vt.RabbitLegacy=yt._createHelper(St),bt.pad.ZeroPadding={pad:function(t,e){var r=4*e;t.clamp(),t.sigBytes+=r-(t.sigBytes%r||r)},unpad:function(t){var e=t.words,r=t.sigBytes-1;for(r=t.sigBytes-1;0<=r;r--)if(e[r>>>2]>>>24-r%4*8&255){t.sigBytes=r+1;break}}},bt}); --------------------------------------------------------------------------------