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; }
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; }
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; }
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; }