main()
{
  int rc;

  rc = ngx_http_parse_time(zero, sizeof(zero) - 1);
  printf("rc: %d\n", rc);

  rc = ngx_http_parse_time(one, sizeof(one) - 1);
  printf("rc: %d\n", rc);

  rc = ngx_http_parse_time(two, sizeof(two) - 1);
  printf("rc: %d\n", rc);

  rc = ngx_http_parse_time(thr, sizeof(thr) - 1);
  printf("rc: %d\n", rc);
}
static ngx_int_t
ngx_http_srcache_process_last_modified(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset)
{
#if 1
    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);
    if (ho == NULL) {
        return NGX_ERROR;
    }

    *ho = *h;

    r->headers_out.last_modified = ho;
#endif

    r->headers_out.last_modified_time = ngx_http_parse_time(h->value.data,
                                                            h->value.len);

    dd("setting last-modified-time: %d",
       (int) r->headers_out.last_modified_time);

    return NGX_OK;
}
Exemple #3
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;
}
static ngx_uint_t
ngx_http_test_if_modified(ngx_http_request_t *r)
{
    time_t                     ims;
    ngx_http_core_loc_conf_t  *clcf;

    if (r->headers_out.last_modified_time == (time_t) -1) {
        return 1;
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
        return 1;
    }

    ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
                              r->headers_in.if_modified_since->value.len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http ims:%T lm:%T", ims, r->headers_out.last_modified_time);

    if (ims == r->headers_out.last_modified_time) {
        return 0;
    }

    if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
        || ims < r->headers_out.last_modified_time)
    {
        return 1;
    }

    return 0;
}
static char *
ngx_http_vod_set_time_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
	char  *p = conf;

	time_t           *sp;
	ngx_str_t        *value;
	ngx_conf_post_t  *post;


	sp = (time_t *)(p + cmd->offset);
	if (*sp != NGX_CONF_UNSET) {
		return "is duplicate";
	}

	value = cf->args->elts;

	*sp = ngx_http_parse_time(value[1].data, value[1].len);
	if (*sp == (time_t)NGX_ERROR) {
		return "invalid value";
	}

	if (cmd->post) {
		post = cmd->post;
		return post->post_handler(cf, post, sp);
	}

	return NGX_CONF_OK;
}
Exemple #6
0
static ngx_int_t nchan_subscriber_get_msg_id(ngx_http_request_t *r, nchan_msg_id_t *id) {
  static ngx_str_t                last_event_id_header = ngx_string("Last-Event-ID");
  ngx_str_t                      *last_event_id;
  ngx_str_t                      *if_none_match;
  
  if((last_event_id = nchan_get_header_value(r, last_event_id_header)) != NULL) {
    u_char       *split, *last;
    ngx_int_t     time;
    //"<msg_time>:<msg_tag>"
    last = last_event_id->data + last_event_id->len;
    if((split = ngx_strlchr(last_event_id->data, last, ':')) != NULL) {
      time = ngx_atoi(last_event_id->data, split - last_event_id->data);
      split++;
      if(time != NGX_ERROR) {
        id->time = time;
        nchan_parse_msg_tag(split, last, id);
        return NGX_OK;
      }
    }
  }
  
  if_none_match = nchan_subscriber_get_etag(r);
  id->time=(r->headers_in.if_modified_since == NULL) ? 0 : ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len);
  if(if_none_match==NULL) {
    int i;
    for(i=0; i< NCHAN_MULTITAG_MAX; i++) {
      id->tag[i]=0;
    }
    id->tagcount=1;
  }
  else {
    nchan_parse_msg_tag(if_none_match->data, if_none_match->data + if_none_match->len, id);
  }
  return NGX_OK;
}
static ngx_int_t subscribe_intervalpoll_callback(ngx_http_push_msg_t *msg, ngx_int_t msg_search_outcome, ngx_http_request_t *r) {
  ngx_chain_t                *chain;
  ngx_str_t                  *content_type, *etag;
  time_t                     last_modified;
  switch(msg_search_outcome) {
    case NGX_HTTP_PUSH_MESSAGE_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=ngx_http_push_subscriber_get_etag(r)) != NULL) {
        ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ETAG, etag);
      }
      ngx_http_finalize_request(r, NGX_HTTP_NOT_MODIFIED);
      return NGX_OK;
      
    case NGX_HTTP_PUSH_MESSAGE_FOUND:
      ngx_http_push_alloc_for_subscriber_response(r->pool, 0, msg, &chain, &content_type, &etag, &last_modified);
      ngx_http_push_prepare_response_to_subscriber_request(r, chain, content_type, etag, last_modified);
      ngx_http_push_store->release_message(NULL, msg);
      ngx_http_finalize_request(r, NGX_OK);
      return NGX_OK;
      
    default:
      ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
      return NGX_ERROR;
  }
}
static ngx_int_t
ngx_http_srcache_test_not_modified(ngx_http_request_t *r)
{
    time_t                     ims;
    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
        return ngx_http_srcache_next_header_filter(r);
    }

    ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
                              r->headers_in.if_modified_since->value.len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "srcache ims:%d lm:%d", ims,
                   r->headers_out.last_modified_time);

    if (ims != r->headers_out.last_modified_time) {

        if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
            || ims < r->headers_out.last_modified_time)
        {
            return ngx_http_srcache_next_header_filter(r);
        }
    }
        
    return ngx_http_srcache_send_not_modified(r);    
}
ngx_int_t ngx_http_push_subscriber_get_msg_id(ngx_http_request_t *r, ngx_http_push_msg_id_t *id) {
  ngx_str_t                      *if_none_match = ngx_http_push_subscriber_get_etag(r);
  ngx_int_t                       tag=0;
  id->time=(r->headers_in.if_modified_since == NULL) ? 0 : ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len);
  if(if_none_match==NULL || (if_none_match!=NULL && (tag = ngx_atoi(if_none_match->data, if_none_match->len))==NGX_ERROR)) {
    tag=0;
  }
  id->tag=ngx_abs(tag);
  return NGX_OK;
}
static ngx_int_t ngx_http_set_last_modified_header(ngx_http_request_t *r,
    ngx_http_shib_request_header_val_t *hv, ngx_str_t *value)
{
    if (value->len == 0) {
        return ngx_http_clear_last_modified_header(r, hv, value);
    }

    r->headers_out.last_modified_time = ngx_http_parse_time(value->data,
                                                            value->len);

    return ngx_http_set_builtin_header(r, hv, value);
}
static ngx_int_t
ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
    ngx_str_t *value)
{
    if (ngx_http_set_response_header(r, hv, value) != NGX_OK) {
        return NGX_ERROR;
    }

    r->headers_out.last_modified_time =
        (value->len) ? ngx_http_parse_time(value->data, value->len) : -1;

    return NGX_OK;
}
static mrb_value ngx_http_mruby_parse_http_time(mrb_state *mrb, mrb_value self)
{
    mrb_value mrb_http_time;
    ngx_str_t http_time;

    mrb_get_args(mrb, "o", &mrb_http_time);
    mrb_http_time = mrb_obj_as_string(mrb, mrb_http_time);

    http_time.data = (u_char *)RSTRING_PTR(mrb_http_time);
    http_time.len  = RSTRING_LEN(mrb_http_time);

    return mrb_fixnum_value(ngx_http_parse_time(http_time.data, http_time.len));
}
nchan_msg_id_t *nchan_subscriber_get_msg_id(ngx_http_request_t *r) {
  static nchan_msg_id_t           id = NCHAN_ZERO_MSGID;
  ngx_str_t                      *if_none_match;
  nchan_loc_conf_t               *cf = ngx_http_get_module_loc_conf(r, nchan_module);
  int                             i;
  
  if(!cf->msg_in_etag_only && r->headers_in.if_modified_since != NULL) {
    id.time=ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len);
    if_none_match = nchan_subscriber_get_etag(r);
    
    if(if_none_match==NULL) {
      id.tagcount=1;
      id.tagactive=0;
    }
    else {
      nchan_parse_msg_tag(if_none_match->data, if_none_match->data + if_none_match->len, &id);
    }
    return &id;
  }
  else if(cf->msg_in_etag_only && (if_none_match = nchan_subscriber_get_etag(r)) != NULL) {
    if(nchan_parse_compound_msgid(&id, if_none_match) == NGX_OK) {
      return &id;
    }
  }
  else {
    nchan_complex_value_arr_t   *alt_msgid_cv_arr = &cf->last_message_id;
    u_char                       buf[128];
    ngx_str_t                    str;
    ngx_int_t                    rc;
    int                          n = alt_msgid_cv_arr->n;
    
    str.len = 0;
    str.data = buf;
    
    for(i=0; i < n; i++) {
      rc = ngx_http_complex_value_noalloc(r, alt_msgid_cv_arr->cv[i], &str, 128);
      if(str.len > 0 && rc == NGX_OK) {
        if(nchan_parse_compound_msgid(&id, &str) == NGX_OK) {
          return &id;
        }
      }
    }
  }
  
  //eh, we didn't find a valid alt_msgid value from variables. use the defaults
  id.time = cf->subscriber_start_at_oldest_message ? 0 : -1;
  id.tagcount=1;
  id.tagactive=0;
  id.tag.fixed[0] = 0;
  return &id;
}
static ngx_int_t
ngx_http_not_modified_header_filter(ngx_http_request_t *r)
{
    time_t                     ims;
    ngx_http_core_loc_conf_t  *clcf;

    if (r->headers_out.status != NGX_HTTP_OK
        || r != r->main
        || r->headers_in.if_modified_since == NULL
        || r->headers_out.last_modified_time == -1)
    {
        return ngx_http_next_header_filter(r);
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
        return ngx_http_next_header_filter(r);
    }

    ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
                              r->headers_in.if_modified_since->value.len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http ims:%d lm:%d", ims, r->headers_out.last_modified_time);

    if (ims != r->headers_out.last_modified_time) {

        if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
            || ims < r->headers_out.last_modified_time)
        {
            return ngx_http_next_header_filter(r);
        }
    }

    r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
    r->headers_out.status_line.len = 0;
    r->headers_out.content_type.len = 0;
    ngx_http_clear_content_length(r);
    ngx_http_clear_accept_ranges(r);

    if (r->headers_out.content_encoding) {
        r->headers_out.content_encoding->hash = 0;
        r->headers_out.content_encoding = NULL;
    }

    return ngx_http_next_header_filter(r);
}
static ngx_uint_t
ngx_http_test_if_unmodified(ngx_http_request_t *r)
{
	time_t  iums;

	iums = ngx_http_parse_time(r->headers_in.if_unmodified_since->value.data,
				   r->headers_in.if_unmodified_since->value.len);

	ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
		       "http iums:%d lm:%d", iums, r->headers_out.last_modified_time);

	if (iums >= r->headers_out.last_modified_time) {
		return 1;
	}

	return 0;
}
static ngx_int_t
ngx_http_test_precondition(ngx_http_request_t *r)
{
    time_t  iums;

    iums = ngx_http_parse_time(r->headers_in.if_unmodified_since->value.data,
                               r->headers_in.if_unmodified_since->value.len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                 "http iums:%d lm:%d", iums, r->headers_out.last_modified_time);

    if (iums >= r->headers_out.last_modified_time) {
        return ngx_http_next_header_filter(r);
    }

    return ngx_http_filter_finalize_request(r, NULL,
                                            NGX_HTTP_PRECONDITION_FAILED);
}
static ngx_int_t
ngx_http_jstore_check_cacheable(ngx_http_request_t *r)
{
    ngx_array_t *cache_control;
    ngx_table_elt_t *h;
    ngx_uint_t i;
    time_t expires;

#if (NGX_HTTP_CACHE)
    if(r->cache && r->cache->valid_sec != 0) {
        return 1;
    }
#endif
    if(r->upstream->cacheable) {
        return 1;
    }

    cache_control = &r->upstream->headers_in.cache_control;
    if(cache_control && cache_control->elts) {
        h = *((ngx_table_elt_t **)cache_control->elts);
        for(i = 0; i < cache_control->nelts; i++, h++) {

            if(ngx_strncasecmp(h->value.data, "max-age=", 8) != 0
                    || h->value.data[8] == '0') {
                return 0;
            }
        }
    }

    h = r->upstream->headers_in.expires;
    if(h) {
        expires = ngx_http_parse_time(h->value.data, h->value.len);
        if (expires == NGX_ERROR || expires < ngx_time()) {
            return 0;
        }
    }

    return 1;
}
static int
ngx_tcp_lua_ngx_parse_tcp_time(lua_State *L)
{
    u_char                              *p;
    size_t                               len;
    time_t                               time;

    if (lua_gettop(L) != 1) {
        return luaL_error(L, "expecting one argument");
    }

    p = (u_char *) luaL_checklstring(L, 1, &len);

    time = ngx_http_parse_time(p, len);
    if (time == NGX_ERROR) {
        lua_pushnil(L);
        return 1;
    }

    lua_pushnumber(L, (lua_Number) time);

    return 1;
}
static ngx_int_t
ngx_http_range_header_filter(ngx_http_request_t *r)
{
    time_t                        if_range_time;
    ngx_str_t                    *if_range, *etag;
    ngx_uint_t                    ranges;
    ngx_http_core_loc_conf_t     *clcf;
    ngx_http_range_filter_ctx_t  *ctx;

    if (r->http_version < NGX_HTTP_VERSION_10
        || r->headers_out.status != NGX_HTTP_OK
        || r != r->main
        || r->headers_out.content_length_n == -1
        || !r->allow_ranges)
    {
        return ngx_http_next_header_filter(r);
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->max_ranges == 0) {
        return ngx_http_next_header_filter(r);
    }

    if (r->headers_in.range == NULL
        || r->headers_in.range->value.len < 7
        || ngx_strncasecmp(r->headers_in.range->value.data,
                           (u_char *) "bytes=", 6)
           != 0)
    {
        goto next_filter;
    }

    if (r->headers_in.if_range) {

        if_range = &r->headers_in.if_range->value;

        if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {

            if (r->headers_out.etag == NULL) {
                goto next_filter;
            }

            etag = &r->headers_out.etag->value;

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http ir:%V etag:%V", if_range, etag);

            if (if_range->len != etag->len
                || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
            {
                goto next_filter;
            }

            goto parse;
        }

        if (r->headers_out.last_modified_time == (time_t) -1) {
            goto next_filter;
        }

        if_range_time = ngx_http_parse_time(if_range->data, if_range->len);

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http ir:%d lm:%d",
                       if_range_time, r->headers_out.last_modified_time);

        if (if_range_time != r->headers_out.last_modified_time) {
            goto next_filter;
        }
    }

parse:

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
    if (ctx == NULL) {
        return NGX_ERROR;
    }

    if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    ranges = r->single_range ? 1 : clcf->max_ranges;

    switch (ngx_http_range_parse(r, ctx, ranges)) {

    case NGX_OK:
        ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);

        r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
        r->headers_out.status_line.len = 0;

        if (ctx->ranges.nelts == 1) {
            return ngx_http_range_singlepart_header(r, ctx);
        }

        return ngx_http_range_multipart_header(r, ctx);

    case NGX_HTTP_RANGE_NOT_SATISFIABLE:
        return ngx_http_range_not_satisfiable(r);

    case NGX_ERROR:
        return NGX_ERROR;

    default: /* NGX_DECLINED */
        break;
    }

