/* uWSGI HTTP router */ #include "common.h" struct uwsgi_http uhttp; struct uwsgi_option http_options[] = { {"http", required_argument, 0, "add an http router/server on the specified address", uwsgi_opt_corerouter, &uhttp, 0}, {"httprouter", required_argument, 0, "add an http router/server on the specified address", uwsgi_opt_corerouter, &uhttp, 0}, #ifdef UWSGI_SSL {"https", required_argument, 0, "add an https router/server on the specified address with specified certificate and key", uwsgi_opt_https, &uhttp, 0}, {"https2", required_argument, 0, "add an https/spdy router/server using keyval options", uwsgi_opt_https2, &uhttp, 0}, {"https-export-cert", no_argument, 0, "export uwsgi variable HTTPS_CC containing the raw client certificate", uwsgi_opt_true, &uhttp.https_export_cert, 0}, {"https-session-context", required_argument, 0, "set the session id context to the specified value", uwsgi_opt_set_str, &uhttp.https_session_context, 0}, {"http-to-https", required_argument, 0, "add an http router/server on the specified address and redirect all of the requests to https", uwsgi_opt_http_to_https, &uhttp, 0}, #endif {"http-processes", required_argument, 0, "set the number of http processes to spawn", uwsgi_opt_set_int, &uhttp.cr.processes, 0}, {"http-workers", required_argument, 0, "set the number of http processes to spawn", uwsgi_opt_set_int, &uhttp.cr.processes, 0}, {"http-var", required_argument, 0, "add a key=value item to the generated uwsgi packet", uwsgi_opt_add_string_list, &uhttp.http_vars, 0}, {"http-to", required_argument, 0, "forward requests to the specified node (you can specify it multiple time for lb)", uwsgi_opt_add_string_list, &uhttp.cr.static_nodes, 0 }, {"http-zerg", required_argument, 0, "attach the http router to a zerg server", uwsgi_opt_corerouter_zerg, &uhttp, 0 }, {"http-fallback", required_argument, 0, "fallback to the specified node in case of error", uwsgi_opt_add_string_list, &uhttp.cr.fallback, 0}, {"http-modifier1", required_argument, 0, "set uwsgi protocol modifier1", uwsgi_opt_set_int, &uhttp.modifier1, 0}, {"http-modifier2", required_argument, 0, "set uwsgi protocol modifier2", uwsgi_opt_set_int, &uhttp.modifier2, 0}, {"http-use-cache", optional_argument, 0, "use uWSGI cache as key->value virtualhost mapper", uwsgi_opt_set_str, &uhttp.cr.use_cache, 0}, {"http-use-pattern", required_argument, 0, "use the specified pattern for mapping requests to unix sockets", uwsgi_opt_corerouter_use_pattern, &uhttp, 0}, {"http-use-base", required_argument, 0, "use the specified base for mapping requests to unix sockets", uwsgi_opt_corerouter_use_base, &uhttp, 0}, {"http-events", required_argument, 0, "set the number of concurrent http async events", uwsgi_opt_set_int, &uhttp.cr.nevents, 0}, {"http-subscription-server", required_argument, 0, "enable the subscription server", uwsgi_opt_corerouter_ss, &uhttp, 0}, {"http-timeout", required_argument, 0, "set internal http socket timeout", uwsgi_opt_set_int, &uhttp.cr.socket_timeout, 0}, {"http-manage-expect", optional_argument, 0, "manage the Expect HTTP request header (optionally checking for Content-Length)", uwsgi_opt_set_64bit, &uhttp.manage_expect, 0}, {"http-keepalive", optional_argument, 0, "HTTP 1.1 keepalive support (non-pipelined) requests", uwsgi_opt_set_int, &uhttp.keepalive, 0}, {"http-auto-chunked", no_argument, 0, "automatically transform output to chunked encoding during HTTP 1.1 keepalive (if needed)", uwsgi_opt_true, &uhttp.auto_chunked, 0}, #ifdef UWSGI_ZLIB {"http-auto-gzip", no_argument, 0, "automatically gzip content if uWSGI-Encoding header is set to gzip, but content size (Content-Length/Transfer-Encoding) and Content-Encoding are not specified", uwsgi_opt_true, &uhttp.auto_gzip, 0}, #endif {"http-raw-body", no_argument, 0, "blindly send HTTP body to backends (required for WebSockets and Icecast support in backends)", uwsgi_opt_true, &uhttp.raw_body, 0}, {"http-websockets", no_argument, 0, "automatically detect websockets connections and put the session in raw mode", uwsgi_opt_true, &uhttp.websockets, 0}, {"http-use-code-string", required_argument, 0, "use code string as hostname->server mapper for the http router", uwsgi_opt_corerouter_cs, &uhttp, 0}, {"http-use-socket", optional_argument, 0, "forward request to the specified uwsgi socket", uwsgi_opt_corerouter_use_socket, &uhttp, 0}, {"http-gracetime", required_argument, 0, "retry connections to dead static nodes after the specified amount of seconds", uwsgi_opt_set_int, &uhttp.cr.static_node_gracetime, 0}, {"http-quiet", required_argument, 0, "do not report failed connections to instances", uwsgi_opt_true, &uhttp.cr.quiet, 0}, {"http-cheap", no_argument, 0, "run the http router in cheap mode", uwsgi_opt_true, &uhttp.cr.cheap, 0}, {"http-stats", required_argument, 0, "run the http router stats server", uwsgi_opt_set_str, &uhttp.cr.stats_server, 0}, {"http-stats-server", required_argument, 0, "run the http router stats server", uwsgi_opt_set_str, &uhttp.cr.stats_server, 0}, {"http-ss", required_argument, 0, "run the http router stats server", uwsgi_opt_set_str, &uhttp.cr.stats_server, 0}, {"http-harakiri", required_argument, 0, "enable http router harakiri", uwsgi_opt_set_int, &uhttp.cr.harakiri, 0}, {"http-stud-prefix", required_argument, 0, "expect a stud prefix (1byte family + 4/16 bytes address) on connections from the specified address", uwsgi_opt_add_addr_list, &uhttp.stud_prefix, 0}, {"http-uid", required_argument, 0, "drop http router privileges to the specified uid", uwsgi_opt_uid, &uhttp.cr.uid, 0 }, {"http-gid", required_argument, 0, "drop http router privileges to the specified gid", uwsgi_opt_gid, &uhttp.cr.gid, 0 }, {"http-resubscribe", required_argument, 0, "forward subscriptions to the specified subscription server", uwsgi_opt_add_string_list, &uhttp.cr.resubscribe, 0}, {"http-buffer-size", required_argument, 0, "set internal buffer size (default: page size)", uwsgi_opt_set_64bit, &uhttp.cr.buffer_size, 0}, {"http-server-name-as-http-host", required_argument, 0, "force SERVER_NAME to HTTP_HOST", uwsgi_opt_true, &uhttp.server_name_as_http_host, 0}, {0, 0, 0, 0, 0, 0, 0}, }; int http_add_uwsgi_header(struct corerouter_peer *peer, char *hh, uint16_t hhlen) { struct uwsgi_buffer *out = peer->out; struct http_session *hr = (struct http_session *) peer->session; int i; int status = 0; char *val = hh; uint16_t keylen = 0, vallen = 0; int prefix = 0; for (i = 0; i < hhlen; i++) { if (!status) { hh[i] = toupper((int) hh[i]); if (hh[i] == '-') hh[i] = '_'; if (hh[i] == ':') { status = 1; keylen = i; } } else if (status == 1 && hh[i] != ' ') { status = 2; val += i; vallen++; } else if (status == 2) { vallen++; } } if (!keylen) return -1; if (hr->websockets) { if (!uwsgi_strncmp("UPGRADE", 7, hh, keylen)) { if (!uwsgi_strnicmp(val, vallen, "websocket", 9)) { hr->websockets++; } goto done; } else if (!uwsgi_strncmp("CONNECTION", 10, hh, keylen)) { if (!uwsgi_strnicmp(val, vallen, "Upgrade", 7)) { hr->websockets++; } goto done; } else if (!uwsgi_strncmp("SEC_WEBSOCKET_VERSION", 21, hh, keylen)) { hr->websockets++; goto done; } else if (!uwsgi_strncmp("SEC_WEBSOCKET_KEY", 17, hh, keylen)) { hr->websocket_key = val; hr->websocket_key_len = vallen; goto done; } } if (!uwsgi_strncmp("HOST", 4, hh, keylen)) { peer->key = val; peer->key_len = vallen; if (uhttp.server_name_as_http_host && uwsgi_buffer_append_keyval(out, "SERVER_NAME", 11, peer->key, peer->key_len)) return -1; } else if (!uwsgi_strncmp("CONTENT_LENGTH", 14, hh, keylen)) { hr->content_length = uwsgi_str_num(val, vallen); } // in the future we could support chunked requests... else if (!uwsgi_strncmp("TRANSFER_ENCODING", 17, hh, keylen)) { hr->session.can_keepalive = 0; } else if (!uwsgi_strncmp("CONNECTION", 10, hh, keylen)) { if (!uwsgi_strnicmp(val, vallen, "close", 5)) { hr->session.can_keepalive = 0; } } #ifdef UWSGI_ZLIB else if (uhttp.auto_gzip && !uwsgi_strncmp("ACCEPT_ENCODING", 15, hh, keylen)) { if ( uwsgi_contains_n(val, vallen, "gzip", 4) ) { hr->can_gzip = 1; } } #endif done: if (uwsgi_strncmp("CONTENT_TYPE", 12, hh, keylen) && uwsgi_strncmp("CONTENT_LENGTH", 14, hh, keylen)) { keylen += 5; prefix = 1; } if (uwsgi_buffer_u16le(out, keylen)) return -1; if (prefix) { if (uwsgi_buffer_append(out, "HTTP_", 5)) return -1; } if (uwsgi_buffer_append(out, hh, keylen - (prefix * 5))) return -1; if (uwsgi_buffer_u16le(out, vallen)) return -1; if (uwsgi_buffer_append(out, val, vallen)) return -1; return 0; } int http_headers_parse(struct corerouter_peer *peer) { struct http_session *hr = (struct http_session *) peer->session; char *ptr = peer->session->main_peer->in->buf; char *watermark = ptr + hr->headers_size; char *base = ptr; char *query_string = NULL; peer->out = uwsgi_buffer_new(uwsgi.page_size); // force this buffer to be destroyed as soon as possibile peer->out_need_free = 1; peer->out->limit = UMAX16; // leave space for the uwsgi header peer->out->pos = 4; peer->out_pos = 0; struct uwsgi_buffer *out = peer->out; int found = 0; // REQUEST_METHOD while (ptr < watermark) { if (*ptr == ' ') { if (uwsgi_buffer_append_keyval(out, "REQUEST_METHOD", 14, base, ptr - base)) return -1; ptr++; found = 1; break; } else if (*ptr == '\r' || *ptr == '\n') break; ptr++; } // ensure we have a method if (!found) { return -1; } // REQUEST_URI / PATH_INFO / QUERY_STRING base = ptr; found = 0; while (ptr < watermark) { if (*ptr == '?' && !query_string) { // PATH_INFO must be url-decoded !!! if (!hr->path_info) { hr->path_info_len = ptr - base; hr->path_info = uwsgi_malloc(hr->path_info_len); } else { size_t new_path_info = ptr - base; if (new_path_info > hr->path_info_len) { char *tmp_buf = realloc(hr->path_info, new_path_info); if (!tmp_buf) return -1; hr->path_info = tmp_buf; } hr->path_info_len = new_path_info; } http_url_decode(base, &hr->path_info_len, hr->path_info); if (uwsgi_buffer_append_keyval(out, "PATH_INFO", 9, hr->path_info, hr->path_info_len)) return -1; query_string = ptr + 1; } else if (*ptr == ' ') { hr->request_uri = base; hr->request_uri_len = ptr - base; if (uwsgi_buffer_append_keyval(out, "REQUEST_URI", 11, base, ptr - base)) return -1; if (!query_string) { // PATH_INFO must be url-decoded !!! if (!hr->path_info) { hr->path_info_len = ptr - base; hr->path_info = uwsgi_malloc(hr->path_info_len); } else { size_t new_path_info = ptr - base; if (new_path_info > hr->path_info_len) { char *tmp_buf = realloc(hr->path_info, new_path_info); if (!tmp_buf) return -1; hr->path_info = tmp_buf; } hr->path_info_len = new_path_info; } http_url_decode(base, &hr->path_info_len, hr->path_info); if (uwsgi_buffer_append_keyval(out, "PATH_INFO", 9, hr->path_info, hr->path_info_len)) return -1; if (uwsgi_buffer_append_keyval(out, "QUERY_STRING", 12, "", 0)) return -1; } else { if (uwsgi_buffer_append_keyval(out, "QUERY_STRING", 12, query_string, ptr - query_string)) return -1; } ptr++; found = 1; break; } ptr++; } // ensure we have a URI if (!found) { return -1; } // SERVER_PROTOCOL base = ptr; found = 0; while (ptr < watermark) { if (*ptr == '\r') { if (ptr + 1 >= watermark) return 0; if (*(ptr + 1) != '\n') return 0; if (uwsgi_buffer_append_keyval(out, "SERVER_PROTOCOL", 15, base, ptr - base)) return -1; if (uhttp.keepalive && !uwsgi_strncmp("HTTP/1.1", 8, base, ptr-base)) { hr->session.can_keepalive = 1; } ptr += 2; found = 1; break; } ptr++; } // ensure we have a protocol if (!found) { return -1; } // SCRIPT_NAME if (uwsgi_buffer_append_keyval(out, "SCRIPT_NAME", 11, "", 0)) return -1; // SERVER_NAME if (!uhttp.server_name_as_http_host && uwsgi_buffer_append_keyval(out, "SERVER_NAME", 11, uwsgi.hostname, uwsgi.hostname_len)) return -1; peer->key = uwsgi.hostname; peer->key_len = uwsgi.hostname_len; // SERVER_PORT if (uwsgi_buffer_append_keyval(out, "SERVER_PORT", 11, hr->port, hr->port_len)) return -1; // UWSGI_ROUTER if (uwsgi_buffer_append_keyval(out, "UWSGI_ROUTER", 12, "http", 4)) return -1; // stud HTTPS if (hr->stud_prefix_pos > 0) { if (uwsgi_buffer_append_keyval(out, "HTTPS", 5, "on", 2)) return -1; } #ifdef UWSGI_SSL if (hr_https_add_vars(hr, out)) return -1; #endif // REMOTE_ADDR if (uwsgi_buffer_append_keyval(out, "REMOTE_ADDR", 11, peer->session->client_address, strlen(peer->session->client_address))) return -1; if (uwsgi_buffer_append_keyval(out, "REMOTE_PORT", 11, peer->session->client_port, strlen(peer->session->client_port))) return -1; //HEADERS base = ptr; while (ptr < watermark) { if (*ptr == '\r') { if (ptr + 1 >= watermark) break; if (*(ptr + 1) != '\n') break; // multiline header ? if (ptr + 2 < watermark) { if (*(ptr + 2) == ' ' || *(ptr + 2) == '\t') { ptr += 2; continue; } } // this is an hack with dumb/wrong/useless error checking if (uhttp.manage_expect) { if (!uwsgi_strncmp("Expect: 100-continue", 20, base, ptr - base)) { hr->send_expect_100 = 1; } } if (http_add_uwsgi_header(peer, base, ptr - base)) return -1; ptr++; base = ptr + 1; } ptr++; } struct uwsgi_string_list *hv = uhttp.http_vars; while (hv) { char *equal = strchr(hv->value, '='); if (equal) { if (uwsgi_buffer_append_keyval(out, hv->value, equal - hv->value, equal + 1, strlen(equal + 1))) return -1; } hv = hv->next; } return 0; } int hr_manage_expect_continue(struct corerouter_peer *peer) { struct corerouter_session *cs = peer->session; struct http_session *hr = (struct http_session *) cs; if (uhttp.manage_expect > 1) { if (hr->content_length > uhttp.manage_expect) { if (uwsgi_buffer_append(peer->in, "HTTP/1.1 413 Request Entity Too Large\r\n\r\n", 41)) return -1; hr->session.wait_full_write = 1; goto ready; } } if (uwsgi_buffer_append(peer->in, "HTTP/1.1 100 Continue\r\n\r\n", 25)) return -1; hr->session.connect_peer_after_write = peer; ready: peer->session->main_peer->out = peer->in; peer->session->main_peer->out_pos = 0; cr_write_to_main(peer, hr->func_write); return 0; } ssize_t hr_instance_write(struct corerouter_peer *peer) { ssize_t len = cr_write(peer, "hr_instance_write()"); // end on empty write if (!len) { peer->session->can_keepalive = 0; return 0; } // the chunk has been sent, start (again) reading from client and instances if (cr_write_complete(peer)) { // destroy the buffer used for the uwsgi packet if (peer->out_need_free == 1) { uwsgi_buffer_destroy(peer->out); peer->out_need_free = 0; peer->out = NULL; // reset the main_peer input stream peer->session->main_peer->in->pos = 0; } // reset the stream (main_peer->in = peer->out) else { peer->out->pos = 0; } cr_reset_hooks(peer); #ifdef UWSGI_SPDY struct http_session *hr = (struct http_session *) peer->session; if (hr->spdy) { if (hr->spdy_update_window) { if (uwsgi_buffer_fix(peer->in, 16)) return -1; peer->in->pos = 16; spdy_window_update(peer->in->buf, hr->spdy_update_window, 8192); peer->session->main_peer->out = peer->in; peer->session->main_peer->out_pos = 0; hr->spdy_update_window = 0; cr_write_to_main(peer, hr->func_write); return 1; } return spdy_parse(peer->session->main_peer); } #endif } return len; } // write to the client ssize_t hr_write(struct corerouter_peer *main_peer) { ssize_t len = cr_write(main_peer, "hr_write()"); // end on empty write if (!len) return 0; // ok this response chunk is sent, let's start reading again if (cr_write_complete(main_peer)) { // reset the original read buffer main_peer->out->pos = 0; if (main_peer->session->wait_full_write) { main_peer->session->wait_full_write = 0; return 0; } if (main_peer->session->connect_peer_after_write) { cr_connect(main_peer->session->connect_peer_after_write, hr_instance_connected); main_peer->session->connect_peer_after_write = NULL; return len; } cr_reset_hooks(main_peer); } return len; } ssize_t hr_instance_connected(struct corerouter_peer* peer) { cr_peer_connected(peer, "hr_instance_connected()"); // we are connected, we cannot retry anymore peer->can_retry = 0; // prepare for write peer->out_pos = 0; // change the write hook (we are already monitoring for write) peer->hook_write = hr_instance_write; // and directly call it (optimistic approach...) return hr_instance_write(peer); } // check if the response allows for keepalive int hr_check_response_keepalive(struct corerouter_peer *peer) { struct http_session *hr = (struct http_session *) peer->session; struct uwsgi_buffer *ub = peer->in; size_t i; for(i=0;ipos;i++) { char c = ub->buf[i]; if (c == '\r' && (peer->r_parser_status == 0 || peer->r_parser_status == 2)) { peer->r_parser_status++; } else if (c == '\r') { peer->r_parser_status = 1; } else if (c == '\n' && peer->r_parser_status == 1) { peer->r_parser_status = 2; } // parsing done else if (c == '\n' && peer->r_parser_status == 3) { // end of headers peer->r_parser_status = 4; if (http_response_parse(hr, ub, i+1)) { return -1; } return 0; } else { peer->r_parser_status = 0; } } return 1; } // data from instance ssize_t hr_instance_read(struct corerouter_peer *peer) { peer->in->limit = UMAX16; if (uwsgi_buffer_ensure(peer->in, uwsgi.page_size)) return -1; struct http_session *hr = (struct http_session *) peer->session; ssize_t len = cr_read(peer, "hr_instance_read()"); if (!len) { // disable keepalive on unread body if (hr->content_length) hr->session.can_keepalive = 0; if (hr->session.can_keepalive) { peer->session->main_peer->disabled = 0; hr->rnrn = 0; #ifdef UWSGI_ZLIB hr->can_gzip = 0; hr->has_gzip = 0; #endif if (uhttp.keepalive > 1) { int orig_timeout = peer->session->corerouter->socket_timeout; peer->session->corerouter->socket_timeout = uhttp.keepalive; peer->session->main_peer->timeout = corerouter_reset_timeout(peer->session->corerouter, peer->session->main_peer); peer->session->corerouter->socket_timeout = orig_timeout; } } #ifdef UWSGI_ZLIB if (hr->force_chunked || hr->force_gzip) { #else if (hr->force_chunked) { #endif hr->force_chunked = 0; if (!hr->last_chunked) { hr->last_chunked = uwsgi_buffer_new(5); } #ifdef UWSGI_ZLIB if (hr->force_gzip) { hr->force_gzip = 0; size_t zlen = 0; char *gzipped = uwsgi_deflate(&hr->z, NULL, 0, &zlen); if (!gzipped) return -1; if (uwsgi_buffer_append_chunked(hr->last_chunked, zlen)) {free(gzipped) ; return -1;} if (uwsgi_buffer_append(hr->last_chunked, gzipped, zlen)) {free(gzipped) ; return -1;} free(gzipped); if (uwsgi_buffer_append(hr->last_chunked, "\r\n", 2)) return -1; if (uwsgi_buffer_append_chunked(hr->last_chunked, 8)) return -1; if (uwsgi_buffer_u32le(hr->last_chunked, hr->gzip_crc32)) return -1; if (uwsgi_buffer_u32le(hr->last_chunked, hr->gzip_size)) return -1; if (uwsgi_buffer_append(hr->last_chunked, "\r\n", 2)) return -1; } #endif if (uwsgi_buffer_append(hr->last_chunked, "0\r\n\r\n", 5)) return -1; peer->session->main_peer->out = hr->last_chunked; peer->session->main_peer->out_pos = 0; cr_write_to_main(peer, hr->func_write); if (!hr->session.can_keepalive) { hr->session.wait_full_write = 1; } } else { cr_reset_hooks(peer); } return 0; } // need to parse response headers #ifdef UWSGI_ZLIB if (hr->session.can_keepalive || hr->can_gzip) { #else if (hr->session.can_keepalive) { #endif if (peer->r_parser_status != 4) { int ret = hr_check_response_keepalive(peer); if (ret < 0) return -1; if (ret > 0) { return 1; } } #ifdef UWSGI_ZLIB else if (hr->force_gzip) { size_t zlen = 0; char *gzipped = uwsgi_deflate(&hr->z, peer->in->buf, peer->in->pos, &zlen); if (!gzipped) return -1; hr->gzip_size += peer->in->pos; uwsgi_crc32(&hr->gzip_crc32, peer->in->buf, peer->in->pos); peer->in->pos = 0; if (uwsgi_buffer_insert_chunked(peer->in, 0, zlen)) {free(gzipped); return -1;} if (uwsgi_buffer_append(peer->in, gzipped, zlen)) { free(gzipped); return -1; } free(gzipped); if (uwsgi_buffer_append(peer->in, "\r\n", 2)) return -1; } #endif else if (hr->force_chunked) { peer->in->limit = 0; if (uwsgi_buffer_insert_chunked(peer->in, 0, len)) return -1; if (uwsgi_buffer_append(peer->in, "\r\n", 2)) return -1; peer->in->len = UMIN(peer->in->len, UMAX16); } } // set the input buffer as the main output one peer->session->main_peer->out = peer->in; peer->session->main_peer->out_pos = 0; // set the default hook in case of blocking writes (optimistic approach) cr_write_to_main(peer, hr->func_write); return 1; } ssize_t http_parse(struct corerouter_peer *main_peer) { struct corerouter_session *cs = main_peer->session; struct http_session *hr = (struct http_session *) cs; // is it http body ? if (hr->rnrn == 4) { // something bad happened in keepalive mode... if (!main_peer->session->peers) return -1; if (hr->content_length == 0 && !hr->raw_body) { // ignore data... main_peer->in->pos = 0; return 1; } else { if (hr->content_length) { if (main_peer->in->pos > hr->content_length) { main_peer->in->pos = hr->content_length; hr->content_length = 0; // on pipeline attempt, disable keepalive hr->session.can_keepalive = 0; } else { hr->content_length -= main_peer->in->pos; if (hr->content_length == 0) { main_peer->disabled = 1; // stop reading from the client if (uwsgi_cr_set_hooks(main_peer, NULL, NULL)) return -1; } } } } main_peer->session->peers->out = main_peer->in; main_peer->session->peers->out_pos = 0; cr_write_to_backend(main_peer->session->peers, hr_instance_write); return 1; } // read until \r\n\r\n is found size_t j; size_t len = main_peer->in->pos; char *ptr = main_peer->in->buf; hr->rnrn = 0; for (j = 0; j < len; j++) { if (*ptr == '\r' && (hr->rnrn == 0 || hr->rnrn == 2)) { hr->rnrn++; } else if (*ptr == '\r') { hr->rnrn = 1; } else if (*ptr == '\n' && hr->rnrn == 1) { hr->rnrn = 2; } else if (*ptr == '\n' && hr->rnrn == 3) { hr->rnrn = 4; hr->headers_size = j; // for security if ((j+1) <= len) { hr->remains = len - (j+1); } struct uwsgi_corerouter *ucr = main_peer->session->corerouter; // create a new peer struct corerouter_peer *new_peer = uwsgi_cr_peer_add(main_peer->session); // default hook new_peer->last_hook_read = hr_instance_read; // parse HTTP request if (http_headers_parse(new_peer)) return -1; // check for a valid hostname if (new_peer->key_len == 0) return -1; #ifdef UWSGI_SSL if (hr->force_https) { if (hr_force_https(new_peer)) return -1; break; } #endif // find an instance using the key if (ucr->mapper(ucr, new_peer)) return -1; // check instance if (new_peer->instance_address_len == 0) return -1; uint16_t pktsize = new_peer->out->pos-4; // fix modifiers new_peer->out->buf[0] = new_peer->session->main_peer->modifier1; new_peer->out->buf[3] = new_peer->session->main_peer->modifier2; // fix pktsize new_peer->out->buf[1] = (uint8_t) (pktsize & 0xff); new_peer->out->buf[2] = (uint8_t) ((pktsize >> 8) & 0xff); if (hr->remains > 0) { if (hr->content_length < hr->remains) { hr->remains = hr->content_length; hr->content_length = 0; // we need to avoid problems with pipelined requests hr->session.can_keepalive = 0; } else { hr->content_length -= hr->remains; } if (uwsgi_buffer_append(new_peer->out, main_peer->in->buf + hr->headers_size + 1, hr->remains)) return -1; } if (hr->session.can_keepalive && hr->content_length == 0) { main_peer->disabled = 1; // stop reading from the client if (uwsgi_cr_set_hooks(main_peer, NULL, NULL)) return -1; } if (hr->send_expect_100) { if (hr_manage_expect_continue(new_peer)) return -1; break; } if (hr->websockets > 2 && hr->websocket_key_len > 0) { hr->raw_body = 1; } new_peer->can_retry = 1; cr_connect(new_peer, hr_instance_connected); break; } else { hr->rnrn = 0; } ptr++; } return 1; } // read from client ssize_t hr_read(struct corerouter_peer *main_peer) { // try to always leave 4k available (this will dinamically increase the buffer...) if (uwsgi_buffer_ensure(main_peer->in, uwsgi.page_size)) return -1; ssize_t len = cr_read(main_peer, "hr_read()"); if (!len) return 0; return http_parse(main_peer); } void hr_session_close(struct corerouter_session *cs) { struct http_session *hr = (struct http_session *) cs; if (hr->path_info) { free(hr->path_info); } if (hr->last_chunked) { uwsgi_buffer_destroy(hr->last_chunked); } #ifdef UWSGI_ZLIB if (hr->z.next_in) { deflateEnd(&hr->z); } #endif } ssize_t hr_recv_stud4(struct corerouter_peer * main_peer) { struct http_session *hr = (struct http_session *) main_peer->session; ssize_t len = read(main_peer->fd, hr->stud_prefix + hr->stud_prefix_pos, hr->stud_prefix_remains - hr->stud_prefix_pos); if (len < 0) { cr_try_again; uwsgi_cr_error(main_peer, "hr_recv_stud4()"); return -1; } hr->stud_prefix_pos += len; if (hr->stud_prefix_pos == hr->stud_prefix_remains) { if (hr->stud_prefix[0] != AF_INET) { uwsgi_cr_log(main_peer, "invalid stud prefix for address family %d\n", hr->stud_prefix[0]); return -1; } // set the passed ip address memcpy(&main_peer->session->client_sockaddr.sa_in.sin_addr, hr->stud_prefix + 1, 4); // optimistic approach main_peer->hook_read = hr_read; return hr_read(main_peer); } return len; } // retry connection to the backend static int hr_retry(struct corerouter_peer *peer) { struct uwsgi_corerouter *ucr = peer->session->corerouter; if (peer->instance_address_len > 0) goto retry; if (ucr->mapper(ucr, peer)) { return -1; } if (peer->instance_address_len == 0) { return -1; } retry: // start async connect (again) cr_connect(peer, hr_instance_connected); return 0; } int http_alloc_session(struct uwsgi_corerouter *ucr, struct uwsgi_gateway_socket *ugs, struct corerouter_session *cs, struct sockaddr *sa, socklen_t s_len) { // set the retry hook cs->retry = hr_retry; struct http_session *hr = (struct http_session *) cs; // set the modifier1 cs->main_peer->modifier1 = uhttp.modifier1; cs->main_peer->modifier2 = uhttp.modifier2; // default hook cs->main_peer->last_hook_read = hr_read; if (uhttp.raw_body) { hr->raw_body = 1; } if (uhttp.websockets) { hr->websockets = 1; } hr->func_write = hr_write; // be sure buffer does not grow over 64k cs->main_peer->in->limit = UMAX16; if (sa && sa->sa_family == AF_INET) { struct uwsgi_string_list *usl = uhttp.stud_prefix; while(usl) { if (!memcmp(&cs->client_sockaddr.sa_in.sin_addr, usl->value, 4)) { hr->stud_prefix_remains = 5; cs->main_peer->last_hook_read = hr_recv_stud4; break; } usl = usl->next; } } hr->port = ugs->port; hr->port_len = ugs->port_len; switch(ugs->mode) { #ifdef UWSGI_SSL case UWSGI_HTTP_SSL: hr_setup_ssl(hr, ugs); break; #endif default: if (uwsgi_cr_set_hooks(cs->main_peer, cs->main_peer->last_hook_read, NULL)) return -1; cs->close = hr_session_close; break; } return 0; } void http_setup() { uhttp.cr.name = uwsgi_str("uWSGI http"); uhttp.cr.short_name = uwsgi_str("http"); } int http_init() { uhttp.cr.session_size = sizeof(struct http_session); uhttp.cr.alloc_session = http_alloc_session; if (uhttp.cr.has_sockets && !uwsgi_corerouter_has_backends(&uhttp.cr)) { if (!uwsgi.sockets) { uwsgi_new_socket(uwsgi_concat2("127.0.0.1:0", "")); } uhttp.cr.use_socket = 1; uhttp.cr.socket_num = 0; } uwsgi_corerouter_init((struct uwsgi_corerouter *) &uhttp); return 0; } struct uwsgi_plugin http_plugin = { .name = "http", .options = http_options, .init = http_init, .on_load = http_setup, };