/*

   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,
};