next_filter:

    r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
    if (r->headers_out.accept_ranges == NULL) {
        return NGX_ERROR;
    }

    r->headers_out.accept_ranges->hash = 1;
    ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");
    ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");

    return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_push_stream_subscriber_handler(ngx_http_request_t *r)
{
    ngx_slab_pool_t                                *shpool = (ngx_slab_pool_t *)ngx_http_push_stream_shm_zone->shm.addr;
    ngx_http_push_stream_loc_conf_t                *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_stream_module);
    ngx_http_push_stream_subscriber_t              *worker_subscriber;
    ngx_http_push_stream_requested_channel_t       *channels_ids, *cur;
    ngx_http_push_stream_subscriber_ctx_t          *ctx;
    time_t                                          if_modified_since;
    ngx_str_t                                      *last_event_id, vv_time = ngx_null_string;
    ngx_str_t                                      *push_mode;
    ngx_flag_t                                      polling, longpolling;
    ngx_int_t                                       rc;
    ngx_int_t                                       status_code;
    ngx_str_t                                      *explain_error_message;

    // add headers to support cross domain requests
    if (cf->allowed_origins.len > 0) {
        ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, &cf->allowed_origins);
        ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_METHODS, &NGX_HTTP_PUSH_STREAM_ALLOW_GET);
        ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, &NGX_HTTP_PUSH_STREAM_ALLOWED_HEADERS);
    }

    if (r->method & NGX_HTTP_OPTIONS) {
        return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_OK, NULL);
    }

    // only accept GET method
    if (!(r->method & NGX_HTTP_GET)) {
        ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_ALLOW, &NGX_HTTP_PUSH_STREAM_ALLOW_GET);
        return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_NOT_ALLOWED, NULL);
    }

    if ((ctx = ngx_http_push_stream_add_request_context(r)) == NULL) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to create request context");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    //get channels ids and backtracks from path
    channels_ids = ngx_http_push_stream_parse_channels_ids_from_path(r, ctx->temp_pool);
    if ((channels_ids == NULL) || ngx_queue_empty(&channels_ids->queue)) {
        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push stream module: the $push_stream_channels_path variable is required but is not set");
        return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_BAD_REQUEST, &NGX_HTTP_PUSH_STREAM_NO_CHANNEL_ID_MESSAGE);
    }

    //validate channels: name, length and quantity. check if channel exists when authorized_channels_only is on. check if channel is full of subscribers
    if (ngx_http_push_stream_validate_channels(r, channels_ids, &status_code, &explain_error_message) == NGX_ERROR) {
        return ngx_http_push_stream_send_only_header_response(r, status_code, explain_error_message);
    }

    if (cf->last_received_message_time != NULL) {
        ngx_http_push_stream_complex_value(r, cf->last_received_message_time, &vv_time);
    } else if (r->headers_in.if_modified_since != NULL) {
        vv_time = r->headers_in.if_modified_since->value;
    }

    // get control headers
    if_modified_since = vv_time.len ? ngx_http_parse_time(vv_time.data, vv_time.len) : -1;
    last_event_id = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_LAST_EVENT_ID);

    push_mode = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_MODE);
    polling = ((cf->location_type == NGX_HTTP_PUSH_STREAM_SUBSCRIBER_MODE_POLLING) || ((push_mode != NULL) && (push_mode->len == NGX_HTTP_PUSH_STREAM_MODE_POLLING.len) && (ngx_strncasecmp(push_mode->data, NGX_HTTP_PUSH_STREAM_MODE_POLLING.data, NGX_HTTP_PUSH_STREAM_MODE_POLLING.len) == 0)));
    longpolling = ((cf->location_type == NGX_HTTP_PUSH_STREAM_SUBSCRIBER_MODE_LONGPOLLING) || ((push_mode != NULL) && (push_mode->len == NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.len) && (ngx_strncasecmp(push_mode->data, NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.data, NGX_HTTP_PUSH_STREAM_MODE_LONGPOLLING.len) == 0)));

    if (polling || longpolling) {
        ngx_int_t result = ngx_http_push_stream_subscriber_polling_handler(r, channels_ids, if_modified_since, last_event_id, longpolling, ctx->temp_pool);
        if (ctx->temp_pool != NULL) {
            ngx_destroy_pool(ctx->temp_pool);
            ctx->temp_pool = NULL;
        }
        return result;
    }

    ctx->padding = ngx_http_push_stream_get_padding_by_user_agent(r);

    // stream access
    if ((worker_subscriber = ngx_http_push_stream_subscriber_prepare_request_to_keep_connected(r)) == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    ngx_http_push_stream_add_response_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_TRANSFER_ENCODING, &NGX_HTTP_PUSH_STREAM_HEADER_CHUNCKED);
    ngx_http_send_header(r);

    // sending response content header
    if (ngx_http_push_stream_send_response_content_header(r, cf) == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "push stream module: could not send content header to subscriber");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    ngx_shmtx_lock(&shpool->mutex);
    rc = ngx_http_push_stream_registry_subscriber_locked(r, worker_subscriber);
    ngx_shmtx_unlock(&shpool->mutex);

    if (rc == NGX_ERROR) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    // adding subscriber to channel(s) and send backtrack messages
    cur = channels_ids;
    while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) {
        if (ngx_http_push_stream_subscriber_assign_channel(shpool, cf, r, cur, if_modified_since, last_event_id, worker_subscriber, ctx->temp_pool) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    }

    if (ctx->temp_pool != NULL) {
        ngx_destroy_pool(ctx->temp_pool);
        ctx->temp_pool = NULL;
    }
    return NGX_DONE;
}
static void ngx_http_mapcache_write_response(mapcache_context *ctx, ngx_http_request_t *r,
      mapcache_http_response *response) {
   if(response->mtime) {
      time_t  if_modified_since;
      if(r->headers_in.if_modified_since) {
         if_modified_since = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
               r->headers_in.if_modified_since->value.len);
         if (if_modified_since != NGX_ERROR) {
            apr_time_t apr_if_m_s;
            apr_time_ansi_put	(	&apr_if_m_s, if_modified_since);
            if(apr_if_m_s<response->mtime) {
               r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
               ngx_http_send_header(r);
               return;
            }
         }
      }
      char *datestr;
      datestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN);
      apr_rfc822_date(datestr, response->mtime);
      apr_table_setn(response->headers,"Last-Modified",datestr);
   }
   if(response->headers && !apr_is_empty_table(response->headers)) {
      const apr_array_header_t *elts = apr_table_elts(response->headers);
      int i;
      for(i=0;i<elts->nelts;i++) {
         apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t);
         if(!strcasecmp(entry.key,"Content-Type")) {
            r->headers_out.content_type.len = strlen(entry.val);
            r->headers_out.content_type.data = (u_char*)entry.val;
         } else {
            ngx_table_elt_t   *h;
            h = ngx_list_push(&r->headers_out.headers);
            if (h == NULL) {
               return;
            }
            h->key.len = strlen(entry.key) ;
            h->key.data = (u_char*)entry.key ;
            h->value.len = strlen(entry.val) ;
            h->value.data = (u_char*)entry.val ;
            h->hash = 1;
         }
      }
   }
   if(response->data) {
      r->headers_out.content_length_n = response->data->size;
   }
   int rc;
   r->headers_out.status = response->code;
   rc = ngx_http_send_header(r);
   if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
      return;
   }

   if(response->data) {
      ngx_buf_t    *b;
      ngx_chain_t   out;
      b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
      if (b == NULL) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 
               "Failed to allocate response buffer.");
         return;
      }

      b->pos = ngx_pcalloc(r->pool,response->data->size);
      memcpy(b->pos,response->data->buf,response->data->size);
      b->last = b->pos + response->data->size;
      b->memory = 1;
      b->last_buf = 1;
      b->flush = 1;
      out.buf = b;
      out.next = NULL;
      ngx_http_output_filter(r, &out);
   }

}
ngx_int_t
ngx_http_srcache_response_no_cache(ngx_http_request_t *r,
    ngx_http_srcache_loc_conf_t *conf, ngx_http_srcache_ctx_t *ctx)
{
    ngx_table_elt_t   **ccp;
    ngx_table_elt_t    *h;
    ngx_uint_t          i;
    u_char             *p, *last;
    ngx_int_t           n;
    time_t              expires;

    dd("checking response cache control settings");

    ccp = r->headers_out.cache_control.elts;

    if (ccp == NULL) {
        goto check_expires;
    }

    for (i = 0; i < r->headers_out.cache_control.nelts; i++) {
        if (!ccp[i]->hash) {
            continue;
        }

        p = ccp[i]->value.data;
        last = p + ccp[i]->value.len;

        if (!conf->store_private
            && ngx_strlcasestrn(p, last, (u_char *) "private", 7 - 1) != NULL)
        {
            return NGX_OK;
        }

        if (!conf->store_no_store
            && ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) != NULL)
        {
            return NGX_OK;
        }

        if (!conf->store_no_cache
            && ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL)
        {
            return NGX_OK;
        }

        if (ctx->valid_sec != 0) {
            continue;
        }

        p = ngx_strlcasestrn(p, last, (u_char *) "max-age=", 8 - 1);

        if (p == NULL) {
            continue;
        }

        n = 0;

        for (p += 8; p < last; p++) {
            if (*p == ',' || *p == ';' || *p == ' ') {
                break;
            }

            if (*p >= '0' && *p <= '9') {
                n = n * 10 + *p - '0';
                continue;
            }

            return NGX_OK;
        }

        if (n == 0) {
            return NGX_OK;
        }

        ctx->valid_sec = ngx_time() + n;
    }

