예제 #1
0
파일: http-chunked.c 프로젝트: t-web/nchan
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";
  static ngx_file_t       file_copy;
  
  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));
  nchan_msg_buf_open_fd_if_needed(&bc[2].buf, &file_copy, NULL);
  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;
}
예제 #2
0
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;
}
예제 #3
0
파일: longpoll.c 프로젝트: t-web/nchan
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;
}
예제 #4
0
ngx_int_t nchan_respond_msg(ngx_http_request_t *r, nchan_msg_t *msg, nchan_msg_id_t *msgid, ngx_int_t finalize, char **err) {
  ngx_buf_t                 *buffer = &msg->buf;
  nchan_buf_and_chain_t     *cb;
  ngx_int_t                  rc;
  ngx_chain_t               *rchain = NULL;
  ngx_buf_t                 *rbuffer;
  nchan_request_ctx_t       *ctx = ngx_http_get_module_ctx(r, ngx_nchan_module);
  
  if(ngx_buf_size(buffer) > 0) {
    cb = ngx_palloc(r->pool, sizeof(*cb));
    if (!cb) {
      if(err) *err = "couldn't allocate memory for buf-and-chain while responding with msg";
      return NGX_ERROR;
    }
    rchain = &cb->chain;
    rbuffer = &cb->buf;
    
    rchain->next = NULL;
    rchain->buf = rbuffer;
    
    ngx_memcpy(rbuffer, buffer, sizeof(*buffer));
    nchan_msg_buf_open_fd_if_needed(rbuffer, NULL, r);
    
    r->headers_out.content_length_n=ngx_buf_size(rbuffer);
  }
  else {
    r->headers_out.content_length_n = 0;
    r->header_only = 1;
  }

  if (msg->content_type) {
    r->headers_out.content_type = *msg->content_type;
  }
  
  if(msgid == NULL) {
    msgid = &msg->id;
  }
  
  if(nchan_set_msgid_http_response_headers(r, ctx, msgid) != NGX_OK) {
    if(err) *err = "can't set msgid headers";
    return NGX_ERROR;
  }
  
  r->headers_out.status=NGX_HTTP_OK;
  
  nchan_include_access_control_if_needed(r, ctx);
  
  //we know the entity length, and we're using just one buffer. so no chunking please.
  if((rc = ngx_http_send_header(r)) >= NGX_HTTP_SPECIAL_RESPONSE) {
    ERR("request %p, send_header response %i", r, rc);
    if(err) *err="WTF just happened to request?";
    return NGX_ERROR;
  }
  
  if(rchain) {
    rc= nchan_output_filter(r, rchain);
    if(rc != NGX_OK && err) *err="failed to write data to connection socket, probably because the connection got closed";
  }
  
  if(finalize) {
    nchan_http_finalize_request(r, rc);
  }
  return rc;
}
예제 #5
0
static ngx_int_t rawstream_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;
  ngx_int_t               rc;
  nchan_loc_conf_t       *cf = ngx_http_get_module_loc_conf(fsub->sub.request, ngx_nchan_module);
  nchan_request_ctx_t    *ctx = ngx_http_get_module_ctx(fsub->sub.request, ngx_nchan_module);
  nchan_buf_and_chain_t  *bc;
  ngx_chain_t            *chain;
  ngx_file_t             *file_copy;
  
  size_t                  separator_len = cf->subscriber_http_raw_stream_separator.len;
  size_t                  msg_len = ngx_buf_size((msg->buf));
  
  
  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);
  }
  
  if(msg_len + separator_len == 0) {
    //nothing to output
    return NGX_OK;
  }
  
  if((bc = nchan_bufchain_pool_reserve(ctx->bcp, (1 + (msg_len > 0 ? 1: 0)))) == NULL) {
    ERR("cant allocate buf-and-chains for http-raw-stream client output");
    return NGX_ERROR;
  }
  
  chain = &bc->chain;
  
  //message
  if(msg_len > 0) {
    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;
    
    chain = chain->next;
  }
  
  //separator 
  buf = chain->buf;
  ngx_memzero(buf, sizeof(ngx_buf_t));
  buf->start = cf->subscriber_http_raw_stream_separator.data;
  buf->pos = buf->start;
  buf->end = buf->start + separator_len;
  buf->last = buf->end;
  buf->memory = 1;
  
  buf->last_buf = 0;
  buf->last_in_chain = 1;
  buf->flush = 1;
  
  rawstream_ensure_headers_sent(fsub);
  
  DBG("%p output msg to subscriber", sub);
  
  rc = nchan_output_msg_filter(fsub->sub.request, msg, &bc->chain);
  
  return rc;
}
예제 #6
0
파일: longpoll.c 프로젝트: BCaoZY/nchan
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;
}
예제 #7
0
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;
}
예제 #8
0
파일: http-chunked.c 프로젝트: slact/nchan
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;
}