static int bb_session_headers_complete(http_parser *parser) { //printf("headers parsed\n"); struct bb_session_request *bbsr = (struct bb_session_request *) parser->data; off_t i; //printf("%s %.*s HTTP/%d.%d\n", http_method_str(parser->method), (int) bbsr->headers[0].keylen, bbsr->headers[0].key, parser->http_major, parser->http_minor); /* for(i=1;i<=bbsr->header_pos;i++) { printf("%.*s: %.*s\n", (int) bbsr->headers[i].keylen, bbsr->headers[i].key, (int)bbsr->headers[i].vallen, bbsr->headers[i].value); } */ // ok get the Host header struct bb_http_header *bbhh = bb_http_req_header(bbsr, "Host", 4); if (!bbhh) { return -1; } if (!bbsr->bbs->dealer) { bbsr->bbs->dealer = bb_get_dealer(bbsr->bbs->acceptor, bbhh->value, bbhh->vallen); if (!bbsr->bbs->dealer) { return -1; } } if (parser->upgrade) { struct bb_http_header *bbhh = bb_http_req_header(bbsr, "Upgrade", 7); if (bbhh) { if (!bb_stricmp("websocket", 9, bbhh->value, bbhh->vallen)) { bbsr->type = BLASTBEAT_TYPE_WEBSOCKET; bb_send_websocket_handshake(bbsr); goto msg; } } } if (!http_should_keep_alive(parser)) { //printf("NO KEEP ALIVE !!!\n"); bbsr->close = 1; } msg: // now encode headers in a uwsgi packet and send it as "headers" message if (bb_uwsgi(bbsr)) { return -1; } bb_zmq_send_msg(bbsr->bbs->dealer->identity, bbsr->bbs->dealer->len, (char *) &bbsr->bbs->uuid_part1, BB_UUID_LEN, "uwsgi", 5, bbsr->uwsgi_buf, bbsr->uwsgi_pos); return 0; }
int bb_manage_socketio(struct bb_session *bbs, char *method, size_t method_len, char *url, size_t url_len) { char *query_string = memchr(url, '?', url_len); size_t query_string_len = 0; if (query_string) { query_string_len = url_len - (query_string-url); url_len = query_string-url; } //fprintf(stderr,"SOCKET.IO %p %.*s %.*s\n", bbs, method_len, method, url_len, url); // handshake // /socket.io/1/ if (url_len == 13) { char handshake[36+3+3+1+21]; uuid_t *session_uuid = (uuid_t *) &bbs->uuid_part1; uuid_unparse(*session_uuid, handshake); if (bbs->stream_id > 0) { // only websockets are supported under SPDY char *supported = "websocket"; memcpy(handshake+36, ":30:60:", 7); memcpy(handshake+36+7, supported, 9); if (bb_spdy_raw_send_headers(bbs, 7, (struct bb_http_header *) handshake_spdy_headers, "200", "HTTP/1.1", 0)) return -1; if (bb_spdy_send_body(bbs, handshake, 52)) return -1; if (bb_spdy_send_end(bbs)) return -1; } else { char *supported = "websocket,xhr-polling"; memcpy(handshake+36, ":30:60:", 7); memcpy(handshake+36+7, supported, 21); if (bb_wq_push(bbs, (char *)handshake_headers, strlen(handshake_headers), 0)) return -1; if (bb_wq_push_copy(bbs, handshake, 64, BB_WQ_FREE)) return -1; // close the connection too... (simplify resource management) if (bb_wq_push_close(bbs)) return -1; } // mark the session as persistent bbs->persistent = 1; // 30 seconds from now to finalize the handshake bb_session_reset_timer(bbs, 30, NULL); // now send the uwsgi pocket, to allow the dealer to close the session return 0; } // now we try to get the transport and session off_t i; char *transport = NULL; size_t transport_len = 0; for(i=13; i<url_len; i++) { if (url[i] == '/') { transport = url+13; transport_len = i-13; break; } } if (!transport) return -1; char *session_id = NULL; size_t session_id_len = 0; for(i=13+transport_len+1; i<url_len; i++) { if (url[i] == '/') { session_id = transport+transport_len+1; session_id_len = i-(13+transport_len+1); break; } } if (!session_id) { session_id = transport+transport_len+1; session_id_len = url_len - (13+transport_len+1); } if (session_id_len == 0 || session_id_len > 36) return -1; uuid_t sio_uuid; char tmp_uuid[37]; memcpy(tmp_uuid, session_id, session_id_len); tmp_uuid[36] = 0; if (uuid_parse(tmp_uuid, sio_uuid)) return -1; struct bb_session *persistent_bbs = bb_sht_get((char *)sio_uuid); if (!persistent_bbs) return -1; // destroy the session !!! // check for non-persisten-sessions or non-vhost matching too if (transport_len == 0 || !bb_strcmp(query_string, query_string_len, "?disconnect", 12) || !persistent_bbs->persistent || persistent_bbs->vhost != bbs->vhost) { persistent_bbs->persistent = 0; // DANGER !!! avoid destroying the current session too early if (persistent_bbs != bbs) { bb_session_close(persistent_bbs); } return -1; } // already the right session if (persistent_bbs == bbs) goto ready; // do not report 'end' message on end of session bbs->stealth = 1; ready: if (!bb_strcmp(transport, transport_len, "websocket", 9)) { // map the connection to allow sending from the persistent session // to the current connection struct bb_connection *bbc = bbs->connection; persistent_bbs->connection = bbc; bbs->connection->func = bb_websocket_func; // set the persistent session bbs->sio_session = persistent_bbs; bb_send_websocket_handshake(bbs); bb_websocket_reply(bbs, "1::", 3); persistent_bbs->sio_connected = 1; persistent_bbs->sio_realtime = 1; bb_session_reset_timer(persistent_bbs, 20, socketio_heartbeat); bbs->request.no_uwsgi = 1; return 0; } if (!bb_strcmp(transport, transport_len, "xhr-polling", 11)) { // sending messages does not require remapping the session if (!bb_strcmp(method, method_len, "POST",4)) { if (!persistent_bbs->sio_connected) return -1; if (bb_wq_push(bbs, (char *)post_headers, strlen(post_headers), 0)) return -1; //if (bb_wq_push_close(bbs)) return -1; if (bbs->request.parser.content_length != ULLONG_MAX && bbs->request.parser.content_length > 0) { bbs->sio_session = persistent_bbs; //bbs->sio_bbs = persistent_bbs; bbs->request.no_uwsgi = 1; // set socket.io hooks bbs->recv_body = bb_socketio_recv_body; bbs->recv_complete = bb_socketio_recv_complete; // do not report session death bbs->stealth = 1; return 0; } return -1; } // ok we are ready if (!bb_strcmp(method, method_len, "GET", 3)) { // already handshaked, this is a poll if (persistent_bbs->sio_connected) { // check if a poller is already running if (persistent_bbs->sio_poller) return -1; // ...and map the connection struct bb_connection *bbc = bbs->connection; persistent_bbs->connection = bbc; // do not forward the request to the dealer bbs->request.no_uwsgi = 1; struct bb_socketio_message *bbsm = persistent_bbs->sio_queue; if (bbsm) { if (bb_socketio_send(persistent_bbs, bbsm->buf, bbsm->len)) { fprintf(stderr,"unable to deliver message\n"); } persistent_bbs->sio_queue = bbsm->next; bb_free(bbsm, sizeof(struct bb_socketio_message)); return 0; } // start the poller persistent_bbs->sio_poller = 1; bb_session_reset_timer(persistent_bbs, 30.0, socketio_poller); return 0; } else { if (bb_wq_push(bbs, (char *)connected_headers, strlen(connected_headers), 0)) return -1; if (bb_wq_push_close(bbs)) return -1; persistent_bbs->sio_connected = 1; // start the sio_timer bb_session_reset_timer(persistent_bbs, 60.0, NULL); return 0; } } } return -1; }