check_expires:
    dd("valid_sec after processing cache-control: %d", (int) ctx->valid_sec);

    if (ctx->valid_sec == 0) {
        h = r->headers_out.expires;

        dd("expires header: %p", h);

        if (h != NULL && h->hash != 0) {
            expires = ngx_http_parse_time(h->value.data, h->value.len);

            if (expires == NGX_ERROR || expires <= ngx_time()) {
                return NGX_OK;
            }

            ctx->valid_sec = expires;
        }
    }

    return NGX_DECLINED;
}
static ngx_int_t
ngx_http_range_header_filter(ngx_http_request_t *r)
{
    time_t                        if_range;
    ngx_int_t                     rc;
    ngx_http_range_filter_ctx_t  *ctx;

    if (r->http_version < NGX_HTTP_VERSION_10
        || r->headers_out.status != NGX_HTTP_OK
        || r != r->main
        || r->headers_out.content_length_n == -1
        || !r->allow_ranges)
    {
        return ngx_http_next_header_filter(r);
    }

    if (r->headers_in.range == NULL
        || r->headers_in.range->value.len < 7
        || ngx_strncasecmp(r->headers_in.range->value.data,
                           (u_char *) "bytes=", 6)
           != 0)
    {
        goto next_filter;
    }

    if (r->headers_in.if_range && r->headers_out.last_modified_time != -1) {

        if_range = ngx_http_parse_time(r->headers_in.if_range->value.data,
                                       r->headers_in.if_range->value.len);

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http ir:%d lm:%d",
                       if_range, r->headers_out.last_modified_time);

        if (if_range != r->headers_out.last_modified_time) {
            goto next_filter;
        }
    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
    if (ctx == NULL) {
        return NGX_ERROR;
    }

    if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    rc = ngx_http_range_parse(r, ctx);

    if (rc == NGX_OK) {

        ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);

        r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
        r->headers_out.status_line.len = 0;

        if (ctx->ranges.nelts == 1) {
            return ngx_http_range_singlepart_header(r, ctx);
        }

        return ngx_http_range_multipart_header(r, ctx);
    }

    if (rc == NGX_HTTP_RANGE_NOT_SATISFIABLE) {
        return ngx_http_range_not_satisfiable(r);
    }

    /* rc == NGX_ERROR */

    return rc;

