Files
uwsgi/plugins/http/http.c
T
2012-09-07 19:45:58 +02:00

1100 lines
31 KiB
C

/*
uWSGI http
requires:
- async
- caching
- pcre (optional)
*/
#include "../../uwsgi.h"
extern struct uwsgi_server uwsgi;
#include "../corerouter/cr.h"
#ifdef __sun__
#define MAX_HTTP_VEC IOV_MAX*64
#else
#ifdef IOV_MAX
#define MAX_HTTP_VEC IOV_MAX
#else
#define MAX_HTTP_VEC 128
#endif
#endif
#ifdef UWSGI_SSL
#define UWSGI_HTTP_SSL 1
#define UWSGI_HTTP_FORCE_SSL 2
#define HTTP_SSL_STATUS_SHUTDOWN 10
#endif
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
} uhttp;
#ifdef UWSGI_SSL
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-subscription-use-regexp", no_argument, 0, "enable regexp usage in subscription system", uwsgi_opt_true, &uhttp.cr.subscription_regexp, 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, "support HTTP keepalive (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},
{0, 0, 0, 0, 0, 0, 0},
};
struct http_session {
struct corerouter_session crs;
struct uwsgi_header uh;
int rnrn;
char *ptr;
char *port;
int port_len;
struct iovec iov[MAX_HTTP_VEC];
int iov_len;
char uss[MAX_HTTP_VEC * 2];
char buffer[UMAX16];
size_t buffer_len;
char path_info[UMAX16];
uint16_t path_info_len;
char *request_uri;
uint16_t request_uri_len;
size_t received_body;
#ifdef UWSGI_SSL
SSL *ssl;
X509 *ssl_client_cert;
char *ssl_client_dn;
BIO *ssl_bio;
char *ssl_cc;
#endif
in_addr_t ip_addr;
char ip[INET_ADDRSTRLEN];
};
#ifdef UWSGI_SSL
int uwsgi_http_ssl_shutdown(struct http_session *, int);
#endif
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->crs.hostname = val;
h_session->crs.hostname_len = vallen;
}
if (!uwsgi_strncmp("CONTENT_LENGTH", 14, hh, keylen)) {
h_session->crs.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;
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->request_uri = base;
h_session->request_uri_len = ptr - base;
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->crs.hostname = uwsgi.hostname;
h_session->crs.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);
// 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);
#ifdef UWSGI_SSL
// HTTPS (adapted from nginx)
if (h_session->crs.ugs->mode == UWSGI_HTTP_SSL) {
h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "HTTPS", 5, "on", 2, &c);
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);
h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "HTTPS_DN", 8, h_session->ssl_client_dn, strlen(h_session->ssl_client_dn), &c);
}
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);
h_session->uh.pktsize += http_add_uwsgi_var(h_session->iov, h_session->uss + c, h_session->uss + c + 2, "HTTPS_CC", 8, h_session->ssl_cc, cc_len, &c);
}
}
}
}
}
#endif
// 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;
}
}
// this is an hack with dumb/wrong/useless error checking
if (uhttp.manage_expect) {
if (!uwsgi_strncmp("Expect: 100-continue", 20, base, ptr - base)) {
if (h_session->crs.send(&uhttp.cr, &h_session->crs, protocol, protocol_len) == (ssize_t) protocol_len)
h_session->crs.send(&uhttp.cr, &h_session->crs, " 100 Continue\r\n\r\n", 17);
}
}
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;
}
// security check
if (c >= MAX_HTTP_VEC-4) {
uwsgi_log("too much headers in request. skipping it.\n");
return 0;
}
return c;
}
void uwsgi_http_switch_events(struct uwsgi_corerouter *ucr, struct corerouter_session *cs, int interesting_fd) {
ssize_t len;
int j;
struct http_session *hs = (struct http_session *) cs;
#ifndef __sun__
struct msghdr msg;
union {
struct cmsghdr cmsg;
char control[CMSG_SPACE(sizeof(int))];
} msg_control;
struct cmsghdr *cmsg;
#endif
char bbuf[UMAX16];
socklen_t solen = sizeof(int);
switch (cs->status) {
case COREROUTER_STATUS_RECV_HDR:
if (interesting_fd == -1) {
goto choose_node;
}
len = cs->recv(&uhttp.cr, cs, hs->buffer + cs->h_pos, UMAX16 - cs->h_pos);
#ifdef UWSGI_EVENT_USE_PORT
event_queue_add_fd_read(ucr->queue, cs->fd);
#endif
if (len <= 0) {
// check for blocking operation on non-blocking socket
if (len < 0 && errno == EINPROGRESS) break;
corerouter_close_session(ucr, cs);
break;
}
cs->h_pos += len;
for (j = 0; j < len; j++) {
//uwsgi_log("%d %d %d\n", j, *cs->ptr, cs->rnrn);
if (*hs->ptr == '\r' && (hs->rnrn == 0 || hs->rnrn == 2)) {
hs->rnrn++;
}
else if (*hs->ptr == '\r') {
hs->rnrn = 1;
}
else if (*hs->ptr == '\n' && hs->rnrn == 1) {
hs->rnrn = 2;
}
else if (*hs->ptr == '\n' && hs->rnrn == 3) {
hs->ptr++;
cs->post_remains = len - (j + 1);
hs->iov_len = http_parse(hs);
if (hs->iov_len == 0 || cs->hostname_len == 0) {
corerouter_close_session(ucr, cs);
break;
}
#ifdef UWSGI_SSL
if (cs->ugs->mode == UWSGI_HTTP_FORCE_SSL) {
if (hs->request_uri_len > 0) {
char *https_url;
char *colon = memchr(cs->hostname, ':', cs->hostname_len);
if (colon) {
if (cs->ugs->ctx) {
https_url = uwsgi_concat4n(cs->hostname, colon-cs->hostname, ":", 1, cs->ugs->ctx, strlen(cs->ugs->ctx), hs->request_uri, hs->request_uri_len);
}
else {
https_url = uwsgi_concat2n(cs->hostname, colon-cs->hostname, hs->request_uri, hs->request_uri_len);
}
}
else {
if (cs->ugs->ctx) {
https_url = uwsgi_concat4n(cs->hostname, cs->hostname_len, ":", 1, cs->ugs->ctx, strlen(cs->ugs->ctx), hs->request_uri, hs->request_uri_len);
}
else {
https_url = uwsgi_concat2n(cs->hostname, cs->hostname_len, hs->request_uri, hs->request_uri_len);
}
}
struct iovec iov[4];
iov[0].iov_base = "HTTP/1.0 301 Moved Permanently\r\n";
iov[0].iov_len = 32;
iov[1].iov_base = "Location: https://";
iov[1].iov_len = 18;
iov[2].iov_base = https_url;
iov[2].iov_len = strlen(https_url);
iov[3].iov_base = "\r\n\r\n";
iov[3].iov_len = 4;
if (writev(cs->fd, iov, 4) <= 0) {
uwsgi_error("writev()");
}
free(https_url);
}
corerouter_close_session(ucr, cs);
break;
}
#endif
// the mapper hook
choose_node:
if (ucr->mapper(ucr, cs))
break;
// no address found
if (!cs->instance_address_len) {
// if fallback nodes are configured, trigger them
if (ucr->fallback) {
cs->instance_failed = 1;
}
corerouter_close_session(ucr, cs);
break;
}
cs->pass_fd = is_unix(cs->instance_address, cs->instance_address_len);
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;
corerouter_close_session(ucr, cs);
break;
}
cs->status = COREROUTER_STATUS_CONNECTING;
ucr->cr_table[cs->instance_fd] = cs;
event_queue_add_fd_write(ucr->queue, cs->instance_fd);
break;
}
else {
hs->rnrn = 0;
}
hs->ptr++;
}
break;
case COREROUTER_STATUS_CONNECTING:
if (interesting_fd == cs->instance_fd) {
if (getsockopt(cs->instance_fd, SOL_SOCKET, SO_ERROR, (void *) (&cs->soopt), &solen) < 0) {
uwsgi_error("getsockopt()");
cs->instance_failed = 1;
corerouter_close_session(ucr, cs);
break;
}
if (cs->soopt) {
cs->instance_failed = 1;
corerouter_close_session(ucr, cs);
break;
}
#ifdef __BIG_ENDIAN__
hs->uh.pktsize = uwsgi_swap16(cs->uh.pktsize);
#endif
hs->uh.modifier1 = cs->modifier1;
hs->iov[0].iov_base = &hs->uh;
hs->iov[0].iov_len = 4;
if (cs->post_remains > 0) {
hs->iov[hs->iov_len].iov_base = hs->ptr;
if (cs->post_remains > cs->post_cl) {
cs->post_remains = cs->post_cl;
}
hs->iov[hs->iov_len].iov_len = cs->post_remains;
hs->received_body += cs->post_remains;
hs->iov_len++;
}
// increment node requests counter
if (cs->un)
cs->un->requests++;
#ifndef __sun__
// fd passing: PERFORMANCE EXTREME BOOST !!!
if (cs->pass_fd && !cs->post_remains && !uwsgi.no_fd_passing) {
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = hs->iov;
msg.msg_iovlen = hs->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), &cs->fd, sizeof(int));
if (sendmsg(cs->instance_fd, &msg, 0) < 0) {
uwsgi_error("sendmsg()");
}
corerouter_close_session(ucr, cs);
break;
}
#endif
#ifdef __sun__
if (hs->iov_len > IOV_MAX) {
int remains = hs->iov_len;
int iov_len;
while (remains) {
if (remains > IOV_MAX) {
iov_len = IOV_MAX;
}
else {
iov_len = remains;
}
if (writev(cs->instance_fd, hs->iov + (hs->iov_len - remains), iov_len) <= 0) {
uwsgi_error("writev()");
corerouter_close_session(ucr, cs);
break;
}
remains -= iov_len;
}
}
#else
if (writev(cs->instance_fd, hs->iov, hs->iov_len) <= 0) {
uwsgi_error("writev()");
corerouter_close_session(ucr, cs);
break;
}
#endif
event_queue_fd_write_to_read(ucr->queue, cs->instance_fd);
cs->status = COREROUTER_STATUS_RESPONSE;
}
break;
case COREROUTER_STATUS_RESPONSE:
// data from instance
if (interesting_fd == cs->instance_fd) {
// writable ?
if (cs->instance_fd_state) {
len = cs->instance_send(&uhttp.cr, cs, NULL, 0);
#ifdef UWSGI_EVENT_USE_PORT
event_queue_add_fd_write(ucr->queue, cs->instance_fd);
#endif
if (len <= 0) {
if (len < 0 && errno == EINPROGRESS) break;
corerouter_close_session(ucr, cs);
}
break;
}
len = cs->instance_recv(&uhttp.cr, cs, hs->buffer, UMAX16);
#ifdef UWSGI_EVENT_USE_PORT
event_queue_add_fd_read(ucr->queue, cs->instance_fd);
#endif
if (len <= 0) {
if (len < 0 && errno == EINPROGRESS) break;
/*
Keep-Alive implementation.
As soon as the backend close the connection, enable it.
The server will start waiting for another request (for a maximum of --http-socket seconds timeout)
To have a reliable implementation, we need to reset a bunch of values
*/
if (len == 0) {
if (uhttp.keepalive) {
#ifdef UWSGI_DEBUG
uwsgi_log("Keep-Alive enabled\n");
#endif
cs->keepalive = 1;
corerouter_close_session(ucr, cs);
cs->status = COREROUTER_STATUS_RECV_HDR;
hs->ptr = hs->buffer;
hs->rnrn = 0;
cs->pos = 0;
cs->h_pos = 0;
hs->received_body = 0;
cs->post_cl = 0;
cs->instance_fd = -1;
hs->uh.pktsize = 0;
cs->post_remains = 0;
cs->instance_address_len = 0;
cs->hostname_len = 0;
break;
}
#ifdef UWSGI_SSL
if (cs->ugs->mode == UWSGI_HTTP_SSL) {
int ssd_ret = SSL_shutdown(hs->ssl);
// it could fail or success, in both cases close the connection
if (ssd_ret != 0) {
corerouter_close_session(ucr, cs);
break;
}
cs->status = HTTP_SSL_STATUS_SHUTDOWN;
if (uwsgi_http_ssl_shutdown(hs, 1) != 0) {
corerouter_close_session(ucr, cs);
}
break;
}
// something to send in the queue ?
if (cs->write_queue) {
cs->write_queue_close = 1;
break;
}
#endif
}
corerouter_close_session(ucr, cs);
break;
}
hs->buffer_len = len;
len = cs->send(&uhttp.cr, cs, hs->buffer, len);
if (len <= 0) {
if (len < 0 && errno == EINPROGRESS) break;
// check for blocking operation non non-blocking socket
corerouter_close_session(ucr, cs);
break;
}
// update transfer statistics
if (cs->un)
cs->un->transferred += len;
}
// body from client or client ready to receive
else if (interesting_fd == cs->fd) {
// writable ?
if (cs->fd_state) {
#ifdef UWSGI_SSL
if (!cs->ugs->mode == UWSGI_HTTP_SSL) {
len = cs->send(&uhttp.cr, cs, NULL, 0);
}
else {
#endif
len = cs->send(&uhttp.cr, cs, hs->buffer,hs->buffer_len);
#ifdef UWSGI_SSL
}
#endif
#ifdef UWSGI_EVENT_USE_PORT
event_queue_add_fd_write(ucr->queue, cs->fd);
#endif
if (len <= 0) {
if (len < 0 && errno == EINPROGRESS) break;
corerouter_close_session(ucr, cs);
}
break;
}
len = cs->recv(&uhttp.cr, cs, bbuf, UMAX16);
#ifdef UWSGI_EVENT_USE_PORT
event_queue_add_fd_read(ucr->queue, cs->fd);
#endif
if (len <= 0) {
// check for blocking operation on non-blocking socket
if (len < 0 && errno == EINPROGRESS) break;
corerouter_close_session(ucr, cs);
break;
}
if (cs->post_cl == 0 && uhttp.raw_body) goto raw;
// avoid pipelined input
if (hs->received_body >= cs->post_cl) {
break;
}
if (len + hs->received_body > cs->post_cl) {
len = cs->post_cl - hs->received_body;
}
raw:
len = cs->instance_send(&uhttp.cr, cs, bbuf, len);
if (len <= 0) {
if (len < 0 && errno == EINPROGRESS) break;
corerouter_close_session(ucr, cs);
break;
}
hs->received_body += len;
}
break;
#ifdef UWSGI_SSL
case HTTP_SSL_STATUS_SHUTDOWN:
if (uwsgi_http_ssl_shutdown(hs, 0) != 0) {
corerouter_close_session(ucr, cs);
}
break;
#endif
// fallback to destroy !!!
default:
uwsgi_log("unknown event: closing session\n");
corerouter_close_session(ucr, cs);
break;
}
}
void http_setup() {
uhttp.cr.name = uwsgi_str("uWSGI http");
uhttp.cr.short_name = uwsgi_str("http");
}
#ifdef UWSGI_SSL
int uwsgi_http_ssl_shutdown(struct http_session *hs, int state) {
int ret = 0;
if (!state) {
ret = SSL_shutdown(hs->ssl);
}
if (ret == 1) return 1;
int err = SSL_get_error(hs->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
if (hs->crs.fd_state) {
event_queue_fd_write_to_read(uhttp.cr.queue, hs->crs.fd);
hs->crs.fd_state = 0;
}
return 0;
}
else if (err == SSL_ERROR_WANT_WRITE) {
if (!hs->crs.fd_state) {
event_queue_fd_read_to_write(uhttp.cr.queue, hs->crs.fd);
hs->crs.fd_state = 1;
}
return 0;
}
return -1;
}
ssize_t uwsgi_http_ssl_recv(struct uwsgi_corerouter *cr, struct corerouter_session *cs, char *buf, size_t len) {
struct http_session *hs = (struct http_session *) cs;
int ret = SSL_read(hs->ssl, buf, len);
if (ret > 0) {
if (hs->crs.fd_state) {
event_queue_fd_write_to_read(cr->queue, hs->crs.fd);
hs->crs.fd_state = 0;
}
return ret;
}
if (ret == 0) return 0;
int err = SSL_get_error(hs->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
if (hs->crs.fd_state) {
event_queue_fd_write_to_read(cr->queue, hs->crs.fd);
hs->crs.fd_state = 0;
}
errno = EINPROGRESS;
return -1;
}
else if (err == SSL_ERROR_WANT_WRITE) {
if (!hs->crs.fd_state) {
event_queue_fd_read_to_write(cr->queue, hs->crs.fd);
hs->crs.fd_state = 1;
}
errno = EINPROGRESS;
return -1;
}
else if (err == SSL_ERROR_SYSCALL) {
uwsgi_error("SSL_read()");
}
else if (err == SSL_ERROR_SSL && uwsgi.ssl_verbose) {
ERR_print_errors_fp(stderr);
}
return -1;
}
ssize_t uwsgi_http_ssl_send(struct uwsgi_corerouter *cr, struct corerouter_session *cs, char *buf, size_t len) {
struct http_session *hs = (struct http_session *) cs;
int ret = SSL_write(hs->ssl, buf, len);
if (ret > 0) {
if (cs->instance_stopped) {
event_queue_add_fd_read(cr->queue, cs->instance_fd);
cs->instance_stopped = 0;
}
if (cs->fd_state) {
event_queue_fd_write_to_read(cr->queue, cs->fd);
cs->fd_state = 0;
}
return ret;
}
int err = SSL_get_error(hs->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
if (cs->instance_fd != -1) {
event_queue_del_fd(cr->queue, cs->instance_fd, event_queue_read());
cs->instance_stopped = 1;
}
if (cs->fd_state) {
event_queue_fd_write_to_read(cr->queue, cs->fd);
cs->fd_state = 0;
}
errno = EINPROGRESS;
return -1;
}
else if (err == SSL_ERROR_WANT_WRITE) {
if (cs->instance_fd != -1) {
event_queue_del_fd(cr->queue, cs->instance_fd, event_queue_read());
cs->instance_stopped = 1;
}
if (!cs->fd_state) {
event_queue_fd_read_to_write(cr->queue, cs->fd);
cs->fd_state = 1;
}
errno = EINPROGRESS;
return -1;
}
else if (err == SSL_ERROR_SYSCALL) {
uwsgi_error("SSL_write()");
}
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;
}
// free ssl memory
void uwsgi_ssl_close(struct uwsgi_corerouter *ucr, struct corerouter_session *cs) {
struct http_session *hs = (struct http_session *) cs;
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);
}
if (!cs->keepalive)
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;
hs->ptr = hs->buffer;
cs->modifier1 = uhttp.modifier1;
if (sa && sa->sa_family == AF_INET) {
hs->ip_addr = ((struct sockaddr_in *) sa)->sin_addr.s_addr;
}
if (ugs) {
hs->port = ugs->port;
hs->port_len = ugs->port_len;
#ifdef UWSGI_SSL
if (ugs->mode == UWSGI_HTTP_SSL) {
hs->ssl = SSL_new(ugs->ctx);
SSL_set_fd(hs->ssl, cs->fd);
SSL_set_accept_state(hs->ssl);
cs->recv = uwsgi_http_ssl_recv;
cs->send = uwsgi_http_ssl_send;
cs->close = uwsgi_ssl_close;
}
#endif
}
}
int http_init() {
uhttp.cr.session_size = sizeof(struct http_session);
uhttp.cr.switch_events = uwsgi_http_switch_events;
uhttp.cr.alloc_session = http_alloc_session;
if (uhttp.cr.has_sockets && !uwsgi.sockets && !uwsgi_courerouter_has_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,
};