From 94c29a396a405649200c3add11630a18b0a4ea43 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 11 Jan 2013 10:22:12 +0100 Subject: [PATCH] added new files --- core/websockets.c | 301 ++++++++++++++++++ plugins/http/common.h | 159 ++++++++++ plugins/http/https.c | 347 +++++++++++++++++++++ plugins/http/spdy3.c | 688 ++++++++++++++++++++++++++++++++++++++++++ plugins/http/spdy3.h | 180 +++++++++++ tests/websockets.py | 51 ++++ 6 files changed, 1726 insertions(+) create mode 100644 core/websockets.c create mode 100644 plugins/http/common.h create mode 100644 plugins/http/https.c create mode 100644 plugins/http/spdy3.c create mode 100644 plugins/http/spdy3.h create mode 100644 tests/websockets.py diff --git a/core/websockets.c b/core/websockets.c new file mode 100644 index 00000000..6d7c60bb --- /dev/null +++ b/core/websockets.c @@ -0,0 +1,301 @@ +#include "uwsgi.h" + +/* + + uWSGI websockets functions + + sponsored by 20Tab S.r.l. + +*/ + +extern struct uwsgi_server uwsgi; + +struct uwsgi_buffer *uwsgi_websocket_message(char *msg, size_t len) { + struct uwsgi_buffer *ub = uwsgi_buffer_new(10 + len); + if (uwsgi_buffer_u8(ub, 0x81)) goto error; + if (len < 126) { + if (uwsgi_buffer_u8(ub, len)) goto error; + } + else if (len < (1 << 16)) { + if (uwsgi_buffer_u8(ub, 126)) goto error; + if (uwsgi_buffer_u16be(ub, len)) goto error; + } + else if (len < ((uint64_t)1 << 63)) { + if (uwsgi_buffer_u8(ub, 127)) goto error; + if (uwsgi_buffer_u64be(ub, len)) goto error; + } + else { + goto error; + } + + if (uwsgi_buffer_append(ub, msg, len)) goto error; + return ub; + +error: + uwsgi_buffer_destroy(ub); + return NULL; +} + +int uwsgi_websockets_ping(struct wsgi_request *wsgi_req) { + ssize_t len = uwsgi.websockets_hook_send(wsgi_req, uwsgi.websockets_ping); + if (len <= 0) { + return -1; + } + wsgi_req->websocket_last_ping = uwsgi_now(); + return 0; +} + +int uwsgi_websockets_pong(struct wsgi_request *wsgi_req) { + time_t now = uwsgi_now(); + if (wsgi_req->websocket_last_ping == 0 || + ( wsgi_req->websocket_last_ping > 0 && now - wsgi_req->websocket_last_ping > uwsgi.websockets_ping_freq)) { + + if (uwsgi_websockets_ping(wsgi_req)) return -1; + return 0; + } + // check if last pong arrived in time + else if (wsgi_req->websocket_last_ping > 0) { + if (wsgi_req->websocket_last_pong < wsgi_req->websocket_last_ping) { + if (wsgi_req->websocket_last_ping - wsgi_req->websocket_last_pong > + uwsgi.websockets_ping_freq) { + uwsgi_log("[uwsgi-websocket] no PONG received in %d seconds !!!\n", uwsgi.websockets_ping_freq); + return -1; + } + } + } + ssize_t len = uwsgi.websockets_hook_send(wsgi_req, uwsgi.websockets_pong); + if (len <= 0) { + return -1; + } + return 0; +} + +ssize_t uwsgi_websocket_send_do(struct wsgi_request *wsgi_req, char *msg, size_t len) { + struct uwsgi_buffer *ub = uwsgi_websocket_message(msg, len); + if (!ub) return -1; + + ssize_t ret = uwsgi.websockets_hook_send(wsgi_req, ub); + uwsgi_buffer_destroy(ub); + if (ret > 0) { + wsgi_req->response_size += ret; + } + return ret; + +} + +ssize_t uwsgi_websocket_send(struct wsgi_request *wsgi_req, char *msg, size_t len) { + if (wsgi_req->websocket_closed) { + return -1; + } + ssize_t ret = uwsgi_websocket_send_do(wsgi_req, msg, len); + if (ret <= 0) { + wsgi_req->websocket_closed = 1; + } + return ret; +} + +void uwsgi_websocket_parse_header(struct wsgi_request *wsgi_req) { + uint8_t byte1 = wsgi_req->websocket_buf->buf[0]; + uint8_t byte2 = wsgi_req->websocket_buf->buf[1]; + wsgi_req->websocket_opcode = byte1 & 0xf; + wsgi_req->websocket_has_mask = byte2 >> 7; + wsgi_req->websocket_size = byte2 & 0x7f; +} + +struct uwsgi_buffer *uwsgi_websockets_parse(struct wsgi_request *wsgi_req) { + // de-mask buffer + uint8_t *ptr = (uint8_t *) (wsgi_req->websocket_buf->buf + (wsgi_req->websocket_pktsize - wsgi_req->websocket_size)); + size_t i; + + if (wsgi_req->websocket_has_mask) { + uint8_t *mask = ptr-4; + for(i=0;iwebsocket_size;i++) { + ptr[i] = ptr[i] ^ mask[i%4]; + } + } + + struct uwsgi_buffer *ub = uwsgi_buffer_new(wsgi_req->websocket_size); + if (uwsgi_buffer_append(ub, (char *) ptr, wsgi_req->websocket_size)) goto error; + if (uwsgi_buffer_decapitate(wsgi_req->websocket_buf, wsgi_req->websocket_pktsize)) goto error; + wsgi_req->websocket_phase = 0; + wsgi_req->websocket_need = 2; + return ub; +error: + uwsgi_buffer_destroy(ub); + return NULL; +} + + +struct uwsgi_buffer *uwsgi_websocket_recv_do(struct wsgi_request *wsgi_req) { + if (!wsgi_req->websocket_buf) { + // this buffer will be destroyed on connection close + wsgi_req->websocket_buf = uwsgi_buffer_new(uwsgi.page_size); + // need 2 byte header + wsgi_req->websocket_need = 2; + // set status code + wsgi_req->status = 101; + } + + for(;;) { + size_t remains = wsgi_req->websocket_buf->pos; + // i have data; + if (remains >= wsgi_req->websocket_need) { + switch(wsgi_req->websocket_phase) { + // header + case 0: + uwsgi_websocket_parse_header(wsgi_req); + wsgi_req->websocket_pktsize = 2 + (wsgi_req->websocket_has_mask*4); + if (wsgi_req->websocket_size == 126) { + wsgi_req->websocket_need += 2; + wsgi_req->websocket_phase = 1; + wsgi_req->websocket_pktsize += 2; + } + else if (wsgi_req->websocket_size == 127) { + wsgi_req->websocket_need += 8; + wsgi_req->websocket_phase = 1; + wsgi_req->websocket_pktsize += 8; + } + else { + wsgi_req->websocket_phase = 2; + } + break; + // size + case 1: + if (wsgi_req->websocket_size == 126) { + wsgi_req->websocket_size = uwsgi_be16(wsgi_req->websocket_buf->buf+2); + } + else if (wsgi_req->websocket_size == 127) { + wsgi_req->websocket_size = uwsgi_be64(wsgi_req->websocket_buf->buf+2); + } + else { + uwsgi_log("[uwsgi-websocket] BUG error in websocket parser\n"); + return NULL; + } + if (wsgi_req->websocket_size > (uwsgi.websockets_max_size*1024)) { + uwsgi_log("[uwsgi-websocket] invalid packet size received: %llu, max allowed: %llu\n", wsgi_req->websocket_size, uwsgi.websockets_max_size * 1024); + return NULL; + } + wsgi_req->websocket_phase = 2; + break; + // mask check + case 2: + if (wsgi_req->websocket_has_mask) { + wsgi_req->websocket_need += 4; + wsgi_req->websocket_phase = 3; + } + else { + wsgi_req->websocket_need += wsgi_req->websocket_size; + wsgi_req->websocket_phase = 4; + } + break; + // mask + case 3: + wsgi_req->websocket_pktsize += wsgi_req->websocket_size; + wsgi_req->websocket_need += wsgi_req->websocket_size; + wsgi_req->websocket_phase = 4; + break; + // message + case 4: + switch (wsgi_req->websocket_opcode) { + // message + case 0: + case 1: + case 2: + return uwsgi_websockets_parse(wsgi_req); + // close + case 0x8: + return NULL; + // ping + case 0x9: + if (uwsgi_websockets_pong(wsgi_req)) { + return NULL; + } + break; + // pong + case 0xA: + wsgi_req->websocket_last_pong = uwsgi_now(); + break; + default: + break; + } + // reset the status + wsgi_req->websocket_phase = 0; + wsgi_req->websocket_need = 2; + // decapitate the buffer + if (uwsgi_buffer_decapitate(wsgi_req->websocket_buf, wsgi_req->websocket_pktsize)) return NULL; + break; + // oops + default: + uwsgi_log("[uwsgi-websocket] BUG error in websocket parser\n"); + return NULL; + } + } + // need more data + else { + if (uwsgi_buffer_ensure(wsgi_req->websocket_buf, uwsgi.page_size)) return NULL; + ssize_t len = uwsgi.websockets_hook_recv(wsgi_req); + if (len <= 0) { + return NULL; + } + // update buffer size + wsgi_req->websocket_buf->pos+=len; + } + } + + return NULL; +} + +struct uwsgi_buffer *uwsgi_websocket_recv(struct wsgi_request *wsgi_req) { + if (wsgi_req->websocket_closed) { + return NULL; + } + struct uwsgi_buffer *ub = uwsgi_websocket_recv_do(wsgi_req); + if (!ub) { + wsgi_req->websocket_closed = 1; + } + return ub; +} + + +ssize_t uwsgi_websockets_simple_send(struct wsgi_request *wsgi_req, struct uwsgi_buffer *ub) { + ssize_t len = wsgi_req->socket->proto_write(wsgi_req, ub->buf, ub->pos); + if (wsgi_req->write_errors > 0) { + return -1; + } + return len; +} + +ssize_t uwsgi_websockets_simple_recv(struct wsgi_request *wsgi_req) { + int fd = wsgi_req->poll.fd; + int ret = -1; +retry: + ret = uwsgi_waitfd(fd, uwsgi.websockets_pong_freq); + if (ret < 0) return -1; + + // send ping + if (ret == 0) { + //unsolicited pong + if (uwsgi_websockets_pong(wsgi_req)) { + return -1; + } + goto retry; + } + + ssize_t len = read(fd, wsgi_req->websocket_buf->buf + wsgi_req->websocket_buf->pos, wsgi_req->websocket_buf->len - wsgi_req->websocket_buf->pos); + if (len <= 0) { + uwsgi_error("[uwsgi-websocket] uwsgi_websockets_simple_recv()/read()"); + } + return len; +} + +void uwsgi_websockets_init() { + uwsgi.websockets_hook_send = uwsgi_websockets_simple_send; + uwsgi.websockets_hook_recv = uwsgi_websockets_simple_recv; + uwsgi.websockets_pong = uwsgi_buffer_new(2); + uwsgi_buffer_append(uwsgi.websockets_pong, "\x8A\0", 2); + uwsgi.websockets_ping = uwsgi_buffer_new(2); + uwsgi_buffer_append(uwsgi.websockets_ping, "\x89\0", 2); + uwsgi.websockets_ping_freq = 30; + uwsgi.websockets_pong_freq = 10; + uwsgi.websockets_max_size = 1024; +} diff --git a/plugins/http/common.h b/plugins/http/common.h new file mode 100644 index 00000000..49f5d3ab --- /dev/null +++ b/plugins/http/common.h @@ -0,0 +1,159 @@ +#include "../../uwsgi.h" + +extern struct uwsgi_server uwsgi; + +#include "../corerouter/cr.h" + +#ifdef UWSGI_SSL +#ifdef OPENSSL_NPN_UNSUPPORTED +#define UWSGI_SPDY +#include +#endif +#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 websockets; + char *https_session_context; + int https_export_cert; +#endif + + struct uwsgi_string_list *stud_prefix; + +#ifdef UWSGI_SPDY + int spdy_index; +#endif + +}; + +struct http_session { + + struct corerouter_session session; + + // used for http parser + int rnrn; + size_t headers_size; + size_t remains; + size_t content_length; + + int raw_body; + + char *port; + int port_len; + + char *request_uri; + uint16_t request_uri_len; + + char *path_info; + uint16_t path_info_len; + +#ifdef UWSGI_SSL + int websockets; + char *origin; + uint16_t origin_len; + char *websocket_key; + uint16_t websocket_key_len; +#endif + + 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 + +#ifdef UWSGI_SPDY + int spdy; + int spdy_initialized; + int spdy_phase; + uint32_t spdy_need; + + z_stream spdy_z_in; + z_stream spdy_z_out; + + uint8_t spdy_frame_type; + + uint16_t spdy_control_version; + uint16_t spdy_control_type; + uint8_t spdy_control_flags; + uint32_t spdy_control_length; + + uint32_t spdy_data_stream_id; + uint8_t spdy_data_flags; + uint32_t spdy_data_length; + + ssize_t (*spdy_hook)(struct corerouter_peer *); +#endif + + int send_expect_100; + + in_addr_t ip_addr; + + // 1 (family) + 4/16 (addr) + char stud_prefix[17]; + size_t stud_prefix_remains; + size_t stud_prefix_pos; + + ssize_t (*func_write)(struct corerouter_peer *); + +}; + + +#ifdef UWSGI_SSL + +#define UWSGI_HTTP_NOSSL 0 +#define UWSGI_HTTP_SSL 1 +#define UWSGI_HTTP_FORCE_SSL 2 + +void uwsgi_opt_https(char *, char *, void *); +void uwsgi_opt_https2(char *, char *, void *); +void uwsgi_opt_http_to_https(char *, char *, void *); + +ssize_t hr_recv_http_ssl(struct corerouter_peer *); +ssize_t hr_read_ssl_body(struct corerouter_peer *); +ssize_t hr_write_ssl_response(struct corerouter_peer *); + +ssize_t hr_send_force_https(struct corerouter_peer *); + +void hr_session_ssl_close(struct corerouter_session *); + +ssize_t hr_ssl_read(struct corerouter_peer *); +ssize_t hr_ssl_write(struct corerouter_peer *); + +int hr_https_add_vars(struct http_session *, struct uwsgi_buffer *); +void hr_setup_ssl(struct http_session *, struct uwsgi_gateway_socket *); + +#endif + +#ifdef UWSGI_SPDY +int uwsgi_spdy_npn(SSL *ssl, const unsigned char **, unsigned int *, void *); +void uwsgi_spdy_info_cb(SSL const *, int, int); +ssize_t hr_recv_spdy_control_frame(struct corerouter_peer *); +ssize_t spdy_parse(struct corerouter_peer *); +#endif + +ssize_t hs_http_manage(struct corerouter_peer *, ssize_t); + +ssize_t hr_instance_connected(struct corerouter_peer *); + +ssize_t hr_instance_read_response(struct corerouter_peer *); +ssize_t hr_read_body(struct corerouter_peer *); +ssize_t hr_write_body(struct corerouter_peer *); + +void hr_session_close(struct corerouter_session *); +ssize_t http_parse(struct corerouter_peer *); diff --git a/plugins/http/https.c b/plugins/http/https.c new file mode 100644 index 00000000..e1bfa27d --- /dev/null +++ b/plugins/http/https.c @@ -0,0 +1,347 @@ +/* + + uWSGI HTTPS router + +*/ + +#include "common.h" + +#ifdef UWSGI_SSL + +extern struct uwsgi_http uhttp; + +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 + char *name = uhttp.https_session_context; + if (!name) { + name = uwsgi_concat3(ucr->short_name, "-", ugs->name); + } + + ugs->ctx = uwsgi_ssl_new_server_context(name, crt, key, ciphers, client_ca); + if (!ugs->ctx) { + exit(1); + } + // set the ssl mode + ugs->mode = UWSGI_HTTP_SSL; + + ucr->has_sockets++; +} + +void uwsgi_opt_https2(char *opt, char *value, void *cr) { + struct uwsgi_corerouter *ucr = (struct uwsgi_corerouter *) cr; + + char *s_addr = NULL; + char *s_cert = NULL; + char *s_key = NULL; + char *s_ciphers = NULL; + char *s_clientca = NULL; + char *s_spdy = NULL; + + if (uwsgi_kvlist_parse(value, strlen(value), ',', '=', + "addr", &s_addr, + "cert", &s_cert, + "crt", &s_cert, + "key", &s_key, + "ciphers", &s_ciphers, + "clientca", &s_clientca, + "client_ca", &s_clientca, + "spdy", &s_spdy, + NULL)) { + uwsgi_log("error parsing --https2 option\n"); + exit(1); + } + + if (!s_addr || !s_cert || !s_key) { + uwsgi_log("--https2 option needs addr, cert and key items\n"); + exit(1); + } + + struct uwsgi_gateway_socket *ugs = uwsgi_new_gateway_socket(s_addr, ucr->name); + // ok we have the socket, initialize ssl if required + if (!uwsgi.ssl_initialized) { + uwsgi_ssl_init(); + } + + // initialize ssl context + char *name = uhttp.https_session_context; + if (!name) { + name = uwsgi_concat3(ucr->short_name, "-", ugs->name); + } + +#ifdef UWSGI_SPDY + if (s_spdy) { + uhttp.spdy_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); + } +#endif + + ugs->ctx = uwsgi_ssl_new_server_context(name, s_cert, s_key, s_ciphers, s_clientca); + if (!ugs->ctx) { + exit(1); + } +#ifdef UWSGI_SPDY + if (s_spdy) { + SSL_CTX_set_info_callback(ugs->ctx, uwsgi_spdy_info_cb); + SSL_CTX_set_next_protos_advertised_cb(ugs->ctx, uwsgi_spdy_npn, NULL); + } +#endif + // 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++; +} + +ssize_t hr_send_force_https(struct corerouter_peer *main_peer) { + return -1; +} +/* + struct corerouter_session *cs = main_peer->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(main_peer->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; +} +*/ + +int hr_https_add_vars(struct http_session *hr, struct uwsgi_buffer *out) { +// HTTPS (adapted from nginx) + if (hr->session.ugs->mode == UWSGI_HTTP_SSL) { + if (uwsgi_buffer_append_keyval(out, "HTTPS", 5, "on", 2)) return -1; + hr->ssl_client_cert = SSL_get_peer_certificate(hr->ssl); + if (hr->ssl_client_cert) { + X509_NAME *name = X509_get_subject_name(hr->ssl_client_cert); + if (name) { + hr->ssl_client_dn = X509_NAME_oneline(name, NULL, 0); + if (uwsgi_buffer_append_keyval(out, "HTTPS_DN", 8, hr->ssl_client_dn, strlen(hr->ssl_client_dn))) return -1; + } + if (uhttp.https_export_cert) { + hr->ssl_bio = BIO_new(BIO_s_mem()); + if (hr->ssl_bio) { + if (PEM_write_bio_X509(hr->ssl_bio, hr->ssl_client_cert) > 0) { + size_t cc_len = BIO_pending(hr->ssl_bio); + hr->ssl_cc = uwsgi_malloc(cc_len); + BIO_read(hr->ssl_bio, hr->ssl_cc, cc_len); + if (uwsgi_buffer_append_keyval(out, "HTTPS_CC", 8, hr->ssl_cc, cc_len)) return -1; + } + } + } + } + } + else if (hr->session.ugs->mode == UWSGI_HTTP_FORCE_SSL) { + hr->force_ssl = 1; + } + + return 0; +} + +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); + } + + SSL_free(hs->ssl); +} + +ssize_t hr_ssl_write(struct corerouter_peer *main_peer) { + struct corerouter_session *cs = main_peer->session; + struct http_session *hs = (struct http_session *) cs; + + int ret = SSL_write(hs->ssl, main_peer->out->buf + main_peer->out_pos, main_peer->out->pos - main_peer->out_pos); + if (ret > 0) { + main_peer->out_pos += ret; + if (main_peer->out->pos == main_peer->out_pos) { + // reset the buffer (if needed) + main_peer->out->pos = 0; + cr_reset_hooks(main_peer); + } + return ret; + } + if (ret == 0) return 0; + int err = SSL_get_error(hs->ssl, ret); + + if (err == SSL_ERROR_WANT_READ) { + cr_reset_hooks_and_read(main_peer, hr_ssl_write); + return 1; + } + + else if (err == SSL_ERROR_WANT_WRITE) { + cr_write_to_main(main_peer, hr_ssl_write); + return 1; + } + + else if (err == SSL_ERROR_SYSCALL) { + uwsgi_error("hr_ssl_write()"); + } + + else if (err == SSL_ERROR_SSL && uwsgi.ssl_verbose) { + ERR_print_errors_fp(stderr); + } + + return -1; +} + +ssize_t hr_ssl_read(struct corerouter_peer *main_peer) { + struct corerouter_session *cs = main_peer->session; + struct http_session *hs = (struct http_session *) cs; + + // try to always leave 4k available + if (uwsgi_buffer_ensure(main_peer->in, uwsgi.page_size)) return -1; + int ret = SSL_read(hs->ssl, main_peer->in->buf + main_peer->in->pos, main_peer->in->len - main_peer->in->pos); + if (ret > 0) { + // fix the buffer + main_peer->in->pos += ret; + // check for pending data + int ret2 = SSL_pending(hs->ssl); + if (ret2 > 0) { + if (uwsgi_buffer_fix(main_peer->in, main_peer->in->len + ret2 )) { + uwsgi_log("[uwsgi-https] cannot fix the buffer to %d\n", main_peer->in->len + ret2); + return -1; + } + if (SSL_read(hs->ssl, main_peer->in->buf + main_peer->in->pos, ret2) != ret2) { + uwsgi_log("[uwsgi-https] SSL_read() on %d bytes of pending data failed\n", ret2); + return -1; + } + // fix the buffer + main_peer->in->pos += ret2; + } + if (hs->spdy) { + uwsgi_log("RUNNING THE SPDY PARSER FOR %d bytes\n", main_peer->in->pos); + return spdy_parse(main_peer); + } + return http_parse(main_peer); + } + if (ret == 0) return 0; + int err = SSL_get_error(hs->ssl, ret); + + if (err == SSL_ERROR_WANT_READ) { + cr_reset_hooks_and_read(main_peer, hr_ssl_read); + return 1; + } + + else if (err == SSL_ERROR_WANT_WRITE) { + cr_write_to_main(main_peer, hr_ssl_read); + return 1; + } + + else if (err == SSL_ERROR_SYSCALL) { + uwsgi_error("hr_ssl_read()"); + } + + else if (err == SSL_ERROR_SSL && uwsgi.ssl_verbose) { + ERR_print_errors_fp(stderr); + } + + return -1; +} + +void hr_setup_ssl(struct http_session *hr, struct uwsgi_gateway_socket *ugs) { + hr->ssl = SSL_new(ugs->ctx); + SSL_set_fd(hr->ssl, hr->session.main_peer->fd); + SSL_set_accept_state(hr->ssl); +#ifdef UWSGI_SPDY + SSL_set_ex_data(hr->ssl, uhttp.spdy_index, hr); +#endif + uwsgi_cr_set_hooks(hr->session.main_peer, hr_ssl_read, NULL); + hr->session.close = hr_session_ssl_close; + hr->func_write = hr_ssl_write; +} + +#endif diff --git a/plugins/http/spdy3.c b/plugins/http/spdy3.c new file mode 100644 index 00000000..ba2a10e7 --- /dev/null +++ b/plugins/http/spdy3.c @@ -0,0 +1,688 @@ +/* + + uWSGI SPDY3 router + +*/ + +#include "common.h" + +#ifdef UWSGI_SPDY + +extern struct uwsgi_http uhttp; + +#include "spdy3.h" + +static uint8_t spdy_h_read_control(uint8_t *header) { + return header[0] >> 7; +} + +static uint16_t spdy_h_read_version(uint8_t *header) { + uint16_t ret = 0; + uint8_t *ptr = (uint8_t *) &ret; + ptr[0] = header[0] & 0x7f; + ptr[1] = header[1]; + return ntohs(ret); +} + +static uint16_t spdy_h_read_type(uint8_t *header) { + uint16_t ret = 0; + uint8_t *ptr = (uint8_t *) &ret; + ptr[0] = header[2]; + ptr[1] = header[3]; + return ntohs(ret); +} + + +static uint8_t spdy_h_read_flags(uint8_t *header) { + return header[4]; +} + +static uint32_t spdy_h_read_length(uint8_t *header) { + uint32_t ret = 0; + uint8_t *ptr = (uint8_t *) &ret; + ptr[1] = header[5]; + ptr[2] = header[6]; + ptr[3] = header[7]; + return ntohl(ret); +} + +static uint32_t spdy_stream_id(uint8_t *body) { + uint32_t ret = 0; + uint8_t *ptr = (uint8_t *) &ret; + ptr[0] = body[0] & 0x7f; + ptr[1] = body[1]; + ptr[2] = body[2]; + ptr[3] = body[3]; + return ntohl(ret); +} + +/* +static uint32_t spdy_associated_stream_id(uint8_t *body) { + uint32_t ret = 0; + uint8_t *ptr = (uint8_t *) &ret; + ptr[0] = body[4] & 0x7f; + ptr[1] = body[5]; + ptr[2] = body[6]; + ptr[3] = body[7]; + return ntohl(ret); +} +*/ + +static char *spdy_translate(char *buf, uint32_t len, uint16_t *d_len) { + uint32_t i; + if (len == 0) return NULL; + + if (buf[0] == ':') { + + if (!uwsgi_strncmp(buf+1, len-1, "method", 6)) { + *d_len = 14; + return uwsgi_str("REQUEST_METHOD"); + } + + if (!uwsgi_strncmp(buf+1, len-1, "path", 4)) { + *d_len = 11; + return uwsgi_str("REQUEST_URI"); + } + + if (!uwsgi_strncmp(buf+1, len-1, "version", 7)) { + *d_len = 15; + return uwsgi_str("SERVER_PROTOCOL"); + } + + if (!uwsgi_strncmp(buf+1, len-1, "host", 4)) { + *d_len = 9; + return uwsgi_str("HTTP_HOST"); + } + + if (!uwsgi_strncmp(buf+1, len-1, "scheme", 6)) { + *d_len = 12; + return uwsgi_str("UWSGI_SCHEME"); + } + + return NULL; + } + + if (!uwsgi_strncmp(buf, len, "content-length", 14)) { + *d_len = 14; + return uwsgi_str("CONTENT_LENGTH"); + } + + if (!uwsgi_strncmp(buf, len, "content-type", 12)) { + *d_len = 12; + return uwsgi_str("CONTENT_TYPE"); + } + + char *buf2 = uwsgi_malloc(len + 5); + memcpy(buf2, "HTTP_", 5); + char *ptr = buf2+5; + for(i=0;ipos+=4; + + int found = 0; + // :version + for(i=0;i= len) goto end; + next = i+1; + found = 1; + break; + } + } + + if (!found) goto end; + + // :status + found = 0; + for(i=next;i= len) goto end; + next = i + 1; + found = 1; + break; + } + } + + if (!found) goto end; + + *hh = 2; + + char *key = NULL; + + // find first header position + for(i=next;i= buf+len) goto end; + // tolower !!! + size_t j; + for(j=0;j> 8) & 0xff); + buf[1] = (uint8_t) ((stream_id >> 16) & 0xff); + buf[0] = (uint8_t) ((stream_id >> 24) & 0xff); + + // FIN + if (len == 0) { + buf[4] = 1; + } + + // length + buf[7] = (uint8_t) (len & 0xff); + buf[6] = (uint8_t) ((len >> 8) & 0xff); + buf[5] = (uint8_t) ((len >> 16) & 0xff); + +} + + +// be sure to have at least 12 free bytes +static void spdy_reply_header(char *buf, uint32_t len, uint32_t stream_id) { + buf[0] = 0x80; + buf[1] = 0x03; + buf[2] = 0; + buf[3] = 0x02; + + // flags + buf[4] = 0; + + // length + buf[7] = (uint8_t) (len & 0xff); + buf[6] = (uint8_t) ((len >> 8) & 0xff); + buf[5] = (uint8_t) ((len >> 16) & 0xff); + + // stream id + buf[11] = (uint8_t) (stream_id & 0xff); + buf[10] = (uint8_t) ((stream_id >> 8) & 0xff); + buf[9] = (uint8_t) ((stream_id >> 16) & 0xff); + buf[8] = (uint8_t) ((stream_id >> 24) & 0xff); + +} + +static ssize_t http_parse_to_spdy(struct corerouter_peer *peer) { + size_t i; + struct uwsgi_buffer *ub = peer->in; + struct uwsgi_buffer *out = peer->out; + + // reset the out buf (it will hold the spdy frame) + out->pos = 0; + peer->session->main_peer->out_pos = 0; + + // end of the stream + if (peer->r_parser_status == 5) { + return -1; + } + + // send DATA frame + if (peer->r_parser_status == 4) { + spdy_data_header(out->buf, ub->pos, peer->sid); + out->pos = 8; + // no need to call append on empty values + if (ub->pos > 0) { + if (uwsgi_buffer_append(out, ub->buf, ub->pos)) return -1; + } + else { + peer->r_parser_status = 5; + } + // reset the input buffer + ub->pos = 0; + return 1; + } + + // try to send REPLY frame + for(i=0;ipos;i++) { + char c = ub->buf[i]; + if (c == '\r' && (peer->r_parser_status == 0 || peer->r_parser_status == 2)) { + peer->r_parser_status++; + } + else if (c == '\r') { + peer->r_parser_status = 1; + } + else if (c == '\n' && peer->r_parser_status == 1) { + peer->r_parser_status = 2; + } + // parsing done + else if (c == '\n' && peer->r_parser_status == 3) { + peer->r_parser_status = 4; + uint32_t hh = 0; + struct uwsgi_buffer *h_buf = spdy_http_to_spdy(ub->buf, i, &hh); + if (!h_buf) return -1; + // put the number of headers on fron of the buffer + h_buf->buf[3] = (uint8_t) (hh & 0xff); + h_buf->buf[2] = (uint8_t) ((hh >> 8) & 0xff); + h_buf->buf[1] = (uint8_t) ((hh >> 16) & 0xff); + h_buf->buf[0] = (uint8_t) ((hh >> 24) & 0xff); + + // ok now we need to deflate the buffer + size_t cb_len = 0; + char *compressed_buf = spdy_deflate_http_headers((struct http_session *) peer->session, h_buf, &cb_len); + uwsgi_buffer_destroy(h_buf); + if (!compressed_buf) { + return -1; + } + // ok now we need an additional buffer + spdy_reply_header(out->buf, 4+cb_len, peer->sid); + out->pos = 12; + if (uwsgi_buffer_append(out, compressed_buf, cb_len)) { + free(compressed_buf); + return -1; + } + free(compressed_buf); + // remains ? + + if (ub->pos-i > 1) { + uint32_t remains = ub->pos-(i+1); + if (uwsgi_buffer_append(out, "\0\0\0\0\0\0\0\0", 8)) { + return -1; + } + spdy_data_header(out->buf + (out->pos - 8), remains, peer->sid); + if (uwsgi_buffer_append(out, ub->buf+i+1, remains)) { + return -1; + } + } + // reset the input buffer + ub->pos = 0; + return 1; + } + else { + peer->r_parser_status = 0; + } + } + + return 0; + +} + +ssize_t hr_instance_read_to_spdy(struct corerouter_peer *peer) { + ssize_t len = cr_read(peer, "hr_instance_read_to_spdy()"); + + // do not check for empty packet, as 0 will trigger a data frame + len = http_parse_to_spdy(peer); + if (len > 0) goto parsed; + if (len < 0) { + if (peer->r_parser_status == 5) return 0; + return -1; + } + // need more data + return 1; + +parsed: + peer->session->main_peer->out = peer->out; + peer->session->main_peer->out_pos = 0; + cr_write_to_main(peer, hr_ssl_write); + return 1; +} + +char *spdy_deflate_http_headers(struct http_session *hr, struct uwsgi_buffer *h_buf, size_t *dlen) { + // calculate the amount of bytes needed for output (+30 should be enough) + Bytef *dbuf = uwsgi_malloc(h_buf->pos+30); + z_stream *z = &hr->spdy_z_out; +z->avail_in = h_buf->pos; z->next_in = (Bytef *) h_buf->buf; z->avail_out = h_buf->pos+30; z->next_out = dbuf; + if (deflate(z, Z_SYNC_FLUSH) != Z_OK) { + free(dbuf); + return NULL; + } + + *dlen = z->next_out - dbuf; + return (char *) dbuf; +} + +static ssize_t spdy_inflate_http_headers(struct http_session *hr) { + + Bytef zbuf[4096]; + + uint8_t *src = (uint8_t *) hr->session.main_peer->in->buf; + + hr->spdy_z_in.avail_in = hr->spdy_control_length - 10; + hr->spdy_z_in.next_in = src + 10; + + struct uwsgi_buffer *ub = uwsgi_buffer_new(4096); + + while(hr->spdy_z_in.avail_in > 0) { + hr->spdy_z_in.avail_out = 4096; + hr->spdy_z_in.next_out = zbuf; + + int ret = inflate(&hr->spdy_z_in, Z_NO_FLUSH); + if (ret == Z_NEED_DICT) { + inflateSetDictionary(&hr->spdy_z_in, (Bytef *) SPDY_dictionary_txt, sizeof(SPDY_dictionary_txt)); + ret = inflate(&hr->spdy_z_in, Z_NO_FLUSH); + } + if (ret != Z_OK) return -1; + size_t zlen = hr->spdy_z_in.next_out-zbuf; + if (uwsgi_buffer_append(ub, (char *) zbuf, zlen)) return -1; + } + + if (ub->pos < 4) return -1; + + uint32_t headers_num = uwsgi_be32(ub->buf); + uint32_t i, watermark = ub->pos, pos = 4; + + struct corerouter_peer *new_peer = uwsgi_cr_peer_add(&hr->session); + new_peer->last_hook_read = hr_instance_read_to_spdy; + new_peer->out = uwsgi_buffer_new(uwsgi.page_size); + // this will avoid the buffer being destroyed on the first instance write + new_peer->out_need_free = 2; + // leave space for uwsgi header + new_peer->out->pos = 4; + new_peer->sid = hr->spdy_data_stream_id; + + // leave space for header + for(i=0;i watermark) return -1; + uint32_t k_len = uwsgi_be32( ub->buf + pos); + pos += 4; + if (pos + k_len > watermark) return -1; + char *k = ub->buf + pos; + pos += k_len; + + // value + if (pos + 4 > watermark) return -1; + uint32_t v_len = uwsgi_be32( ub->buf + pos); + pos += 4; + if (pos + v_len > watermark) return -1; + char *v = ub->buf + pos; + pos += v_len; + + uint16_t nk_len = 0; + char *cgi_name = spdy_translate(k, k_len, &nk_len); + if (!cgi_name) return -1; + + if (uwsgi_buffer_append_keyval(new_peer->out, cgi_name, nk_len, v, v_len)) return -1; + if (!uwsgi_strncmp(cgi_name, nk_len, "HTTP_HOST", 9)) { + new_peer->key = new_peer->out->buf + (new_peer->out->pos - v_len); + new_peer->key_len = v_len; + } + else if (!uwsgi_strncmp(cgi_name, nk_len, "REQUEST_URI", 11)) { + char *path_info = new_peer->out->buf + (new_peer->out->pos - v_len); + uint16_t path_info_len = v_len; + char *query_string = memchr(path_info, '?', v_len); + if (query_string) { + query_string++; + path_info_len = (query_string - path_info) -1; + uint16_t query_string_len = v_len - (path_info_len + 1); + if (uwsgi_buffer_append_keyval(new_peer->out, "QUERY_STRING", 12, query_string, query_string_len)) return -1; + } + if (uwsgi_buffer_append_keyval(new_peer->out, "PATH_INFO", 9, path_info, path_info_len)) return -1; + } + free(cgi_name); + } + + // find the backend node + if (new_peer->key_len == 0) return -1; + + if (uwsgi_buffer_append_keyval(new_peer->out, "HTTPS", 5, "on", 2)) return -1; + if (uwsgi_buffer_append_keyval(new_peer->out, "SPDY", 4, "on", 2)) return -1; + if (uwsgi_buffer_append_keynum(new_peer->out, "SPDY.version", 12, 3)) return -1; + if (uwsgi_buffer_append_keynum(new_peer->out, "SPDY.stream", 11, new_peer->sid)) return -1; + + + struct uwsgi_corerouter *ucr = hr->session.corerouter; + + // get instance name + if (ucr->mapper(ucr, new_peer )) return -1; + + if (new_peer->instance_address_len == 0) { + return -1; + } + + cr_connect(new_peer, hr_instance_connected); + + return 1; +} + +ssize_t spdy_manage_settings(struct http_session *hs) { + uwsgi_log("settings received !!!\n"); + return 1; +} + +ssize_t spdy_manage_syn_stream(struct http_session *hr) { + uint8_t *buf = (uint8_t *) hr->session.main_peer->in->buf; + hr->spdy_data_stream_id = spdy_stream_id(buf); + uwsgi_log("SYN_STREAM received %u !!!\n", hr->spdy_data_stream_id) ; + //uwsgi_log("associated stream %u\n", spdy_associated_stream_id(buf)); + return spdy_inflate_http_headers(hr); +} + +ssize_t spdy_manage_rst_stream(struct http_session *hr) { + uint8_t *buf = (uint8_t *) hr->session.main_peer->in->buf; + hr->spdy_data_stream_id = spdy_stream_id(buf); + uwsgi_log("RST_STREAM received %u !!!\n", hr->spdy_data_stream_id) ; + struct corerouter_peer *peer = uwsgi_cr_peer_find_by_sid(&hr->session, hr->spdy_data_stream_id); + if (peer) { + corerouter_close_peer(hr->session.corerouter, peer); + } + return 0; +} + + +/* + + read from ssl peer. + + This must be able to efficiently manage both http and spdy packets + + when the first chunk is received the spdy field of the session is checked. + + If it is a spdy packet, the SPDY parser will run... + +*/ + + + +#define UWSGI_SPDY_PHASE_HEADER 0 +#define UWSGI_SPDY_PHASE_CONTROL 1 +#define UWSGI_SPDY_PHASE_DATA 2 + +ssize_t spdy_parse(struct corerouter_peer *main_peer) { + struct corerouter_session *cs = main_peer->session; + struct http_session *hr = (struct http_session *) cs; + + ssize_t ret = -1; + + if (!hr->spdy_initialized) { + hr->spdy_z_in.zalloc = Z_NULL; + hr->spdy_z_in.zfree = Z_NULL; + hr->spdy_z_in.opaque = Z_NULL; + if (inflateInit(&hr->spdy_z_in) != Z_OK) { + return -1; + } + hr->spdy_z_out.zalloc = Z_NULL; + hr->spdy_z_out.zfree = Z_NULL; + hr->spdy_z_out.opaque = Z_NULL; + if (deflateInit(&hr->spdy_z_out, Z_DEFAULT_COMPRESSION) != Z_OK) { + return -1; + } + if (deflateSetDictionary(&hr->spdy_z_out, (Bytef *) SPDY_dictionary_txt, sizeof(SPDY_dictionary_txt)) != Z_OK) { + return -1; + } + hr->session.refcnt++; + hr->spdy_initialized = 1; + + hr->spdy_phase = UWSGI_SPDY_PHASE_HEADER; + hr->spdy_need = 8; + + uwsgi_log("SPDY %d INITIALIZED\n", hr->spdy); + } + + + for(;;) { + size_t len = main_peer->in->pos; + if (len == 0) { + errno = EINPROGRESS; + return -1; + } + uint8_t *buf = (uint8_t *) main_peer->in->buf; + //uwsgi_log("%d bytes available\n", len); + switch(hr->spdy_phase) { + uwsgi_log("phase = %u\n", hr->spdy_phase); + case UWSGI_SPDY_PHASE_HEADER: + if (len >= hr->spdy_need) { + hr->spdy_frame_type = spdy_h_read_control(buf); + if (hr->spdy_frame_type) { + hr->spdy_control_version = spdy_h_read_version(buf); + hr->spdy_control_type = spdy_h_read_type(buf); + hr->spdy_control_flags = spdy_h_read_flags(buf); + hr->spdy_control_length = spdy_h_read_length(buf); + hr->spdy_phase = UWSGI_SPDY_PHASE_CONTROL; + hr->spdy_need = hr->spdy_control_length; + //uwsgi_log("now i need %d bytes for type %u\n", hr->spdy_need, hr->spdy_control_type); + } + else { + uwsgi_log("OOOOPS\n"); + hr->spdy_phase = UWSGI_SPDY_PHASE_DATA; + hr->spdy_need = 1; + } + if (uwsgi_buffer_decapitate(main_peer->in, 8)) return -1; + continue; + } + errno = EINPROGRESS; + return -1; + case UWSGI_SPDY_PHASE_CONTROL: + if (len >= hr->spdy_need) { + switch(hr->spdy_control_type) { + // SYN_STREAM + case 1: + ret = spdy_manage_syn_stream(hr); + if (ret == 0) goto goon; + goto newframe; + // RST_STREAM + case 3: + ret = spdy_manage_rst_stream(hr); + if (ret == 0) goto goon; + goto newframe; + case 4: + //uwsgi_log("settings request...\n"); + break; + default: + uwsgi_log("i do not know how to manage type %u\n", hr->spdy_control_type); + break; + } +goon: + hr->spdy_phase = UWSGI_SPDY_PHASE_HEADER; + hr->spdy_need = 8; + if (uwsgi_buffer_decapitate(main_peer->in, hr->spdy_control_length)) return -1; + continue; + } + errno = EINPROGRESS; + return -1; + case UWSGI_SPDY_PHASE_DATA: + if (len >= hr->spdy_need) { + //peer = find_stream_id(); + //cr_write_to_backend(peer, spdy_data); + } + errno = EINPROGRESS; + return -1; + default: + return -1; + } + } + + return -1; + +newframe: + hr->spdy_phase = UWSGI_SPDY_PHASE_HEADER; + hr->spdy_need = 8; + if (uwsgi_buffer_decapitate(main_peer->in, hr->spdy_control_length)) return -1; + return ret; + +} + + +int uwsgi_spdy_npn(SSL *ssl, const unsigned char **data, unsigned int *len, void *arg) { + *data = (const unsigned char *) "\x06spdy/3\x06spdy/2\x08http/1.1\x08http/1.0"; + *len = strlen((const char *) *data); + return SSL_TLSEXT_ERR_OK; +} + +void uwsgi_spdy_info_cb(SSL const *ssl, int where, int ret) { + if (where & SSL_CB_HANDSHAKE_DONE) { + const unsigned char * proto = NULL; + unsigned len = 0; + SSL_get0_next_proto_negotiated(ssl, &proto, &len); + if (len == 6) { + if (!memcmp(proto, "spdy/3", 6)) { + //uwsgi_log("SPDY 3 !!!\n"); + struct http_session *hr = SSL_get_ex_data(ssl, uhttp.spdy_index); + hr->spdy = 3; + //hr->spdy_hook = hr_recv_spdy_control_frame; + } + else if (!memcmp(proto, "spdy/2", 6)) { + //uwsgi_log("SPDY 2 !!!\n"); + struct http_session *hr = SSL_get_ex_data(ssl, uhttp.spdy_index); + hr->spdy = 2; + uwsgi_log("SPDY/2\n"); + //hr->spdy_hook = hr_recv_spdy_control_frame; + } + } + if (ssl->s3) { + ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; + } + } +} + +#endif diff --git a/plugins/http/spdy3.h b/plugins/http/spdy3.h new file mode 100644 index 00000000..5f870ac1 --- /dev/null +++ b/plugins/http/spdy3.h @@ -0,0 +1,180 @@ +const unsigned char SPDY_dictionary_txt[] = { + 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, //\ - - - - o p t i + 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, //\ o n s - - - - h + 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, //\ e a d - - - - p + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, //\ o s t - - - - p + 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, //\ u t - - - - d e + 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, //\ l e t e - - - - + 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, //\ t r a c e - - - + 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, //\ - a c c e p t - + 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, //\ - - - a c c e p + 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, //\ t - c h a r s e + 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, //\ t - - - - a c c + 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, //\ e p t - e n c o + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, //\ d i n g - - - - + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, //\ a c c e p t - l + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, //\ a n g u a g e - + 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, //\ - - - a c c e p + 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, //\ t - r a n g e s + 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, //\ - - - - a g e - + 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, //\ - - - a l l o w + 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, //\ - - - - a u t h + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, //\ o r i z a t i o + 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, //\ n - - - - c a c + 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, //\ h e - c o n t r + 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, //\ o l - - - - c o + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, //\ n n e c t i o n + 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, //\ - - - - c o n t + 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, //\ e n t - b a s e + 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, //\ - - - - c o n t + 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, //\ e n t - e n c o + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, //\ d i n g - - - - + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, //\ c o n t e n t - + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, //\ l a n g u a g e + 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, //\ - - - - c o n t + 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, //\ e n t - l e n g + 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, //\ t h - - - - c o + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, //\ n t e n t - l o + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, //\ c a t i o n - - + 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, //\ - - c o n t e n + 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, //\ t - m d 5 - - - + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, //\ - c o n t e n t + 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, //\ - r a n g e - - + 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, //\ - - c o n t e n + 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, //\ t - t y p e - - + 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, //\ - - d a t e - - + 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, //\ - - e t a g - - + 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, //\ - - e x p e c t + 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, //\ - - - - e x p i + 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, //\ r e s - - - - f + 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, //\ r o m - - - - h + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, //\ o s t - - - - i + 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, //\ f - m a t c h - + 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, //\ - - - i f - m o + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, //\ d i f i e d - s + 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, //\ i n c e - - - - + 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, //\ i f - n o n e - + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, //\ m a t c h - - - + 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, //\ - i f - r a n g + 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, //\ e - - - - i f - + 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, //\ u n m o d i f i + 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, //\ e d - s i n c e + 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, //\ - - - - l a s t + 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, //\ - m o d i f i e + 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, //\ d - - - - l o c + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, //\ a t i o n - - - + 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, //\ - m a x - f o r + 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, //\ w a r d s - - - + 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, //\ - p r a g m a - + 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, //\ - - - p r o x y + 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, //\ - a u t h e n t + 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, //\ i c a t e - - - + 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, //\ - p r o x y - a + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, //\ u t h o r i z a + 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, //\ t i o n - - - - + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, //\ r a n g e - - - + 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, //\ - r e f e r e r + 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, //\ - - - - r e t r + 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, //\ y - a f t e r - + 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, //\ - - - s e r v e + 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, //\ r - - - - t e - + 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, //\ - - - t r a i l + 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, //\ e r - - - - t r + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, //\ a n s f e r - e + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, //\ n c o d i n g - + 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, //\ - - - u p g r a + 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, //\ d e - - - - u s + 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, //\ e r - a g e n t + 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, //\ - - - - v a r y + 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, //\ - - - - v i a - + 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, //\ - - - w a r n i + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, //\ n g - - - - w w + 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, //\ w - a u t h e n + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, //\ t i c a t e - - + 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, //\ - - m e t h o d + 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, //\ - - - - g e t - + 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, //\ - - - s t a t u + 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, //\ s - - - - 2 0 0 + 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, //\ - O K - - - - v + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, //\ e r s i o n - - + 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, //\ - - H T T P - 1 + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, //\ - 1 - - - - u r + 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, //\ l - - - - p u b + 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, //\ l i c - - - - s + 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, //\ e t - c o o k i + 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, //\ e - - - - k e e + 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, //\ p - a l i v e - + 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, //\ - - - o r i g i + 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, //\ n 1 0 0 1 0 1 2 + 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, //\ 0 1 2 0 2 2 0 5 + 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, //\ 2 0 6 3 0 0 3 0 + 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, //\ 2 3 0 3 3 0 4 3 + 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, //\ 0 5 3 0 6 3 0 7 + 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, //\ 4 0 2 4 0 5 4 0 + 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, //\ 6 4 0 7 4 0 8 4 + 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, //\ 0 9 4 1 0 4 1 1 + 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, //\ 4 1 2 4 1 3 4 1 + 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, //\ 4 4 1 5 4 1 6 4 + 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, //\ 1 7 5 0 2 5 0 4 + 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, //\ 5 0 5 2 0 3 - N + 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, //\ o n - A u t h o + 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, //\ r i t a t i v e + 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, //\ - I n f o r m a + 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, //\ t i o n 2 0 4 - + 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, //\ N o - C o n t e + 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, //\ n t 3 0 1 - M o + 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, //\ v e d - P e r m + 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, //\ a n e n t l y 4 + 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, //\ 0 0 - B a d - R + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, //\ e q u e s t 4 0 + 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, //\ 1 - U n a u t h + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, //\ o r i z e d 4 0 + 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, //\ 3 - F o r b i d + 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, //\ d e n 4 0 4 - N + 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, //\ o t - F o u n d + 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, //\ 5 0 0 - I n t e + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, //\ r n a l - S e r + 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, //\ v e r - E r r o + 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, //\ r 5 0 1 - N o t + 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, //\ - I m p l e m e + 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, //\ n t e d 5 0 3 - + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, //\ S e r v i c e - + 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, //\ U n a v a i l a + 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, //\ b l e J a n - F + 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, //\ e b - M a r - A + 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, //\ p r - M a y - J + 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, //\ u n - J u l - A + 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, //\ u g - S e p t - + 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, //\ O c t - N o v - + 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, //\ D e c - 0 0 - 0 + 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, //\ 0 - 0 0 - M o n + 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, //\ - - T u e - - W + 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, //\ e d - - T h u - + 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, //\ - F r i - - S a + 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, //\ t - - S u n - - + 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, //\ G M T c h u n k + 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, //\ e d - t e x t - + 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, //\ h t m l - i m a + 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, //\ g e - p n g - i + 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, //\ m a g e - j p g + 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, //\ - i m a g e - g + 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, //\ i f - a p p l i + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, //\ c a t i o n - x + 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, //\ m l - a p p l i + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, //\ c a t i o n - x + 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, //\ h t m l - x m l + 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, //\ - t e x t - p l + 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, //\ a i n - t e x t + 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, //\ - j a v a s c r + 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, //\ i p t - p u b l + 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, //\ i c p r i v a t + 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, //\ e m a x - a g e + 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, //\ - g z i p - d e + 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, //\ f l a t e - s d + 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, //\ c h c h a r s e + 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, //\ t - u t f - 8 c + 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, //\ h a r s e t - i + 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, //\ s o - 8 8 5 9 - + 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, //\ 1 - u t f - - - + 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e //\ - e n q - 0 - +}; diff --git a/tests/websockets.py b/tests/websockets.py new file mode 100644 index 00000000..800e9649 --- /dev/null +++ b/tests/websockets.py @@ -0,0 +1,51 @@ +import uwsgi +import time + +def application(env, sr): + + if env['PATH_INFO'] == '/': + sr('200 OK', [('Content-Type','text/html')]) + return """ + + + + + +

WebSocket

+ + +
+
+ + + """ + else: + print "websockets..." + while True: + msg = uwsgi.websocket_recv() + print len(msg) + uwsgi.websocket_send("hello %s = %s" % (time.time(), msg))