/* uWSGI httprouter */ #include "../../uwsgi.h" extern struct uwsgi_server uwsgi; #include "../corerouter/cr.h" struct uwsgi_http { struct uwsgi_corerouter cr; uint8_t modifier1; struct uwsgi_string_list *http_vars; int manage_expect; int raw_body; int keepalive; #ifdef UWSGI_SSL int https_export_cert; #endif struct uwsgi_string_list *stud_prefix; } uhttp; #ifdef UWSGI_SSL #define UWSGI_HTTP_NOSSL 0 #define UWSGI_HTTP_SSL 1 #define UWSGI_HTTP_FORCE_SSL 2 void uwsgi_opt_https(char *opt, char *value, void *cr) { struct uwsgi_corerouter *ucr = (struct uwsgi_corerouter *) cr; char *client_ca = NULL; // build socket, certificate and key file char *sock = uwsgi_str(value); char *crt = strchr(sock, ','); if (!crt) { uwsgi_log("invalid https syntax must be socket,crt,key\n"); exit(1); } *crt = '\0'; crt++; char *key = strchr(crt, ','); if (!key) { uwsgi_log("invalid https syntax must be socket,crt,key\n"); exit(1); } *key = '\0'; key++; char *ciphers = strchr(key, ','); if (ciphers) { *ciphers = '\0'; ciphers++; client_ca = strchr(ciphers, ','); if (client_ca) { *client_ca = '\0'; client_ca++; } } struct uwsgi_gateway_socket *ugs = uwsgi_new_gateway_socket(sock, ucr->name); // ok we have the socket, initialize ssl if required if (!uwsgi.ssl_initialized) { uwsgi_ssl_init(); } // initialize ssl context ugs->ctx = uwsgi_ssl_new_server_context(uwsgi_concat3(ucr->short_name, "-", ugs->name),crt, key, ciphers, client_ca); // set the ssl mode ugs->mode = UWSGI_HTTP_SSL; ucr->has_sockets++; } void uwsgi_opt_http_to_https(char *opt, char *value, void *cr) { struct uwsgi_corerouter *ucr = (struct uwsgi_corerouter *) cr; char *sock = uwsgi_str(value); char *port = strchr(sock, ','); if (port) { *port = '\0'; port++; } struct uwsgi_gateway_socket *ugs = uwsgi_new_gateway_socket(sock, ucr->name); // set context to the port ugs->ctx = port; // force SSL mode ugs->mode = UWSGI_HTTP_FORCE_SSL; ucr->has_sockets++; } #endif struct uwsgi_option http_options[] = { {"http", 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}, {"https-export-cert", no_argument, 0, "export uwsgi variable HTTPS_CC containing the raw client certificate", uwsgi_opt_true, &uhttp.https_export_cert, 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-use-cache", no_argument, 0, "use uWSGI cache as key->value virtualhost mapper", uwsgi_opt_true, &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-use-cluster", no_argument, 0, "load balance to nodes subscribed to the cluster", uwsgi_opt_true, &uhttp.cr.use_cluster, 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", no_argument, 0, "manage the Expect HTTP request header", uwsgi_opt_true, &uhttp.manage_expect, 0}, {"http-keepalive", no_argument, 0, "experimental HTTP keepalive support (non-pipelined) requests (requires backend support)", uwsgi_opt_true, &uhttp.keepalive, 0}, {"http-raw-body", no_argument, 0, "blindly send HTTP body to backends (required for WebSockets and Icecast support)", uwsgi_opt_true, &uhttp.raw_body, 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}, {0, 0, 0, 0, 0, 0, 0}, }; struct http_session { struct corerouter_session cs; int rnrn; char *port; int port_len; char *request_uri; uint16_t request_uri_len; char *path_info; uint16_t path_info_len; size_t received_body; #ifdef UWSGI_SSL SSL *ssl; X509 *ssl_client_cert; char *ssl_client_dn; BIO *ssl_bio; char *ssl_cc; int force_ssl; struct uwsgi_buffer *force_ssl_buf; #endif struct uwsgi_buffer *uwsgi_req; int send_expect_100; in_addr_t ip_addr; char ip[INET_ADDRSTRLEN]; struct uwsgi_buffer *post_buf; size_t post_buf_max; size_t post_buf_len; off_t post_buf_pos; // 1 (family) + 4/16 (addr) char stud_prefix[17]; size_t stud_prefix_remains; size_t stud_prefix_pos; }; #ifdef UWSGI_SSL int uwsgi_http_ssl_shutdown(struct http_session *, int); #endif int http_add_uwsgi_header(struct http_session *hs, char *hh, uint16_t hhlen) { struct corerouter_session *cs = &hs->cs; struct uwsgi_buffer *buf = hs->uwsgi_req; int i; int status = 0; char *val = hh; uint16_t keylen = 0, vallen = 0; int prefix = 0; char strsize[2]; 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 (!uwsgi_strncmp("HOST", 4, hh, keylen)) { cs->hostname = val; cs->hostname_len = vallen; } if (!uwsgi_strncmp("CONTENT_LENGTH", 14, hh, keylen)) { cs->post_cl = uwsgi_str_num(val, vallen); } if (uwsgi_strncmp("CONTENT_TYPE", 12, hh, keylen) && uwsgi_strncmp("CONTENT_LENGTH", 14, hh, keylen)) { keylen += 5; prefix = 1; } strsize[0] = (uint8_t) (keylen & 0xff); strsize[1] = (uint8_t) ((keylen >> 8) & 0xff); if (uwsgi_buffer_append(buf, strsize, 2)) return -1; if (prefix) { if (uwsgi_buffer_append(buf, "HTTP_", 5)) return -1; } if (uwsgi_buffer_append(buf, hh, keylen - (prefix * 5))) return -1; strsize[0] = (uint8_t) (vallen & 0xff); strsize[1] = (uint8_t) ((vallen >> 8) & 0xff); if (uwsgi_buffer_append(buf, strsize, 2)) return -1; if (uwsgi_buffer_append(buf, val, vallen)) return -1; return 0; } int http_add_uwsgi_var(struct http_session *hs, char *key, uint16_t keylen, char *val, uint16_t vallen) { struct uwsgi_buffer *buf = hs->uwsgi_req; char strsize[2]; strsize[0] = (uint8_t) (keylen & 0xff); strsize[1] = (uint8_t) ((keylen >> 8) & 0xff); if (uwsgi_buffer_append(buf, strsize, 2)) return -1; if (uwsgi_buffer_append(buf, key, keylen)) return -1; strsize[0] = (uint8_t) (vallen & 0xff); strsize[1] = (uint8_t) ((vallen >> 8) & 0xff); if (uwsgi_buffer_append(buf, strsize, 2)) return -1; if (uwsgi_buffer_append(buf, val, vallen)) return -1; return 0; } int http_parse(struct http_session *h_session, size_t http_req_len) { char *ptr = h_session->cs.buffer->buf; char *watermark = ptr + http_req_len; char *base = ptr; char *query_string = NULL; h_session->uwsgi_req = uwsgi_buffer_new(uwsgi.page_size); if (!h_session->uwsgi_req) return -1; h_session->uwsgi_req->limit = UMAX16; // REQUEST_METHOD while (ptr < watermark) { if (*ptr == ' ') { if (http_add_uwsgi_var(h_session, "REQUEST_METHOD", 14, base, ptr - base)) return -1; ptr++; break; } ptr++; } // REQUEST_URI / PATH_INFO / QUERY_STRING base = ptr; while (ptr < watermark) { if (*ptr == '?' && !query_string) { // PATH_INFO must be url-decoded !!! h_session->path_info_len = ptr - base; h_session->path_info = uwsgi_malloc(h_session->path_info_len); http_url_decode(base, &h_session->path_info_len, h_session->path_info); if (http_add_uwsgi_var(h_session, "PATH_INFO", 9, h_session->path_info, h_session->path_info_len)) return -1; query_string = ptr + 1; } else if (*ptr == ' ') { h_session->request_uri = base; h_session->request_uri_len = ptr - base; if (http_add_uwsgi_var(h_session, "REQUEST_URI", 11, base, ptr - base)) return -1; if (!query_string) { // PATH_INFO must be url-decoded !!! h_session->path_info_len = ptr - base; h_session->path_info = uwsgi_malloc(h_session->path_info_len); http_url_decode(base, &h_session->path_info_len, h_session->path_info); if (http_add_uwsgi_var(h_session, "PATH_INFO", 9, h_session->path_info, h_session->path_info_len)) return -1; if (http_add_uwsgi_var(h_session, "QUERY_STRING", 12, "", 0)) return -1; } else { if (http_add_uwsgi_var(h_session, "QUERY_STRING", 12, query_string, ptr - query_string)) return -1; } ptr++; break; } ptr++; } // SERVER_PROTOCOL base = ptr; while (ptr < watermark) { if (*ptr == '\r') { if (ptr + 1 >= watermark) return 0; if (*(ptr + 1) != '\n') return 0; if (http_add_uwsgi_var(h_session, "SERVER_PROTOCOL", 15, base, ptr - base)) return -1; ptr += 2; break; } ptr++; } // SCRIPT_NAME if (http_add_uwsgi_var(h_session, "SCRIPT_NAME", 11, "", 0)) return -1; // SERVER_NAME if (http_add_uwsgi_var(h_session, "SERVER_NAME", 11, uwsgi.hostname, uwsgi.hostname_len)) return -1; h_session->cs.hostname = uwsgi.hostname; h_session->cs.hostname_len = uwsgi.hostname_len; // SERVER_PORT if (http_add_uwsgi_var(h_session, "SERVER_PORT", 11, h_session->port, h_session->port_len)) return -1; // UWSGI_ROUTER if (http_add_uwsgi_var(h_session, "UWSGI_ROUTER", 12, "http", 4)) return -1; // stud HTTPS if (h_session->stud_prefix_pos > 0) { if (http_add_uwsgi_var(h_session, "HTTPS", 5, "on", 2)) return -1; } #ifdef UWSGI_SSL // HTTPS (adapted from nginx) if (h_session->cs.ugs->mode == UWSGI_HTTP_SSL) { if (http_add_uwsgi_var(h_session, "HTTPS", 5, "on", 2)) return -1; h_session->ssl_client_cert = SSL_get_peer_certificate(h_session->ssl); if (h_session->ssl_client_cert) { X509_NAME *name = X509_get_subject_name(h_session->ssl_client_cert); if (name) { h_session->ssl_client_dn = X509_NAME_oneline(name, NULL, 0); if (http_add_uwsgi_var(h_session, "HTTPS_DN", 8, h_session->ssl_client_dn, strlen(h_session->ssl_client_dn))) return -1; } if (uhttp.https_export_cert) { h_session->ssl_bio = BIO_new(BIO_s_mem()); if (h_session->ssl_bio) { if (PEM_write_bio_X509(h_session->ssl_bio, h_session->ssl_client_cert) > 0) { size_t cc_len = BIO_pending(h_session->ssl_bio); h_session->ssl_cc = uwsgi_malloc(cc_len); BIO_read(h_session->ssl_bio, h_session->ssl_cc, cc_len); if (http_add_uwsgi_var(h_session, "HTTPS_CC", 8, h_session->ssl_cc, cc_len)) return -1; } } } } } else if (h_session->cs.ugs->mode == UWSGI_HTTP_FORCE_SSL) { h_session->force_ssl = 1; } #endif // REMOTE_ADDR if (inet_ntop(AF_INET, &h_session->ip_addr, h_session->ip, INET_ADDRSTRLEN)) { if (http_add_uwsgi_var(h_session, "REMOTE_ADDR", 11, h_session->ip, strlen(h_session->ip))) return -1; } else { uwsgi_error("inet_ntop()"); } //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)) { h_session->send_expect_100 = 1; } } if (http_add_uwsgi_header(h_session, 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 (http_add_uwsgi_var(h_session, hv->value, equal - hv->value, equal + 1, strlen(equal + 1))) return -1; } hv = hv->next; } return 0; } ssize_t hr_instance_read_response(struct corerouter_session *); ssize_t hr_read_body(struct corerouter_session *); #ifdef UWSGI_SSL ssize_t hr_read_ssl_body(struct corerouter_session *); ssize_t hr_send_force_https(struct corerouter_session * cs) { struct http_session *hs = (struct http_session *) cs; if (!hs->force_ssl_buf) { hs->force_ssl_buf = uwsgi_buffer_new(uwsgi.page_size); if (!hs->force_ssl_buf) return -1; if (uwsgi_buffer_append(hs->force_ssl_buf, "HTTP/1.0 301 Moved Permanently\r\nLocation: https://", 50)) return -1; char *colon = memchr(cs->hostname, ':', cs->hostname_len); if (colon) { if (uwsgi_buffer_append(hs->force_ssl_buf, cs->hostname, colon-cs->hostname)) return -1; } else { if (uwsgi_buffer_append(hs->force_ssl_buf, cs->hostname, cs->hostname_len)) return -1; } if (cs->ugs->ctx) { if (uwsgi_buffer_append(hs->force_ssl_buf, ":", 1)) return -1; if (uwsgi_buffer_append(hs->force_ssl_buf,cs->ugs->ctx, strlen(cs->ugs->ctx))) return -1; } if (uwsgi_buffer_append(hs->force_ssl_buf, hs->request_uri, hs->request_uri_len)) return -1; if (uwsgi_buffer_append(hs->force_ssl_buf, "\r\n\r\n", 4)) return -1; } ssize_t len = write(cs->fd, hs->force_ssl_buf->buf + cs->buffer_pos, hs->force_ssl_buf->pos - cs->buffer_pos); if (len < 0) { cr_try_again; uwsgi_error("hr_send_force_https()"); return -1; } cs->buffer_pos += len; if (cs->buffer_pos == hs->force_ssl_buf->pos) { return 0; } return len; } #endif ssize_t hr_write_body(struct corerouter_session * cs) { struct http_session *hs = (struct http_session *) cs; ssize_t len = write(cs->instance_fd, hs->post_buf->buf + hs->post_buf_pos, hs->post_buf_len - hs->post_buf_pos); if (len < 0) { cr_try_again; uwsgi_error("hr_write_body()"); return -1; } hs->post_buf_pos += len; // the body chunk has been sent, start again reading from client and instance if (hs->post_buf_pos == (ssize_t) hs->post_buf_len) { uwsgi_cr_hook_instance_write(cs, NULL); uwsgi_cr_hook_instance_read(cs, hr_instance_read_response); #ifdef UWSGI_SSL if (!hs->ssl) { #endif uwsgi_cr_hook_read(cs, hr_read_body); #ifdef UWSGI_SSL } else { uwsgi_cr_hook_read(cs, hr_read_ssl_body); } #endif } return len; } #ifdef UWSGI_SSL ssize_t hr_read_ssl_body(struct corerouter_session * cs) { struct http_session *hs = (struct http_session *) cs; int ret = SSL_read(hs->ssl, hs->post_buf->buf, hs->post_buf_max); if (ret > 0) { int ret2 = SSL_pending(hs->ssl); if (ret2 > 0) { if (uwsgi_buffer_fix(hs->post_buf, hs->post_buf->len + ret2 )) { uwsgi_log("[uwsgi-https] cannot fix the buffer to %d\n", hs->post_buf->len + ret2); return -1; } if (SSL_read(hs->ssl, hs->post_buf->buf + ret, ret2) != ret2) { uwsgi_log("[uwsgi-https] SSL_read() on %d bytes of pending data failed\n", ret2); return -1; } ret += ret2; } hs->post_buf_len = ret; hs->post_buf_pos = 0; uwsgi_cr_hook_read(cs, NULL); uwsgi_cr_hook_instance_read(cs, NULL); uwsgi_cr_hook_instance_write(cs, hr_write_body); return ret; } if (ret == 0) return 0; int err = SSL_get_error(hs->ssl, ret); if (err == SSL_ERROR_WANT_READ) { if (cs->event_hook_write) { uwsgi_cr_hook_write(cs, NULL); } uwsgi_cr_hook_read(cs, hr_read_ssl_body); errno = EINPROGRESS; return -1; } else if (err == SSL_ERROR_WANT_WRITE) { if (cs->event_hook_read) { uwsgi_cr_hook_read(cs, NULL); } uwsgi_cr_hook_write(cs, hr_read_ssl_body); errno = EINPROGRESS; return -1; } else if (err == SSL_ERROR_SYSCALL) { uwsgi_error("hr_read_ssl_body()"); } else if (err == SSL_ERROR_SSL && uwsgi.ssl_verbose) { ERR_print_errors_fp(stderr); } return -1; } #endif ssize_t hr_read_body(struct corerouter_session * cs) { struct http_session *hs = (struct http_session *) cs; ssize_t len = read(cs->fd, hs->post_buf->buf, hs->post_buf_max); if (len < 0) { cr_try_again; uwsgi_error("hr_read_body()"); return -1; } // connection closed if (len == 0) return 0; hs->post_buf_len = len; hs->post_buf_pos = 0; // ok we have a body, stop reading from the client and the instance and start writing to the instance uwsgi_cr_hook_read(cs, NULL); uwsgi_cr_hook_instance_read(cs, NULL); uwsgi_cr_hook_instance_write(cs, hr_write_body); return len; } #ifdef UWSGI_SSL ssize_t hr_write_ssl_response(struct corerouter_session * cs) { struct http_session *hs = (struct http_session *) cs; int ret = SSL_write(hs->ssl, cs->buffer->buf + cs->buffer_pos, cs->buffer_len - cs->buffer_pos); if (ret > 0) { cs->buffer_pos += ret; // could be a partial write uwsgi_cr_hook_write(cs, hr_write_ssl_response); // ok this response chunk is sent, let's wait for another one if (cs->buffer_pos == (ssize_t) cs->buffer_len) { uwsgi_cr_hook_write(cs, NULL); uwsgi_cr_hook_instance_read(cs, hr_instance_read_response); } return ret; } int err = SSL_get_error(hs->ssl, ret); if (err == SSL_ERROR_WANT_READ) { if (cs->event_hook_write) { uwsgi_cr_hook_write(cs, NULL); } uwsgi_cr_hook_read(cs, hr_write_ssl_response); errno = EINPROGRESS; return -1; } else if (err == SSL_ERROR_WANT_WRITE) { if (cs->event_hook_read) { uwsgi_cr_hook_read(cs, NULL); } uwsgi_cr_hook_write(cs, hr_write_ssl_response); errno = EINPROGRESS; return -1; } else if (err == SSL_ERROR_SYSCALL) { uwsgi_error("hr_write_ssl_response()"); } else if (err == SSL_ERROR_SSL && uwsgi.ssl_verbose) { ERR_print_errors_fp(stderr); } else if (err == SSL_ERROR_ZERO_RETURN) { return 0; } return -1; } #endif ssize_t hr_write_response(struct corerouter_session * cs) { ssize_t len = write(cs->fd, cs->buffer->buf + cs->buffer_pos, cs->buffer_len - cs->buffer_pos); if (len < 0) { cr_try_again; uwsgi_error("hr_write_response()"); return -1; } cs->buffer_pos += len; // ok this response chunk is sent, let's wait for another one if (cs->buffer_pos == (ssize_t) cs->buffer_len) { uwsgi_cr_hook_write(cs, NULL); uwsgi_cr_hook_instance_read(cs, hr_instance_read_response); } return len; } ssize_t hr_instance_read_response(struct corerouter_session * cs) { ssize_t len = read(cs->instance_fd, cs->buffer->buf, cs->buffer->len); if (len < 0) { cr_try_again; uwsgi_error("hr_instance_read_response()"); return -1; } // end of the response if (len == 0) { return 0; } cs->buffer_pos = 0; cs->buffer_len = len; // ok stop reading from the instance, and start writing to the client uwsgi_cr_hook_instance_read(cs, NULL); #ifdef UWSGI_SSL struct http_session *hs = (struct http_session *) cs; if (!hs->ssl) { #endif uwsgi_cr_hook_write(cs, hr_write_response); #ifdef UWSGI_SSL } else { uwsgi_cr_hook_write(cs, hr_write_ssl_response); } #endif return len; } int hr_start_waiting(struct corerouter_session * cs) { struct http_session *hs = (struct http_session *) cs; // stop writing to the instance uwsgi_cr_hook_instance_write(cs, NULL); // start reading from the instance uwsgi_cr_hook_instance_read(cs, hr_instance_read_response); // re-start reading from the client (for body or connection close) // allocate a buffer for client body (could be delimited or dynamic) hs->post_buf_max = UMAX16; if (cs->post_cl > 0) { hs->post_buf_max = UMIN(UMAX16, cs->post_cl); } hs->post_buf = uwsgi_buffer_new(hs->post_buf_max); if (!hs->post_buf) return -1; #ifdef UWSGI_SSL if (!hs->ssl) { #endif uwsgi_cr_hook_read(cs, hr_read_body); #ifdef UWSGI_SSL } else { uwsgi_cr_hook_read(cs, hr_read_ssl_body); } #endif return 0; } ssize_t hr_post_remains(struct corerouter_session * cs) { char *ptr = (cs->buffer->buf + cs->buffer->pos) - cs->post_remains; ssize_t len = write(cs->instance_fd, ptr + cs->buffer_pos, cs->post_remains - cs->buffer_pos); if (len < 0) { cr_try_again; uwsgi_error("hr_post_remains()"); return -1; } cs->buffer_pos += len; if (cs->buffer_pos == (ssize_t) cs->post_remains) { if (hr_start_waiting(cs)) return -1; } return len; } ssize_t hr_instance_send_request(struct corerouter_session * cs) { struct http_session *hs = (struct http_session *) cs; ssize_t len = write(cs->instance_fd, hs->uwsgi_req->buf + cs->buffer_pos, cs->uh.pktsize - cs->buffer_pos); if (len < 0) { cr_try_again; uwsgi_error("hr_instance_send_request()"); return -1; } cs->buffer_pos += len; // ok the request is sent, we can start sending client body (if any) and we can start waiting // for response if (cs->buffer_pos == cs->uh.pktsize) { cs->buffer_pos = 0; // some HTTP body left ? if (cs->post_remains > 0) { uwsgi_cr_hook_instance_write(cs, hr_post_remains); return len; } if (hr_start_waiting(cs)) return -1; } return len; } ssize_t hr_instance_send_request_header(struct corerouter_session * cs) { #ifdef __BIG_ENDIAN__ // on the first round fix endianess if (cs->buffer_pos == 0) { cs->uh.pktsize = uwsgi_swap16(cs->uh.pktsize); } #endif ssize_t len = write(cs->instance_fd, &cs->uh + cs->buffer_pos, 4 - cs->buffer_pos); if (len < 0) { cr_try_again; uwsgi_error("hr_instance_send_request_header()"); return -1; } cs->buffer_pos += len; // ok the request is sent, we can start sending client body (if any) and we can start waiting // for response if (cs->buffer_pos == 4) { cs->buffer_pos = 0; #ifdef __BIG_ENDIAN__ // on the last round restore endianess cs->uh.pktsize = uwsgi_swap16(cs->uh.pktsize); #endif uwsgi_cr_hook_instance_write(cs, hr_instance_send_request); } return len; } ssize_t hr_send_expect_continue(struct corerouter_session * cs) { char *msg = "HTTP/1.0 100 Continue\r\n\r\n" ; ssize_t len; #ifdef UWSGI_SSL struct http_session *hs = (struct http_session *) cs; if (!hs->ssl) { #endif len = write(cs->fd, msg + cs->buffer_pos, 25 - cs->buffer_pos); if (len < 0) { cr_try_again; uwsgi_error("hr_send_expect_continue()"); return -1; } cs->buffer_pos += len; #ifdef UWSGI_SSL } else { int ret = SSL_write(hs->ssl, msg + cs->buffer_pos, 25 - cs->buffer_pos); if (ret > 0) { len = ret; cs->buffer_pos += ret; // could be a partial write uwsgi_cr_hook_write(cs, hr_send_expect_continue); goto done; } int err = SSL_get_error(hs->ssl, ret); if (err == SSL_ERROR_WANT_READ) { if (cs->event_hook_write) { uwsgi_cr_hook_write(cs, NULL); } uwsgi_cr_hook_read(cs, hr_write_ssl_response); errno = EINPROGRESS; return -1; } else if (err == SSL_ERROR_WANT_WRITE) { if (cs->event_hook_read) { uwsgi_cr_hook_read(cs, NULL); } uwsgi_cr_hook_write(cs, hr_write_ssl_response); errno = EINPROGRESS; return -1; } else if (err == SSL_ERROR_SYSCALL) { uwsgi_error("hr_write_ssl_response()"); } else if (err == SSL_ERROR_SSL && uwsgi.ssl_verbose) { ERR_print_errors_fp(stderr); } else if (err == SSL_ERROR_ZERO_RETURN) { return 0; } return -1; } done: #endif if (cs->buffer_pos == 25) { cs->buffer_pos = 0; uwsgi_cr_hook_write(cs, NULL); uwsgi_cr_hook_instance_write(cs, hr_instance_send_request_header); } return len; } ssize_t hr_instance_connected(struct corerouter_session * cs) { cs->connecting = 0; socklen_t solen = sizeof(int); // first check for errors if (getsockopt(cs->instance_fd, SOL_SOCKET, SO_ERROR, (void *) (&cs->soopt), &solen) < 0) { uwsgi_error("hr_instance_connected()/getsockopt()"); cs->instance_failed = 1; return -1; } if (cs->soopt) { cs->instance_failed = 1; return -1; } cs->buffer_pos = 0; cs->uh.modifier1 = cs->modifier1; cs->uh.modifier2 = 0; struct http_session *hs = (struct http_session *) cs; cs->uh.pktsize = hs->uwsgi_req->pos; // check for expect/continue if (hs->send_expect_100) { uwsgi_cr_hook_write(cs, hr_send_expect_continue); return 1; } // ok instance is connected, wait for write again if (cs->static_node) cs->static_node->custom2++; if (cs->un) cs->un->requests++; uwsgi_cr_hook_instance_write(cs, hr_instance_send_request_header); // return a value > 0 return 1; } ssize_t hs_http_manage(struct corerouter_session *, ssize_t); #ifdef UWSGI_SSL ssize_t hr_recv_http_ssl(struct corerouter_session * cs) { // be sure buffer does not grow over 64k cs->buffer->limit = UMAX16; // try to always leave 4k available if (uwsgi_buffer_ensure(cs->buffer, uwsgi.page_size)) { uwsgi_log("[uwsgi-https] cannot ensure the buffer (size: %d)\n", cs->buffer->len); return -1; } struct http_session *hs = (struct http_session *) cs; int ret = SSL_read(hs->ssl, cs->buffer->buf + cs->buffer_pos, cs->buffer->len - cs->buffer_pos); if (ret > 0) { int ret2 = SSL_pending(hs->ssl); if (ret2 > 0) { if (uwsgi_buffer_fix(cs->buffer, cs->buffer->len + ret2 )) { uwsgi_log("[uwsgi-https] cannot fix the buffer to %d\n", cs->buffer->len + ret2 ); return -1; } if (SSL_read(hs->ssl, cs->buffer->buf + cs->buffer_pos + ret, ret2) != ret2) { uwsgi_log("[uwsgi-https] SSL_read() on %d bytes of pending data failed\n", ret2); return -1; } ret += ret2; } return hs_http_manage(cs, ret); } if (ret == 0) return 0; int err = SSL_get_error(hs->ssl, ret); if (err == SSL_ERROR_WANT_READ) { if (cs->event_hook_write) { uwsgi_cr_hook_write(cs, NULL); } uwsgi_cr_hook_read(cs, hr_recv_http_ssl); errno = EINPROGRESS; return -1; } else if (err == SSL_ERROR_WANT_WRITE) { if (cs->event_hook_read) { uwsgi_cr_hook_read(cs, NULL); } uwsgi_cr_hook_write(cs, hr_recv_http_ssl); errno = EINPROGRESS; return -1; } else if (err == SSL_ERROR_SYSCALL) { uwsgi_error("hr_recv_http_ssl()"); } else if (err == SSL_ERROR_SSL && uwsgi.ssl_verbose) { ERR_print_errors_fp(stderr); } return -1; } #endif ssize_t hr_recv_http(struct corerouter_session * cs) { // be sure buffer does not grow over 64k cs->buffer->limit = UMAX16; // try to always leave 4k available if (uwsgi_buffer_ensure(cs->buffer, uwsgi.page_size)) return -1; ssize_t len = read(cs->fd, cs->buffer->buf + cs->buffer_pos, cs->buffer->len - cs->buffer_pos); if (len < 0) { cr_try_again; uwsgi_error("hr_recv_http()"); return -1; } return hs_http_manage(cs, len); } ssize_t hs_http_manage(struct corerouter_session * cs, ssize_t len) { // fix buffer usage (TODO a bit ugly...) cs->buffer->pos += len; // read until \r\n\r\n is found int j; char *ptr = cs->buffer->buf + cs->buffer_pos; struct http_session *hs = (struct http_session *) cs; cs->buffer_pos += len; for (j = 0; j < len; j++) { if (*ptr == '\r' && (hs->rnrn == 0 || hs->rnrn == 2)) { hs->rnrn++; } else if (*ptr == '\r') { hs->rnrn = 1; } else if (*ptr == '\n' && hs->rnrn == 1) { hs->rnrn = 2; } else if (*ptr == '\n' && hs->rnrn == 3) { cs->post_remains = len - (j + 1); // parse HTTP request size_t http_req_len = (cs->buffer_pos-len)+j; if (http_parse(hs, http_req_len)) return -1; // check for a valid hostname if (cs->hostname_len == 0) return -1; #ifdef UWSGI_SSL if (hs->force_ssl) { cs->buffer_pos = 0; uwsgi_cr_hook_read(cs, NULL); uwsgi_cr_hook_write(cs, hr_send_force_https); break; } #endif // get instance name if (cs->corerouter->mapper(cs->corerouter, cs)) return -1; struct uwsgi_corerouter *ucr = cs->corerouter; if (cs->instance_address_len == 0) { // if fallback nodes are configured, trigger them if (ucr->fallback) { cs->instance_failed = 1; } return -1; } // stop receiving from the client uwsgi_cr_hook_read(cs, NULL); // start async connect cs->instance_fd = uwsgi_connectn(cs->instance_address, cs->instance_address_len, 0, 1); if (cs->instance_fd < 0) { cs->instance_failed = 1; cs->soopt = errno; return -1; } // map the instance cs->corerouter->cr_table[cs->instance_fd] = cs; // wait for connection cs->connecting = 1; uwsgi_cr_hook_instance_write(cs, hr_instance_connected); break; } else { hs->rnrn = 0; } ptr++; } return len; } void hr_session_close(struct corerouter_session *cs) { struct http_session *hs = (struct http_session *) cs; if (hs->path_info) { free(hs->path_info); } if (hs->uwsgi_req) { uwsgi_buffer_destroy(hs->uwsgi_req); } if (hs->post_buf) { uwsgi_buffer_destroy(hs->post_buf); } } static ssize_t hr_recv_stud4(struct corerouter_session * cs) { struct http_session *hs = (struct http_session *) cs; ssize_t len = read(cs->fd, hs->stud_prefix + hs->stud_prefix_pos, hs->stud_prefix_remains - hs->stud_prefix_pos); if (len < 0) { cr_try_again; uwsgi_error("hr_recv_stud4()"); return -1; } hs->stud_prefix_pos += len; if (hs->stud_prefix_pos == hs->stud_prefix_remains) { if (hs->stud_prefix[0] != AF_INET) { uwsgi_log("[uwsgi-http] invalid stud prefix\n"); return -1; } // set the passed ip address memcpy(&hs->ip_addr, hs->stud_prefix + 1, 4); uwsgi_cr_hook_read(cs, hr_recv_http); } return len; } #ifdef UWSGI_SSL void hr_session_ssl_close(struct corerouter_session *cs) { hr_session_close(cs); struct http_session *hs = (struct http_session *) cs; SSL_shutdown(hs->ssl); if (hs->ssl_client_dn) { OPENSSL_free(hs->ssl_client_dn); } if (hs->ssl_cc) { free(hs->ssl_cc); } if (hs->ssl_bio) { BIO_free(hs->ssl_bio); } if (hs->ssl_client_cert) { X509_free(hs->ssl_client_cert); } // clear the errors (otherwise they could be propagated) ERR_clear_error(); SSL_free(hs->ssl); } #endif void http_alloc_session(struct uwsgi_corerouter *ucr, struct uwsgi_gateway_socket *ugs, struct corerouter_session *cs, struct sockaddr *sa, socklen_t s_len) { struct http_session *hs = (struct http_session *) cs; cs->modifier1 = uhttp.modifier1; if (sa && sa->sa_family == AF_INET) { hs->ip_addr = ((struct sockaddr_in *) sa)->sin_addr.s_addr; struct uwsgi_string_list *usl = uhttp.stud_prefix; while(usl) { if (!memcmp(&hs->ip_addr, usl->value, 4)) { hs->stud_prefix_remains = 5; uwsgi_cr_hook_read(cs, hr_recv_stud4); break; } usl = usl->next; } } hs->rnrn = 0; hs->port = ugs->port; hs->port_len = ugs->port_len; #ifdef UWSGI_SSL if (ugs->mode == UWSGI_HTTP_SSL) { if (ugs->mode == UWSGI_HTTP_FORCE_SSL) { uwsgi_cr_hook_write(cs, hr_send_force_https); cs->close = hr_session_close; return; } hs->ssl = SSL_new(ugs->ctx); SSL_set_fd(hs->ssl, cs->fd); SSL_set_accept_state(hs->ssl); uwsgi_cr_hook_read(cs, hr_recv_http_ssl); cs->close = hr_session_ssl_close; } else { #endif if (!cs->event_hook_read) { uwsgi_cr_hook_read(cs, hr_recv_http); } cs->close = hr_session_close; #ifdef UWSGI_SSL } #endif } 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.sockets && !uwsgi_corerouter_has_backends(&uhttp.cr)) { 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, };