next_filter:

    r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
    if (r->headers_out.accept_ranges == NULL) {
        return NGX_ERROR;
    }

    r->headers_out.accept_ranges->hash = 1;
    ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");
    ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");

    return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_secure_cookie_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data)
{
    u_char                          hash_buf[16], md5_buf[16], time_buf[64];
    u_char                         *p, *last;
    ngx_str_t                       val, hash, time_dst, time_src;
    time_t                          expires;
    ngx_md5_t                       md5;
    ngx_http_secure_cookie_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_cookie_module);

    if (conf->variable == NULL || conf->md5 == NULL) {
        goto not_found;
    }

    if (ngx_http_complex_value(r, conf->variable, &val) != NGX_OK) {
        return NGX_ERROR;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secure cookie: \"%V\"", &val);

    last = val.data + val.len;

    p = ngx_strlchr(val.data, last, ',');
    expires = 0;

    if (p) {
        val.len = p++ - val.data;

        time_src.data = p;
        time_src.len = last - p;

        if (time_src.len > 64) {
            goto not_found;
        }

        time_dst.data = time_buf;
        time_dst.len = 64;

        if (ngx_decode_base64(&time_dst, &time_src) != NGX_OK) {
            goto not_found;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "secure time: \"%V\"", &time_dst);

        expires = ngx_http_parse_time(time_dst.data, time_dst.len);
        if (expires <= 0) {
            goto not_found;
        }
    }

    if (val.len > 24) {
        goto not_found;
    }

    hash.len = 16;
    hash.data = hash_buf;

    if (ngx_decode_base64(&hash, &val) != NGX_OK) {
        goto not_found;
    }

    if (hash.len != 16) {
        goto not_found;
    }

    if (ngx_http_complex_value(r, conf->md5, &val) != NGX_OK) {
        return NGX_ERROR;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secure cookie md5: \"%V\"", &val);

    ngx_md5_init(&md5);
    ngx_md5_update(&md5, val.data, val.len);
    ngx_md5_final(md5_buf, &md5);

    if (ngx_memcmp(hash_buf, md5_buf, 16) != 0) {
        goto not_found;
    }

    v->data = (u_char *) ((expires && expires < ngx_time()) ? "0" : "1");
    v->len = 1;
    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;

    return NGX_OK;

not_found:

    v->not_found = 1;

    return NGX_OK;
}
/** find message with entity tags matching those of the request r.
  * @param r subscriber request
  */
