Beispiel #1
0
static ngx_int_t spool_respond_general(subscriber_pool_t *self, nchan_msg_t *msg, ngx_int_t code, void *code_data, unsigned notice) {
  ngx_uint_t                  numsubs[SUBSCRIBER_TYPES];
  spooled_subscriber_t       *nsub, *nnext;
  subscriber_t               *sub;
  
  //channel_spooler_t          *spl = self->spooler;
  //validate_spooler(spl, "before respond_general");
  //nchan_msg_id_t             unid;
  //nchan_msg_id_t             unprevid;
  //int8_t                     i, max;
  
  ngx_memzero(numsubs, sizeof(numsubs));
  self->generation++;
  
  DBG("spool %p (%V) (subs: %i) respond with msg %p or code %i", self, msgid_to_str(&self->id), self->sub_count, msg, code);
  if(msg) {
    DBG("msgid: %V", msgid_to_str(&msg->id));
    DBG("prev: %V", msgid_to_str(&msg->prev_id));
  }
  
  /*
  if(msg && msg->prev_id.time > 0 && msg->id.tagcount > 1) {
    assert(msg->shared == 0);
    max = msg->id.tagcount;
    for(i=0; i< max; i++) {
      unid.tag[i] =     msg->id.tag[i];
      unprevid.tag[i] = msg->prev_id.tag[i];
      if(unid.tag[i] == -1)     msg->id.tag[i]   =    self->id.tag[i];
      if(unprevid.tag[i] == -1) msg->prev_id.tag[i] = self->id.tag[i];
    }
  }
  */
  
  //uint8_t publish_events = self->spooler->publish_events;
  
  for(nsub = self->first; nsub != NULL; nsub = nnext) {
    sub = nsub->sub;
    nnext = nsub->next;
    
    if(msg) {
      self->responded_count++;
      sub->fn->respond_message(sub, msg);
    }
    else if(!notice) {
      self->responded_count++;
      sub->fn->respond_status(sub, code, code_data);
    }
    else {
      sub->fn->notify(sub, code, code_data);
    }
  }
  
  if(!notice && code != NGX_HTTP_NO_CONTENT) self->responded_count++;
  //assert(validate_spooler(spl, "after respond_general"));
  return NGX_OK;
}
ngx_int_t nchan_set_msgid_http_response_headers(ngx_http_request_t *r, nchan_request_ctx_t *ctx, nchan_msg_id_t *msgid) {
    ngx_str_t                 *etag, *tmp_etag;
    nchan_loc_conf_t          *cf = ngx_http_get_module_loc_conf(r, ngx_nchan_module);
    int8_t                     output_etag = 1, cross_origin;

    if(!ctx) {
        ctx = ngx_http_get_module_ctx(r, ngx_nchan_module);
    }
    cross_origin = ctx && ctx->request_origin_header.data;

    if(!cf->msg_in_etag_only) {
        //last-modified
        if(msgid->time > 0) {
            r->headers_out.last_modified_time = msgid->time;
        }
        else {
            output_etag = 0;
        }
        tmp_etag = msgtag_to_str(msgid);
    }
    else {
        tmp_etag = msgid_to_str(msgid);
    }

    if((etag = ngx_palloc(r->pool, sizeof(*etag) + tmp_etag->len))==NULL) {
        return NGX_ERROR;
    }
    etag->data = (u_char *)(etag+1);
    etag->len = tmp_etag->len;
    ngx_memcpy(etag->data, tmp_etag->data, tmp_etag->len);

    if(cf->custom_msgtag_header.len == 0) {
        if(output_etag) {
            nchan_add_response_header(r, &NCHAN_HEADER_ETAG, etag);
        }
        if(cross_origin) {
            nchan_add_response_header(r, &NCHAN_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, &NCHAN_MSG_RESPONSE_ALLOWED_HEADERS);
        }
    }
    else {
        if(output_etag) {
            nchan_add_response_header(r, &cf->custom_msgtag_header, etag);
        }
        if(cross_origin) {
            u_char        *cur = ngx_palloc(r->pool, 255);
            if(cur == NULL) {
                return NGX_ERROR;
            }
            ngx_str_t      allowed;
            allowed.data = cur;
            cur = ngx_snprintf(cur, 255, NCHAN_MSG_RESPONSE_ALLOWED_CUSTOM_ETAG_HEADERS_STRF, &cf->custom_msgtag_header);
            allowed.len = cur - allowed.data;
            nchan_add_response_header(r, &NCHAN_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, &allowed);
        }
    }

    //Vary header needed for proper HTTP caching.
    nchan_add_response_header(r, &NCHAN_HEADER_VARY, &NCHAN_VARY_HEADER_VALUE);
    return NGX_OK;
}
Beispiel #3
0
static ngx_int_t spooler_respond_message(channel_spooler_t *self, nchan_msg_t *msg) {
  spooler_respond_data_t     srdata;
  subscriber_pool_t         *spool;
  ngx_int_t                  responded_subs = 0;
  
  srdata.min = msg->prev_id;
  srdata.max = msg->id;
  srdata.multi = msg->id.tagcount;
  srdata.overflow = NULL;
  srdata.msg = msg;
  srdata.n = 0;
  //find all spools between msg->prev_id and msg->id
  rbtree_conditional_walk(&self->spoolseed, (rbtree_walk_conditional_callback_pt )collect_spool_range_callback, &srdata);
  
  if(srdata.n == 0) {
    DBG("no spools in range %V -- ", msgid_to_str(&msg->prev_id));
    DBG(" -- %V", msgid_to_str(&msg->id));
  }
  
  while((spool = spoolcollector_unwind_nextspool(&srdata)) != NULL) {
    responded_subs += spool->sub_count;
    if(msg->id.tagcount > NCHAN_FIXED_MULTITAG_MAX) {
      assert(spool->id.tag.allocd != msg->id.tag.allocd);
    }
    spool_respond_general(spool, msg, 0, NULL, 0);
    if(msg->id.tagcount > NCHAN_FIXED_MULTITAG_MAX) {
      assert(spool->id.tag.allocd != msg->id.tag.allocd);
    }
    spool_nextmsg(spool, &msg->id);
  }
  
  spool = get_spool(self, &latest_msg_id);
  if(spool->sub_count > 0) {
#if NCHAN_BENCHMARK
    responded_subs += spool->sub_count;
#endif
    spool_respond_general(spool, msg, 0, NULL, 0);
    spool_nextmsg(spool, &msg->id);
  }

  nchan_copy_msg_id(&self->prev_msg_id, &msg->id, NULL);
  
#if NCHAN_BENCHMARK
  self->last_responded_subscriber_count = responded_subs;
#endif
  return NGX_OK;
}
Beispiel #4
0
ngx_str_t nchan_subscriber_set_recyclable_msgid_str(nchan_request_ctx_t *ctx, nchan_msg_id_t *msgid) {
  ngx_str_t               ret;
  msgidbuf_t             *msgidbuf;
  
  msgidbuf = nchan_reuse_queue_push(ctx->output_str_queue);
  ret.data = &msgidbuf->chr[0];
  
  nchan_strcpy(&ret, msgid_to_str(msgid), MSGID_BUF_LEN);
  
  return ret;
}
Beispiel #5
0
static ngx_int_t spool_fetch_msg(subscriber_pool_t *spool) {
  fetchmsg_data_t        *data;
  channel_spooler_t      *spl = spool->spooler;
  if(*spl->channel_status != READY) {
    DBG("%p wanted to fetch msg %V, but channel %V not ready", spool, msgid_to_str(&spool->id), spl->chid);
    spool->msg_status = MSG_CHANNEL_NOTREADY;
    return NGX_DECLINED;
  }
  DBG("%p fetch msg %V for channel %V", spool, msgid_to_str(&spool->id), spl->chid);
  data = ngx_alloc(sizeof(*data), ngx_cycle->log); //correctness over efficiency (at first).
  //TODO: optimize this alloc away
  
  assert(data);
  
  data->next = spl->fetchmsg_cb_data_list;
  if(data->next) {
    data->next->prev = data;
  }
  spl->fetchmsg_cb_data_list = data;
  data->prev = NULL;
  
  nchan_copy_new_msg_id(&data->msgid, &spool->id);
  data->spooler = spool->spooler;
  
  assert(spool->msg == NULL);
  assert(spool->msg_status == MSG_INVALID);
  spool->msg_status = MSG_PENDING;
  if(spl->handlers->get_message_start) {
    spl->handlers->get_message_start(spl, spl->handlers_privdata);
  }
  switch(spl->fetching_strategy) {
    case FETCH:
    case FETCH_IGNORE_MSG_NOTFOUND:
      spool->spooler->store->get_message(spool->spooler->chid, &spool->id, spool->spooler->cf, (callback_pt )spool_fetch_msg_callback, data);
      break;
    case NO_FETCH:
      //do nothing
      break;
  }
  return NGX_OK;
}
static ngx_int_t nchan_message_id_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) {
  static u_char        msgidbuf[100];
  ngx_str_t           *msgid;
  
  nchan_request_ctx_t        *ctx = get_main_request_ctx(r);
  if(ctx == NULL || (ctx->msg_id.time == 0 && ctx->msg_id.tagcount == 0)) {
    v->not_found = 1;
    return NGX_OK;
  }
  
  msgid = msgid_to_str(&ctx->msg_id);
  ngx_memcpy(msgidbuf, msgid->data, msgid->len);
  set_varval(v, msgidbuf, msgid->len);
  
  return NGX_OK;
}
Beispiel #7
0
static subscriber_pool_t *get_spool(channel_spooler_t *spl, nchan_msg_id_t *id) {
  rbtree_seed_t      *seed = &spl->spoolseed;
  ngx_rbtree_node_t  *node;
  subscriber_pool_t *spool;
  
  if(id->time == NCHAN_NEWEST_MSGID_TIME) {
    spool = &spl->current_msg_spool;
    spool->msg_status = MSG_EXPECTED;
    return &spl->current_msg_spool;
  }
  
  if((node = rbtree_find_node(seed, id)) == NULL) {
    
    if((node = rbtree_create_node(seed, sizeof(*spool))) == NULL) {
      ERR("can't create rbtree node for spool");
      return NULL;
    }
    
   // DBG("CREATED spool node %p for msgid %V", node, msgid_to_str(id));
    spool = (subscriber_pool_t *)rbtree_data_from_node(node);
    
    init_spool(spl, spool, id);
    
    if(rbtree_insert_node(seed, node) != NGX_OK) {
      ERR("couldn't insert spool node");
      rbtree_destroy_node(seed, node);
      return NULL;
    }
  }
  else {
    spool = (subscriber_pool_t *)rbtree_data_from_node(node);
    DBG("found spool node %p with msgid %V", node, msgid_to_str(id));
    assert(spool->id.time == id->time);
  }
  return spool;
}
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;
}
Beispiel #9
0
static ngx_int_t sub_respond_message(ngx_int_t status, nchan_msg_t *msg, sub_data_t* d) {
  nchan_msg_copy_t  remsg;
  //nchan_msg_id_t   *last_msgid;
  ngx_int_t         mcount;
  
  int16_t          tags[NCHAN_MULTITAG_MAX], prevtags[NCHAN_MULTITAG_MAX];
  
  //remsg = ngx_alloc(sizeof(*remsg), ngx_cycle->log);
  //assert(remsg);
  
  assert( msg->id.tagcount == 1 );
  assert( msg->prev_id.tagcount == 1 );
  
  remsg.original = msg;
  
  remsg.copy = *msg;
  remsg.copy.shared = 0;
  remsg.copy.temp_allocd = 0;
  
  mcount = d->multi_chanhead->multi_count;
  
  remsg.copy.prev_id.tagcount = mcount;
  remsg.copy.prev_id.tagactive = d->n;
  
  remsg.copy.id.tagcount = mcount;
  remsg.copy.id.tagactive = d->n;
  
  if(mcount > NCHAN_FIXED_MULTITAG_MAX) {
    remsg.copy.id.tag.allocd = tags;
    tags[0]=msg->id.tag.fixed[0];
    remsg.copy.prev_id.tag.allocd = prevtags;
    prevtags[0]=msg->prev_id.tag.fixed[0];
  }
  
  
  nchan_expand_msg_id_multi_tag(&remsg.copy.prev_id, 0, d->n, -1);
  nchan_expand_msg_id_multi_tag(&remsg.copy.id, 0, d->n, -1);
  
  memstore_ensure_chanhead_is_ready(d->multi_chanhead, 1);
  
  DBG("%p respond with transformed message %p %V (%p %V %i) %V", d->multi->sub, &remsg.copy, msgid_to_str(&remsg.copy.id), d->multi_chanhead, &d->multi_chanhead->id, d->n, &d->multi->id);
  
  nchan_memstore_publish_generic(d->multi_chanhead, &remsg.copy, 0, NULL);
  
  return NGX_OK;
}
Beispiel #10
0
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;
}
Beispiel #11
0
static ngx_int_t spool_fetch_msg_callback(nchan_msg_status_t findmsg_status, nchan_msg_t *msg, fetchmsg_data_t *data) {
  nchan_msg_status_t    prev_status;
  subscriber_pool_t    *spool, *nuspool;
  channel_spooler_t    *spl = data->spooler;
  int                   free_msg_id = 1;
  
  if(spl && data == spl->fetchmsg_cb_data_list) {
    spl->fetchmsg_cb_data_list = data->next;
  }
  if(data->next) {
    data->next->prev = data->prev;
  }
  if(data->prev) {
    data->prev->next = data->next;
  }
  
  if(spl == NULL) { //channel already deleted
    nchan_free_msg_id(&data->msgid);
    ngx_free(data);
    return NGX_OK;
  }
  
  if(spl->handlers->get_message_finish) {
    spl->handlers->get_message_finish(spl, spl->handlers_privdata);
  }
  
  if((spool = find_spool(spl, &data->msgid)) == NULL) {
    DBG("spool for msgid %V not found. discarding getmsg callback response.", msgid_to_str(&data->msgid));
    nchan_free_msg_id(&data->msgid);
    ngx_free(data);
    return NGX_ERROR;
  }
  
  prev_status = spool->msg_status;
  
  switch(findmsg_status) {
    case MSG_FOUND:
      spool->msg_status = findmsg_status;
      DBG("fetchmsg callback for spool %p msg FOUND %p %V", spool, msg, msgid_to_str(&msg->id));
      assert(msg != NULL);
      spool->msg = msg;
      spool_respond_general(spool, spool->msg, 0, NULL, 0);
      
      spool_nextmsg(spool, &msg->id);      
      break;
    
    case MSG_EXPECTED:
      // ♫ It's gonna be the future soon ♫
      if(spool->id.time == NCHAN_NTH_MSGID_TIME) {
        //wait for message in the NEWEST_ID spool
        nchan_msg_id_t  newest_id = NCHAN_NEWEST_MSGID;
        spool_nextmsg(spool, &newest_id); 
      }
      else {
        spool->msg_status = findmsg_status;
        DBG("fetchmsg callback for spool %p msg EXPECTED", spool);
        spool_respond_general(spool, NULL, NGX_HTTP_NO_CONTENT, NULL, 0);
        assert(msg == NULL);
        spool->msg = NULL;
      }
      break;
      
    case MSG_NORESPONSE:
      if(prev_status == MSG_PENDING) {
        spool->msg_status = MSG_INVALID;
        if(spool->sub_count > 0) {
          nomsg_retry_data_t *retry_data = ngx_alloc(sizeof(*retry_data), ngx_cycle->log);
          
          retry_data->spooler = spl;
          
          free_msg_id = 0;
          retry_data->msg_id = data->msgid;
          
          spooler_add_timer(spl, NCHAN_MSG_NORESPONSE_RETRY_TIME, spool_fetch_msg_noresponse_retry_callback, spool_fetch_msg_noresponse_retry_cancel, retry_data);
        }
      }
      break;
      
    case MSG_NOTFOUND:
      if(spl->fetching_strategy == FETCH_IGNORE_MSG_NOTFOUND) {
        spool->msg_status = prev_status;
        break;
      }
    case MSG_EXPIRED:
      //is this right?
      //TODO: maybe message-expired notification
      spool->msg_status = findmsg_status;
      spool_respond_general(spool, NULL, NGX_HTTP_NO_CONTENT, NULL, 0);
      nuspool = get_spool(spool->spooler, &oldest_msg_id);
      if(spool != nuspool) {
        spool_transfer_subscribers(spool, nuspool, 1);
        destroy_spool(spool);
      }
      else {
        ERR("Unexpected spool == nuspool during spool fetch_msg_callback. This is weird, please report this to the developers. findmsg_status: %i", findmsg_status);
        assert(0);
      }
      break;
    
    case MSG_PENDING:
      ERR("spool %p set status to MSG_PENDING", spool);
      break;
      
    default:
      assert(0);
      break;
  }
  
  if(free_msg_id) {
    nchan_free_msg_id(&data->msgid);
  }
  ngx_free(data);
  return NGX_OK;
}
Beispiel #12
0
static ngx_int_t spool_nextmsg(subscriber_pool_t *spool, nchan_msg_id_t *new_last_id) {
  subscriber_pool_t      *newspool;
  channel_spooler_t      *spl = spool->spooler;
  
  ngx_int_t               immortal_spool = spool->id.time == NCHAN_NEWEST_MSGID_TIME;
  int16_t                 largetags[NCHAN_MULTITAG_MAX];
  nchan_msg_id_t          new_id = NCHAN_ZERO_MSGID;
  
  nchan_copy_msg_id(&new_id, &spool->id, largetags);
  nchan_update_multi_msgid(&new_id, new_last_id, largetags);
  
  //ERR("spool %p nextmsg (%V) --", spool, msgid_to_str(&spool->id));
  //ERR(" --  update with               (%V) --", msgid_to_str(new_last_id));
  //ERR(" -- newid                       %V", msgid_to_str(&new_id));
  
  if(msg_ids_equal(&spool->id, &new_id)) {
    ERR("nextmsg id same as curmsg (%V)", msgid_to_str(&spool->id));
    assert(0);
  }
  else {
    newspool = !immortal_spool ? find_spool(spl, &new_id) : get_spool(spl, &new_id);
    
    if(newspool != NULL) {
      assert(spool != newspool);
      spool_transfer_subscribers(spool, newspool, 0);
      if(!immortal_spool) destroy_spool(spool);
    }
    else {
      ngx_rbtree_node_t       *node;
      assert(!immortal_spool);
      node = rbtree_node_from_data(spool);
      rbtree_remove_node(&spl->spoolseed, node);
      nchan_copy_msg_id(&spool->id, &new_id, NULL);
      rbtree_insert_node(&spl->spoolseed, node);
      spool->msg_status = MSG_INVALID;
      spool->msg = NULL;
      newspool = spool;
      
      /*
      newspool = get_spool(spl, &new_id);
      assert(spool != newspool);
      spool_transfer_subscribers(spool, newspool, 0);
      destroy_spool(spool);
      */
    }

    
    if(newspool->non_internal_sub_count > 0 && spl->handlers->bulk_post_subscribe != NULL) {
      spl->handlers->bulk_post_subscribe(spl, newspool->non_internal_sub_count, spl->handlers_privdata);
    }
    
    if(newspool->sub_count > 0) {
      switch(newspool->msg_status) {
        case MSG_CHANNEL_NOTREADY:
          newspool->msg_status = MSG_INVALID;
        case MSG_INVALID:
          spool_fetch_msg(newspool);
          break;
        case MSG_EXPECTED:
          spool_respond_general(newspool, NULL, NGX_HTTP_NO_CONTENT, NULL, 0);
          break;
        default:
          break;
      }
    }
  }
  
  return NGX_OK;
}
ngx_buf_t *nchan_channel_info_buf(ngx_str_t *accept_header, ngx_uint_t messages, ngx_uint_t subscribers, time_t last_seen, nchan_msg_id_t *last_msgid, ngx_str_t **generated_content_type) {
  ngx_buf_t                      *b = &channel_info_buf;
  ngx_uint_t                      len;
  const ngx_str_t                *format = &NCHAN_CHANNEL_INFO_PLAIN;
  time_t                          time_elapsed = ngx_time() - last_seen;
  static nchan_msg_id_t           zero_msgid = NCHAN_ZERO_MSGID;
  if(!last_msgid) {
    last_msgid = &zero_msgid;
  }
 
  ngx_memcpy(&channel_info_content_type, &NCHAN_CONTENT_TYPE_TEXT_PLAIN, sizeof(NCHAN_CONTENT_TYPE_TEXT_PLAIN));
  
  b->start = channel_info_buf_str;
  b->pos = b->start;
  b->last_buf = 1;
  b->last_in_chain = 1;
  b->flush = 1;
  b->memory = 1;
  
  if(accept_header) {
    //lame content-negotiation (without regard for qvalues)
    u_char                    *accept = accept_header->data;
    len = accept_header->len;
    size_t                     rem;
    u_char                    *cur = accept;
    u_char                    *priority=&accept[len-1];
    
    for(rem=len; (cur = ngx_strnstr(cur, "text/", rem))!=NULL; cur += sizeof("text/")-1) {
      rem=len - ((size_t)(cur-accept)+sizeof("text/")-1);
      if(ngx_strncmp(cur+sizeof("text/")-1, "plain", rem<5 ? rem : 5)==0) {
        if(priority) {
          format = &NCHAN_CHANNEL_INFO_PLAIN;
          priority = cur+sizeof("text/")-1;
          //content-type is already set by default
        }
      }
      nchan_match_channel_info_subtype(sizeof("text/")-1, cur, rem, &priority, &format, &channel_info_content_type);
    }
    cur = accept;
    for(rem=len; (cur = ngx_strnstr(cur, "application/", rem))!=NULL; cur += sizeof("application/")-1) {
      rem=len - ((size_t)(cur-accept)+sizeof("application/")-1);
      nchan_match_channel_info_subtype(sizeof("application/")-1, cur, rem, &priority, &format, &channel_info_content_type);
    }
  }
  
  if(generated_content_type) {
    *generated_content_type = &channel_info_content_type;
  }
  
  len = format->len - 8 - 1 + 3*NGX_INT_T_LEN; //minus 8 sprintf
  
  if(len > 512) {
    ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "NCHAN: Channel info string too long: max: 512, is: %i", len);
    len = 512;
  }
  
  b->last = ngx_sprintf(b->start, (char *)format->data, messages, last_seen==0 ? -1 : (ngx_int_t) time_elapsed, subscribers, msgid_to_str(last_msgid));
  b->end = b->last;
  
  return b;
}