static ngx_int_t chunked_respond_message(subscriber_t *sub, nchan_msg_t *msg) { static u_char chunk_start[15]; //that's enough static u_char *chunk_end=(u_char *)"\r\n"; full_subscriber_t *fsub = (full_subscriber_t *)sub; ngx_buf_t *msg_buf = msg->buf; ngx_int_t rc; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(fsub->sub.request, nchan_module); if (ngx_buf_size(msg_buf) == 0) { //empty messages are skipped, because a zero-length chunk finalizes the request return NGX_OK; } nchan_buf_and_chain_t bc[3]; bc[0].chain.buf = &bc[0].buf; bc[0].chain.next = &bc[1].chain; ngx_memzero(&bc[0].buf, sizeof(ngx_buf_t)); bc[0].buf.memory = 1; bc[0].buf.start = chunk_start; bc[0].buf.pos = chunk_start; bc[0].buf.end = ngx_snprintf(chunk_start, 15, "%xi\r\n", ngx_buf_size(msg_buf)); bc[0].buf.last = bc[0].buf.end; bc[1].chain.buf = &bc[1].buf; bc[1].chain.next = &bc[2].chain; ngx_memcpy(&bc[1].buf, msg_buf, sizeof(*msg_buf)); bc[1].buf.last_buf = 0; bc[1].buf.last_in_chain = 0; bc[1].buf.flush = 0; bc[2].chain.buf = &bc[2].buf; bc[2].chain.next = NULL; ngx_memzero(&bc[2].buf, sizeof(ngx_buf_t)); bc[2].buf.start = chunk_end; bc[2].buf.pos = chunk_end; bc[2].buf.end = chunk_end + 2; bc[2].buf.last = bc[2].buf.end; bc[2].buf.memory = 1; bc[2].buf.last_buf = 0; bc[2].buf.last_in_chain = 1; bc[2].buf.flush = 1; ctx->prev_msg_id = fsub->sub.last_msgid; update_subscriber_last_msg_id(sub, msg); ctx->msg_id = fsub->sub.last_msgid; chunked_ensure_headers_sent(fsub); DBG("%p output msg to subscriber", sub); rc = nchan_output_filter(fsub->sub.request, &bc[0].chain); 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, 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 multipart_respond_message(subscriber_t *sub, nchan_msg_t *msg) { full_subscriber_t *fsub = (full_subscriber_t *)sub; ngx_buf_t *buf, *msg_buf = msg->buf, *msgid_buf; ngx_int_t rc; nchan_loc_conf_t *cf = ngx_http_get_module_loc_conf(fsub->sub.request, nchan_module); nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(fsub->sub.request, nchan_module); ngx_int_t n; nchan_buf_and_chain_t *bc; ngx_chain_t *chain; ngx_file_t *file_copy; multipart_privdata_t *mpd = (multipart_privdata_t *)fsub->privdata; headerbuf_t *headerbuf = nchan_reuse_queue_push(ctx->output_str_queue); u_char *cur = headerbuf->charbuf; if(fsub->data.timeout_ev.timer_set) { ngx_del_timer(&fsub->data.timeout_ev); ngx_add_timer(&fsub->data.timeout_ev, sub->cf->subscriber_timeout * 1000); } //generate the headers if(!cf->msg_in_etag_only) { //msgtime cur = ngx_cpymem(cur, "\r\nLast-Modified: ", sizeof("\r\nLast-Modified: ") - 1); cur = ngx_http_time(cur, msg->id.time); *cur++ = CR; *cur++ = LF; //msgtag cur = ngx_cpymem(cur, "Etag: ", sizeof("Etag: ") - 1); cur += msgtag_to_strptr(&msg->id, (char *)cur); *cur++ = CR; *cur++ = LF; } else { ngx_str_t *tmp_etag = msgid_to_str(&msg->id); cur = ngx_snprintf(cur, 58 + 10*NCHAN_FIXED_MULTITAG_MAX, "\r\nEtag: %V\r\n", tmp_etag); } n=4; if(msg->content_type.len == 0) { //don't need content_type buf'n'chain n--; } if(ngx_buf_size(msg_buf) == 0) { //don't need msgbuf n --; } if((bc = nchan_bufchain_pool_reserve(ctx->bcp, n)) == NULL) { ERR("cant allocate buf-and-chains for multipart/mixed client output"); return NGX_ERROR; } chain = &bc->chain; msgid_buf = chain->buf; //message id ngx_memzero(chain->buf, sizeof(ngx_buf_t)); chain->buf->memory = 1; chain->buf->start = headerbuf->charbuf; chain->buf->pos = headerbuf->charbuf; //content_type maybe if(msg->content_type.len > 0) { chain = chain->next; buf = chain->buf; msgid_buf->last = cur; msgid_buf->end = cur; ngx_memzero(buf, sizeof(ngx_buf_t)); buf->memory = 1; buf->start = cur; buf->pos = cur; buf->last = ngx_snprintf(cur, 255, "Content-Type: %V\r\n\r\n", &msg->content_type); buf->end = buf->last; } else { *cur++ = CR; *cur++ = LF; msgid_buf->last = cur; msgid_buf->end = cur; } chain = chain->next; buf = chain->buf; //msgbuf if(ngx_buf_size(msg_buf) > 0) { ngx_memcpy(buf, msg_buf, sizeof(*msg_buf)); if(msg_buf->file) { file_copy = nchan_bufchain_pool_reserve_file(ctx->bcp); nchan_msg_buf_open_fd_if_needed(buf, file_copy, NULL); } buf->last_buf = 0; buf->last_in_chain = 0; buf->flush = 0; } chain = chain->next; buf = chain->buf; ngx_memzero(buf, sizeof(ngx_buf_t)); buf->start = &mpd->boundary[0]; buf->pos = buf->start; buf->end = mpd->boundary_end; buf->last = buf->end; buf->memory = 1; buf->last_buf = 0; buf->last_in_chain = 1; buf->flush = 1; ctx->prev_msg_id = fsub->sub.last_msgid; update_subscriber_last_msg_id(sub, msg); ctx->msg_id = fsub->sub.last_msgid; multipart_ensure_headers_sent(fsub); DBG("%p output msg to subscriber", sub); rc = nchan_output_msg_filter(fsub->sub.request, msg, &bc->chain); return rc; }
static ngx_int_t es_respond_message(subscriber_t *sub, nchan_msg_t *msg) { static ngx_str_t terminal_newlines=ngx_string("\n\n"); full_subscriber_t *fsub = (full_subscriber_t *)sub; u_char *cur = NULL, *last = NULL; ngx_buf_t *msg_buf = msg->buf; ngx_buf_t databuf; ngx_pool_t *pool; nchan_buf_and_chain_t *bc; size_t len; ngx_chain_t *first_link = NULL, *last_link = NULL; ngx_file_t *msg_file; ngx_int_t rc; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(fsub->sub.request, nchan_module); ctx->prev_msg_id = fsub->sub.last_msgid; update_subscriber_last_msg_id(sub, msg); ctx->msg_id = fsub->sub.last_msgid; es_ensure_headers_sent(fsub); DBG("%p output msg to subscriber", sub); pool = ngx_create_pool(NGX_DEFAULT_LINEBREAK_POOL_SIZE, ngx_cycle->log); assert(pool); ngx_memcpy(&databuf, msg_buf, sizeof(*msg_buf)); databuf.last_buf = 0; if(!databuf.in_file) { cur = msg_buf->start; last = msg_buf->end; do { databuf.start = cur; databuf.pos = cur; databuf.end = last; databuf.last = last; cur = ngx_strlchr(cur, last, '\n'); if(cur == NULL) { //sweet, no newlines! //let's get out of this hellish loop databuf.end = last; databuf.last = last; cur = last + 1; } else { cur++; //include the newline databuf.end = cur; databuf.last = cur; } create_dataline_bufchain(pool, &first_link, &last_link, &databuf); } while(cur <= last); } else { //great, we've gotta scan this whole damn file for line breaks. //EventStream really isn't designed for large chunks of data off_t fcur, flast; ngx_fd_t fd; int chr_int; FILE *stream; msg_file = ngx_palloc(pool, sizeof(*msg_file)); databuf.file = msg_file; ngx_memcpy(msg_file, msg_buf->file, sizeof(*msg_file)); if(msg_file->fd == NGX_INVALID_FILE) { msg_file->fd = nchan_fdcache_get(&msg_file->name); } fd = msg_file->fd; stream = fdopen(dup(fd), "r"); fcur = databuf.file_pos; flast = databuf.file_last; fseek(stream, fcur, SEEK_SET); do { databuf.file_pos = fcur; databuf.file_last = flast; //getc that shit for(;;) { chr_int = getc(stream); if(chr_int == EOF) { break; } else if(chr_int == (int )'\n') { fcur++; break; } fcur++; } databuf.file_last = fcur; create_dataline_bufchain(pool, &first_link, &last_link, &databuf); } while(fcur < flast); fclose(stream); } //now 2 newlines at the end if(last_link) { bc = ngx_palloc(pool, sizeof(*bc)); last_link->next=&bc->chain; ngx_init_set_membuf(&bc->buf, terminal_newlines.data, terminal_newlines.data + terminal_newlines.len); bc->buf.flush = 1; bc->buf.last_buf = 0; bc->chain.next = NULL; bc->chain.buf = &bc->buf; last_link = &bc->chain; } //okay, this crazy data chain is finished. now how about the mesage tag? len = 10 + 2*NGX_INT_T_LEN; bc = ngx_palloc(pool, sizeof(*bc) + len); ngx_memzero(&bc->buf, sizeof(bc->buf)); cur = (u_char *)&bc[1]; ngx_init_set_membuf(&bc->buf, cur, ngx_snprintf(cur, len, "id: %V\n", msgid_to_str(&sub->last_msgid))); bc->chain.buf = &bc->buf; bc->chain.next = first_link; first_link=&bc->chain; rc = nchan_output_filter(fsub->sub.request, first_link); ngx_destroy_pool(pool); return rc; }
static ngx_int_t chunked_respond_message(subscriber_t *sub, nchan_msg_t *msg) { full_subscriber_t *fsub = (full_subscriber_t *)sub; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(fsub->sub.request, ngx_nchan_module); chunksizebuf_t *chunksizebuf = nchan_reuse_queue_push(ctx->output_str_queue); u_char *chunk_start = &chunksizebuf->chr[0]; static u_char *chunk_end=(u_char *)"\r\n"; ngx_file_t *file_copy; nchan_buf_and_chain_t *bc = nchan_bufchain_pool_reserve(ctx->bcp, 3); ngx_chain_t *chain; ngx_buf_t *buf, *msg_buf = &msg->buf; ngx_int_t rc; if(fsub->data.timeout_ev.timer_set) { ngx_del_timer(&fsub->data.timeout_ev); ngx_add_timer(&fsub->data.timeout_ev, sub->cf->subscriber_timeout * 1000); } ctx->prev_msg_id = fsub->sub.last_msgid; update_subscriber_last_msg_id(sub, msg); ctx->msg_id = fsub->sub.last_msgid; if (ngx_buf_size(msg_buf) == 0) { //empty messages are skipped, because a zero-length chunk finalizes the request return NGX_OK; } //chunk size chain = &bc->chain; buf = chain->buf; ngx_memzero(buf, sizeof(*buf)); buf->memory = 1; buf->start = chunk_start; buf->pos = chunk_start; buf->end = ngx_snprintf(chunk_start, 15, "%xi\r\n", ngx_buf_size(msg_buf)); buf->last = buf->end; //message chain = chain->next; buf = chain->buf; *buf = *msg_buf; if(buf->file) { file_copy = nchan_bufchain_pool_reserve_file(ctx->bcp); nchan_msg_buf_open_fd_if_needed(buf, file_copy, NULL); } buf->last_buf = 0; buf->last_in_chain = 0; buf->flush = 0; //trailing newlines chain = chain->next; buf = chain->buf; ngx_memzero(buf, sizeof(*buf)); buf->start = chunk_end; buf->pos = chunk_end; buf->end = chunk_end + 2; buf->last = buf->end; buf->memory = 1; buf->last_buf = 0; buf->last_in_chain = 1; buf->flush = 1; chunked_ensure_headers_sent(fsub); DBG("%p output msg to subscriber", sub); rc = nchan_output_msg_filter(fsub->sub.request, msg, &bc->chain); return rc; }