static void nchan_publisher_body_handler(ngx_http_request_t *r) { ngx_str_t *channel_id; nchan_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_nchan_module); ngx_table_elt_t *content_length_elt; ngx_http_complex_value_t *authorize_request_url_ccv = cf->authorize_request_url; if((channel_id = nchan_get_channel_id(r, PUB, 1))==NULL) { nchan_http_finalize_request(r, r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if(!authorize_request_url_ccv) { nchan_publisher_body_handler_continued(r, channel_id, cf); } else { nchan_pub_subrequest_stuff_t *psr_stuff; if((psr_stuff = ngx_palloc(r->pool, sizeof(*psr_stuff))) == NULL) { nchan_log_request_error(r, "can't allocate memory for publisher auth subrequest"); nchan_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } ngx_http_post_subrequest_t *psr = &psr_stuff->psr; nchan_pub_subrequest_data_t *psrd = &psr_stuff->psr_data; ngx_http_request_t *sr; ngx_str_t auth_request_url; ngx_http_complex_value(r, authorize_request_url_ccv, &auth_request_url); psr->handler = nchan_publisher_body_authorize_handler; psr->data = psrd; psrd->ch_id = channel_id; ngx_http_subrequest(r, &auth_request_url, NULL, &sr, psr, 0); if((sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t))) == NULL) { nchan_log_request_error(r, "can't allocate memory for publisher auth subrequest body"); nchan_http_finalize_request(r, r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if((content_length_elt = ngx_palloc(r->pool, sizeof(*content_length_elt))) == NULL) { nchan_log_request_error(r, "can't allocate memory for publisher auth subrequest content-length header"); nchan_http_finalize_request(r, r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if(sr->headers_in.content_length) { *content_length_elt = *sr->headers_in.content_length; content_length_elt->value.len=1; content_length_elt->value.data=(u_char *)"0"; sr->headers_in.content_length = content_length_elt; } sr->headers_in.content_length_n = 0; sr->args = r->args; sr->header_only = 1; } }
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; }
subscriber_t *websocket_subscriber_create(ngx_http_request_t *r, nchan_msg_id_t *msg_id) { nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_nchan_module); DBG("create for req %p", r); full_subscriber_t *fsub; if((fsub = ngx_alloc(sizeof(*fsub), ngx_cycle->log)) == NULL) { ERR("Unable to allocate"); return NULL; } nchan_subscriber_init(&fsub->sub, &new_websocket_sub, r, msg_id); fsub->cln = NULL; fsub->ctx = ctx; fsub->ws_meta_subprotocol = 0; fsub->finalize_request = 0; fsub->holding = 0; fsub->shook_hands = 0; fsub->connected = 0; fsub->pinging = 0; fsub->closing = 0; ngx_memzero(&fsub->ping_ev, sizeof(fsub->ping_ev)); nchan_subscriber_init_timeout_timer(&fsub->sub, &fsub->timeout_ev); fsub->dequeue_handler = empty_handler; fsub->dequeue_handler_data = NULL; fsub->awaiting_destruction = 0; ngx_memzero(&fsub->closing_ev, sizeof(fsub->closing_ev)); #if nginx_version >= 1008000 fsub->closing_ev.cancelable = 1; #endif //what should the buffers look like? /* //mesage buf b->last_buf = 1; b->last_in_chain = 1; b->flush = 1; b->memory = 1; b->temporary = 0; */ if(fsub->sub.cf->pub.websocket) { fsub->publish_channel_id = nchan_get_channel_id(r, PUB, 0); } fsub->upstream_stuff = NULL; websocket_init_frame(&fsub->frame); //http request sudden close cleanup if((fsub->cln = ngx_http_cleanup_add(r, 0)) == NULL) { ERR("Unable to add request cleanup for websocket subscriber"); return NULL; } fsub->cln->data = fsub; fsub->cln->handler = (ngx_http_cleanup_pt )sudden_abort_handler; DBG("%p created for request %p", &fsub->sub, r); assert(ctx != NULL); ctx->sub = &fsub->sub; //gonna need this for recv ctx->subscriber_type = fsub->sub.name; #if NCHAN_SUBSCRIBER_LEAK_DEBUG subscriber_debug_add(&fsub->sub); #endif //send-frame buffer ctx->output_str_queue = ngx_palloc(r->pool, sizeof(*ctx->output_str_queue)); nchan_reuse_queue_init(ctx->output_str_queue, offsetof(framebuf_t, prev), offsetof(framebuf_t, next), framebuf_alloc, NULL, r->pool); //bufchain pool ctx->bcp = ngx_palloc(r->pool, sizeof(nchan_bufchain_pool_t)); nchan_bufchain_pool_init(ctx->bcp, r->pool); return &fsub->sub; }
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; }
static void nchan_publisher_body_handler(ngx_http_request_t * r) { ngx_str_t *channel_id; nchan_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, nchan_module); ngx_buf_t *buf; size_t content_type_len; nchan_msg_t *msg; struct timeval tv; if((channel_id = nchan_get_channel_id(r, PUB, 1))==NULL) { ngx_http_finalize_request(r, r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR); return; } switch(r->method) { case NGX_HTTP_GET: cf->storage_engine->find_channel(channel_id, (callback_pt) &channel_info_callback, (void *)r); break; case NGX_HTTP_PUT: case NGX_HTTP_POST: memstore_pub_debug_start(); msg = ngx_pcalloc(r->pool, sizeof(*msg)); msg->shared = 0; NGX_REQUEST_VAL_CHECK(msg, NULL, r, "nchan: can't allocate msg in request pool"); //buf = ngx_create_temp_buf(r->pool, 0); //NGX_REQUEST_VAL_CHECK(buf, NULL, r, "nchan: can't allocate buf in request pool"); //content type content_type_len = (r->headers_in.content_type!=NULL ? r->headers_in.content_type->value.len : 0); if(content_type_len > 0) { msg->content_type.len = content_type_len; msg->content_type.data = r->headers_in.content_type->value.data; } if(r->headers_in.content_length_n == -1 || r->headers_in.content_length_n == 0) { buf = ngx_create_temp_buf(r->pool, 0); } else if(r->request_body->bufs!=NULL) { buf = nchan_request_body_to_single_buffer(r); } else { ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "nchan: unexpected publisher message request body buffer location. please report this to the nchan developers."); ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } ngx_gettimeofday(&tv); msg->id.time = tv.tv_sec; msg->id.tag[0] = 0; msg->id.tagactive = 0; msg->id.tagcount = 1; msg->buf = buf; #if NCHAN_MSG_LEAK_DEBUG msg->lbl = r->uri; #endif cf->storage_engine->publish(channel_id, msg, cf, (callback_pt) &publish_callback, r); memstore_pub_debug_end(); break; case NGX_HTTP_DELETE: cf->storage_engine->delete_channel(channel_id, (callback_pt) &channel_info_callback, (void *)r); break; default: nchan_respond_status(r, NGX_HTTP_FORBIDDEN, NULL, 0); } }
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; }
subscriber_t *websocket_subscriber_create(ngx_http_request_t *r, nchan_msg_id_t *msg_id) { ngx_buf_t *b; nchan_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, nchan_module); DBG("create for req %p", r); full_subscriber_t *fsub; if((fsub = ngx_alloc(sizeof(*fsub), ngx_cycle->log)) == NULL) { ERR("Unable to allocate"); return NULL; } ngx_memcpy(&fsub->sub, &new_websocket_sub, sizeof(new_websocket_sub)); fsub->request = r; fsub->cln = NULL; fsub->finalize_request = 0; fsub->holding = 0; fsub->shook_hands = 0; fsub->sub.cf = ngx_http_get_module_loc_conf(r, nchan_module); if(msg_id) { fsub->sub.last_msg_id.time = msg_id->time; fsub->sub.last_msg_id.tag = msg_id->tag; } ngx_memzero(&fsub->timeout_ev, sizeof(fsub->timeout_ev)); fsub->timeout_handler = empty_handler; fsub->timeout_handler_data = NULL; fsub->dequeue_handler = empty_handler; fsub->dequeue_handler_data = NULL; fsub->already_enqueued = 0; fsub->awaiting_destruction = 0; fsub->reserved = 0; //initialize reusable chains and bufs ngx_memzero(&fsub->hdr_buf, sizeof(fsub->hdr_buf)); ngx_memzero(&fsub->msg_buf, sizeof(fsub->msg_buf)); //space for frame header fsub->hdr_buf.start = ngx_pcalloc(r->pool, WEBSOCKET_FRAME_HEADER_MAX_LENGTH); fsub->hdr_chain.buf = &fsub->hdr_buf; fsub->hdr_chain.next = &fsub->msg_chain; fsub->msg_chain.buf = &fsub->msg_buf; fsub->msg_chain.next = NULL; //what should the buffers look like? b = &fsub->msg_buf; b->last_buf = 1; b->last_in_chain = 1; b->flush = 1; b->memory = 1; b->temporary = 0; if(cf->pub.websocket) { fsub->publish_channel_id = nchan_get_channel_id(r, PUB, 0); } websocket_init_frame(&fsub->frame); fsub->owner = memstore_slot(); //http request sudden close cleanup if((fsub->cln = ngx_http_cleanup_add(r, 0)) == NULL) { ERR("Unable to add request cleanup for websocket subscriber"); return NULL; } fsub->cln->data = fsub; fsub->cln->handler = (ngx_http_cleanup_pt )sudden_abort_handler; DBG("%p created for request %p", &fsub->sub, r); ngx_http_set_ctx(r, fsub, nchan_module); //gonna need this for recv return &fsub->sub; }