ngx_int_t nchan_pubsub_handler(ngx_http_request_t *r) { nchan_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_nchan_module); ngx_str_t *channel_id; subscriber_t *sub; nchan_msg_id_t *msg_id; ngx_int_t rc = NGX_DONE; nchan_request_ctx_t *ctx; nchan_group_limits_t group_limits; #if NCHAN_BENCHMARK struct timeval tv; ngx_gettimeofday(&tv); #endif if(r->connection && (r->connection->read->eof || r->connection->read->pending_eof)) { ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST); return NGX_ERROR; } if((ctx = ngx_pcalloc(r->pool, sizeof(nchan_request_ctx_t))) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_set_ctx(r, ctx, ngx_nchan_module); #if NCHAN_BENCHMARK ctx->start_tv = tv; #endif //X-Accel-Redirected requests get their method mangled to GET. De-mangle it if necessary if(r->upstream && r->upstream->headers_in.x_accel_redirect) { //yep, we got x-accel-redirected. what was the original method?... nchan_recover_x_accel_redirected_request_method(r); } if(!nchan_match_origin_header(r, cf, ctx)) { goto forbidden; } if((channel_id = nchan_get_channel_id(r, SUB, 1)) == NULL) { //just get the subscriber_channel_id for now. the publisher one is handled elsewhere return r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR; } if((msg_id = nchan_subscriber_get_msg_id(r)) == NULL) { goto bad_msgid; } if(parse_group_limits(r, cf, &group_limits) == NGX_OK) { // unless the group already exists, these limits may only be set after this incoming request. // TODO: fix this, although that will lead to even gnarlier control flow. cf->storage_engine->set_group_limits(nchan_get_group_name(r, cf, ctx), cf, &group_limits, NULL, NULL); } else { // there waas an error parsing group limit strings, and it has already been sent in the response. // just quit. return NGX_OK; } if(cf->pub.websocket || cf->pub.http) { char *err; if(!nchan_parse_message_buffer_config(r, cf, &err)) { if(err) { nchan_respond_cstring(r, NGX_HTTP_FORBIDDEN, &NCHAN_CONTENT_TYPE_TEXT_PLAIN, err, 0); return NGX_OK; } else { nchan_respond_status(r, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL, 0); return NGX_OK; } } } if(nchan_detect_websocket_request(r)) { //want websocket? if(cf->sub.websocket) { //we prefer to subscribe #if FAKESHARD memstore_sub_debug_start(); #endif if((msg_id = nchan_subscriber_get_msg_id(r)) == NULL) { goto bad_msgid; } if((sub = websocket_subscriber_create(r, msg_id)) == NULL) { nchan_log_request_error(r, "unable to create websocket subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } sub->fn->subscribe(sub, channel_id); #if FAKESHARD memstore_sub_debug_end(); #endif } else if(cf->pub.websocket) { //no need to subscribe, but keep a connection open for publishing nchan_create_websocket_publisher(r); } else goto forbidden; return NGX_DONE; } else { subscriber_t *(*sub_create)(ngx_http_request_t *r, nchan_msg_id_t *msg_id) = NULL; switch(r->method) { case NGX_HTTP_GET: if(cf->sub.eventsource && nchan_detect_eventsource_request(r)) { sub_create = eventsource_subscriber_create; } else if(cf->sub.http_chunked && nchan_detect_chunked_subscriber_request(r)) { sub_create = http_chunked_subscriber_create; } else if(cf->sub.http_multipart && nchan_detect_multipart_subscriber_request(r)) { sub_create = http_multipart_subscriber_create; } else if(cf->sub.poll) { sub_create = intervalpoll_subscriber_create; } else if(cf->sub.http_raw_stream) { sub_create = http_raw_stream_subscriber_create; } else if(cf->sub.longpoll) { sub_create = longpoll_subscriber_create; } else if(cf->pub.http) { nchan_http_publisher_handler(r); } else { goto forbidden; } if(sub_create) { #if FAKESHARD memstore_sub_debug_start(); #endif if((msg_id = nchan_subscriber_get_msg_id(r)) == NULL) { goto bad_msgid; } if((sub = sub_create(r, msg_id)) == NULL) { nchan_log_request_error(r, "unable to create subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } sub->fn->subscribe(sub, channel_id); #if FAKESHARD memstore_sub_debug_end(); #endif } break; case NGX_HTTP_POST: case NGX_HTTP_PUT: if(cf->pub.http) { nchan_http_publisher_handler(r); } else goto forbidden; break; case NGX_HTTP_DELETE: if(cf->pub.http) { nchan_http_publisher_handler(r); } else goto forbidden; break; case NGX_HTTP_OPTIONS: if(cf->pub.http) { nchan_OPTIONS_respond(r, &NCHAN_ACCESS_CONTROL_ALLOWED_PUBLISHER_HEADERS, &NCHAN_ALLOW_GET_POST_PUT_DELETE); } else if(cf->sub.poll || cf->sub.longpoll || cf->sub.eventsource || cf->sub.websocket) { nchan_OPTIONS_respond(r, &NCHAN_ACCESS_CONTROL_ALLOWED_SUBSCRIBER_HEADERS, &NCHAN_ALLOW_GET); } else goto forbidden; break; } } ctx->request_ran_content_handler = 1; return rc; forbidden: nchan_respond_status(r, NGX_HTTP_FORBIDDEN, NULL, 0); ctx->request_ran_content_handler = 1; return NGX_OK; bad_msgid: nchan_respond_cstring(r, NGX_HTTP_BAD_REQUEST, &NCHAN_CONTENT_TYPE_TEXT_PLAIN, "Message ID invalid", 0); ctx->request_ran_content_handler = 1; return NGX_OK; }
ngx_int_t nchan_pubsub_handler(ngx_http_request_t *r) { nchan_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, nchan_module); ngx_str_t *channel_id; subscriber_t *sub; nchan_msg_id_t *msg_id; ngx_int_t rc = NGX_DONE; nchan_request_ctx_t *ctx; ngx_str_t *origin_header; #if NCHAN_BENCHMARK struct timeval tv; ngx_gettimeofday(&tv); #endif if((ctx = ngx_pcalloc(r->pool, sizeof(nchan_request_ctx_t))) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_set_ctx(r, ctx, nchan_module); #if NCHAN_BENCHMARK ctx->start_tv = tv; #endif if((origin_header = nchan_get_header_value(r, NCHAN_HEADER_ORIGIN)) != NULL) { ctx->request_origin_header = *origin_header; if(!(cf->allow_origin.len == 1 && cf->allow_origin.data[0] == '*')) { if(!(origin_header->len == cf->allow_origin.len && ngx_strnstr(origin_header->data, (char *)cf->allow_origin.data, origin_header->len) != NULL)) { //CORS origin match failed! return a 403 forbidden goto forbidden; } } } else { ctx->request_origin_header.len=0; ctx->request_origin_header.data=NULL; } if((channel_id = nchan_get_channel_id(r, SUB, 1)) == NULL) { //just get the subscriber_channel_id for now. the publisher one is handled elsewhere return r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR; } if(nchan_detect_websocket_request(r)) { //want websocket? if(cf->sub.websocket) { //we prefer to subscribe #if FAKESHARD memstore_sub_debug_start(); #endif if((msg_id = nchan_subscriber_get_msg_id(r)) == NULL) { goto bad_msgid; } if((sub = websocket_subscriber_create(r, msg_id)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unable to create websocket subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } sub->fn->subscribe(sub, channel_id); #if FAKESHARD memstore_sub_debug_end(); #endif } else if(cf->pub.websocket) { //no need to subscribe, but keep a connection open for publishing //not yet implemented nchan_create_websocket_publisher(r); } else goto forbidden; return NGX_DONE; } else { subscriber_t *(*sub_create)(ngx_http_request_t *r, nchan_msg_id_t *msg_id) = NULL; switch(r->method) { case NGX_HTTP_GET: if(cf->sub.eventsource && nchan_detect_eventsource_request(r)) { sub_create = eventsource_subscriber_create; } else if(cf->sub.http_chunked && nchan_detect_chunked_subscriber_request(r)) { sub_create = http_chunked_subscriber_create; } else if(cf->sub.http_multipart && nchan_detect_multipart_subscriber_request(r)) { sub_create = http_multipart_subscriber_create; } else if(cf->sub.poll) { sub_create = intervalpoll_subscriber_create; } else if(cf->sub.longpoll) { sub_create = longpoll_subscriber_create; } else if(cf->pub.http) { nchan_http_publisher_handler(r); } else { goto forbidden; } if(sub_create) { #if FAKESHARD memstore_sub_debug_start(); #endif if((msg_id = nchan_subscriber_get_msg_id(r)) == NULL) { goto bad_msgid; } if((sub = sub_create(r, msg_id)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unable to create subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } sub->fn->subscribe(sub, channel_id); #if FAKESHARD memstore_sub_debug_end(); #endif } break; case NGX_HTTP_POST: case NGX_HTTP_PUT: if(cf->pub.http) { nchan_http_publisher_handler(r); } else goto forbidden; break; case NGX_HTTP_DELETE: if(cf->pub.http) { nchan_http_publisher_handler(r); } else goto forbidden; break; case NGX_HTTP_OPTIONS: if(cf->pub.http) { nchan_OPTIONS_respond(r, &cf->allow_origin, &NCHAN_ACCESS_CONTROL_ALLOWED_PUBLISHER_HEADERS, &NCHAN_ALLOW_GET_POST_PUT_DELETE); } else if(cf->sub.poll || cf->sub.longpoll || cf->sub.eventsource || cf->sub.websocket) { nchan_OPTIONS_respond(r, &cf->allow_origin, &NCHAN_ACCESS_CONTROL_ALLOWED_SUBSCRIBER_HEADERS, &NCHAN_ALLOW_GET); } else goto forbidden; break; } } return rc; forbidden: nchan_respond_status(r, NGX_HTTP_FORBIDDEN, NULL, 0); return NGX_OK; bad_msgid: nchan_respond_cstring(r, NGX_HTTP_BAD_REQUEST, &NCHAN_CONTENT_TYPE_TEXT_PLAIN, "Message ID invalid", 0); return NGX_OK; }
ngx_int_t nchan_pubsub_handler(ngx_http_request_t *r) { nchan_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, nchan_module); ngx_str_t *channel_id; subscriber_t *sub; nchan_msg_id_t msg_id = {0}; //memzeroed for debugging ngx_int_t rc = NGX_DONE; nchan_request_ctx_t *ctx; if((ctx = ngx_pcalloc(r->pool, sizeof(nchan_request_ctx_t))) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_set_ctx(r, ctx, nchan_module); if((channel_id = nchan_get_channel_id(r, SUB, 1)) == NULL) { //just get the subscriber_channel_id for now. the publisher one is handled elsewhere return r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR; } if(nchan_detect_websocket_handshake(r)) { //want websocket? if(cf->sub.websocket) { //we prefer to subscribe memstore_sub_debug_start(); nchan_subscriber_get_msg_id(r, &msg_id); if((sub = websocket_subscriber_create(r, &msg_id)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unable to create websocket subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } cf->storage_engine->subscribe(channel_id, sub, (callback_pt )&subscribe_websocket_callback, (void *)r); memstore_sub_debug_end(); } else if(cf->pub.websocket) { //no need to subscribe, but keep a connection open for publishing //not yet implemented nchan_create_websocket_publisher(r); } else goto forbidden; return NGX_DONE; } else { switch(r->method) { case NGX_HTTP_GET: if(cf->sub.eventsource && nchan_detect_eventsource_request(r)) { memstore_sub_debug_start(); nchan_subscriber_get_msg_id(r, &msg_id); if((sub = eventsource_subscriber_create(r, &msg_id)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unable to create longpoll subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } cf->storage_engine->subscribe(channel_id, sub, (callback_pt )&subscribe_eventsource_callback, (void *)r); memstore_sub_debug_end(); } else if(cf->sub.poll) { memstore_sub_debug_start(); nchan_subscriber_get_msg_id(r, &msg_id); assert(msg_id.tagcount == 1); r->main->count++; cf->storage_engine->get_message(channel_id, &msg_id, (callback_pt )&subscribe_intervalpoll_callback, (void *)r); memstore_sub_debug_end(); } else if(cf->sub.longpoll) { memstore_sub_debug_start(); nchan_subscriber_get_msg_id(r, &msg_id); if((sub = longpoll_subscriber_create(r, &msg_id)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unable to create longpoll subscriber"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } cf->storage_engine->subscribe(channel_id, sub, (callback_pt )&subscribe_longpoll_callback, (void *)r); memstore_sub_debug_end(); } else if(cf->pub.http) { nchan_http_publisher_handler(r); } else goto forbidden; break; case NGX_HTTP_POST: case NGX_HTTP_PUT: if(cf->pub.http) { nchan_http_publisher_handler(r); } else goto forbidden; break; case NGX_HTTP_DELETE: if(cf->pub.http) { nchan_http_publisher_handler(r); } else goto forbidden; break; case NGX_HTTP_OPTIONS: if(cf->pub.http) { nchan_OPTIONS_respond(r, &NCHAN_ANYSTRING, &NCHAN_ACCESS_CONTROL_ALLOWED_PUBLISHER_HEADERS, &NCHAN_ALLOW_GET_POST_PUT_DELETE_OPTIONS); } else if(cf->sub.poll || cf->sub.longpoll || cf->sub.eventsource || cf->sub.websocket) { nchan_OPTIONS_respond(r, &NCHAN_ANYSTRING, &NCHAN_ACCESS_CONTROL_ALLOWED_SUBSCRIBER_HEADERS, &NCHAN_ALLOW_GET_OPTIONS); } else goto forbidden; break; } } return rc; forbidden: nchan_respond_status(r, NGX_HTTP_FORBIDDEN, NULL, 0); return NGX_OK; }