/* uWSGI http requires: - async - caching - pcre (optional) */ #include "../../uwsgi.h" extern struct uwsgi_server uwsgi; #include "../../lib/corerouter.h" #define MAX_HTTP_VEC 128 #define HTTP_STATUS_FREE 0 #define HTTP_STATUS_CONNECTING 1 #define HTTP_STATUS_RECV 2 #define HTTP_STATUS_RESPONSE 4 #define add_timeout(x) uwsgi_add_rb_timer(uhttp.timeouts, time(NULL)+uhttp.socket_timeout, x) #define del_timeout(x) rb_erase(&x->timeout->rbt, uhttp.timeouts); free(x->timeout); struct uwsgi_http { int has_sockets; int processes; int use_cache; int use_cluster; int nevents; int cheap; int i_am_cheap; int has_subscription_sockets; int subscription_regexp; char *pattern; int pattern_len; char *base; int base_len; char *to; int to_len; struct uwsgi_string_list *http_vars; uint8_t modifier1; int load; int socket_timeout; struct uwsgi_subscribe_slot *subscriptions; int manage_expect; struct rb_root *timeouts; } uhttp; void uwsgi_opt_http(char *opt, char *value, void *foobar) { uwsgi_new_gateway_socket(value, "uWSGI http"); uhttp.has_sockets++; } void uwsgi_opt_http_ss(char *opt, char *value, void *foobar) { struct uwsgi_gateway_socket *ugs = uwsgi_new_gateway_socket(value, "uWSGI http"); ugs->subscription = 1; uhttp.has_subscription_sockets++; } void uwsgi_opt_http_use_pattern(char *opt, char *value, void *foobar) { uhttp.pattern = value; // optimization uhttp.pattern_len = strlen(uhttp.pattern); } void uwsgi_opt_http_use_base(char *opt, char *value, void *foobar) { uhttp.base = value; // optimization uhttp.base_len = strlen(uhttp.base); } void uwsgi_opt_http_use_to(char *opt, char *value, void *foobar) { uhttp.to = value; // optimization uhttp.to_len = strlen(uhttp.to); } struct uwsgi_option http_options[] = { {"http", required_argument, 0, "add an http router/server on the specified address", uwsgi_opt_http, NULL, 0}, {"http-processes", required_argument, 0, "set the number of http processes to spawn", uwsgi_opt_set_int, &uhttp.processes, 0}, {"http-workers", required_argument, 0, "set the number of http processes to spawn", uwsgi_opt_set_int, &uhttp.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", uwsgi_opt_http_use_to, NULL, 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.use_cache, 0}, {"http-use-pattern", required_argument, 0, "use the specified pattern for mapping requests to unix sockets", uwsgi_opt_http_use_pattern, NULL, 0}, {"http-use-base", required_argument, 0, "use the specified base for mapping requests to unix sockets", uwsgi_opt_http_use_base, NULL, 0}, {"http-use-cluster", no_argument, 0, "load balance to nodes subscribed to the cluster", uwsgi_opt_true, &uhttp.use_cluster, 0}, {"http-events", required_argument, 0, "set the number of concurrent http async events", uwsgi_opt_set_int, &uhttp.nevents, 0}, {"http-subscription-server", required_argument, 0, "enable the subscription server", uwsgi_opt_http_ss, NULL, 0}, {"http-subscription-use-regexp", no_argument, 0, "enable regexp usage in subscription system", uwsgi_opt_true, &uhttp.subscription_regexp, 0}, {"http-timeout", required_argument, 0, "set internal http socket timeout", uwsgi_opt_set_int, &uhttp.socket_timeout, 0}, {"http-manage-expect", no_argument, 0, "manage the Expect HTTP request header", uwsgi_opt_true, &uhttp.manage_expect, 0}, {0, 0, 0, 0, 0, 0, 0}, }; extern struct uwsgi_server uwsgi; void http_manage_subscription(char *key, uint16_t keylen, char *val, uint16_t vallen, void *data) { struct uwsgi_subscribe_req *usr = (struct uwsgi_subscribe_req *) data; if (!uwsgi_strncmp("key", 3, key, keylen)) { usr->key = val; usr->keylen = vallen; } else if (!uwsgi_strncmp("auth", 4, key, keylen)) { usr->auth = val; usr->auth_len = vallen; } else if (!uwsgi_strncmp("address", 7, key, keylen)) { usr->address = val; usr->address_len = vallen; } } struct http_session { int fd; int instance_fd; int status; struct uwsgi_header uh; uint8_t h_pos; uint16_t pos; uint16_t parse_pos; char *ptr; int rnrn; char *hostname; uint16_t hostname_len; char *instance_address; uint64_t instance_address_len; int instance_failed; int pass_fd; size_t remains; char *port; int port_len; struct iovec iov[MAX_HTTP_VEC]; int iov_len; char uss[MAX_HTTP_VEC * 2]; char buffer[UMAX16]; char path_info[UMAX16]; uint16_t path_info_len; size_t content_length; size_t received_body; struct uwsgi_subscribe_node *un; in_addr_t ip_addr; char ip[INET_ADDRSTRLEN]; struct uwsgi_rb_timer *timeout; }; static struct uwsgi_rb_timer *reset_timeout(struct http_session *uhttp_session) { del_timeout(uhttp_session); return add_timeout(uhttp_session); } static void close_session(struct http_session **uhttp_table, struct http_session *uhttp_session) { close(uhttp_session->fd); uhttp_table[uhttp_session->fd] = NULL; if (uhttp_session->instance_fd != -1) { if (uhttp.subscriptions && (uhttp_session->instance_failed || uhttp_session->status == HTTP_STATUS_CONNECTING)) { uwsgi_log("marking %.*s as failed\n", (int) uhttp_session->instance_address_len, uhttp_session->instance_address); uwsgi_remove_subscribe_node(&uhttp.subscriptions, uhttp_session->un); } close(uhttp_session->instance_fd); uhttp_table[uhttp_session->instance_fd] = NULL; } uhttp.load--; del_timeout(uhttp_session); free(uhttp_session); } static void expire_timeouts(struct http_session **uhttp_table) { time_t current = time(NULL); struct uwsgi_rb_timer *urbt; for (;;) { urbt = uwsgi_min_rb_timer(uhttp.timeouts); if (urbt == NULL) return; if (urbt->key <= current) { close_session(uhttp_table, (struct http_session *) urbt->data); uwsgi_log("timeout !!!\n"); continue; } break; } } struct http_session *alloc_uhttp_session() { return uwsgi_malloc(sizeof(struct http_session)); } uint16_t http_add_uwsgi_header(struct http_session *h_session, struct iovec *iov, char *strsize1, char *strsize2, char *hh, uint16_t hhlen, int *c) { int i; int status = 0; char *val = hh; uint16_t keylen = 0, vallen = 0; int prefix = 0; if (*c >= MAX_HTTP_VEC) return 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 0; if ((*c) + 4 >= MAX_HTTP_VEC) return 0; if (!uwsgi_strncmp("HOST", 4, hh, keylen)) { h_session->hostname = val; h_session->hostname_len = vallen; } if (!uwsgi_strncmp("CONTENT_LENGTH", 14, hh, keylen)) { h_session->content_length = uwsgi_str_num(val, vallen); } if (uwsgi_strncmp("CONTENT_TYPE", 12, hh, keylen) && uwsgi_strncmp("CONTENT_LENGTH", 14, hh, keylen)) { keylen += 5; prefix = 1; if ((*c) + 5 >= MAX_HTTP_VEC) return 0; } strsize1[0] = (uint8_t) (keylen & 0xff); strsize1[1] = (uint8_t) ((keylen >> 8) & 0xff); iov[*c].iov_base = strsize1; iov[*c].iov_len = 2; *c += 1; if (prefix) { iov[*c].iov_base = "HTTP_"; iov[*c].iov_len = 5; *c += 1; } iov[*c].iov_base = hh; iov[*c].iov_len = keylen - (prefix * 5); *c += 1; strsize2[0] = (uint8_t) (vallen & 0xff); strsize2[1] = (uint8_t) ((vallen >> 8) & 0xff); iov[*c].iov_base = strsize2; iov[*c].iov_len = 2; *c += 1; iov[*c].iov_base = val; iov[*c].iov_len = vallen; *c += 1; return 2 + keylen + 2 + vallen; } uint16_t http_add_uwsgi_var(struct iovec * iov, char *strsize1, char *strsize2, char *key, uint16_t keylen, char *val, uint16_t vallen, int *c) { if ((*c) + 4 >= MAX_HTTP_VEC) return 0; strsize1[0] = (uint8_t) (keylen & 0xff); strsize1[1] = (uint8_t) ((keylen >> 8) & 0xff); iov[*c].iov_base = strsize1; iov[*c].iov_len = 2; *c += 1; iov[*c].iov_base = key; iov[*c].iov_len = keylen; *c += 1; strsize2[0] = (uint8_t) (vallen & 0xff); strsize2[1] = (uint8_t) ((vallen >> 8) & 0xff); iov[*c].iov_base = strsize2; iov[*c].iov_len = 2; *c += 1; iov[*c].iov_base = val; iov[*c].iov_len = vallen; *c += 1; return 2 + keylen + 2 + vallen; } int http_parse(struct http_session *h_session) { char *ptr = h_session->buffer; char *watermark = h_session->ptr; char *base = ptr; // leave a slot for uwsgi header int c = 1; char *query_string = NULL; char *protocol = NULL; size_t protocol_len = 0; // REQUEST_METHOD while (ptr < watermark) { if (*ptr == ' ') { h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "REQUEST_METHOD", 14, base, ptr - base, &c); 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; http_url_decode(base, &h_session->path_info_len, h_session->path_info); h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "PATH_INFO", 9, h_session->path_info, h_session->path_info_len, &c); query_string = ptr + 1; } else if (*ptr == ' ') { h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "REQUEST_URI", 11, base, ptr - base, &c); if (!query_string) { // PATH_INFO must be url-decoded !!! h_session->path_info_len = ptr - base; http_url_decode(base, &h_session->path_info_len, h_session->path_info); h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "PATH_INFO", 9, h_session->path_info, h_session->path_info_len, &c); h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "QUERY_STRING", 12, "", 0, &c); } else { h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "QUERY_STRING", 12, query_string, ptr - query_string, &c); } 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; h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "SERVER_PROTOCOL", 15, base, ptr - base, &c); protocol = base; protocol_len = ptr - base; ptr += 2; break; } ptr++; } // SCRIPT_NAME h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "SCRIPT_NAME", 11, "", 0, &c); // SERVER_NAME h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "SERVER_NAME", 11, uwsgi.hostname, uwsgi.hostname_len, &c); h_session->hostname = uwsgi.hostname; h_session->hostname_len = uwsgi.hostname_len; // SERVER_PORT h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "SERVER_PORT", 11, h_session->port, h_session->port_len, &c); h_session->hostname = uwsgi.hostname; h_session->hostname_len = uwsgi.hostname_len; // UWSGI_ROUTER h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "UWSGI_ROUTER", 12, "http", 4, &c); // REMOTE_ADDR if (inet_ntop(AF_INET, &h_session->ip_addr, h_session->ip, INET_ADDRSTRLEN)) { h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "REMOTE_ADDR", 11, h_session->ip, strlen(h_session->ip), &c); } else { uwsgi_error("inet_ntop()"); } //HEADERS base = ptr; while (ptr < watermark) { if (*ptr == '\r') { if (ptr + 1 >= watermark) return 0; if (*(ptr + 1) != '\n') return 0; // multiline header ? if (ptr + 2 < watermark) { if (*(ptr + 2) == ' ' || *(ptr + 2) == '\t') { ptr += 2; continue; } } if (uhttp.manage_expect) { if (!uwsgi_strncmp("Expect: 100-continue", 20, base, ptr - base)) { if (send(h_session->fd, protocol, protocol_len, 0) == (ssize_t) protocol_len) { if (send(h_session->fd, " 100 Continue\r\n\r\n", 17, 0) != 17) { uwsgi_error("send()"); } } else { uwsgi_error("send()"); } } } h_session->uh.pktsize += http_add_uwsgi_header(h_session, h_session->iov, h_session->uss + c, h_session->uss + c + 2, base, ptr - base, &c); ptr++; base = ptr + 1; } ptr++; } struct uwsgi_string_list *hv = uhttp.http_vars; while(hv) { char *equal = strchr(hv->value, '='); if (equal) { h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, hv->value, equal - hv->value, equal + 1, strlen(equal + 1), &c); } hv = hv->next; } #ifdef UWSGI_DEBUG uwsgi_log("vec size: %d pkt size: %d load %d\n", c, h_session->uh.pktsize, uhttp.load); #endif return c; } void http_loop(int id) { int nevents; int interesting_fd; int new_connection; ssize_t len; int i, j; char *magic_table[0xff]; char bbuf[UMAX16]; time_t delta; struct uwsgi_rb_timer *min_timeout; #ifndef __sun__ struct msghdr msg; union { struct cmsghdr cmsg; char control[CMSG_SPACE(sizeof(int))]; } msg_control; struct cmsghdr *cmsg; #endif union uwsgi_sockaddr uhttp_addr; socklen_t uhttp_addr_len = sizeof(struct sockaddr_un); struct http_session *uhttp_session; struct http_session **uhttp_table; int uhttp_queue; int soopt; socklen_t solen = sizeof(int); uhttp_table = uwsgi_malloc(sizeof(struct http_session *) * uwsgi.max_fd); for (i = 0; i < (int) uwsgi.max_fd; i++) { uhttp_table[i] = NULL; } void *events = uwsgi_corerouter_setup_event_queue("uWSGI http", id, uhttp.nevents, &uhttp_queue, 0); if (uhttp.has_subscription_sockets) event_queue_add_fd_read(uhttp_queue, ushared->gateways[id].internal_subscription_pipe[1]); if (uhttp.pattern) { init_magic_table(magic_table); } uhttp.timeouts = uwsgi_init_rb_timer(); if (!uhttp.socket_timeout) uhttp.socket_timeout = 30; for (;;) { min_timeout = uwsgi_min_rb_timer(uhttp.timeouts); if (min_timeout == NULL) { delta = -1; } else { delta = min_timeout->key - time(NULL); if (delta <= 0) { expire_timeouts(uhttp_table); delta = 0; } } nevents = event_queue_wait_multi(uhttp_queue, delta, events, uhttp.nevents); if (nevents == 0) { // manage timeout expire_timeouts(uhttp_table); } for (i = 0; i < nevents; i++) { interesting_fd = event_queue_interesting_fd(events, i); struct uwsgi_gateway_socket *ugs = uwsgi.gateway_sockets; int taken = 0; while (ugs) { if (ugs->gateway == &ushared->gateways[id] && interesting_fd == ugs->fd) { if (!ugs->subscription) { new_connection = accept(ugs->fd, (struct sockaddr *) &uhttp_addr, &uhttp_addr_len); #ifdef UWSGI_EVENT_USE_PORT event_queue_add_fd_read(uhttp_queue, ugs->fd); #endif if (new_connection < 0) { taken = 1; break; } #ifndef __linux__ uwsgi_socket_b(new_connection); #endif uhttp_table[new_connection] = alloc_uhttp_session(); uhttp_table[new_connection]->fd = new_connection; uhttp_table[new_connection]->instance_fd = -1; uhttp_table[new_connection]->status = HTTP_STATUS_RECV; uhttp_table[new_connection]->h_pos = 0; uhttp_table[new_connection]->pos = 0; uhttp_table[new_connection]->rnrn = 0; uhttp_table[new_connection]->parse_pos = 0; uhttp_table[new_connection]->pass_fd = 0; uhttp_table[new_connection]->ptr = uhttp_table[new_connection]->buffer; uhttp_table[new_connection]->instance_address_len = 0; uhttp_table[new_connection]->uh.modifier1 = uhttp.modifier1; uhttp_table[new_connection]->uh.pktsize = 0; uhttp_table[new_connection]->uh.modifier2 = 0; uhttp_table[new_connection]->ip_addr = ((struct sockaddr_in *) &uhttp_addr)->sin_addr.s_addr; uhttp_table[new_connection]->instance_failed = 0; uhttp_table[new_connection]->content_length = 0; uhttp_table[new_connection]->received_body = 0; uhttp_table[new_connection]->port = ugs->port; uhttp_table[new_connection]->port_len = ugs->port_len; uhttp_table[new_connection]->timeout = add_timeout(uhttp_table[new_connection]); uhttp.load++; event_queue_add_fd_read(uhttp_queue, new_connection); } else { uwsgi_corerouter_manage_subscription("uWSGI http", id, ugs, uhttp_queue, &uhttp.subscriptions, uhttp.subscription_regexp, http_manage_subscription, 0, &uhttp.i_am_cheap); } taken = 1; break; } ugs = ugs->next; } if (taken) continue; if (interesting_fd == ushared->gateways[id].internal_subscription_pipe[1]) { uwsgi_corerouter_manage_internal_subscription("uWSGI http", uhttp_queue, interesting_fd, &uhttp.subscriptions, uhttp.subscription_regexp, http_manage_subscription, 0, &uhttp.i_am_cheap); } // process already active sessions; uhttp_session = uhttp_table[interesting_fd]; // something is going wrong... if (uhttp_session == NULL) continue; if (event_queue_interesting_fd_has_error(events, i)) { close_session(uhttp_table, uhttp_session); continue; } uhttp_session->timeout = reset_timeout(uhttp_session); switch (uhttp_session->status) { case HTTP_STATUS_RECV: len = recv(uhttp_session->fd, uhttp_session->buffer + uhttp_session->h_pos, UMAX16 - uhttp_session->h_pos, 0); #ifdef UWSGI_EVENT_USE_PORT event_queue_add_fd_read(uhttp_queue, uhttp_session->fd); #endif if (len <= 0) { if (len < 0) uwsgi_error("recv()"); close_session(uhttp_table, uhttp_session); break; } uhttp_session->h_pos += len; for (j = 0; j < len; j++) { //uwsgi_log("%d %d %d\n", j, *uhttp_session->ptr, uhttp_session->rnrn); if (*uhttp_session->ptr == '\r' && (uhttp_session->rnrn == 0 || uhttp_session->rnrn == 2)) { uhttp_session->rnrn++; } else if (*uhttp_session->ptr == '\r') { uhttp_session->rnrn = 1; } else if (*uhttp_session->ptr == '\n' && uhttp_session->rnrn == 1) { uhttp_session->rnrn = 2; } else if (*uhttp_session->ptr == '\n' && uhttp_session->rnrn == 3) { uhttp_session->ptr++; uhttp_session->remains = len - (j + 1); uhttp_session->iov_len = http_parse(uhttp_session); if (uhttp_session->iov_len == 0) { close_session(uhttp_table, uhttp_session); break; } if (uhttp.use_cluster) { uhttp_session->instance_address = uwsgi_cluster_best_node(); if (uhttp_session->instance_address) { uhttp_session->instance_address_len = strlen(uhttp_session->instance_address); } } else if (uhttp.use_cache) { uhttp_session->instance_address = uwsgi_cache_get(uhttp_session->hostname, uhttp_session->hostname_len, &uhttp_session->instance_address_len); } else if (uhttp.base) { uhttp_session->instance_address = uwsgi_concat2n(uhttp.base, uhttp.base_len, uhttp_session->hostname, uhttp_session->hostname_len); uhttp_session->instance_address_len = uhttp.base_len + uhttp_session->hostname_len; } else if (uhttp.pattern) { magic_table['s'] = uwsgi_concat2n(uhttp_session->hostname, uhttp_session->hostname_len, "", 0); int tmp_addr_len = 0; uhttp_session->instance_address = magic_sub(uhttp.pattern, uhttp.pattern_len, &tmp_addr_len, magic_table); uhttp_session->instance_address_len = tmp_addr_len; free(magic_table['s']); } else if (uhttp.to) { uhttp_session->instance_address = uhttp.to; uhttp_session->instance_address_len = uhttp.to_len; } else if (uhttp.has_subscription_sockets) { uhttp_session->un = uwsgi_get_subscribe_node(&uhttp.subscriptions, uhttp_session->hostname, uhttp_session->hostname_len, uhttp.subscription_regexp); if (uhttp_session->un && uhttp_session->un->len) { uhttp_session->instance_address = uhttp_session->un->name; uhttp_session->instance_address_len = uhttp_session->un->len; } } else if (uwsgi.sockets) { uhttp_session->instance_address = uwsgi.sockets->name; uhttp_session->instance_address_len = strlen(uwsgi.sockets->name); } if (!uhttp_session->instance_address_len) { close_session(uhttp_table, uhttp_session); break; } uhttp_session->pass_fd = is_unix(uhttp_session->instance_address, uhttp_session->instance_address_len); uhttp_session->instance_fd = uwsgi_connectn(uhttp_session->instance_address, uhttp_session->instance_address_len, 0, 1); #ifdef UWSGI_DEBUG uwsgi_log("uwsgi backend: %.*s\n", (int) uhttp_session->instance_address_len, uhttp_session->instance_address); #endif if (uhttp.pattern || uhttp.base) { free(uhttp_session->instance_address); } if (uhttp_session->instance_fd < 0) { uhttp_session->instance_failed = 1; close_session(uhttp_table, uhttp_session); break; } uhttp_session->status = HTTP_STATUS_CONNECTING; uhttp_table[uhttp_session->instance_fd] = uhttp_session; event_queue_add_fd_write(uhttp_queue, uhttp_session->instance_fd); break; } else { uhttp_session->rnrn = 0; } uhttp_session->ptr++; } break; case HTTP_STATUS_CONNECTING: if (interesting_fd == uhttp_session->instance_fd) { if (getsockopt(uhttp_session->instance_fd, SOL_SOCKET, SO_ERROR, (void *) (&soopt), &solen) < 0) { uwsgi_error("getsockopt()"); uhttp_session->instance_failed = 1; close_session(uhttp_table, uhttp_session); break; } if (soopt) { uwsgi_log("unable to connect() to uwsgi instance: %s\n", strerror(soopt)); uhttp_session->instance_failed = 1; close_session(uhttp_table, uhttp_session); break; } #ifdef __BIG_ENDIAN__ uhttp_session->uh.pktsize = uwsgi_swap16(uhttp_session->uh.pktsize); #endif uhttp_session->iov[0].iov_base = &uhttp_session->uh; uhttp_session->iov[0].iov_len = 4; if (uhttp_session->remains > 0) { uhttp_session->iov[uhttp_session->iov_len].iov_base = uhttp_session->ptr; if (uhttp_session->remains > uhttp_session->content_length) { uhttp_session->remains = uhttp_session->content_length; } uhttp_session->iov[uhttp_session->iov_len].iov_len = uhttp_session->remains; uhttp_session->received_body += uhttp_session->remains; uhttp_session->iov_len++; } #ifndef __sun__ // fd passing: PERFORMANCE EXTREME BOOST !!! if (uhttp_session->pass_fd && !uhttp_session->remains && !uwsgi.no_fd_passing) { msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = uhttp_session->iov; msg.msg_iovlen = uhttp_session->iov_len; msg.msg_flags = 0; msg.msg_control = &msg_control; msg.msg_controllen = sizeof(msg_control); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(cmsg), &uhttp_session->fd, sizeof(int)); if (sendmsg(uhttp_session->instance_fd, &msg, 0) < 0) { uwsgi_error("sendmsg()"); } close(uhttp_session->fd); close(uhttp_session->instance_fd); uhttp_table[uhttp_session->fd] = NULL; uhttp_table[uhttp_session->instance_fd] = NULL; uhttp.load--; del_timeout(uhttp_session); free(uhttp_session); break; } #endif #ifdef __sun__ if (uhttp_session->iov_len > IOV_MAX) { int remains = uhttp_session->iov_len; int iov_len; while (remains) { if (remains > IOV_MAX) { iov_len = IOV_MAX; } else { iov_len = remains; } if (writev(uhttp_session->instance_fd, uhttp_session->iov + (uhttp_session->iov_len - remains), iov_len) <= 0) { uwsgi_error("writev()"); close_session(uhttp_table, uhttp_session); break; } remains -= iov_len; } } #else if (writev(uhttp_session->instance_fd, uhttp_session->iov, uhttp_session->iov_len) <= 0) { uwsgi_error("writev()"); close_session(uhttp_table, uhttp_session); break; } #endif event_queue_fd_write_to_read(uhttp_queue, uhttp_session->instance_fd); uhttp_session->status = HTTP_STATUS_RESPONSE; } break; case HTTP_STATUS_RESPONSE: // data from instance if (interesting_fd == uhttp_session->instance_fd) { len = recv(uhttp_session->instance_fd, bbuf, UMAX16, 0); #ifdef UWSGI_EVENT_USE_PORT event_queue_add_fd_read(uhttp_queue, uhttp_session->instance_fd); #endif if (len <= 0) { if (len < 0) uwsgi_error("recv()"); close_session(uhttp_table, uhttp_session); break; } ssize_t s_len = send(uhttp_session->fd, bbuf, len, 0); if (s_len <= 0) { if (s_len < 0) uwsgi_error("send()"); close(uhttp_session->fd); close(uhttp_session->instance_fd); uhttp_table[uhttp_session->fd] = NULL; uhttp_table[uhttp_session->instance_fd] = NULL; uhttp.load--; del_timeout(uhttp_session); free(uhttp_session); break; } } // body from client else if (interesting_fd == uhttp_session->fd) { len = recv(uhttp_session->fd, bbuf, UMAX16, 0); #ifdef UWSGI_EVENT_USE_PORT event_queue_add_fd_read(uhttp_queue, uhttp_session->fd); #endif if (len <= 0) { if (len < 0) uwsgi_error("recv()"); close(uhttp_session->fd); close(uhttp_session->instance_fd); uhttp_table[uhttp_session->fd] = NULL; uhttp_table[uhttp_session->instance_fd] = NULL; uhttp.load--; del_timeout(uhttp_session); free(uhttp_session); break; } if (uhttp_session->received_body >= uhttp_session->content_length) { break; } if (len + uhttp_session->received_body > uhttp_session->content_length) { len = uhttp_session->content_length - uhttp_session->received_body; } len = send(uhttp_session->instance_fd, bbuf, len, 0); if (len <= 0) { if (len < 0) uwsgi_error("send()"); close_session(uhttp_table, uhttp_session); break; } uhttp_session->received_body += len; } break; // fallback to destroy !!! default: uwsgi_log("default action\n"); close(uhttp_session->fd); uhttp_table[uhttp_session->fd] = NULL; if (uhttp_session->instance_fd != -1) { close(uhttp_session->instance_fd); uhttp_table[uhttp_session->instance_fd] = NULL; } uhttp.load--; del_timeout(uhttp_session); free(uhttp_session); break; } } } } int http_init() { int i; if (uhttp.has_sockets) { if (uhttp.use_cache && !uwsgi.cache_max_items) { uwsgi_log("you need to create a uwsgi cache to use the http (add --cache )\n"); exit(1); } if (!uhttp.nevents) uhttp.nevents = 64; if (!uhttp.base && !uhttp.use_cache && !uhttp.to && !uwsgi.sockets && !uhttp.has_subscription_sockets && !uhttp.use_cluster) { uwsgi_new_socket(uwsgi_concat2("127.0.0.1:0", "")); } uwsgi_corerouter_setup_sockets("uWSGI http"); if (uhttp.processes < 1) uhttp.processes = 1; for (i = 0; i < uhttp.processes; i++) { if (register_gateway("uWSGI http", http_loop) == NULL) { uwsgi_log("unable to register the http gateway\n"); exit(1); } } } return 0; } struct uwsgi_plugin http_plugin = { .name = "http", .options = http_options, .init = http_init, };