Example #1
0
void bb_zmq_receiver(struct ev_loop *loop, struct ev_io *w, int revents) {

	uint32_t zmq_events = 0;
	size_t opt_len = sizeof(uint32_t);

	for(;;) {
		int ret = zmq_getsockopt(blastbeat.router, ZMQ_EVENTS, &zmq_events, &opt_len);		
		if (ret < 0) {
			perror("zmq_getsockopt()");
			break;
		}
	
		if (zmq_events & ZMQ_POLLIN) {
			uint64_t more = 0;
			size_t more_size = sizeof(more);
			int headers = 0;
			int i;
			zmq_msg_t msg[4];
			for(i=0;i<4;i++) {
				zmq_msg_init(&msg[i]);
				zmq_recv(blastbeat.router, &msg[i], ZMQ_NOBLOCK);
				if (zmq_getsockopt(blastbeat.router, ZMQ_RCVMORE, &more, &more_size)) {
					perror("zmq_getsockopt()");
					break;
				}
				if (!more && i < 3) {
					break;
				}
			}

			// invalid message
			if (i != 4) goto next;

			// manage "pong" messages

			// message with uuid ?
			if (zmq_msg_size(&msg[1]) != BB_UUID_LEN) goto next;

			// dead/invalid session ?
			struct bb_session *bbs = bb_sht_get(zmq_msg_data(&msg[1]));
			if (!bbs) goto next;

			struct bb_session_request *bbsr = bbs->requests_tail;
			// no request running ?
			if (!bbsr) goto next;

			if (!strncmp(zmq_msg_data(&msg[2]), "body", zmq_msg_size(&msg[2]))) {
				if (bb_wq_push_copy(bbs,zmq_msg_data(&msg[3]), zmq_msg_size(&msg[3]), 1)) {
					bb_session_close(bbs);
					goto next;
				}
				bbsr->written_bytes += zmq_msg_size(&msg[2]);
				// if Content-Length is specified, check it...
				if (bbsr->content_length != ULLONG_MAX && bbsr->written_bytes >= bbsr->content_length && bbsr->close) {
					if (bb_wq_push_close(bbs))
						bb_session_close(bbs);
				}
				goto next;
			}

			if (!strncmp(zmq_msg_data(&msg[2]), "websocket", zmq_msg_size(&msg[2]))) {
				if (bb_websocket_reply(bbsr, zmq_msg_data(&msg[3]), zmq_msg_size(&msg[3])))
					bb_session_close(bbs);
				goto next;
			}

			if (!strncmp(zmq_msg_data(&msg[2]), "chunk", zmq_msg_size(&msg[2]))) {
				if (bb_manage_chunk(bbsr, zmq_msg_data(&msg[3]), zmq_msg_size(&msg[3])))
					bb_session_close(bbs);
				goto next;
			}

			if (!strncmp(zmq_msg_data(&msg[2]), "headers", zmq_msg_size(&msg[2]))) {
				http_parser parser;
				http_parser_init(&parser, HTTP_RESPONSE);
				parser.data = bbsr;
				int res = http_parser_execute(&parser, &bb_http_response_parser_settings, zmq_msg_data(&msg[3]), zmq_msg_size(&msg[3]));
				// invalid headers ?
				if (res != zmq_msg_size(&msg[3])) {
					bb_session_close(bbs);	
					goto next;
				}
				if (bb_wq_push_copy(bbs, zmq_msg_data(&msg[3]), zmq_msg_size(&msg[3]), 1))
					bb_session_close(bbs);
				goto next;
			}

			if (!strncmp(zmq_msg_data(&msg[2]), "retry", zmq_msg_size(&msg[2]))) {
				if (bbs->hops >= blastbeat.max_hops) {
					bb_session_close(bbs);
					goto next;	
				}
				bbs->dealer = bb_get_dealer(bbs->acceptor, bbs->dealer->vhost->name, bbs->dealer->vhost->len);
                		if (!bbs->dealer) {
					bb_session_close(bbs);
					goto next;	
                		}
				bb_zmq_send_msg(bbs->dealer->identity, bbs->dealer->len, (char *) &bbs->uuid_part1, BB_UUID_LEN, "uwsgi", 5, bbsr->uwsgi_buf, bbsr->uwsgi_pos);
				bbs->hops++;
				goto next;
			}

			if (!strncmp(zmq_msg_data(&msg[2]), "end", zmq_msg_size(&msg[2]))) {
				if (bb_wq_push_close(bbs)) {
					bb_session_close(bbs);	
				}
				goto next;
			}

next:
			zmq_msg_close(&msg[0]);
			zmq_msg_close(&msg[1]);
			zmq_msg_close(&msg[2]);
			zmq_msg_close(&msg[3]);
		
			continue;
		}

		break;
	}
}
Example #2
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;
}