static ngx_http_push_msg_t * ngx_http_push_find_message_locked(ngx_http_push_channel_t *channel, ngx_http_request_t *r, ngx_int_t *status) {
	//TODO: consider using an RBTree for message storage.
	ngx_queue_t                    *sentinel = &channel->message_queue->queue;
	ngx_queue_t                    *cur = ngx_queue_head(sentinel);
	ngx_http_push_msg_t            *msg;
	ngx_int_t                       tag = -1;
	time_t                          time = (r->headers_in.if_modified_since == NULL) ? 0 : ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len);
	
	//channel's message buffer empty?
	if(channel->messages==0) {
		*status=NGX_HTTP_PUSH_MESSAGE_EXPECTED; //wait.
		return NULL;
	}
	
	// do we want a future message?
	msg = ngx_queue_data(sentinel->prev, ngx_http_push_msg_t, queue); 
	if(time <= msg->message_time) { //that's an empty check (Sentinel's values are zero)
		if(time == msg->message_time) {
			if(tag<0) { tag = ngx_http_push_subscriber_get_etag_int(r); }
			if(tag >= msg->message_tag) {
				*status=NGX_HTTP_PUSH_MESSAGE_EXPECTED;
				return NULL;
			}
		}
	}
	else {
		*status=NGX_HTTP_PUSH_MESSAGE_EXPECTED;
		return NULL;
	}
	
	while(cur!=sentinel) {
		msg = ngx_queue_data(cur, ngx_http_push_msg_t, queue);
		if (time < msg->message_time) {
			*status = NGX_HTTP_PUSH_MESSAGE_FOUND;
			return msg;
		}
		else if(time == msg->message_time) {
			if(tag<0) { tag = ngx_http_push_subscriber_get_etag_int(r); }
			while (tag >= msg->message_tag  && time == msg->message_time && ngx_queue_next(cur)!=sentinel) {
				cur=ngx_queue_next(cur);
				msg = ngx_queue_data(cur, ngx_http_push_msg_t, queue);
			}
			if(time == msg->message_time && tag < msg->message_tag) {
				*status = NGX_HTTP_PUSH_MESSAGE_FOUND;
				return msg;
			}
			continue;
		}
		cur=ngx_queue_next(cur);
	}
	*status = NGX_HTTP_PUSH_MESSAGE_EXPIRED; //message too old and was not found.
	return NULL;
}
static ngx_int_t ngx_http_push_subscriber_handler(ngx_http_request_t *r) {
	ngx_http_push_loc_conf_t       *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_module);
	ngx_slab_pool_t                *shpool = (ngx_slab_pool_t *)ngx_http_push_shm_zone->shm.addr;
	ngx_str_t                      *id;
	ngx_http_push_channel_t        *channel;
	ngx_http_push_msg_t            *msg;
	ngx_int_t                       msg_search_outcome;
	
	ngx_str_t                      *content_type=NULL;
	ngx_str_t                      *etag;
	
	if (r->method != NGX_HTTP_GET) {
		ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ALLOW, &NGX_HTTP_PUSH_ALLOW_GET); //valid HTTP for teh win
		return NGX_HTTP_NOT_ALLOWED;
	}
	
	if((id=ngx_http_push_get_channel_id(r, cf)) == NULL) {
		return r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR;
	}

	//get the channel and check channel authorization while we're at it.
	ngx_shmtx_lock(&shpool->mutex);
	channel = (cf->authorize_channel==1 ? ngx_http_push_find_channel : ngx_http_push_get_channel)(id, &((ngx_http_push_shm_data_t *) ngx_http_push_shm_zone->data)->tree, shpool, r->connection->log);

	if (channel==NULL) {
		//unable to allocate channel OR channel not found
		ngx_shmtx_unlock(&shpool->mutex);
		if(cf->authorize_channel) {
			return NGX_HTTP_FORBIDDEN;
		}
		else {
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate shared memory for channel");
			return NGX_HTTP_INTERNAL_SERVER_ERROR;
		}
	}
	msg = ngx_http_push_find_message_locked(channel, r, &msg_search_outcome); 
	channel->last_seen = ngx_time();
	ngx_shmtx_unlock(&shpool->mutex);
	
	switch(ngx_http_push_handle_subscriber_concurrency_setting(cf->subscriber_concurrency, channel, r, shpool)) {
		case NGX_DECLINED: //this request was declined for some reason.
			//status codes and whatnot should have already been written. just get out of here quickly.
			return NGX_OK;
		case NGX_ERROR:
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: error handling subscriber concurrency setting");
			return NGX_ERROR;
	}

	switch(msg_search_outcome) {
		//for message-found:
		ngx_chain_t                *chain;
		time_t                      last_modified;
		size_t                      content_type_len;
		ngx_http_postponed_request_t  *pr, *p;

		case NGX_HTTP_PUSH_MESSAGE_EXPECTED:
			// ♫ It's gonna be the future soon ♫
			switch(cf->subscriber_poll_mechanism) {
				//for NGX_HTTP_PUSH_MECHANISM_LONGPOLL
				ngx_http_push_pid_queue_t  *sentinel, *cur, *found;
				ngx_http_push_subscriber_t *subscriber;
				ngx_http_push_subscriber_t *subscriber_sentinel;
				
				case NGX_HTTP_PUSH_MECHANISM_LONGPOLL:
					//long-polling subscriber. wait for a message.
					
					//subscribers are queued up in a local pool. Queue sentinels are separate and also local, but not in the pool.
					ngx_shmtx_lock(&shpool->mutex);
					sentinel = &channel->workers_with_subscribers;
					cur = (ngx_http_push_pid_queue_t *)ngx_queue_head(&sentinel->queue);
					found = NULL;
					
					ngx_http_push_subscriber_cleanup_t *clndata;
					ngx_pool_cleanup_t             *cln;
					while(cur!=sentinel) {
						if(cur->pid==ngx_pid) {
							found = cur;
							break;
						}
						cur = (ngx_http_push_pid_queue_t *)ngx_queue_next(&cur->queue);
					}
					if(found==NULL) { //found nothing
						if((found=ngx_slab_alloc_locked(shpool, sizeof(*found)))==NULL) {
							ngx_shmtx_unlock(&shpool->mutex);
							ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate worker subscriber queue marker in shared memory");
							return NGX_HTTP_INTERNAL_SERVER_ERROR;
						}
						//initialize
						ngx_queue_insert_tail(&sentinel->queue, &found->queue);
						found->pid=ngx_pid;
						found->slot=ngx_process_slot;
						found->subscriber_sentinel=NULL;
					}
					ngx_shmtx_unlock(&shpool->mutex);
					
					if((subscriber = ngx_palloc(ngx_http_push_pool, sizeof(*subscriber)))==NULL) { //unable to allocate request queue element
						return NGX_ERROR;
					}

					//postpone the request. this seems to be magical.
					pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
					if (pr == NULL) {
						return NGX_ERROR;
					}
					pr->request = r; //really?
					pr->out = NULL;
					pr->next = NULL;
					if (r->postponed) {
						for (p = r->postponed; p->next; p = p->next) { /* void */ }
						p->next = pr;
					} else {
						r->postponed = pr;
					}
					
					 //attach a cleaner to remove the request from the channel.
					if ((cln=ngx_pool_cleanup_add(r->pool, sizeof(*clndata))) == NULL) { //make sure we can.
						return NGX_ERROR;
					}
					cln->handler = (ngx_pool_cleanup_pt) ngx_http_push_subscriber_cleanup;
					clndata = (ngx_http_push_subscriber_cleanup_t *) cln->data;
					clndata->channel=channel;
					clndata->subscriber=subscriber;
					
					subscriber->request = r;
					subscriber->clndata=clndata;
					
					ngx_shmtx_lock(&shpool->mutex);
					channel->subscribers++; // do this only when we know everything went okay.
					
					//figure out the subscriber sentinel
					subscriber_sentinel = ((ngx_http_push_pid_queue_t *)found)->subscriber_sentinel;
					if(subscriber_sentinel==NULL) {
						if((subscriber_sentinel=ngx_palloc(ngx_http_push_pool, sizeof(*subscriber_sentinel)))==NULL) {
							ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate channel subscriber sentinel");
							return NGX_HTTP_INTERNAL_SERVER_ERROR;
						}
						ngx_queue_init(&subscriber_sentinel->queue);
						((ngx_http_push_pid_queue_t *)found)->subscriber_sentinel=subscriber_sentinel;
					}
					ngx_shmtx_unlock(&shpool->mutex);
					
					ngx_queue_insert_tail(&subscriber_sentinel->queue, &subscriber->queue);
					
#if defined(nginx_version) && nginx_version >= 7000
					return NGX_OK; //do recall that the request was postponed
#else
					return NGX_DONE; //oldschool
#endif
				case NGX_HTTP_PUSH_MECHANISM_INTERVALPOLL:
				
					//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=ngx_http_push_subscriber_get_etag(r)) != NULL) {
						r->headers_out.etag=ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ETAG, etag);
					}
					return NGX_HTTP_NOT_MODIFIED;
					
				default:
					//if this ever happens, there's a bug somewhere else. probably config stuff.
					return NGX_HTTP_INTERNAL_SERVER_ERROR;
			}
		
		case NGX_HTTP_PUSH_MESSAGE_EXPIRED:
			//subscriber wants an expired message
			//TODO: maybe respond with entity-identifiers for oldest available message?
			return NGX_HTTP_NO_CONTENT; 
		
		case NGX_HTTP_PUSH_MESSAGE_FOUND:
			//found the message
			ngx_shmtx_lock(&shpool->mutex);
			msg->refcount++; // this probably isn't necessary, but i'm not thinking too straight at the moment. so just in case.
			if((msg->received)!=(ngx_uint_t) NGX_MAX_UINT32_VALUE){ //overflow check?
				msg->received++;
			}
			NGX_HTTP_PUSH_MAKE_ETAG(msg->message_tag, etag, ngx_palloc, r->pool);
			if(etag==NULL) {
				//oh, nevermind...
				ngx_shmtx_unlock(&shpool->mutex);
				ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate memory for Etag header");
				return NGX_ERROR;
			}
			
			content_type_len = msg->content_type.len;
			if(content_type_len>0) {
				NGX_HTTP_PUSH_MAKE_CONTENT_TYPE(content_type, content_type_len, msg, r->pool);
				if(content_type==NULL) {
					ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate memory for content-type header while responding to subscriber request");
					ngx_shmtx_unlock(&shpool->mutex);
					return NGX_ERROR;
				}
			}
			
			//preallocate output chain. yes, same one for every waiting subscriber
			if((chain = ngx_http_push_create_output_chain_locked(msg->buf, r->pool, r->connection->log, shpool))==NULL) {
				ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate buffer chain while responding to subscriber request");
				ngx_shmtx_unlock(&shpool->mutex);
				return NGX_ERROR;
			}
			
			last_modified = msg->message_time;
			
			if(msg->received!=NGX_MAX_UINT32_VALUE) {
				msg->received++;
			}
			//is the message still needed?
			if(msg!=NULL && (--msg->refcount)==0 && msg->queue.next==NULL) { 
				//message was dequeued, and nobody needs it anymore
				ngx_http_push_free_message_locked(msg, shpool);
			}
			ngx_shmtx_unlock(&shpool->mutex);
			
			if(chain->buf->file!=NULL) {
				//close file when we're done with it
				ngx_pool_cleanup_t *cln;
				ngx_pool_cleanup_file_t *clnf;

				if((cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)))==NULL) {
					return NGX_HTTP_INTERNAL_SERVER_ERROR;
				}
				cln->handler = ngx_pool_cleanup_file;
				clnf = cln->data;
				clnf->fd = chain->buf->file->fd;
				clnf->name = chain->buf->file->name.data;
				clnf->log = r->pool->log;
			}

			
			return ngx_http_push_prepare_response_to_subscriber_request(r, chain, content_type, etag, last_modified);
			
		default: //we shouldn't be here.
			return NGX_HTTP_INTERNAL_SERVER_ERROR;
	}
}
static ngx_int_t
ngx_http_zip_set_headers(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx)
{
    time_t if_range, last_modified;

    if (ngx_http_zip_add_cache_control(r) == NGX_ERROR) {
        return NGX_ERROR;
    }

    r->headers_out.content_type_len = sizeof(NGX_ZIP_MIME_TYPE) - 1;
    ngx_str_set(&r->headers_out.content_type, NGX_ZIP_MIME_TYPE);
    ngx_http_clear_content_length(r);

    if (ctx->missing_crc32) {
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "mod_zip: Clearing Accept-Ranges header");
        ngx_http_clear_accept_ranges(r);
    }
    r->headers_out.content_length_n = ctx->archive_size;
    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
            "mod_zip: Archive will be %O bytes", ctx->archive_size);
    if (r->headers_in.range) {
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "mod_zip: Range found");
        if (ctx->missing_crc32) {
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "mod_zip: Missing checksums, ignoring Range");
            return NGX_OK;
        }
        if (r->headers_in.if_range && r->upstream) {
            if_range = ngx_http_parse_time(r->headers_in.if_range->value.data,
                    r->headers_in.if_range->value.len);
            if (if_range == NGX_ERROR) { /* treat as ETag */
                if (r->upstream->headers_in.etag) {
                    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "mod_zip: If-Range = %V, ETag = %V", 
                            &r->headers_in.if_range->value, &r->upstream->headers_in.etag->value);
                    if (r->upstream->headers_in.etag->value.len != r->headers_in.if_range->value.len
                            || ngx_strncmp(r->upstream->headers_in.etag->value.data,
                                r->headers_in.if_range->value.data,
                                r->headers_in.if_range->value.len)) {
                        return NGX_OK;
                    }
                } else {
                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "mod_zip: No ETag from upstream");
                    return NGX_OK;
                }
            } else { /* treat as modification time */
                if (r->upstream->headers_in.last_modified) {
                    last_modified = ngx_http_parse_time(r->upstream->headers_in.last_modified->value.data,
                            r->upstream->headers_in.last_modified->value.len);
                    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "mod_zip: If-Range = %d, Last-Modified = %d", 
                            if_range, last_modified);
                    if (if_range != last_modified && last_modified != -1) {
                        return NGX_OK;
                    }
                } else {
                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "mod_zip: No Last-Modified from upstream");
                    return NGX_OK;
                }
            }
        }
        if (ngx_http_zip_parse_range(r, &r->headers_in.range->value, ctx) 
                == NGX_ERROR) {
            r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
            if (ngx_http_zip_add_full_content_range(r) == NGX_ERROR) {
                return NGX_ERROR;
            }
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "mod_zip: Range not satisfiable");
            ctx->ranges.nelts = 0;
            return NGX_HTTP_RANGE_NOT_SATISFIABLE;
        }
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "mod_zip: Range is satisfiable");
        if (ctx->ranges.nelts == 1) {
            if (ngx_http_zip_add_partial_content_range(r, ctx) == NGX_ERROR) {
                return NGX_ERROR;
            }
        } else {
            if (ngx_http_zip_init_multipart_range(r, ctx) == NGX_ERROR) {
                return NGX_ERROR;
            }
        }
        r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
        r->headers_out.status_line.len = 0;
    }

    return NGX_OK;
}
static ngx_int_t ngx_http_push_subscriber_handler(ngx_http_request_t *r) {
	ngx_http_push_loc_conf_t       *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_module);
	ngx_slab_pool_t                *shpool = (ngx_slab_pool_t *)ngx_http_push_shm_zone->shm.addr;
	ngx_str_t                      *id;
	ngx_http_push_channel_t        *channel;
	ngx_http_push_msg_t            *msg;
	ngx_int_t                       msg_search_outcome;
	
	ngx_str_t                      *content_type=NULL;
	ngx_str_t                      *etag;
	
    if (r->method == NGX_HTTP_OPTIONS) {
        ngx_buf_t *buf = ngx_create_temp_buf(r->pool, sizeof(NGX_HTTP_PUSH_OPTIONS_OK_MESSAGE));
		ngx_chain_t *chain;
		buf->pos=(u_char *)NGX_HTTP_PUSH_OPTIONS_OK_MESSAGE;
		buf->last=buf->pos + sizeof(NGX_HTTP_PUSH_OPTIONS_OK_MESSAGE)-1;
		chain = ngx_http_push_create_output_chain(buf, r->pool, r->connection->log);
		buf->last_buf=1;
        r->headers_out.content_length_n=ngx_buf_size(buf);
		r->headers_out.status=NGX_HTTP_OK;
		ngx_http_send_header(r);
		ngx_http_output_filter(r, chain);
        return NGX_OK;
    }
    
	if (r->method != NGX_HTTP_GET) {
		ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ALLOW, &NGX_HTTP_PUSH_ALLOW_GET); //valid HTTP for the win
		return NGX_HTTP_NOT_ALLOWED;
	}    
	
	if((id=ngx_http_push_get_channel_id(r, cf)) == NULL) {
		return r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR;
	}

	//get the channel and check channel authorization while we're at it.
	ngx_shmtx_lock(&shpool->mutex);
	if (cf->authorize_channel==1) {
		channel = ngx_http_push_find_channel(id, r->connection->log);
	}else{
		channel = ngx_http_push_get_channel(id, r->connection->log, cf->channel_timeout);
	}

	if (channel==NULL) {
		//unable to allocate channel OR channel not found
		ngx_shmtx_unlock(&shpool->mutex);
		if(cf->authorize_channel) {
			return NGX_HTTP_FORBIDDEN;
		}
		else {
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate shared memory for channel");
			return NGX_HTTP_INTERNAL_SERVER_ERROR;
		}
	}

    msg = ngx_http_push_find_message_locked(channel, r, &msg_search_outcome); 
    channel->last_seen = ngx_time();
    channel->expires = ngx_time() + cf->channel_timeout;
    ngx_shmtx_unlock(&shpool->mutex);
    
    if (cf->ignore_queue_on_no_cache && !ngx_http_push_allow_caching(r)) {
        msg_search_outcome = NGX_HTTP_PUSH_MESSAGE_EXPECTED; 
        msg = NULL;
    }
	
	switch(ngx_http_push_handle_subscriber_concurrency(r, channel, cf)) {
		case NGX_DECLINED: //this request was declined for some reason.
			//status codes and whatnot should have already been written. just get out of here quickly.
			return NGX_OK;
		case NGX_ERROR:
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: error handling subscriber concurrency setting");
			return NGX_ERROR;
	}

	switch(msg_search_outcome) {
		//for message-found:
		ngx_chain_t                *chain;
		time_t                      last_modified;
		size_t                      content_type_len;

		case NGX_HTTP_PUSH_MESSAGE_EXPECTED:
			// ♫ It's gonna be the future soon ♫
			switch(cf->subscriber_poll_mechanism) {
				//for NGX_HTTP_PUSH_MECHANISM_LONGPOLL
				ngx_http_push_pid_queue_t  *sentinel, *cur, *found;
				ngx_http_push_subscriber_t *subscriber;
				ngx_http_push_subscriber_t *subscriber_sentinel;
				
				case NGX_HTTP_PUSH_MECHANISM_LONGPOLL:
					//long-polling subscriber. wait for a message.
					
					//subscribers are queued up in a local pool. Queue sentinels are separate and also local, but not in the pool.
					ngx_shmtx_lock(&shpool->mutex);
					sentinel = &channel->workers_with_subscribers;
					cur = (ngx_http_push_pid_queue_t *)ngx_queue_head(&sentinel->queue);
					found = NULL;
					
					ngx_http_push_subscriber_cleanup_t *clndata;
					ngx_pool_cleanup_t             *cln;
					while(cur!=sentinel) {
						if(cur->pid==ngx_pid) {
							found = cur;
							break;
						}
						cur = (ngx_http_push_pid_queue_t *)ngx_queue_next(&cur->queue);
					}
					if(found == NULL) { //found nothing
						if((found=ngx_http_push_slab_alloc_locked(sizeof(*found)))==NULL) {
							ngx_shmtx_unlock(&shpool->mutex);
							ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate worker subscriber queue marker in shared memory");
							return NGX_HTTP_INTERNAL_SERVER_ERROR;
						}
						//initialize
						ngx_queue_insert_tail(&sentinel->queue, &found->queue);
						found->pid=ngx_pid;
						found->slot=ngx_process_slot;
						found->subscriber_sentinel=NULL;
					}
					ngx_shmtx_unlock(&shpool->mutex);
					
					if((subscriber = ngx_palloc(ngx_http_push_pool, sizeof(*subscriber)))==NULL) { //unable to allocate request queue element
						return NGX_ERROR;
					}
					
					 //attach a cleaner to remove the request from the channel.
					if ((cln=ngx_pool_cleanup_add(r->pool, sizeof(*clndata))) == NULL) { //make sure we can.
						return NGX_ERROR;
					}
					cln->handler = (ngx_pool_cleanup_pt) ngx_http_push_subscriber_cleanup;
					clndata = (ngx_http_push_subscriber_cleanup_t *) cln->data;
					clndata->channel=channel;
					clndata->subscriber=subscriber;
					
					subscriber->request = r;
					subscriber->clndata=clndata;
					
					ngx_shmtx_lock(&shpool->mutex);
					channel->subscribers++; // do this only when we know everything went okay.
					
					//figure out the subscriber sentinel
					subscriber_sentinel = ((ngx_http_push_pid_queue_t *)found)->subscriber_sentinel;
					if(subscriber_sentinel==NULL) {
						//it's perfectly nornal for the sentinel to be NULL.
						if((subscriber_sentinel=ngx_palloc(ngx_http_push_pool, sizeof(*subscriber_sentinel)))==NULL) {
							ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate channel subscriber sentinel");
							return NGX_HTTP_INTERNAL_SERVER_ERROR;
						}
						ngx_queue_init(&subscriber_sentinel->queue);
						((ngx_http_push_pid_queue_t *)found)->subscriber_sentinel=subscriber_sentinel;
					}
					ngx_shmtx_unlock(&shpool->mutex);
					
					ngx_queue_insert_tail(&subscriber_sentinel->queue, &subscriber->queue);
					
					ngx_memzero(&subscriber->event, sizeof(subscriber->event));
					if (cf->subscriber_timeout > 0) {		
						subscriber->event.handler = ngx_http_push_clean_timeouted_subscriber;	
						subscriber->event.data = subscriber;
						subscriber->event.log = r->connection->log;
						ngx_add_timer(&subscriber->event, cf->subscriber_timeout * 1000);
					}

					//r->read_event_handler = ngx_http_test_reading;
					//r->write_event_handler = ngx_http_request_empty_handler;
					r->discard_body = 1;
					//r->keepalive = 1; //stayin' alive!!
					return NGX_DONE;
					
				case NGX_HTTP_PUSH_MECHANISM_INTERVALPOLL:
				
					//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=ngx_http_push_subscriber_get_etag(r)) != NULL) {
						r->headers_out.etag=ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ETAG, etag);
					}
					return NGX_HTTP_NOT_MODIFIED;
					
				default:
					//if this ever happens, there's a bug somewhere else. probably config stuff.
					return NGX_HTTP_INTERNAL_SERVER_ERROR;
			}
		
		case NGX_HTTP_PUSH_MESSAGE_EXPIRED:
			//subscriber wants an expired message
			//TODO: maybe respond with entity-identifiers for oldest available message?
			return NGX_HTTP_NO_CONTENT; 
		
		case NGX_HTTP_PUSH_MESSAGE_FOUND:
			//found the message
			ngx_shmtx_lock(&shpool->mutex);
			ngx_http_push_reserve_message_locked(channel, msg);
			NGX_HTTP_PUSH_MAKE_ETAG(msg->message_tag, etag, ngx_palloc, r->pool);
			if(etag==NULL) {
				//oh, nevermind...
				ngx_shmtx_unlock(&shpool->mutex);
				ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate memory for Etag header");
				return NGX_ERROR;
			}
			
			content_type_len = msg->content_type.len;
			if(content_type_len>0) {
				NGX_HTTP_PUSH_MAKE_CONTENT_TYPE(content_type, content_type_len, msg, r->pool);
				if(content_type==NULL) {
					ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate memory for content-type header while responding to subscriber request");
					ngx_shmtx_unlock(&shpool->mutex);
					return NGX_ERROR;
				}
			}
			
			//preallocate output chain. yes, same one for every waiting subscriber
			if((chain = ngx_http_push_create_output_chain_locked(msg->buf, r->pool, r->connection->log, shpool))==NULL) {
				ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate buffer chain while responding to subscriber request");
				ngx_shmtx_unlock(&shpool->mutex);
				return NGX_ERROR;
			}
			
			last_modified = msg->message_time;
			
			//is the message still needed?
			ngx_http_push_release_message_locked(channel, msg);
			ngx_shmtx_unlock(&shpool->mutex);
			
			if(chain->buf->file!=NULL) {
				//close file when we're done with it
				ngx_pool_cleanup_t *cln;
				ngx_pool_cleanup_file_t *clnf;
			 
				if((cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)))==NULL) {
					return NGX_HTTP_INTERNAL_SERVER_ERROR;
				}
				cln->handler = ngx_pool_cleanup_file;
				clnf = cln->data;
				clnf->fd = chain->buf->file->fd;
				clnf->name = chain->buf->file->name.data;
				clnf->log = r->pool->log;
			}
			
			return ngx_http_push_prepare_response_to_subscriber_request(r, chain, content_type, etag, last_modified);
			
		default: //we shouldn't be here.
			return NGX_HTTP_INTERNAL_SERVER_ERROR;
	}
}
static void
ngx_http_dav_put_handler(ngx_http_request_t *r)
{
    size_t                    root;
    time_t                    date;
    ngx_str_t                *temp, path;
    ngx_uint_t                status;
    ngx_file_info_t           fi;
    ngx_ext_rename_file_t     ext;
    ngx_http_dav_loc_conf_t  *dlcf;

    if (r->request_body == NULL || r->request_body->temp_file == NULL) {
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

    ngx_http_map_uri_to_path(r, &path, &root, 0);

    path.len--;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http put filename: \"%s\"", path.data);

    temp = &r->request_body->temp_file->file.name;

    if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
        status = NGX_HTTP_CREATED;

    } else {
        status = NGX_HTTP_NO_CONTENT;

        if (ngx_is_dir(&fi)) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
                          "\"%s\" could not be created", path.data);

            if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {
                ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                              ngx_delete_file_n " \"%s\" failed",
                              temp->data);
            }

            ngx_http_finalize_request(r, NGX_HTTP_CONFLICT);
            return;
        }
    }

    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

    ext.access = dlcf->access;
    ext.path_access = dlcf->access;
    ext.time = -1;
    ext.create_path = dlcf->create_full_put_path;
    ext.delete_file = 1;
    ext.log = r->connection->log;

    if (r->headers_in.date) {
        date = ngx_http_parse_time(r->headers_in.date->value.data,
                                   r->headers_in.date->value.len);

        if (date != NGX_ERROR) {
            ext.time = date;
            ext.fd = r->request_body->temp_file->file.fd;
        }
    }

    if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

    if (status == NGX_HTTP_CREATED) {
        if (ngx_http_dav_location(r, path.data) != NGX_OK) {
            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return;
        }

        r->headers_out.content_length_n = 0;
    }

    r->headers_out.status = status;
    r->header_only = 1;

    ngx_http_finalize_request(r, ngx_http_send_header(r));
    return;
}
Exemple #30
0
nchan_msg_id_t *nchan_subscriber_get_msg_id(ngx_http_request_t *r) {
  static nchan_msg_id_t           id = NCHAN_ZERO_MSGID;
  
  ngx_str_t                      *if_none_match;
  nchan_loc_conf_t               *cf = ngx_http_get_module_loc_conf(r, ngx_nchan_module);
  nchan_request_ctx_t            *ctx = ngx_http_get_module_ctx(r, ngx_nchan_module);
  int                             i;
  ngx_int_t                       rc;
  
  if_none_match = nchan_subscriber_get_etag(r);
  
  if(!cf->msg_in_etag_only && r->headers_in.if_modified_since != NULL) {
    id.time=ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len);
    
    if(id.time <= 0) { //anything before 1-1-1970 is reserved and treated as no msgid provided
      set_default_id(cf, &id);
      return &id;
    }

    u_char *first = NULL, *last = NULL;
    if(if_none_match != NULL) {
      first = if_none_match->data;
      last = if_none_match->data + if_none_match->len;
    }

    if(nchan_parse_msg_tag(first, last, &id, ctx->channel_id_count) == NGX_ERROR) {
      return NULL;
    }

    return &id;
  }
  else if((cf->msg_in_etag_only || r->headers_in.if_modified_since == NULL) && if_none_match) {
    rc = nchan_parse_compound_msgid(&id, if_none_match, ctx->channel_id_count);
    if(rc == NGX_OK) {
      return &id;
    }
    else if(rc == NGX_ERROR) {
      return NULL;
    }
  }
  else {
    nchan_complex_value_arr_t   *alt_msgid_cv_arr = &cf->last_message_id;
    u_char                       buf[128];
    ngx_str_t                    str;
    int                          n = alt_msgid_cv_arr->n;
    ngx_int_t                    rc2;
    
    str.len = 0;
    str.data = buf;
    
    for(i=0; i < n; i++) {
      rc = ngx_http_complex_value_noalloc(r, alt_msgid_cv_arr->cv[i], &str, 128);
      if(str.len > 0 && rc == NGX_OK) {
        rc2 = nchan_parse_compound_msgid(&id, nchan_urldecode_str(r, &str), ctx->channel_id_count);
        if(rc2 == NGX_OK) {
          return &id;
        }
        else if(rc2 == NGX_ERROR) {
          return NULL;
        }
      }
    }
  }
  
  set_default_id(cf, &id);
  return &id;
}