| 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 | |
| 49 | int http_connections; |
| 50 | long long http_queries, , http_queries_size; |
| 51 | |
| 52 | char * = "" ; |
| 53 | |
| 54 | int hts_std_wakeup (connection_job_t c); |
| 55 | int hts_parse_execute (connection_job_t c); |
| 56 | int hts_std_alarm (connection_job_t c); |
| 57 | int hts_do_wakeup (connection_job_t c); |
| 58 | int hts_init_accepted (connection_job_t c); |
| 59 | int hts_close_connection (connection_job_t c, int who); |
| 60 | int hts_write_packet (connection_job_t C, struct raw_message *raw); |
| 61 | |
| 62 | conn_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 | |
| 77 | enum 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 | |
| 93 | int hts_default_execute (connection_job_t c, struct raw_message *raw, int op); |
| 94 | |
| 95 | struct 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 | |
| 101 | int 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 | |
| 124 | int hts_init_accepted (connection_job_t c) { |
| 125 | http_connections++; |
| 126 | return 0; |
| 127 | } |
| 128 | |
| 129 | int 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 | |
| 139 | static 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 | |
| 174 | static 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 | |
| 183 | int 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 | |
| 198 | int 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 | |
| 209 | int 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 | |
| 215 | int 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 | |
| 642 | int 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 | |
| 650 | int 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 | |
| 658 | int 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 |
| 671 | char now_date_string[] = "Thu, 01 Jan 1970 00:00:00 GMT" ; |
| 672 | int now_date_utime; |
| 673 | |
| 674 | static char months [] = "JanFebMarAprMayJunJulAugSepOctNovDecGlk" ; |
| 675 | static char dows [] = "SunMonTueWedThuFriSatEar" ; |
| 676 | |
| 677 | |
| 678 | int dd [] = |
| 679 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
| 680 | |
| 681 | void 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 | |
| 730 | int 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 | |
| 774 | char *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 | |
| 781 | int (const char *, const int , 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 | |
| 827 | static char [] = |
| 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 | |
| 834 | int (connection_job_t C, struct raw_message *raw, int code, int date, int len, const char *, 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 | |
| 866 | void 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 | |