1use std::{cmp::min, collections::HashMap, sync::Arc};
2
3use percent_encoding::percent_decode_str;
4
5use crate::sys::{
6 action::ActionData,
7 request::{HttpMethod, HttpVersion, Input, RawData, Request},
8 worker::{StreamRead, StreamWrite, Worker, WorkerData},
9};
10
11#[derive(Debug)]
19pub(crate) struct Header {
20 pub header_type: u8,
22 pub content_length: u16,
24 pub padding_length: u8,
26}
27
28#[derive(Debug)]
35pub(crate) struct Record {
36 pub header: Header,
38 pub data: Vec<u8>,
40}
41
42#[derive(Debug)]
49pub(crate) enum RecordType {
50 Some(Record),
52 StreamClose,
54}
55
56pub const FASTCGI_HEADER_LEN: usize = 8;
58pub const FASTCGI_MAX_CONTENT_LEN: usize = 65535;
60
61pub const FASTCGI_BEGIN_REQUEST: u8 = 1;
63pub const FASTCGI_END_REQUEST: u8 = 3;
65pub const FASTCGI_PARAMS: u8 = 4;
67pub const FASTCGI_STDIN: u8 = 5;
69pub const FASTCGI_STDOUT: u8 = 6;
71
72pub const U16_BE_1: [u8; 2] = u16::to_be_bytes(1);
74pub const U16_BE_8: [u8; 2] = u16::to_be_bytes(8);
76
77pub(crate) struct Net;
79type FastCGI = Net;
81
82impl Net {
83 pub async fn run(mut stream_read: StreamRead, stream_write: Arc<StreamWrite>, data: WorkerData) {
85 loop {
86 let record = match FastCGI::read_record_raw(&mut stream_read, 0).await {
88 RecordType::Some(r) => r,
89 RecordType::StreamClose => break,
90 };
91 if FASTCGI_BEGIN_REQUEST == record.header.header_type {
93 let mut is_param_done = false;
94 let mut is_stdin_done = false;
95 let mut params = Vec::new();
96 let mut stdin = Vec::new();
97
98 loop {
100 let record = match FastCGI::read_record_raw(&mut stream_read, 300).await {
102 RecordType::Some(r) => r,
103 RecordType::StreamClose => break,
104 };
105 match record.header.header_type {
106 FASTCGI_PARAMS => {
107 if record.data.is_empty() {
108 is_param_done = true;
109 } else {
110 params.extend_from_slice(&record.data);
111 }
112 }
113 FASTCGI_STDIN => {
114 if record.data.is_empty() {
115 is_stdin_done = true;
116 } else {
117 stdin.extend_from_slice(&record.data);
118 }
119 }
120 _ => return,
121 }
122 if is_stdin_done && is_param_done {
123 break;
124 }
125 }
126 let (mut request, content_type, session) = FastCGI::read_param(params, Arc::clone(&data.session_key));
128
129 let (post, file, raw) = Worker::read_input(stdin, content_type).await;
131 request.input.file = file;
132 request.input.post = post;
133 request.input.raw = raw;
134
135 let stop = match data.stop {
136 Some((ref rpc, stop)) => Some((Arc::clone(rpc), stop)),
137 None => None,
138 };
139
140 let data = ActionData {
141 engine: Arc::clone(&data.engine),
142 lang: Arc::clone(&data.lang),
143 html: Arc::clone(&data.html),
144 cache: Arc::clone(&data.cache),
145 db: Arc::clone(&data.db),
146 session_key: Arc::clone(&data.session_key),
147 salt: Arc::clone(&data.salt),
148 mail: Arc::clone(&data.mail),
149 request,
150 session,
151 tx: Arc::clone(&stream_write.tx),
152 action_index: Arc::clone(&data.action_index),
153 action_not_found: Arc::clone(&data.action_not_found),
154 action_err: Arc::clone(&data.action_err),
155 stop,
156 root: Arc::clone(&data.root),
157 };
158
159 let answer = Worker::call_action(data).await;
161 stream_write.write(answer).await;
162 } else {
163 break;
164 }
165 }
166 }
167
168 fn read_param(mut data: Vec<u8>, session: Arc<String>) -> (Request, Option<String>, Option<String>) {
176 let mut params = HashMap::with_capacity(16);
177 let len = data.len();
178 let mut size = 0;
179
180 let mut ajax = false;
181 let mut host = String::new();
182 let mut scheme = "https".to_owned();
183 let mut agent = String::new();
184 let mut referer = String::new();
185 let mut ip = String::new();
186 let mut method = String::new();
187 let mut path = String::new();
188 let mut url = String::new();
189
190 let mut get = HashMap::new();
191 let mut cookie = HashMap::new();
192 let mut content_type = None;
193 let mut session_key = None;
194
195 while size < len {
196 let key_len;
197 let value_len;
198
199 unsafe {
207 if (*data.get_unchecked(size) >> 7) == 0 {
209 if size + 1 > len {
210 break;
211 }
212 key_len = usize::from(*data.get_unchecked(size));
213 size += 1;
214 } else {
215 if size + 4 > len {
216 break;
217 }
218 let elem = data.get_unchecked_mut(size);
219 *elem &= 0x7F;
220 key_len = u32::from_be_bytes([
221 *data.get_unchecked(size),
222 *data.get_unchecked(size + 1),
223 *data.get_unchecked(size + 2),
224 *data.get_unchecked(size + 3),
225 ]) as usize;
226 size += 4;
227 }
228 if key_len == 0 {
229 break;
230 }
231 if (*data.get_unchecked(size) >> 7) == 0 {
233 if size + 1 > len {
234 break;
235 }
236 value_len = usize::from(*data.get_unchecked(size));
237 size += 1;
238 } else {
239 if size + 4 > len {
240 break;
241 }
242 let elem = data.get_unchecked_mut(size);
243 *elem &= 0x7F;
244 value_len = u32::from_be_bytes([
245 *data.get_unchecked(size),
246 *data.get_unchecked(size + 1),
247 *data.get_unchecked(size + 2),
248 *data.get_unchecked(size + 3),
249 ]) as usize;
250 size += 4;
251 }
252 if size + key_len + value_len > len {
253 break;
254 }
255 }
256 let key = unsafe { data.get_unchecked(size..size + key_len) };
257 size += key_len;
258 let value = match String::from_utf8(unsafe { data.get_unchecked(size..size + value_len) }.to_vec()) {
259 Ok(value) => value,
260 Err(_) => break,
261 };
262 size += value_len;
263 match key {
265 b"HTTP_X_REQUESTED_WITH" => ajax = value.to_lowercase().eq("xmlhttprequest"),
266 b"HTTP_HOST" => host = value,
267 b"REQUEST_SCHEME" => scheme = value,
268 b"HTTP_USER_AGENT" => agent = value,
269 b"HTTP_REFERER" => referer = value,
270 b"REMOTE_ADDR" => ip = value,
271 b"REQUEST_METHOD" => method = value,
272 b"DOCUMENT_ROOT" => path = value,
273 b"REDIRECT_URL" => {
274 if let Some(u) = value.split('?').next() {
275 if let Ok(u) = percent_decode_str(u).decode_utf8() {
276 url = u.to_string();
277 }
278 }
279 }
280 b"QUERY_STRING" => {
281 if !value.is_empty() {
282 let gets: Vec<&str> = value.split('&').collect();
283 get.reserve(gets.len());
284 for v in gets {
285 let key: Vec<&str> = v.splitn(2, '=').collect();
286 match key.len() {
287 1 => {
288 if let Ok(u) = percent_decode_str(v).decode_utf8() {
289 get.insert(u.to_string(), String::new());
290 }
291 }
292 _ => {
293 if let Ok(u) = percent_decode_str(unsafe { key.get_unchecked(0) }).decode_utf8() {
294 if let Ok(v) = percent_decode_str(unsafe { key.get_unchecked(1) }).decode_utf8() {
295 get.insert(u.to_string(), v.to_string());
296 }
297 }
298 }
299 };
300 }
301 }
302 }
303 b"CONTENT_TYPE" => content_type = Some(value),
304 b"HTTP_COOKIE" => {
305 let cooks: Vec<&str> = value.split("; ").collect();
306 cookie.reserve(cooks.len());
307 for v in cooks {
308 let key: Vec<&str> = v.splitn(2, '=').collect();
309 if key.len() == 2 && *unsafe { key.get_unchecked(0) } == session.as_str() {
310 let val = *unsafe { key.get_unchecked(1) };
311 if val.len() == 128 {
312 for b in val.as_bytes() {
313 if !((*b > 47 && *b < 58) || (*b > 96 && *b < 103)) {
314 continue;
315 }
316 }
317 session_key = Some((*unsafe { key.get_unchecked(1) }).to_owned());
318 } else {
319 cookie.insert((*unsafe { key.get_unchecked(0) }).to_owned(), (*unsafe { key.get_unchecked(1) }).to_owned());
320 }
321 }
322 }
323 }
324 _ => {
325 let key = match String::from_utf8(key.to_vec()) {
326 Ok(key) => key,
327 Err(_) => break,
328 };
329 params.insert(key, value);
330 }
331 }
332 }
333 params.shrink_to_fit();
334 let method = method.parse().unwrap_or(HttpMethod::Get);
335 let site = format!("{}://{}", scheme, host);
336 (
337 Request {
338 ajax,
339 host,
340 scheme,
341 agent,
342 referer,
343 ip,
344 method,
345 path,
346 url,
347 input: Input {
348 get,
349 post: HashMap::new(),
350 file: HashMap::new(),
351 cookie,
352 params,
353 raw: RawData::None,
354 },
355 site,
356 version: HttpVersion::None,
357 },
358 content_type,
359 session_key,
360 )
361 }
362
363 async fn read_record_raw(stream: &mut StreamRead, timeout: u64) -> RecordType {
365 let mut buf = stream.get(stream.available());
367
368 while buf.len() < FASTCGI_HEADER_LEN {
369 if stream.read(timeout).await.is_err() {
370 return RecordType::StreamClose;
371 }
372 buf = stream.get(stream.available());
373 }
374
375 let header = FastCGI::read_header(unsafe { buf.get_unchecked(..FASTCGI_HEADER_LEN) });
376 stream.shift(FASTCGI_HEADER_LEN);
377 let mut total = header.content_length as usize;
378
379 let mut max_read;
382 let mut buf_len;
383 let mut vec = Vec::with_capacity(total);
384 while total > 0 {
385 max_read = min(total, stream.available());
386 while max_read == 0 {
387 if stream.read(300).await.is_err() {
388 return RecordType::StreamClose;
389 }
390 max_read = min(total, stream.available());
391 }
392 buf = stream.get(max_read);
393 buf_len = buf.len();
394 vec.extend_from_slice(buf);
395 stream.shift(buf_len);
396 total -= buf_len;
397 }
398 stream.shift(header.padding_length as usize);
399 RecordType::Some(Record { header, data: vec })
400 }
401
402 fn read_header(data: &[u8]) -> Header {
408 unsafe {
409 Header {
410 header_type: *data.get_unchecked(1),
411 content_length: u16::from_be_bytes([*data.get_unchecked(4), *data.get_unchecked(5)]),
412 padding_length: *data.get_unchecked(6),
413 }
414 }
415 }
416
417 pub fn write(answer: Vec<u8>, end: bool) -> Vec<u8> {
419 let mut seek: usize = 0;
420 let len = answer.len();
421 let capacity = len + FASTCGI_HEADER_LEN * (4 + len / FASTCGI_MAX_CONTENT_LEN);
422
423 let mut data: Vec<u8> = Vec::with_capacity(capacity);
424 let mut size;
425
426 while seek < len {
428 if seek + FASTCGI_MAX_CONTENT_LEN < len {
429 size = FASTCGI_MAX_CONTENT_LEN;
430 } else {
431 size = len - seek;
432 };
433 data.push(1_u8);
434 data.push(FASTCGI_STDOUT);
435 data.extend_from_slice(&U16_BE_1);
436 data.extend_from_slice(&u16::to_be_bytes(size as u16));
437 data.push(0);
438 data.push(0);
439 data.extend_from_slice(unsafe { answer.get_unchecked(seek..seek + size) });
440 seek += size;
441 }
442 if end {
443 data.push(1_u8);
445 data.push(FASTCGI_STDOUT);
446 data.extend_from_slice(&U16_BE_1);
447 data.push(0);
448 data.push(0);
449 data.push(0);
450 data.push(0);
451
452 data.push(1_u8);
454 data.push(FASTCGI_END_REQUEST);
455 data.extend_from_slice(&U16_BE_1);
456 data.extend_from_slice(&U16_BE_8);
457 data.push(0);
458 data.push(0);
459
460 data.push(0);
462 data.push(0);
463 data.push(0);
464 data.push(0);
465 data.push(0);
466 data.push(0);
467 data.push(0);
468 data.push(0);
469 }
470 data
471 }
472}