Esempio n. 1
0
static ngx_int_t subscribe_intervalpoll_callback(nchan_msg_status_t msg_search_outcome, nchan_msg_t *msg, ngx_http_request_t *r) {
  //inefficient, but close enough for now
  ngx_str_t               *etag;
  char                    *err;
  switch(msg_search_outcome) {
    case MSG_EXPECTED:
      //interval-polling subscriber requests get a 304 with their entity tags preserved.
      if (r->headers_in.if_modified_since != NULL) {
        r->headers_out.last_modified_time=ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len);
      }
      if ((etag=nchan_subscriber_get_etag(r)) != NULL) {
        nchan_add_response_header(r, &NCHAN_HEADER_ETAG, etag);
      }
      nchan_respond_status(r, NGX_HTTP_NOT_MODIFIED, NULL, 1);
      break;
      
    case MSG_FOUND:
      if(nchan_respond_msg(r, msg, NULL, 1, &err) != NGX_OK) {
        nchan_respond_cstring(r, NGX_HTTP_INTERNAL_SERVER_ERROR, &TEXT_PLAIN, err, 1);
      }
      break;
      
    case MSG_NOTFOUND:
    case MSG_EXPIRED:
      nchan_respond_status(r, NGX_HTTP_NOT_FOUND, NULL, 1);
      break;
      
    default:
      nchan_respond_status(r, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL, 1);
      return NGX_ERROR;
  }
  return NGX_DONE;
}
Esempio n. 2
0
static ngx_int_t nchan_response_channel_ptr_info(nchan_channel_t *channel, ngx_http_request_t *r, ngx_int_t status_code) {
  static const ngx_str_t CREATED_LINE = ngx_string("201 Created");
  static const ngx_str_t ACCEPTED_LINE = ngx_string("202 Accepted");
  
  time_t             last_seen = 0;
  ngx_uint_t         subscribers = 0;
  ngx_uint_t         messages = 0;
  if(channel!=NULL) {
    subscribers = channel->subscribers;
    last_seen = channel->last_seen;
    messages  = channel->messages;
    r->headers_out.status = status_code == (ngx_int_t) NULL ? NGX_HTTP_OK : status_code;
    if (status_code == NGX_HTTP_CREATED) {
      ngx_memcpy(&r->headers_out.status_line, &CREATED_LINE, sizeof(ngx_str_t));
    }
    else if (status_code == NGX_HTTP_ACCEPTED) {
      ngx_memcpy(&r->headers_out.status_line, &ACCEPTED_LINE, sizeof(ngx_str_t));
    }
    nchan_channel_info(r, messages, subscribers, last_seen);
  }
  else {
    //404!
    nchan_respond_status(r, NGX_HTTP_NOT_FOUND, NULL, 0);
  }
  return NGX_OK;
}
Esempio n. 3
0
ngx_int_t subscriber_respond_unqueued_status(full_subscriber_t *fsub, ngx_int_t status_code, const ngx_str_t *status_line) {
  ngx_http_request_t     *r = fsub->sub.request;
  fsub->data.cln->handler = (ngx_http_cleanup_pt )empty_handler;
  fsub->data.finalize_request = 0;
  fsub->sub.status = DEAD;
  fsub->sub.fn->dequeue(&fsub->sub);
  return nchan_respond_status(r, status_code, status_line, 1);
}
Esempio n. 4
0
ngx_str_t *nchan_get_channel_id(ngx_http_request_t *r, pub_or_sub_t what, ngx_int_t fail_hard) {
  static const ngx_str_t          NO_CHANNEL_ID_MESSAGE = ngx_string("No channel id provided.");
  nchan_loc_conf_t               *cf = ngx_http_get_module_loc_conf(r, nchan_module);
  ngx_int_t                       rc;
  ngx_str_t                      *id;
  nchan_chid_loc_conf_t          *chid_conf;
  
  chid_conf = what == PUB ? &cf->pub_chid : &cf->sub_chid;
  if(chid_conf->n == 0) {
    chid_conf = &cf->pubsub_chid;
  }
  
  if(chid_conf->n > 0) {
    rc = nchan_process_multi_channel_id(r, chid_conf, cf, &id);
  }
  else {
    //fallback to legacy $push_channel_id
    rc = nchan_process_legacy_channel_id(r, cf, &id);
  }
  
  if(id == NULL && fail_hard) {
    assert(rc != NGX_OK);
    switch(rc) {
      case NGX_ERROR:
        nchan_respond_status(r, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL, 0);
        break;
      
      case NGX_DECLINED:
        nchan_respond_status(r, NGX_HTTP_FORBIDDEN, NULL, 0);
        break;
      
      case NGX_ABORT:
        nchan_respond_string(r, NGX_HTTP_NOT_FOUND, &TEXT_PLAIN, &NO_CHANNEL_ID_MESSAGE, 0);
        break;
    }
    DBG("%s channel id NULL", what == PUB ? "pub" : "sub");
  }
  else {
    DBG("%s channel id %V", what == PUB ? "pub" : "sub", id);
  }
  
  return id;
}
Esempio n. 5
0
static void nchan_publisher_body_handler_continued(ngx_http_request_t *r, ngx_str_t *channel_id, nchan_loc_conf_t *cf) {
  ngx_http_complex_value_t       *publisher_upstream_request_url_ccv;
  static ngx_str_t                POST_REQUEST_STRING = {4, (u_char *)"POST "};
  
  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:
      publisher_upstream_request_url_ccv = cf->publisher_upstream_request_url;
      if(publisher_upstream_request_url_ccv == NULL) {
        ngx_str_t    *content_type = (r->headers_in.content_type ? &r->headers_in.content_type->value : NULL);
        ngx_int_t     content_length = r->headers_in.content_length_n > 0 ? r->headers_in.content_length_n : 0;
        
        nchan_publisher_post_request(r, content_type, content_length, r->request_body->bufs, channel_id, cf);
      }
      else {
        nchan_pub_upstream_stuff_t    *psr_stuff;
        
        if((psr_stuff = ngx_palloc(r->pool, sizeof(*psr_stuff))) == NULL) {
          ERR("can't allocate memory for publisher auth subrequest");
          ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
          return;
        }
        
        ngx_http_post_subrequest_t    *psr = &psr_stuff->psr;
        nchan_pub_upstream_data_t     *psrd = &psr_stuff->psr_data;
        ngx_http_request_t            *sr;
        ngx_str_t                      publisher_upstream_request_url;
        
        ngx_http_complex_value(r, publisher_upstream_request_url_ccv, &publisher_upstream_request_url);
        
        psr->handler = nchan_publisher_upstream_handler;
        psr->data = psrd;
        
        psrd->ch_id = channel_id;
        
        ngx_http_subrequest(r, &publisher_upstream_request_url, NULL, &sr, psr, NGX_HTTP_SUBREQUEST_IN_MEMORY);
        nchan_adjust_subrequest(sr, NGX_HTTP_POST, &POST_REQUEST_STRING, r->request_body, r->headers_in.content_length_n, NULL);
      }
      break;
      
    case NGX_HTTP_DELETE:
      cf->storage_engine->delete_channel(channel_id, (callback_pt) &channel_info_callback, (void *)r);
      nchan_maybe_send_channel_event_message(r, CHAN_DELETE);
      break;
      
    default: 
      nchan_respond_status(r, NGX_HTTP_FORBIDDEN, NULL, 0);
  }
  
}
Esempio n. 6
0
ngx_int_t nchan_OPTIONS_respond(ngx_http_request_t *r, const ngx_str_t *allowed_headers, const ngx_str_t *allowed_methods) {
  nchan_request_ctx_t      *ctx = ngx_http_get_module_ctx(r, ngx_nchan_module);
  
  nchan_add_response_header(r, &NCHAN_HEADER_ALLOW, allowed_methods);
  
  if(ctx && nchan_get_header_value_origin(r, ctx)) {
    //Access-Control-Allow-Origin is included later
    nchan_add_response_header(r, &NCHAN_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, allowed_headers);
    nchan_add_response_header(r, &NCHAN_HEADER_ACCESS_CONTROL_ALLOW_METHODS, allowed_methods);
  }
  return nchan_respond_status(r, NGX_HTTP_OK, NULL, NULL, 0);
}
Esempio n. 7
0
static ngx_int_t multipart_respond_status(subscriber_t *sub, ngx_int_t status_code, const ngx_str_t *status_line){
  nchan_buf_and_chain_t    *bc;
  static u_char            *end_boundary=(u_char *)"--\r\n";
  full_subscriber_t        *fsub = (full_subscriber_t  *)sub;
  //nchan_request_ctx_t      *ctx = ngx_http_get_module_ctx(fsub->sub.request, nchan_module);
  
  if(status_code == NGX_HTTP_NO_CONTENT || (status_code == NGX_HTTP_NOT_MODIFIED && !status_line)) {
    //ignore
    return NGX_OK;
  }
  
  if(fsub->data.shook_hands == 0 && status_code >= 400 && status_code <600) {
    nchan_respond_status(sub->request, status_code, status_line, 1);
    return NGX_OK;
  }
  
  multipart_ensure_headers_sent(fsub);
  
  if((bc = nchan_bufchain_pool_reserve(fsub_bcp(fsub), 1)) == NULL) {
    nchan_respond_status(sub->request, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL, 1);
    return NGX_ERROR;
  }
  
  ngx_memzero(&bc->buf, sizeof(ngx_buf_t));
  bc->buf.memory = 1;
  bc->buf.last_buf = 1;
  bc->buf.last_in_chain = 1;
  bc->buf.flush = 1;
  bc->buf.start = end_boundary;
  bc->buf.pos = end_boundary;
  bc->buf.end = end_boundary + 4;
  bc->buf.last = bc->buf.end;
  
  nchan_output_filter(fsub->sub.request, &bc->chain);
  
  subscriber_maybe_dequeue_after_status_response(fsub, status_code);

  return NGX_OK;
}
Esempio n. 8
0
static ngx_int_t chunked_respond_status(subscriber_t *sub, ngx_int_t status_code, const ngx_str_t *status_line){
  nchan_buf_and_chain_t     bc;
  ngx_chain_t              *chain = NULL;
  
  full_subscriber_t        *fsub = (full_subscriber_t  *)sub;
  
  if(chain == NULL) {
    bc.chain.buf=&bc.buf;
    bc.chain.next=NULL;
    ngx_memzero(&bc.buf, sizeof(ngx_buf_t));
    bc.buf.last_buf = 1;
    bc.buf.last_in_chain = 1;
    bc.buf.flush = 1;
    bc.buf.memory = 1;
    chain = &bc.chain;
  }
  
  bc.buf.start = (u_char *)"0\r\n\r\n";
  bc.buf.end = bc.buf.start + 5;
  bc.buf.pos = bc.buf.start;
  bc.buf.last = bc.buf.end;
  
  if(status_code == NGX_HTTP_NO_CONTENT || status_code == NGX_HTTP_NOT_MODIFIED) {
    //ignore
    return NGX_OK;
  }
  
  if(fsub->data.shook_hands == 0 && status_code >= 400 && status_code <600) {
    nchan_respond_status(sub->request, status_code, status_line, 1);
    return NGX_OK;
  }
  
  chunked_ensure_headers_sent(fsub);
  
  nchan_output_filter(fsub->sub.request, chain);
  
  if(status_code >=400 && status_code <599) {
    fsub->data.cln->handler = (ngx_http_cleanup_pt )empty_handler;
    fsub->sub.request->keepalive=0;
    fsub->data.finalize_request=1;
    sub->fn->dequeue(sub);
  }

  return NGX_OK;
}
Esempio n. 9
0
static ngx_int_t rawstream_respond_status(subscriber_t *sub, ngx_int_t status_code, const ngx_str_t *status_line){
  full_subscriber_t        *fsub = (full_subscriber_t  *)sub;
  //nchan_request_ctx_t      *ctx = ngx_http_get_module_ctx(fsub->sub.request, ngx_nchan_module);
  
  if(status_code == NGX_HTTP_NO_CONTENT || (status_code == NGX_HTTP_NOT_MODIFIED && !status_line)) {
    //ignore
    return NGX_OK;
  }
  
  if(fsub->data.shook_hands == 0 && status_code >= 400 && status_code <600) {
    nchan_respond_status(sub->request, status_code, status_line, 1);
    return NGX_OK;
  }
  
  subscriber_maybe_dequeue_after_status_response(fsub, status_code);

  return NGX_OK;
}
Esempio n. 10
0
ngx_int_t nchan_respond_sprintf(ngx_http_request_t *r, ngx_int_t status_code, const ngx_str_t *content_type, const ngx_int_t finalize, char *fmt, ...) {
  u_char *p;
  ngx_str_t str;
  va_list   args;
    
  str.len = 1024;
  str.data = ngx_palloc(r->pool, 1024);
  if(str.data == NULL) {
    return nchan_respond_status(r, status_code, NULL, NULL, finalize);
    return NGX_ERROR;
  }

  va_start(args, fmt);
  p = ngx_vslprintf(str.data, str.data + str.len, fmt, args);
  va_end(args);
  
  str.len = p - str.data;
  return nchan_respond_string(r, status_code, content_type, &str, finalize);
}
Esempio n. 11
0
static ngx_int_t longpoll_respond_status(subscriber_t *self, ngx_int_t status_code, const ngx_str_t *status_line) {
  
  full_subscriber_t     *fsub = (full_subscriber_t *)self;
  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;
  
  if(fsub->data.act_as_intervalpoll) {
    if(status_code == NGX_HTTP_NO_CONTENT || status_code == NGX_HTTP_NOT_MODIFIED || status_code == NGX_HTTP_NOT_FOUND ) {
      status_code = NGX_HTTP_NOT_MODIFIED;
    }
  }
  else if(status_code == NGX_HTTP_NO_CONTENT || status_code == NGX_HTTP_NOT_MODIFIED) {
    if(cf->longpoll_multimsg) {
      if(fsub->data.multimsg_first != NULL) {
        longpoll_multimsg_respond(fsub);
        dequeue_maybe(self);
      }
      return NGX_OK;
    }
    else { 
      //don't care, ignore
      return NGX_OK;
    }
  }
  
  DBG("%p respond req %p status %i", self, r, status_code);
  
  nchan_set_msgid_http_response_headers(r, &self->last_msgid);
  
  //disable abort handler
  fsub->data.cln->handler = empty_handler;
  
  if(ctx->request_origin_header.len > 0) {
    nchan_add_response_header(r, &NCHAN_HEADER_ALLOW_ORIGIN, &cf->allow_origin);
  }
  
  nchan_respond_status(r, status_code, status_line, 0);

  dequeue_maybe(self);
  return NGX_OK;
}
Esempio n. 12
0
static ngx_int_t longpoll_respond_status(subscriber_t *self, ngx_int_t status_code, const ngx_str_t *status_line) {
  
  full_subscriber_t     *fsub = (full_subscriber_t *)self;
  ngx_http_request_t    *r = fsub->sub.request;
  nchan_loc_conf_t      *cf = fsub->sub.cf;
  
  //DBG("%p got status %i", self, status_code);
  
  if(fsub->data.act_as_intervalpoll) {
    if(status_code == NGX_HTTP_NO_CONTENT || status_code == NGX_HTTP_NOT_MODIFIED || status_code == NGX_HTTP_NOT_FOUND ) {
      status_code = NGX_HTTP_NOT_MODIFIED;
    }
  }
  else if(status_code == NGX_HTTP_NO_CONTENT || (status_code == NGX_HTTP_NOT_MODIFIED && !status_line)) {
    if(cf->longpoll_multimsg) {
      if(fsub->data.multimsg_first != NULL) {
        longpoll_multipart_respond(fsub);
        dequeue_maybe(self);
      }
      return NGX_OK;
    }
    else { 
      //don't care, ignore
      return NGX_OK;
    }
  }
  
  DBG("%p respond req %p status %i", self, r, status_code);
  
  fsub->sub.dequeue_after_response = 1;
  
  nchan_set_msgid_http_response_headers(r, NULL, &self->last_msgid);
  
  //disable abort handler
  fsub->data.cln->handler = empty_handler;
  
  nchan_respond_status(r, status_code, status_line, 0);

  dequeue_maybe(self);
  return NGX_OK;
}
Esempio n. 13
0
static ngx_int_t chunked_respond_status(subscriber_t *sub, ngx_int_t status_code, const ngx_str_t *status_line){
  nchan_buf_and_chain_t     bc;
  ngx_chain_t              *chain = NULL;
  
  full_subscriber_t        *fsub = (full_subscriber_t  *)sub;
  
  if(chain == NULL) {
    bc.chain.buf=&bc.buf;
    bc.chain.next=NULL;
    ngx_memzero(&bc.buf, sizeof(ngx_buf_t));
    bc.buf.last_buf = 1;
    bc.buf.last_in_chain = 1;
    bc.buf.flush = 1;
    bc.buf.memory = 1;
    chain = &bc.chain;
  }
  
  bc.buf.start = (u_char *)"0\r\n\r\n";
  bc.buf.end = bc.buf.start + 5;
  bc.buf.pos = bc.buf.start;
  bc.buf.last = bc.buf.end;
  
  if(status_code == NGX_HTTP_NO_CONTENT || (status_code == NGX_HTTP_NOT_MODIFIED && !status_line)) {
    //ignore
    return NGX_OK;
  }
  
  if(fsub->data.shook_hands == 0 && status_code >= 400 && status_code < 600) {
    nchan_respond_status(sub->request, status_code, status_line, 1);
    return NGX_OK;
  }
  
  chunked_ensure_headers_sent(fsub);
  
  nchan_output_filter(fsub->sub.request, chain);
  
  subscriber_maybe_dequeue_after_status_response(fsub, status_code);

  return NGX_OK;
}
Esempio n. 14
0
static ngx_int_t es_respond_status(subscriber_t *sub, ngx_int_t status_code, const ngx_str_t *status_line){
  
  static ngx_str_t          empty_line = ngx_string("");
  full_subscriber_t        *fsub = (full_subscriber_t  *)sub;
  u_char                    resp_buf[256];
  nchan_buf_and_chain_t     bc;
  
  if(status_code == NGX_HTTP_NO_CONTENT || status_code == NGX_HTTP_NOT_MODIFIED) {
    //ignore
    return NGX_OK;
  }
  
  if(fsub->data.shook_hands == 0 && status_code >= 400 && status_code <600) {
    nchan_respond_status(sub->request, status_code, status_line, 1);
    return NGX_OK;
  }
  
  es_ensure_headers_sent(fsub);
  
  DBG("%p output status to subscriber", sub);
  
  bc.chain.buf = &bc.buf;
  bc.chain.next = NULL;
  ngx_init_set_membuf(&bc.buf, resp_buf, ngx_snprintf(resp_buf, 256, ":%i: %V\n", status_code, status_line ? status_line : &empty_line));
  bc.buf.flush = 1;
  bc.buf.last_buf = 1;
  
  nchan_output_filter(fsub->sub.request, &bc.chain);
  
  if(status_code >=400 && status_code <599) {
    fsub->data.cln->handler = (ngx_http_cleanup_pt )empty_handler;
    fsub->sub.request->keepalive=0;
    fsub->data.finalize_request=1;
    sub->fn->dequeue(sub);
  }

  return NGX_OK;
}
Esempio n. 15
0
static void multipart_ensure_headers_sent(full_subscriber_t *fsub) {
  nchan_buf_and_chain_t          *bc;
  
  ngx_http_request_t             *r = fsub->sub.request;
  ngx_http_core_loc_conf_t       *clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
  nchan_request_ctx_t            *ctx = ngx_http_get_module_ctx(r, nchan_module);
  multipart_privdata_t           *mpd = (multipart_privdata_t *)fsub->privdata;
  
  if(!fsub->data.shook_hands) {
    clcf->chunked_transfer_encoding = 0;
    nchan_request_set_content_type_multipart_boundary_header(r, ctx);
    
    nchan_cleverly_output_headers_only_for_later_response(r);
    
    //set preamble in the request ctx. it would be nicer to store in in the subscriber data, 
    //but that would mean not reusing longpoll's fsub directly
    
    if((bc = nchan_bufchain_pool_reserve(ctx->bcp, 1)) == NULL) {
      ERR("can't reserve bufchain for multipart headers");
      nchan_respond_status(fsub->sub.request, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL, 1);
      return;
    }
    
    ngx_memzero(&bc->buf, sizeof(ngx_buf_t));
    bc->buf.start = mpd->boundary + 2;
    bc->buf.pos = bc->buf.start;
    bc->buf.end = mpd->boundary_end;
    bc->buf.last = bc->buf.end;
    bc->buf.memory = 1;
    bc->buf.last_buf = 0;
    bc->buf.last_in_chain = 1;
    bc->buf.flush = 1;
    
    nchan_output_filter(r, &bc->chain);
    
    fsub->data.shook_hands = 1; 
  }
}
Esempio n. 16
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;
  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;
  
}
Esempio n. 17
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;
}
Esempio n. 18
0
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;
  
}
Esempio n. 19
0
ngx_int_t nchan_group_handler(ngx_http_request_t *r) {
  nchan_loc_conf_t       *cf = ngx_http_get_module_loc_conf(r, ngx_nchan_module);
  nchan_request_ctx_t    *ctx;
  ngx_int_t               rc = NGX_DONE;
  ngx_str_t              *group;
  
  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(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(!cf->group.enable_accounting) {
    nchan_respond_cstring(r, NGX_HTTP_FORBIDDEN, &NCHAN_CONTENT_TYPE_TEXT_PLAIN, "Channel group accounting is disabled.", 0);
    return NGX_OK;
  }
  
  group = nchan_get_group_name(r, cf, ctx);
  if(group == NULL) {
    nchan_respond_cstring(r, NGX_HTTP_BAD_REQUEST, &NCHAN_CONTENT_TYPE_TEXT_PLAIN, "No group specified", 0);
    return NGX_OK;
  }
  
  switch(r->method) {
    case NGX_HTTP_GET:
      if(!cf->group.get) {
        rc = nchan_respond_status(r, NGX_HTTP_FORBIDDEN, NULL, 0);
      }
      r->main->count++;
      cf->storage_engine->get_group(group, cf, (callback_pt )group_handler_callback, r);
      
      break;
      
    case NGX_HTTP_POST:
      if(!cf->group.set) {
        rc = nchan_respond_status(r, NGX_HTTP_FORBIDDEN, NULL, 0);
      }
      
      nchan_group_limits_t     limits;
      
      if(parse_group_limits(r, cf, &limits) != NGX_OK) {
        return NGX_OK;
      }
      
      r->main->count++;
      cf->storage_engine->set_group_limits(group, cf, &limits, (callback_pt )group_handler_callback, r);
      break;
      
    case NGX_HTTP_DELETE:
      if(!cf->group.delete) {
        rc = nchan_respond_status(r, NGX_HTTP_FORBIDDEN, NULL, 0);
      }
      r->main->count++;
      cf->storage_engine->delete_group(group, cf, (callback_pt )group_handler_callback, r);
      break;
      
    case NGX_HTTP_OPTIONS:
        rc= nchan_OPTIONS_respond(r, &NCHAN_ACCESS_CONTROL_ALLOWED_GROUP_HEADERS, &NCHAN_ALLOW_GET_POST_DELETE);
      break;
  }
  ctx->request_ran_content_handler = 1;
  return rc;
}
Esempio n. 20
0
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);
  }
}