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;
}
Esempio n. 2
0
static void websocket_perform_handshake(full_subscriber_t *fsub) {
  static ngx_str_t    magic = ngx_string("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
  ngx_str_t           ws_accept_key, sha1_str;
  u_char              buf_sha1[21];
  u_char              buf[255];
  ngx_str_t          *tmp, *ws_key;
  ngx_int_t           ws_version;
  ngx_http_request_t *r = fsub->sub.request;
  
  ngx_sha1_t          sha1;
  
  ws_accept_key.data = buf;
  
  r->headers_out.content_length_n = 0;
  r->header_only = 1;  
  
  if((tmp = nchan_get_header_value(r, NCHAN_HEADER_SEC_WEBSOCKET_VERSION)) == NULL) {
    r->headers_out.status = NGX_HTTP_BAD_REQUEST;
    fsub->sub.dequeue_after_response=1;
  }
  else {
    ws_version=ngx_atoi(tmp->data, tmp->len);
    if(ws_version != 13) {
      r->headers_out.status = NGX_HTTP_BAD_REQUEST;
      fsub->sub.dequeue_after_response=1;
    }
  }
  
  if((ws_key = nchan_get_header_value(r, NCHAN_HEADER_SEC_WEBSOCKET_KEY)) == NULL) {
    r->headers_out.status = NGX_HTTP_BAD_REQUEST;
    fsub->sub.dequeue_after_response=1;
  }
  
  if(r->headers_out.status != NGX_HTTP_BAD_REQUEST) {
    //generate accept key
    ngx_sha1_init(&sha1);
    ngx_sha1_update(&sha1, ws_key->data, ws_key->len);
    ngx_sha1_update(&sha1, magic.data, magic.len);
    ngx_sha1_final(buf_sha1, &sha1);
    sha1_str.len=20;
    sha1_str.data=buf_sha1;
    
    ws_accept_key.len=ngx_base64_encoded_length(sha1_str.len);
    assert(ws_accept_key.len < 255);
    ngx_encode_base64(&ws_accept_key, &sha1_str);
    
    nchan_include_access_control_if_needed(r, fsub->ctx);
    nchan_add_response_header(r, &NCHAN_HEADER_SEC_WEBSOCKET_ACCEPT, &ws_accept_key);
    nchan_add_response_header(r, &NCHAN_HEADER_UPGRADE, &NCHAN_WEBSOCKET);
#if nginx_version < 1003013
    nchan_add_response_header(r, &NCHAN_HEADER_CONNECTION, &NCHAN_UPGRADE);
#endif
    r->headers_out.status_line = NCHAN_HTTP_STATUS_101;
    r->headers_out.status = NGX_HTTP_SWITCHING_PROTOCOLS;
    
    r->keepalive=0; //apparently, websocket must not use keepalive.
  }
  
  ngx_http_send_header(r);
}
Esempio n. 3
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. 4
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. 5
0
void nchan_include_access_control_if_needed(ngx_http_request_t *r, nchan_request_ctx_t *ctx) {
  ngx_str_t          *origin;
  ngx_str_t          *allow_origin_val;
  if(!ctx) {
    ctx = ngx_http_get_module_ctx(r, ngx_nchan_module);
  }
  if(!ctx) {
    return;
  }
  
  if((origin = nchan_get_header_value_origin(r, ctx)) != NULL) {
    allow_origin_val = nchan_get_allow_origin_value(r, NULL, ctx);
    if(allow_origin_val && allow_origin_val->len == 1 && allow_origin_val->data[0]=='*') {
      nchan_add_response_header(r, &NCHAN_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, allow_origin_val);
    }
    else { //otherwise echo the origin
      nchan_add_response_header(r, &NCHAN_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, origin);
    }
  }
}
void nchan_include_access_control_if_needed(ngx_http_request_t *r, nchan_request_ctx_t *ctx) {
    if(!ctx) {
        ctx = ngx_http_get_module_ctx(r, ngx_nchan_module);
    }
    nchan_loc_conf_t       *cf;
    if(!ctx) {
        return;
    }
    if(ctx->request_origin_header.data) {
        cf = ngx_http_get_module_loc_conf(r, ngx_nchan_module);
        nchan_add_response_header(r, &NCHAN_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, &cf->allow_origin);
    }
}
Esempio n. 7
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. 8
0
static void chunked_ensure_headers_sent(full_subscriber_t *fsub) {
  static ngx_str_t         transfer_encoding_header = ngx_string("Transfer-Encoding");
  static ngx_str_t         transfer_encoding = ngx_string("chunked");
  
  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);
  if(!fsub->data.shook_hands) {
  
    clcf->chunked_transfer_encoding = 0;
    
    //r->headers_out.content_type.len = content_type.len;
    //r->headers_out.content_type.data = content_type.data;
    
    nchan_add_response_header(r, &transfer_encoding_header, &transfer_encoding);
    
    nchan_cleverly_output_headers_only_for_later_response(r);
    
    fsub->data.shook_hands = 1; 
  }
}
Esempio n. 9
0
static void es_ensure_headers_sent(full_subscriber_t *fsub) {
  static const ngx_str_t   content_type = ngx_string("text/event-stream; charset=utf-8");
  static const ngx_str_t   hello = ngx_string(": hi\n\n");
  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_buf_and_chain_t           bc;
  nchan_request_ctx_t            *ctx = ngx_http_get_module_ctx(r, nchan_module);
  nchan_loc_conf_t               *cf;
  
  if(!fsub->data.shook_hands) {
  
    clcf->chunked_transfer_encoding = 0;
    
    r->headers_out.content_type.len = content_type.len;
    r->headers_out.content_type.data = content_type.data;
    r->headers_out.content_length_n = -1;
    //send headers
    
    if(ctx->request_origin_header.len > 0) {
      cf = ngx_http_get_module_loc_conf(r, nchan_module);
      nchan_add_response_header(r, &NCHAN_HEADER_ALLOW_ORIGIN, &cf->allow_origin);
    }
    
    nchan_cleverly_output_headers_only_for_later_response(r);

    //send a ":hi" comment
    ngx_init_set_membuf(&bc.buf, hello.data, hello.data + hello.len);
    bc.chain.buf = &bc.buf;
    
    bc.buf.last_buf = 0;
    bc.buf.flush = 1;

    bc.chain.next = NULL;
    nchan_output_filter(fsub->sub.request, &bc.chain);
    
    fsub->data.shook_hands = 1; 
  }
}
Esempio n. 10
0
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;
}
Esempio n. 11
0
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;
}
Esempio n. 12
0
File: common.c Progetto: slact/nchan
static ngx_int_t subscriber_authorize_callback(ngx_int_t rc, ngx_http_request_t *sr, void *data) {
  nchan_subscribe_auth_request_data_t *d = data;
  subscriber_t                        *sub = d->sub;
  
  if(sub->status == DEAD) {
    nchan_requestmachine_request_cleanup_manual(d->subrequest);
    sub->fn->release(d->sub, 0);
  }
  else if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST) {
    //this shouldn't happen, but if it does, no big deal
    nchan_requestmachine_request_cleanup_manual(d->subrequest);
    sub->fn->release(d->sub, 1);
    sub->fn->respond_status(sub, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL, NULL); //couldn't reach upstream
  }
  else if(rc == NGX_OK) {
    ngx_int_t        code = sr->headers_out.status;
    sub->fn->release(sub, 1);
    if(code >= 200 && code <299) {
      nchan_requestmachine_request_cleanup_manual(d->subrequest);
      nchan_subscriber_subscribe(sub, d->ch_id);
    }
    else {
      //forbidden, but with some data to forward to the subscriber
      ngx_http_request_t       *r = d->sub->request;
      ngx_str_t                *content_type;
      ngx_int_t                 content_length;
      ngx_chain_t              *request_chain = NULL;
      content_type = (sr->upstream->headers_in.content_type ? &sr->upstream->headers_in.content_type->value : NULL);
      content_length = nchan_subrequest_content_length(sr);
      if(content_length > 0) {
#if nginx_version >= 1013010
        request_chain = sr->out;
#else
        request_chain = sr->upstream->out_bufs;
#endif
      }
      //copy headers
      ngx_uint_t                       i;
      ngx_list_part_t                 *part = &sr->headers_out.headers.part;
      ngx_table_elt_t                 *header= part->elts;
      for (i = 0; /* void */ ; i++) {
        if (i >= part->nelts) {
          if (part->next == NULL) {
            break;
          }
          part = part->next;
          header = part->elts;
          i = 0;
        }
        if (!nchan_strmatch(&header[i].key, 4, "Content-Type", "Server", "Content-Length", "Connection")) {
          //copy header to main request's response
          nchan_add_response_header(r, &header[i].key, &header[i].value);
        }
      }
      
      if(content_type) {
        r->headers_out.content_type = *content_type;
      }
      r->headers_out.content_length_n = content_length;
      nchan_requestmachine_request_cleanup_on_request_finalize(d->subrequest, r);
      sub->fn->respond_status(sub, code, NULL, request_chain); //auto-closes subscriber
    }
  }
  else if(rc >= 500 && rc < 600) {
    nchan_requestmachine_request_cleanup_manual(d->subrequest);
    sub->fn->release(d->sub, 1);
    sub->fn->respond_status(sub, rc, NULL, NULL); //auto-closes subscriber
  }
  else {
    nchan_requestmachine_request_cleanup_manual(d->subrequest);
    sub->fn->release(d->sub, 1);
    d->sub->fn->respond_status(d->sub, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL, NULL); //auto-closes subscriber
  }
  
  return NGX_OK;
}