subscriber_t *http_multipart_subscriber_create(ngx_http_request_t *r, nchan_msg_id_t *msg_id) { subscriber_t *sub = longpoll_subscriber_create(r, msg_id); full_subscriber_t *fsub = (full_subscriber_t *)sub; multipart_privdata_t *multipart_data; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(fsub->sub.request, nchan_module); if(multipart_fn == NULL) { multipart_fn = &multipart_fn_data; *multipart_fn = *sub->fn; multipart_fn->enqueue = multipart_enqueue; multipart_fn->respond_message = multipart_respond_message; multipart_fn->respond_status = multipart_respond_status; } fsub->data.shook_hands = 0; fsub->privdata = ngx_palloc(sub->request->pool, sizeof(multipart_privdata_t)); multipart_data = (multipart_privdata_t *)fsub->privdata; multipart_data->boundary_end = ngx_snprintf(multipart_data->boundary, 50, "\r\n--%V", nchan_request_multipart_boundary(fsub->sub.request, ctx)); //header bufs -- unique per response ctx->output_str_queue = ngx_palloc(r->pool, sizeof(*ctx->output_str_queue)); nchan_reuse_queue_init(ctx->output_str_queue, offsetof(headerbuf_t, prev), offsetof(headerbuf_t, next), headerbuf_alloc, NULL, sub->request->pool); ctx->bcp = ngx_palloc(r->pool, sizeof(nchan_bufchain_pool_t)); nchan_bufchain_pool_init(ctx->bcp, r->pool); nchan_subscriber_common_setup(sub, HTTP_MULTIPART, &sub_name, multipart_fn, 0); return sub; }
subscriber_t *http_chunked_subscriber_create(ngx_http_request_t *r, nchan_msg_id_t *msg_id) { subscriber_t *sub; full_subscriber_t *fsub; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(r, nchan_module); sub = longpoll_subscriber_create(r, msg_id); if(chunked_fn == NULL) { chunked_fn = &chunked_fn_data; *chunked_fn = *sub->fn; chunked_fn->enqueue = chunked_enqueue; chunked_fn->respond_message = chunked_respond_message; chunked_fn->respond_status = chunked_respond_status; } fsub = (full_subscriber_t *)sub; sub->fn = chunked_fn; sub->name = &sub_name; sub->type = HTTP_CHUNKED; sub->dequeue_after_response = 0; fsub->data.shook_hands = 0; DBG("%p create subscriber", sub); if(ctx) { ctx->subscriber_type = sub->name; } return sub; }
subscriber_t *eventsource_subscriber_create(ngx_http_request_t *r, nchan_msg_id_t *msg_id) { subscriber_t *sub; full_subscriber_t *fsub; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(r, nchan_module); sub = longpoll_subscriber_create(r, msg_id); if(eventsource_fn == NULL) { eventsource_fn = &eventsource_fn_data; *eventsource_fn = *sub->fn; eventsource_fn->enqueue = es_enqueue; eventsource_fn->respond_message= es_respond_message; eventsource_fn->respond_status = es_respond_status; } fsub = (full_subscriber_t *)sub; sub->fn = eventsource_fn; sub->name = &sub_name; sub->type = EVENTSOURCE; sub->dequeue_after_response = 0; fsub->data.shook_hands = 0; DBG("%p create subscriber", sub); if(ctx) { ctx->subscriber_type = sub->name; } return sub; }
subscriber_t *http_chunked_subscriber_create(ngx_http_request_t *r, nchan_msg_id_t *msg_id) { subscriber_t *sub = longpoll_subscriber_create(r, msg_id); if(chunked_fn == NULL) { chunked_fn = &chunked_fn_data; *chunked_fn = *sub->fn; chunked_fn->enqueue = chunked_enqueue; chunked_fn->respond_message = chunked_respond_message; chunked_fn->respond_status = chunked_respond_status; } ((full_subscriber_t *)sub)->data.shook_hands = 0; nchan_subscriber_common_setup(sub, HTTP_CHUNKED, &sub_name, chunked_fn, 0); return sub; }
subscriber_t *http_raw_stream_subscriber_create(ngx_http_request_t *r, nchan_msg_id_t *msg_id) { subscriber_t *sub = longpoll_subscriber_create(r, msg_id); full_subscriber_t *fsub = (full_subscriber_t *)sub; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(fsub->sub.request, ngx_nchan_module); if(rawstream_fn == NULL) { rawstream_fn = &rawstream_fn_data; *rawstream_fn = *sub->fn; rawstream_fn->enqueue = rawstream_enqueue; rawstream_fn->respond_message = rawstream_respond_message; rawstream_fn->respond_status = rawstream_respond_status; } fsub->data.shook_hands = 0; r->keepalive=0; ctx->bcp = ngx_palloc(r->pool, sizeof(nchan_bufchain_pool_t)); nchan_bufchain_pool_init(ctx->bcp, r->pool); nchan_subscriber_common_setup(sub, HTTP_RAW_STREAM, &sub_name, rawstream_fn, 0); return sub; }
subscriber_t *http_chunked_subscriber_create(ngx_http_request_t *r, nchan_msg_id_t *msg_id) { subscriber_t *sub = longpoll_subscriber_create(r, msg_id); full_subscriber_t *fsub = (full_subscriber_t *)sub; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(sub->request, ngx_nchan_module); if(chunked_fn == NULL) { chunked_fn = &chunked_fn_data; *chunked_fn = *sub->fn; chunked_fn->enqueue = chunked_enqueue; chunked_fn->respond_message = chunked_respond_message; chunked_fn->respond_status = chunked_respond_status; } fsub->data.shook_hands = 0; ctx->output_str_queue = ngx_palloc(r->pool, sizeof(*ctx->output_str_queue)); nchan_reuse_queue_init(ctx->output_str_queue, offsetof(chunksizebuf_t, prev), offsetof(chunksizebuf_t, next), chunksizebuf_alloc, NULL, r->pool); ctx->bcp = ngx_palloc(r->pool, sizeof(nchan_bufchain_pool_t)); nchan_bufchain_pool_init(ctx->bcp, r->pool); nchan_subscriber_common_setup(sub, HTTP_CHUNKED, &sub_name, chunked_fn, 1, 0); return sub; }
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; }