static ngx_int_t longpoll_respond_message(subscriber_t *self, nchan_msg_t *msg) { full_subscriber_t *fsub = (full_subscriber_t *)self; ngx_int_t rc; char *err = NULL; ngx_http_request_t *r = fsub->sub.request; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(r, nchan_module); nchan_loc_conf_t *cf = fsub->sub.cf; DBG("%p respond req %p msg %p", self, r, msg); ctx->prev_msg_id = self->last_msgid; update_subscriber_last_msg_id(self, msg); ctx->msg_id = self->last_msgid; //disable abort handler fsub->data.cln->handler = empty_handler; //verify_unique_response(&fsub->data.request->uri, &self->last_msgid, msg, self); if(!cf->longpoll_multimsg) { assert(fsub->data.already_responded != 1); fsub->data.already_responded = 1; if(ctx->request_origin_header.len > 0) { nchan_add_response_header(r, &NCHAN_HEADER_ALLOW_ORIGIN, &cf->allow_origin); } if((rc = nchan_respond_msg(r, msg, &self->last_msgid, 0, &err)) != NGX_OK) { return abort_response(self, err); } } else { if((rc = longpoll_multimsg_add(fsub, msg, &err)) != NGX_OK) { return abort_response(self, err); } } dequeue_maybe(self); return rc; }
static ngx_int_t longpoll_respond_message(subscriber_t *self, nchan_msg_t *msg) { full_subscriber_t *fsub = (full_subscriber_t *)self; ngx_int_t rc; char *err = NULL; ngx_http_request_t *r = fsub->sub.request; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_nchan_module); nchan_loc_conf_t *cf = fsub->sub.cf; DBG("%p respond req %p msg %p", self, r, msg); ctx->prev_msg_id = self->last_msgid; update_subscriber_last_msg_id(self, msg); ctx->msg_id = self->last_msgid; //verify_unique_response(&fsub->data.request->uri, &self->last_msgid, msg, self); if(fsub->data.timeout_ev.timer_set) { ngx_del_timer(&fsub->data.timeout_ev); } if(!cf->longpoll_multimsg) { //disable abort handler fsub->data.cln->handler = empty_handler; assert(fsub->data.already_responded != 1); fsub->data.already_responded = 1; if((rc = nchan_respond_msg(r, msg, &self->last_msgid, 0, &err)) != NGX_OK) { return abort_response(self, err); } } else { if((rc = longpoll_multipart_add(fsub, msg, &err)) != NGX_OK) { return abort_response(self, err); } } dequeue_maybe(self); return rc; }
static ngx_int_t longpoll_multimsg_respond(full_subscriber_t *fsub) { ngx_http_request_t *r = fsub->sub.request; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(r, nchan_module); nchan_loc_conf_t *cf = fsub->sub.cf; char *err; ngx_int_t rc; u_char char_boundary[50]; u_char *char_boundary_last; ngx_int_t i; ngx_buf_t boundary[3]; //first, mid, and last boundary ngx_chain_t *chains, *first_chain = NULL, *last_chain = NULL; ngx_buf_t *buf; ngx_buf_t double_newline_buf; ngx_str_t *content_type; size_t size = 0; nchan_longpoll_multimsg_t *first, *cur; fsub->sub.dequeue_after_response = 1; if(ctx->request_origin_header.len > 0) { nchan_add_response_header(r, &NCHAN_HEADER_ALLOW_ORIGIN, &cf->allow_origin); } if(fsub->data.multimsg_first == fsub->data.multimsg_last) { //just one message. if((rc = nchan_respond_msg(r, fsub->data.multimsg_first->msg, &fsub->sub.last_msgid, 0, &err)) != NGX_OK) { return abort_response(&fsub->sub, err); } return NGX_OK; } //multi messages nchan_request_set_content_type_multipart_boundary_header(r, ctx); char_boundary_last = ngx_snprintf(char_boundary, 50, ("\r\n--%V--\r\n"), nchan_request_multipart_boundary(r, ctx)); ngx_memzero(&double_newline_buf, sizeof(double_newline_buf)); double_newline_buf.start = (u_char *)"\r\n\r\n"; double_newline_buf.end = double_newline_buf.start + 4; double_newline_buf.pos = double_newline_buf.start; double_newline_buf.last = double_newline_buf.end; double_newline_buf.memory = 1; //set up the boundaries for(i=0; i<3; i++) { ngx_memzero(&boundary[i], sizeof(ngx_buf_t)); boundary[i].memory = 1; if(i==0) { boundary[i].start = &char_boundary[2]; boundary[i].end = &char_boundary_last[-4]; } else if(i==1) { boundary[i].start = &char_boundary[0]; boundary[i].end = &char_boundary_last[-4]; } else if(i==2) { boundary[i].start = &char_boundary[0]; boundary[i].end = char_boundary_last; boundary[i].last_buf = 1; boundary[i].last_in_chain = 1; boundary[i].flush = 1; } boundary[i].pos = boundary[i].start; boundary[i].last = boundary[i].end; } first = fsub->data.multimsg_first; for(cur = first; cur != NULL; cur = cur->next) { chains = ngx_palloc(r->pool, sizeof(*chains)*4); if(last_chain) { last_chain->next = &chains[0]; } if(!first_chain) { first_chain = &chains[0]; } chains[0].buf = cur == first ? &boundary[0] : &boundary[1]; chains[0].next = &chains[1]; size += ngx_buf_size(chains[0].buf); content_type = &cur->msg->content_type; if (content_type->data != NULL) { buf = ngx_pcalloc(r->pool, sizeof(*buf) + content_type->len + 25); buf->memory = 1; buf->start = (u_char *)&buf[1]; buf->end = ngx_snprintf(buf->start, content_type->len + 25, "\r\nContent-Type: %V\r\n\r\n", content_type); buf->pos = buf->start; buf->last = buf->end; chains[1].buf = buf; } else { chains[1].buf = &double_newline_buf; } size += ngx_buf_size(chains[1].buf); if(ngx_buf_size(cur->msg->buf) > 0) { chains[1].next = &chains[2]; buf = ngx_palloc(r->pool, sizeof(*buf)); ngx_memcpy(buf, cur->msg->buf, sizeof(*buf)); nchan_msg_buf_open_fd_if_needed(buf, NULL, r); chains[2].buf = buf; size += ngx_buf_size(chains[2].buf); last_chain = &chains[2]; } else { last_chain = &chains[1]; } if(cur->next == NULL) { last_chain->next = &chains[3]; chains[3].buf = &boundary[2]; chains[3].next = NULL; last_chain = &chains[3]; size += ngx_buf_size(chains[3].buf); } } r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = size; nchan_set_msgid_http_response_headers(r, &fsub->data.multimsg_last->msg->id); ngx_http_send_header(r); nchan_output_filter(r, first_chain); return NGX_OK; }
static ngx_int_t longpoll_multipart_respond(full_subscriber_t *fsub) { ngx_http_request_t *r = fsub->sub.request; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_nchan_module); char *err; ngx_int_t rc; u_char *char_boundary = NULL; u_char *char_boundary_last; ngx_buf_t boundary[3]; //first, mid, and last boundary ngx_buf_t newline_buf; ngx_chain_t *chain, *first_chain = NULL, *last_chain = NULL; ngx_buf_t *buf; ngx_buf_t double_newline_buf; ngx_str_t *content_type; size_t size = 0; nchan_loc_conf_t *cf = fsub->sub.cf; int use_raw_stream_separator = cf->longpoll_multimsg_use_raw_stream_separator; nchan_buf_and_chain_t *bc; nchan_longpoll_multimsg_t *first, *cur; //disable abort handler fsub->data.cln->handler = empty_handler; first = fsub->data.multimsg_first; fsub->sub.dequeue_after_response = 1; //cleanup to release msgs fsub->data.cln = ngx_http_cleanup_add(fsub->sub.request, 0); fsub->data.cln->data = first; fsub->data.cln->handler = (ngx_http_cleanup_pt )multipart_request_cleanup_handler; if(fsub->data.multimsg_first == fsub->data.multimsg_last) { //just one message. if((rc = nchan_respond_msg(r, fsub->data.multimsg_first->msg, &fsub->sub.last_msgid, 0, &err)) != NGX_OK) { return abort_response(&fsub->sub, err); } return NGX_OK; } //multi messages if(!use_raw_stream_separator) { nchan_request_set_content_type_multipart_boundary_header(r, ctx); char_boundary = ngx_palloc(r->pool, 50); char_boundary_last = ngx_snprintf(char_boundary, 50, ("\r\n--%V--\r\n"), nchan_request_multipart_boundary(r, ctx)); ngx_init_set_membuf_char(&double_newline_buf, "\r\n\r\n"); //set up the boundaries ngx_init_set_membuf(&boundary[0], &char_boundary[2], &char_boundary_last[-4]); ngx_init_set_membuf(&boundary[1], &char_boundary[0], &char_boundary_last[-4]); ngx_init_set_membuf(&boundary[2], &char_boundary[0], char_boundary_last); ngx_init_set_membuf_char(&newline_buf, "\n"); } int n=0; for(cur = first; cur != NULL; cur = cur->next) { bc = nchan_bufchain_pool_reserve(ctx->bcp, 4); chain = &bc->chain; n++; if(last_chain) { last_chain->next = chain; } if(!first_chain) { first_chain = chain; } if(!use_raw_stream_separator) { // each buffer needs to be unique for the purpose of dealing with nginx output guts // (something about max. 64 iovecs per write call and counting the number of bytes already sent) *chain->buf = cur == first ? boundary[0] : boundary[1]; size += ngx_buf_size((chain->buf)); chain = chain->next; content_type = &cur->msg->content_type; buf = chain->buf; if (content_type->data != NULL) { u_char *char_cur = ngx_pcalloc(r->pool, content_type->len + 25); ngx_init_set_membuf(buf, char_cur, ngx_snprintf(char_cur, content_type->len + 25, "\r\nContent-Type: %V\r\n\r\n", content_type)); } else { *buf = double_newline_buf; } size += ngx_buf_size(buf); chain = chain->next; } if(ngx_buf_size(cur->msg->buf) > 0) { buf = chain->buf; *buf = *cur->msg->buf; if(buf->file) { ngx_file_t *file_copy = nchan_bufchain_pool_reserve_file(ctx->bcp); nchan_msg_buf_open_fd_if_needed(buf, file_copy, NULL); } buf->last_buf = 0; size += ngx_buf_size(buf); } if(use_raw_stream_separator) { chain = chain->next; ngx_init_set_membuf_str(chain->buf, &cf->subscriber_http_raw_stream_separator); size += ngx_buf_size((chain->buf)); } else { if(cur->next == NULL) { chain = chain->next; chain->buf = &boundary[2]; size += ngx_buf_size((chain->buf)); } } last_chain = chain; } buf = last_chain->buf; buf->last_buf = 1; buf->last_in_chain = 1; buf->flush = 1; last_chain->next = NULL; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = size; nchan_set_msgid_http_response_headers(r, ctx, &fsub->data.multimsg_last->msg->id); nchan_include_access_control_if_needed(r, ctx); ngx_http_send_header(r); nchan_output_filter(r, first_chain); return NGX_OK; }
static ngx_int_t longpoll_multipart_respond(full_subscriber_t *fsub) { ngx_http_request_t *r = fsub->sub.request; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(r, nchan_module); char *err; ngx_int_t rc; u_char char_boundary[50]; u_char *char_boundary_last; ngx_int_t i; ngx_buf_t boundary[3]; //first, mid, and last boundary ngx_chain_t *chains, *first_chain = NULL, *last_chain = NULL; ngx_buf_t *buf; ngx_buf_t double_newline_buf; ngx_str_t *content_type; size_t size = 0; nchan_longpoll_multimsg_t *first, *cur; //disable abort handler fsub->data.cln->handler = empty_handler; first = fsub->data.multimsg_first; fsub->sub.dequeue_after_response = 1; //cleanup to release msgs fsub->data.cln = ngx_http_cleanup_add(fsub->sub.request, 0); fsub->data.cln->data = first; fsub->data.cln->handler = (ngx_http_cleanup_pt )multipart_request_cleanup_handler; if(fsub->data.multimsg_first == fsub->data.multimsg_last) { //just one message. if((rc = nchan_respond_msg(r, fsub->data.multimsg_first->msg, &fsub->sub.last_msgid, 0, &err)) != NGX_OK) { return abort_response(&fsub->sub, err); } return NGX_OK; } //multi messages nchan_request_set_content_type_multipart_boundary_header(r, ctx); char_boundary_last = ngx_snprintf(char_boundary, 50, ("\r\n--%V--\r\n"), nchan_request_multipart_boundary(r, ctx)); ngx_memzero(&double_newline_buf, sizeof(double_newline_buf)); double_newline_buf.start = (u_char *)"\r\n\r\n"; double_newline_buf.end = double_newline_buf.start + 4; double_newline_buf.pos = double_newline_buf.start; double_newline_buf.last = double_newline_buf.end; double_newline_buf.memory = 1; //set up the boundaries for(i=0; i<3; i++) { ngx_memzero(&boundary[i], sizeof(ngx_buf_t)); boundary[i].memory = 1; if(i==0) { boundary[i].start = &char_boundary[2]; boundary[i].end = &char_boundary_last[-4]; } else if(i==1) { boundary[i].start = &char_boundary[0]; boundary[i].end = &char_boundary_last[-4]; } else if(i==2) { boundary[i].start = &char_boundary[0]; boundary[i].end = char_boundary_last; boundary[i].last_buf = 1; boundary[i].last_in_chain = 1; boundary[i].flush = 1; } boundary[i].pos = boundary[i].start; boundary[i].last = boundary[i].end; } int n=0; for(cur = first; cur != NULL; cur = cur->next) { chains = ngx_palloc(r->pool, sizeof(*chains)*4); n++; if(last_chain) { last_chain->next = &chains[0]; } if(!first_chain) { first_chain = &chains[0]; } // each buffer needs to be unique for the purpose of dealing with nginx output guts // (something about max. 64 iovecs per write call and counting the number of bytes already sent) buf = ngx_pcalloc(r->pool, sizeof(*buf)); *buf = cur == first ? boundary[0] : boundary[1]; chains[0].buf = buf; chains[0].next = &chains[1]; size += ngx_buf_size(chains[0].buf); content_type = &cur->msg->content_type; if (content_type->data != NULL) { buf = ngx_pcalloc(r->pool, sizeof(*buf) + content_type->len + 25); buf->memory = 1; buf->start = (u_char *)&buf[1]; buf->end = ngx_snprintf(buf->start, content_type->len + 25, "\r\nContent-Type: %V\r\n\r\n", content_type); buf->pos = buf->start; buf->last = buf->end; chains[1].buf = buf; } else { buf = ngx_palloc(r->pool, sizeof(*buf)); chains[1].buf = buf; *buf = double_newline_buf; } size += ngx_buf_size(chains[1].buf); if(ngx_buf_size(cur->msg->buf) > 0) { chains[1].next = &chains[2]; buf = ngx_palloc(r->pool, sizeof(*buf)); *buf = *cur->msg->buf; nchan_msg_buf_open_fd_if_needed(buf, NULL, r); buf->last_buf = 0; chains[2].buf = buf; size += ngx_buf_size(chains[2].buf); last_chain = &chains[2]; } else { last_chain = &chains[1]; } if(cur->next == NULL) { last_chain->next = &chains[3]; chains[3].buf = &boundary[2]; chains[3].next = NULL; last_chain = &chains[3]; size += ngx_buf_size(chains[3].buf); } } r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = size; nchan_set_msgid_http_response_headers(r, ctx, &fsub->data.multimsg_last->msg->id); nchan_include_access_control_if_needed(r, ctx); ngx_http_send_header(r); nchan_output_filter(r, first_chain); return NGX_OK; }