1/*
2 This file is part of Mtproto-proxy Library.
3
4 Mtproto-proxy Library is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 Mtproto-proxy Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
16
17 Copyright 2010-2012 Vkontakte Ltd
18 2010-2012 Nikolai Durov
19 2010-2012 Andrey Lopatin
20 2012 Anton Maydell
21
22 Copyright 2014-2016 Telegram Messenger Inc
23 2015-2016 Vitaly Valtman
24*/
25
26#define _FILE_OFFSET_BITS 64
27
28#include <assert.h>
29#include <string.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <unistd.h>
33
34#include "crc32.h"
35#include "kprintf.h"
36#include "net/net-events.h"
37#include "precise-time.h"
38#include "net/net-connections.h"
39#include "net/net-http-server.h"
40
41/*
42 *
43 * HTTP SERVER INTERFACE
44 *
45 */
46
47#define SERVER_VERSION "MTProxy/1.0"
48
49int http_connections;
50long long http_queries, http_bad_headers, http_queries_size;
51
52char *extra_http_response_headers = "";
53
54int hts_std_wakeup (connection_job_t c);
55int hts_parse_execute (connection_job_t c);
56int hts_std_alarm (connection_job_t c);
57int hts_do_wakeup (connection_job_t c);
58int hts_init_accepted (connection_job_t c);
59int hts_close_connection (connection_job_t c, int who);
60int hts_write_packet (connection_job_t C, struct raw_message *raw);
61
62conn_type_t ct_http_server = {
63 .magic = CONN_FUNC_MAGIC,
64 .title = "http_server",
65 .flags = C_RAWMSG,
66 .accept = net_accept_new_connections,
67 .init_accepted = hts_init_accepted,
68 .parse_execute = hts_parse_execute,
69 .close = hts_close_connection,
70 .init_outbound = server_failed,
71 .connected = server_failed,
72 .wakeup = hts_std_wakeup,
73 .alarm = hts_std_alarm,
74 .write_packet = hts_write_packet
75};
76
77enum http_query_parse_state {
78 htqp_start,
79 htqp_readtospace,
80 htqp_readtocolon,
81 htqp_readint,
82 htqp_skipspc,
83 htqp_skiptoeoln,
84 htqp_skipspctoeoln,
85 htqp_eoln,
86 htqp_wantlf,
87 htqp_wantlastlf,
88 htqp_linestart,
89 htqp_fatal,
90 htqp_done
91};
92
93int hts_default_execute (connection_job_t c, struct raw_message *raw, int op);
94
95struct http_server_functions default_http_server = {
96 .execute = hts_default_execute,
97 .ht_wakeup = hts_do_wakeup,
98 .ht_alarm = hts_do_wakeup
99};
100
101int hts_default_execute (connection_job_t c, struct raw_message *raw, int op) {
102 struct hts_data *D = HTS_DATA(c);
103
104 vkprintf (1, "http_server: op=%d, header_size=%d\n", op, D->header_size);
105
106 switch (op) {
107
108 case htqt_empty:
109 break;
110
111 case htqt_get:
112 case htqt_post:
113 case htqt_head:
114 case htqt_options:
115
116 default:
117 D->query_flags |= QF_ERROR;
118 break;
119 }
120
121 return D->data_size >= 0 ? -413 : -501;
122}
123
124int hts_init_accepted (connection_job_t c) {
125 http_connections++;
126 return 0;
127}
128
129int hts_close_connection (connection_job_t c, int who) {
130 http_connections--;
131
132 if (HTS_FUNC(c)->ht_close != NULL) {
133 HTS_FUNC(c)->ht_close (c, who);
134 }
135
136 return cpu_server_close_connection (c, who);
137}
138
139static inline char *http_get_error_msg_text (int *code) {
140 /* the most frequent case */
141 if (*code == 200) {
142 return "OK";
143 }
144 switch (*code) {
145 /* python generated from old array */
146 case 201: return "Created";
147 case 202: return "Accepted";
148 case 204: return "No Content";
149 case 206: return "Partial Content";
150 case 301: return "Moved Permanently";
151 case 302: return "Found";
152 case 303: return "See Other";
153 case 304: return "Not Modified";
154 case 307: return "Temporary Redirect";
155 case 400: return "Bad Request";
156 case 403: return "Forbidden";
157 case 404: return "Not Found";
158 case 405: return "Method Not Allowed";
159 case 406: return "Not Acceptable";
160 case 408: return "Request Timeout";
161 case 411: return "Length Required";
162 case 413: return "Request Entity Too Large";
163 case 414: return "Request URI Too Long";
164 case 418: return "I'm a teapot";
165 case 429: return "Too Many Requests";
166 case 501: return "Not Implemented";
167 case 502: return "Bad Gateway";
168 case 503: return "Service Unavailable";
169 default: *code = 500;
170 }
171 return "Internal Server Error";
172}
173
174static char error_text_pattern[] =
175"<html>\r\n"
176"<head><title>%d %s</title></head>\r\n"
177"<body bgcolor=\"white\">\r\n"
178"<center><h1>%d %s</h1></center>\r\n"
179"<hr><center>" SERVER_VERSION "</center>\r\n"
180"</body>\r\n"
181"</html>\r\n";
182
183int write_http_error_raw (connection_job_t C, struct raw_message *raw, int code) {
184 if (code == 204) {
185 write_basic_http_header_raw (C, raw, code, 0, -1, 0, 0);
186 return 0;
187 } else {
188 static char buff[1024];
189 char *ptr = buff;
190 const char *error_message = http_get_error_msg_text (&code);
191 ptr += sprintf (ptr, error_text_pattern, code, error_message, code, error_message);
192 write_basic_http_header_raw (C, raw, code, 0, ptr - buff, 0, 0);
193 assert (rwm_push_data (raw, buff, ptr - buff) == ptr - buff);
194 return ptr - buff;
195 }
196}
197
198int write_http_error (connection_job_t C, int code) {
199 struct raw_message *raw = calloc (sizeof (*raw), 1);
200 rwm_init (raw, 0);
201 int r = write_http_error_raw (C, raw, code);
202
203 mpq_push_w (CONN_INFO(C)->out_queue, raw, 0);
204 job_signal (JOB_REF_CREATE_PASS (C), JS_RUN);
205
206 return r;
207}
208
209int hts_write_packet (connection_job_t C, struct raw_message *raw) {
210 rwm_union (&CONN_INFO(C)->out, raw);
211 return 0;
212}
213
214
215int hts_parse_execute (connection_job_t C) {
216 struct connection_info *c = CONN_INFO (C);
217
218 struct hts_data *D = HTS_DATA(C);
219 char *ptr, *ptr_s, *ptr_e;
220 int len;
221 long long tt;
222
223 D->parse_state = htqp_start;
224
225 struct raw_message raw;
226 rwm_clone (&raw, &c->in);
227
228 while (c->status == conn_working && !c->pending_queries && raw.total_bytes) {
229 if (c->flags & (C_ERROR | C_STOPPARSE)) {
230 break;
231 }
232
233 len = rwm_get_block_ptr_bytes (&raw);
234 assert (len > 0);
235 ptr = ptr_s = rwm_get_block_ptr (&raw);
236 ptr_e = ptr + len;
237
238 assert (ptr);
239
240 while (ptr < ptr_e && D->parse_state != htqp_done) {
241 switch (D->parse_state) {
242 case htqp_start:
243 //fprintf (stderr, "htqp_start: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
244 memset (D, 0, offsetof (struct hts_data, query_seqno));
245 D->query_seqno++;
246 D->query_type = htqt_none;
247 D->data_size = -1;
248 D->parse_state = htqp_readtospace;
249
250 case htqp_readtospace:
251 //fprintf (stderr, "htqp_readtospace: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
252 while (ptr < ptr_e && ((unsigned) *ptr > ' ')) {
253 if (D->wlen < 15) {
254 D->word[D->wlen] = *ptr;
255 }
256 D->wlen++;
257 ptr++;
258 }
259 if (D->wlen > 4096) {
260 D->parse_state = htqp_fatal;
261 break;
262 }
263 if (ptr == ptr_e) {
264 break;
265 }
266 D->parse_state = htqp_skipspc;
267 D->query_words++;
268 if (D->query_words == 1) {
269 D->query_type = htqt_error;
270 if (D->wlen == 3 && !memcmp (D->word, "GET", 3)) {
271 D->query_type = htqt_get;
272 } else if (D->wlen == 4) {
273 if (!memcmp (D->word, "HEAD", 4)) {
274 D->query_type = htqt_head;
275 } else if (!memcmp (D->word, "POST", 4)) {
276 D->query_type = htqt_post;
277 }
278 } else if (D->wlen == 7 && !memcmp (D->word, "OPTIONS", 7)) {
279 D->query_type = htqt_options;
280 }
281 if (D->query_type == htqt_error) {
282 D->parse_state = htqp_skiptoeoln;
283 D->query_flags |= QF_ERROR;
284 }
285 } else if (D->query_words == 2) {
286 D->uri_offset = D->header_size;
287 D->uri_size = D->wlen;
288 if (!D->wlen) {
289 D->parse_state = htqp_skiptoeoln;
290 D->query_flags |= QF_ERROR;
291 }
292 } else if (D->query_words == 3) {
293 D->parse_state = htqp_skipspctoeoln;
294 if (D->wlen != 0) {
295 /* HTTP/x.y */
296 if (D->wlen != 8) {
297 D->parse_state = htqp_skiptoeoln;
298 D->query_flags |= QF_ERROR;
299 } else {
300 if (!memcmp (D->word, "HTTP/1.0", 8)) {
301 D->http_ver = HTTP_V10;
302 } else if (!memcmp (D->word, "HTTP/1.1", 8)) {
303 D->http_ver = HTTP_V11;
304 } else {
305 D->parse_state = htqp_skiptoeoln;
306 D->query_flags |= QF_ERROR;
307 }
308 }
309 } else {
310 D->http_ver = HTTP_V09;
311 }
312 } else {
313 assert (D->query_flags & (QF_HOST | QF_CONNECTION));
314 if (D->wlen) {
315 if (D->query_flags & QF_HOST) {
316 D->host_offset = D->header_size;
317 D->host_size = D->wlen;
318 } else if (D->wlen == 10 && !strncasecmp (D->word, "keep-alive", 10)) {
319 D->query_flags |= QF_KEEPALIVE;
320 }
321 }
322 D->query_flags &= ~(QF_HOST | QF_CONNECTION);
323 D->parse_state = htqp_skipspctoeoln;
324 }
325 D->header_size += D->wlen;
326 break;
327
328 case htqp_skipspc:
329 case htqp_skipspctoeoln:
330 //fprintf (stderr, "htqp_skipspc[toeoln]: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
331 while (D->header_size < MAX_HTTP_HEADER_SIZE && ptr < ptr_e && (*ptr == ' ' || (*ptr == '\t' && D->query_words >= 8))) {
332 D->header_size++;
333 ptr++;
334 }
335 if (D->header_size >= MAX_HTTP_HEADER_SIZE) {
336 D->parse_state = htqp_fatal;
337 break;
338 }
339 if (ptr == ptr_e) {
340 break;
341 }
342 if (D->parse_state == htqp_skipspctoeoln) {
343 D->parse_state = htqp_eoln;
344 break;
345 }
346 if (D->query_words < 3) {
347 D->wlen = 0;
348 D->parse_state = htqp_readtospace;
349 } else {
350 assert (D->query_words >= 4);
351 if (D->query_flags & QF_DATASIZE) {
352 if (D->data_size != -1) {
353 D->parse_state = htqp_skiptoeoln;
354 D->query_flags |= QF_ERROR;
355 } else {
356 D->parse_state = htqp_readint;
357 D->data_size = 0;
358 }
359 } else if (D->query_flags & (QF_HOST | QF_CONNECTION)) {
360 D->wlen = 0;
361 D->parse_state = htqp_readtospace;
362 } else {
363 D->parse_state = htqp_skiptoeoln;
364 }
365 }
366 break;
367
368 case htqp_readtocolon:
369 //fprintf (stderr, "htqp_readtocolon: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
370 while (ptr < ptr_e && *ptr != ':' && *ptr > ' ') {
371 if (D->wlen < 15) {
372 D->word[D->wlen] = *ptr;
373 }
374 D->wlen++;
375 ptr++;
376 }
377 if (D->wlen > 4096) {
378 D->parse_state = htqp_fatal;
379 break;
380 }
381 if (ptr == ptr_e) {
382 break;
383 }
384
385 if (*ptr != ':') {
386 D->header_size += D->wlen;
387 D->parse_state = htqp_skiptoeoln;
388 D->query_flags |= QF_ERROR;
389 break;
390 }
391
392 ptr++;
393
394 if (D->wlen == 4 && !strncasecmp (D->word, "host", 4)) {
395 D->query_flags |= QF_HOST;
396 } else if (D->wlen == 10 && !strncasecmp (D->word, "connection", 10)) {
397 D->query_flags |= QF_CONNECTION;
398 } else if (D->wlen == 14 && !strncasecmp (D->word, "content-length", 14)) {
399 D->query_flags |= QF_DATASIZE;
400 } else {
401 D->query_flags &= ~(QF_HOST | QF_DATASIZE | QF_CONNECTION);
402 }
403
404 D->header_size += D->wlen + 1;
405 D->parse_state = htqp_skipspc;
406 break;
407
408 case htqp_readint:
409 //fprintf (stderr, "htqp_readint: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
410
411 tt = D->data_size;
412 while (ptr < ptr_e && *ptr >= '0' && *ptr <= '9') {
413 if (tt >= 0x7fffffffL / 10) {
414 D->query_flags |= QF_ERROR;
415 D->parse_state = htqp_skiptoeoln;
416 break;
417 }
418 tt = tt * 10 + (*ptr - '0');
419 ptr++;
420 D->header_size++;
421 D->query_flags &= ~QF_DATASIZE;
422 }
423
424 D->data_size = tt;
425 if (ptr == ptr_e) {
426 break;
427 }
428
429 if (D->query_flags & QF_DATASIZE) {
430 D->query_flags |= QF_ERROR;
431 D->parse_state = htqp_skiptoeoln;
432 } else {
433 D->parse_state = htqp_skipspctoeoln;
434 }
435 break;
436
437 case htqp_skiptoeoln:
438 //fprintf (stderr, "htqp_skiptoeoln: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
439
440 while (D->header_size < MAX_HTTP_HEADER_SIZE && ptr < ptr_e && (*ptr != '\r' && *ptr != '\n')) {
441 D->header_size++;
442 ptr++;
443 }
444 if (D->header_size >= MAX_HTTP_HEADER_SIZE) {
445 D->parse_state = htqp_fatal;
446 break;
447 }
448 if (ptr == ptr_e) {
449 break;
450 }
451
452 D->parse_state = htqp_eoln;
453
454 case htqp_eoln:
455
456 if (ptr == ptr_e) {
457 break;
458 }
459 if (*ptr == '\r') {
460 ptr++;
461 D->header_size++;
462 }
463 D->parse_state = htqp_wantlf;
464
465 case htqp_wantlf:
466 //fprintf (stderr, "htqp_wantlf: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
467
468 if (ptr == ptr_e) {
469 break;
470 }
471 if (++D->query_words < 8) {
472 D->query_words = 8;
473 if (D->query_flags & QF_ERROR) {
474 D->parse_state = htqp_fatal;
475 break;
476 }
477 }
478
479 if (D->http_ver <= HTTP_V09) {
480 D->parse_state = htqp_wantlastlf;
481 break;
482 }
483
484 if (*ptr != '\n') {
485 D->query_flags |= QF_ERROR;
486 D->parse_state = htqp_skiptoeoln;
487 break;
488 }
489
490 ptr++;
491 D->header_size++;
492
493 D->parse_state = htqp_linestart;
494
495 case htqp_linestart:
496 //fprintf (stderr, "htqp_linestart: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
497
498 if (ptr == ptr_e) {
499 break;
500 }
501
502 if (!D->first_line_size) {
503 D->first_line_size = D->header_size;
504 }
505
506 if (*ptr == '\r') {
507 ptr++;
508 D->header_size++;
509 D->parse_state = htqp_wantlastlf;
510 break;
511 }
512 if (*ptr == '\n') {
513 D->parse_state = htqp_wantlastlf;
514 break;
515 }
516
517 if (D->query_flags & QF_ERROR) {
518 D->parse_state = htqp_skiptoeoln;
519 } else {
520 D->wlen = 0;
521 D->parse_state = htqp_readtocolon;
522 }
523 break;
524
525 case htqp_wantlastlf:
526 //fprintf (stderr, "htqp_wantlastlf: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
527
528 if (ptr == ptr_e) {
529 break;
530 }
531 if (*ptr != '\n') {
532 D->parse_state = htqp_fatal;
533 break;
534 }
535 ptr++;
536 D->header_size++;
537
538 if (!D->first_line_size) {
539 D->first_line_size = D->header_size;
540 }
541
542 D->parse_state = htqp_done;
543
544 case htqp_done:
545 //fprintf (stderr, "htqp_done: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
546 break;
547
548 case htqp_fatal:
549 //fprintf (stderr, "htqp_fatal: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
550 D->query_flags |= QF_ERROR;
551 D->parse_state = htqp_done;
552 break;
553
554 default:
555 assert (0);
556 }
557 }
558
559 len = ptr - ptr_s;
560 assert (rwm_skip_data (&raw, len) == len);
561
562 if (D->parse_state == htqp_done) {
563 if (D->header_size >= MAX_HTTP_HEADER_SIZE) {
564 D->query_flags |= QF_ERROR;
565 }
566 if (!(D->query_flags & QF_ERROR)) {
567 if (!HTS_FUNC(C)->execute) {
568 HTS_FUNC(C)->execute = hts_default_execute;
569 }
570
571 int res;
572 if (D->query_type == htqt_post && D->data_size < 0) {
573 // assert (rwm_skip_data (&c->in, D->header_size) == D->header_size);
574 res = -411;
575 } else if (D->query_type != htqt_post && D->data_size > 0) {
576 res = -413;
577 } else {
578 int bytes = D->header_size;
579 if (D->query_type == htqt_post) {
580 bytes += D->data_size;
581 }
582 struct raw_message r;
583 rwm_clone (&r, &c->in);
584 if (bytes < c->in.total_bytes) {
585 rwm_trunc (&r, bytes);
586 }
587
588 res = HTS_FUNC(C)->execute (C, &r, D->query_type);
589 rwm_free (&r);
590 }
591 http_queries++;
592 http_queries_size += D->header_size + D->data_size;
593 if (res > 0) {
594 //c->status = conn_reading_query;
595 rwm_free (&raw);
596 return res; // need more bytes
597 } else {
598 assert (rwm_skip_data (&c->in, D->header_size) == D->header_size);
599 if (res == SKIP_ALL_BYTES || !res) {
600 if (D->data_size > 0) {
601 int x = c->in.total_bytes;
602 int y = x > D->data_size ? D->data_size : x;
603 assert (rwm_skip_data (&c->in, y) == y);
604 if (y < x) {
605 D->parse_state = htqp_start;
606 return y - x;
607 }
608 }
609 } else {
610 if (res == -413) {
611 D->query_flags &= ~QF_KEEPALIVE;
612 }
613 write_http_error (C, -res);
614 D->query_flags &= ~QF_ERROR;
615 }
616 }
617 } else {
618 //fprintf (stderr, "[parse error]\n");
619 assert (rwm_skip_data (&c->in, D->header_size) == D->header_size);
620 http_bad_headers++;
621 }
622 if (D->query_flags & QF_ERROR) {
623 D->query_flags &= ~QF_KEEPALIVE;
624 write_http_error (C, 400);
625 }
626 if (!c->pending_queries && !(D->query_flags & QF_KEEPALIVE)) {
627 connection_write_close (C);
628 D->parse_state = -1;
629 return 0;
630 }
631 D->parse_state = htqp_start;
632 rwm_free (&raw);
633 rwm_clone (&raw, &c->in);
634 }
635 }
636
637 rwm_free (&raw);
638 return NEED_MORE_BYTES;
639}
640
641
642int hts_std_wakeup (connection_job_t c) {
643 if (HTS_FUNC(c)->ht_wakeup) {
644 HTS_FUNC(c)->ht_wakeup (c);
645 }
646 CONN_INFO(c)->generation = new_conn_generation ();
647 return 0;
648}
649
650int hts_std_alarm (connection_job_t c) {
651 if (HTS_FUNC(c)->ht_alarm) {
652 HTS_FUNC(c)->ht_alarm (c);
653 }
654 CONN_INFO(c)->generation = new_conn_generation ();
655 return 0;
656}
657
658int hts_do_wakeup (connection_job_t c) {
659 //struct hts_data *D = HTS_DATA(c);
660 assert (0);
661 return 0;
662}
663
664/*
665 *
666 * USEFUL HTTP FUNCTIONS
667 *
668 */
669
670#define HTTP_DATE_LEN 29
671char now_date_string[] = "Thu, 01 Jan 1970 00:00:00 GMT";
672int now_date_utime;
673
674static char months [] = "JanFebMarAprMayJunJulAugSepOctNovDecGlk";
675static char dows [] = "SunMonTueWedThuFriSatEar";
676
677
678int dd [] =
679{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
680
681void gen_http_date (char date_buffer[29], int time) {
682 int day, mon, year, hour, min, sec, xd, i, dow;
683 if (time < 0) time = 0;
684 sec = time % 60;
685 time /= 60;
686 min = time % 60;
687 time /= 60;
688 hour = time % 24;
689 time /= 24;
690 dow = (time + 4) % 7;
691 xd = time % (365 * 3 + 366);
692 time /= (365 * 3 + 366);
693 year = time * 4 + 1970;
694 if (xd >= 365) {
695 year++;
696 xd -= 365;
697 if (xd >= 365) {
698 year++;
699 xd -= 365;
700 if (xd >= 366) {
701 year++;
702 xd -= 366;
703 }
704 }
705 }
706 if (year & 3) {
707 dd[1] = 28;
708 } else {
709 dd[1] = 29;
710 }
711
712 for (i = 0; i < 12; i++) {
713 if (xd < dd[i]) {
714 break;
715 }
716 xd -= dd[i];
717 }
718
719 day = xd + 1;
720 mon = i;
721 assert (day >= 1 && day <= 31 && mon >=0 && mon <= 11 &&
722 year >= 1970 && year <= 2039);
723
724 sprintf (date_buffer, "%.3s, %.2d %.3s %d %.2d:%.2d:%.2d GM",
725 dows + dow * 3, day, months + mon * 3, year,
726 hour, min, sec);
727 date_buffer[28] = 'T';
728}
729
730int gen_http_time (char *date_buffer, int *time) {
731 char dow[4];
732 char month[4];
733 char tz[16];
734 int i, year, mon, day, hour, min, sec;
735 int argc = sscanf (date_buffer, "%3s, %d %3s %d %d:%d:%d %15s", dow, &day, month, &year, &hour, &min, &sec, tz);
736 if (argc != 8) {
737 return (argc > 0) ? -argc : -8;
738 }
739 for (mon = 0; mon < 12; mon++) {
740 if (!memcmp (months + mon * 3, month, 3)) {
741 break;
742 }
743 }
744 if (mon == 12) {
745 return -11;
746 }
747 if (year < 1970 || year > 2039) {
748 return -12;
749 }
750 if (hour < 0 || hour >= 24) {
751 return -13;
752 }
753 if (min < 0 || min >= 60) {
754 return -14;
755 }
756 if (sec < 0 || sec >= 60) {
757 return -15;
758 }
759 if (strcmp (tz, "GMT")) {
760 return -16;
761 }
762 int d = (year - 1970) * 365 + ((year - 1969) >> 2) + (day - 1);
763 if (!(year & 3) && mon >= 2) {
764 d++;
765 }
766 dd[1] = 28;
767 for (i = 0; i < mon; i++) {
768 d += dd[i];
769 }
770 *time = (((d * 24 + hour) * 60 + min) * 60) + sec;
771 return 0;
772}
773
774char *cur_http_date (void) {
775 if (now_date_utime != now) {
776 gen_http_date (now_date_string, now_date_utime = now);
777 }
778 return now_date_string;
779}
780
781int get_http_header (const char *qHeaders, const int qHeadersLen, char *buffer, int b_len, const char *arg_name, const int arg_len) {
782 const char *where = qHeaders;
783 const char *where_end = where + qHeadersLen;
784 while (where < where_end) {
785 const char *start = where;
786 while (where < where_end && (*where != ':' && *where != '\n')) {
787 ++where;
788 }
789 if (where == where_end) {
790 buffer[0] = 0;
791 return -1;
792 }
793 if (*where == ':') {
794 if (arg_len == where - start && !strncasecmp (arg_name, start, arg_len)) {
795 where++;
796 while (where < where_end && (*where == 9 || *where == 32)) {
797 where++;
798 }
799 start = where;
800 while (where < where_end && *where != '\r' && *where != '\n') {
801 ++where;
802 }
803 while (where > start && (where[-1] == ' ' || where[-1] == 9)) {
804 where--;
805 }
806 b_len--;
807 if (where - start < b_len) {
808 b_len = where - start;
809 }
810 memcpy (buffer, start, b_len);
811 buffer[b_len] = 0;
812 return b_len;
813 }
814 ++where;
815 }
816 while (where < where_end && *where != '\n') {
817 ++where;
818 }
819 if (where < where_end) {
820 ++where;
821 }
822 }
823 buffer[0] = 0;
824 return -1;
825}
826
827static char header_pattern[] =
828"HTTP/1.1 %d %s\r\n"
829"Server: " SERVER_VERSION "\r\n"
830"Date: %s\r\n"
831"Content-Type: %.256s\r\n"
832"Connection: %s\r\n%.1024s%.1024s";
833
834int write_basic_http_header_raw (connection_job_t C, struct raw_message *raw, int code, int date, int len, const char *add_header, const char *content_type) {
835 struct hts_data *D = HTS_DATA(C);
836
837 if (D->http_ver >= HTTP_V10 || D->http_ver == 0) {
838#define B_SZ 4096
839 static char buff[B_SZ], date_buff[32];
840 char *ptr = buff;
841 const char *error_message = http_get_error_msg_text (&code);
842 if (date) {
843 gen_http_date (date_buff, date);
844 }
845 ptr += snprintf (ptr, B_SZ - 64, header_pattern, code, error_message,
846 date ? date_buff : cur_http_date(),
847 content_type ? content_type : "text/html",
848 (D->query_flags & QF_KEEPALIVE) ? "keep-alive" : "close",
849 (D->query_flags & QF_EXTRA_HEADERS) && extra_http_response_headers ? extra_http_response_headers : "",
850 add_header ?: "");
851 D->query_flags &= ~QF_EXTRA_HEADERS;
852 assert (ptr < buff + B_SZ - 64);
853 if (len >= 0) {
854 ptr += sprintf (ptr, "Content-Length: %d\r\n", len);
855 }
856
857 ptr += sprintf (ptr, "\r\n");
858
859 assert (rwm_push_data (raw, buff, ptr - buff) == ptr - buff);
860 return ptr - buff;
861 }
862
863 return 0;
864}
865
866void http_flush (connection_job_t C, struct raw_message *raw) {
867 if (raw) {
868 mpq_push_w (CONN_INFO(C)->out_queue, raw, 0);
869 }
870 struct hts_data *D = HTS_DATA(C);
871 if (!CONN_INFO(C)->pending_queries && !(D->query_flags & QF_KEEPALIVE)) {
872 connection_write_close (C);
873 D->parse_state = -1;
874 }
875 job_signal (JOB_REF_CREATE_PASS (C), JS_RUN);
876}
877
878//int write_basic_http_header (connection_job_t C, struct raw_message *raw, int code, int date, int len, const char *add_header, const char *content_type) {
879
880
881/*
882 *
883 * END (HTTP SERVER)
884 *
885 */
886
887