/* 将文件添加到系统的file_cache管理中 * 参数path: 为普通文件的完整路径 */ static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) { ngx_msec_t elapsed; ngx_http_file_cache_t *cache; cache = ctx->data; // 添加文件到file_cache中,建立node节点在红黑树中 if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) { (void) ngx_http_file_cache_delete_file(ctx, path); } // 统计缓存文件并检查是否达到系统限制 if (++cache->files >= cache->loader_files) { // why?? 为什么加载的cache文件达到限制后,将休眠并将 cache->files 的个数清零呢??? ngx_http_file_cache_loader_sleep(cache); } else { ngx_time_update(); elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache loader time elapsed: %M", elapsed); if (elapsed >= cache->loader_threshold) { ngx_http_file_cache_loader_sleep(cache); } } return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK; }
static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) { ngx_msec_t elapsed; ngx_http_file_cache_t *cache; cache = ctx->data; if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) { (void) ngx_http_file_cache_delete_file(ctx, path); } if (++cache->files >= cache->loader_files) { ngx_http_file_cache_loader_sleep(cache); } else { ngx_time_update(); elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache loader time elapsed: %M", elapsed); if (elapsed >= cache->loader_threshold) { ngx_http_file_cache_loader_sleep(cache); } } return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK; }
static ngx_int_t ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache) { ngx_msec_t elapsed; if (cache->files++ > 100) { ngx_time_update(); elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache manager time: %M", elapsed); if (elapsed > 200) { /* * if processing 100 files takes more than 200ms, * it seems that many operations require disk i/o, * therefore sleep 200ms */ ngx_msleep(200); ngx_time_update(); } cache->last = ngx_current_msec; cache->files = 0; } return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK; }
static ngx_int_t ngx_http_push_subscriber_get_etag_int(ngx_http_request_t * r) { ngx_str_t *if_none_match = ngx_http_push_subscriber_get_etag(r); ngx_int_t tag; if(if_none_match==NULL || (if_none_match!=NULL && (tag = ngx_atoi(if_none_match->data, if_none_match->len))==NGX_ERROR)) { tag=0; } return ngx_abs(tag); }
static void ngx_http_limit_req2_expire(ngx_http_request_t *r, ngx_http_limit_req2_ctx_t *ctx, ngx_uint_t n) { ngx_int_t excess; ngx_time_t *tp; ngx_msec_t now; ngx_queue_t *q; ngx_msec_int_t ms; ngx_rbtree_node_t *node; ngx_http_limit_req2_node_t *lr; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); /* * n == 1 deletes one or two zero rate entries * n == 0 deletes oldest entry by force * and one or two zero rate entries */ while (n < 3) { if (ngx_queue_empty(&ctx->sh->queue)) { return; } q = ngx_queue_last(&ctx->sh->queue); lr = ngx_queue_data(q, ngx_http_limit_req2_node_t, queue); if (n++ != 0) { ms = (ngx_msec_int_t) (now - lr->last); ms = ngx_abs(ms); if (ms < 60000) { return; } excess = lr->excess - ctx->rate * ms / 1000; if (excess > 0) { return; } } ngx_queue_remove(q); node = (ngx_rbtree_node_t *) ((u_char *) lr - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } }
static void ngx_http_ip_behavior_expire(ngx_http_ip_behavior_ctx_t *ctx, ngx_uint_t force) { ngx_time_t *tp; ngx_msec_t now; ngx_msec_int_t ms; ngx_queue_t *q; ngx_rbtree_node_t *node; ngx_http_ip_behavior_node_t *ibn; ngx_uint_t i; ngx_uint_t n; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); /* delete at most 2 oldest nodes when a new request comes in * or * delete 10 oldest nodes ignoring the expires when "force" is set to 1 */ if (force) { n = 10; } else { n = 2; } for (i = 0; i < n; i++) { if (ngx_queue_empty(&ctx->sh->queue)) { return; } q = ngx_queue_last(&ctx->sh->queue); ibn = ngx_queue_data(q, ngx_http_ip_behavior_node_t, queue); if (!force) { ms = (ngx_msec_int_t) (now - ibn->last); ms = ngx_abs(ms); if (ms < ctx->sample_cycle) { /* the oldest is not expired, no need to check prev nodes */ return; } } ngx_queue_remove(q); node = (ngx_rbtree_node_t *) ((u_char *) ibn - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } }
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; }
void ngx_time_update(void) { u_char *p0, *p1, *p2, *p3; ngx_tm_t tm, gmt; time_t sec; ngx_uint_t msec; ngx_time_t *tp; struct timeval tv; if (!ngx_trylock(&ngx_time_lock)) { return; } ngx_gettimeofday(&tv); sec = tv.tv_sec; msec = tv.tv_usec / 1000; ngx_current_msec = (ngx_msec_t) sec * 1000 + msec; tp = &cached_time[slot]; if (tp->sec == sec) { tp->msec = msec; ngx_unlock(&ngx_time_lock); return; } if (slot == NGX_TIME_SLOTS - 1) { slot = 0; } else { slot++; } tp = &cached_time[slot]; tp->sec = sec; tp->msec = msec; ngx_gmtime(sec, &gmt); p0 = &cached_http_time[slot][0]; (void) ngx_sprintf(p0, "%s, %02d %s %4d %02d:%02d:%02d GMT", week[gmt.ngx_tm_wday], gmt.ngx_tm_mday, months[gmt.ngx_tm_mon - 1], gmt.ngx_tm_year, gmt.ngx_tm_hour, gmt.ngx_tm_min, gmt.ngx_tm_sec); #if (NGX_HAVE_GETTIMEZONE) tp->gmtoff = ngx_gettimezone(); ngx_gmtime(sec + tp->gmtoff * 60, &tm); #elif (NGX_HAVE_GMTOFF) ngx_localtime(sec, &tm); cached_gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60); tp->gmtoff = cached_gmtoff; #else ngx_localtime(sec, &tm); cached_gmtoff = ngx_timezone(tm.ngx_tm_isdst); tp->gmtoff = cached_gmtoff; #endif p1 = &cached_err_log_time[slot][0]; (void) ngx_sprintf(p1, "%4d/%02d/%02d %02d:%02d:%02d", tm.ngx_tm_year, tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec); p2 = &cached_http_log_time[slot][0]; (void) ngx_sprintf(p2, "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d", tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1], tm.ngx_tm_year, tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec, tp->gmtoff < 0 ? '-' : '+', ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60)); p3 = &cached_http_log_iso8601[slot][0]; (void) ngx_sprintf(p3, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", tm.ngx_tm_year, tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec, tp->gmtoff < 0 ? '-' : '+', ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60)); ngx_memory_barrier(); ngx_cached_time = tp; ngx_cached_http_time.data = p0; ngx_cached_err_log_time.data = p1; ngx_cached_http_log_time.data = p2; ngx_cached_http_log_iso8601.data = p3; ngx_unlock(&ngx_time_lock); }
static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) { ngx_int_t excess; ngx_msec_t now; ngx_queue_t *q; ngx_msec_int_t ms; ngx_rbtree_node_t *node; ngx_http_limit_req_node_t *lr; now = ngx_current_msec; /* * n == 1 deletes one or two zero rate entries * n == 0 deletes oldest entry by force * and one or two zero rate entries */ while (n < 3) { if (ngx_queue_empty(&ctx->sh->queue)) { return; } q = ngx_queue_last(&ctx->sh->queue); lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue); if (lr->count) { /* * There is not much sense in looking further, * because we bump nodes on the lookup stage. */ return; } if (n++ != 0) { ms = (ngx_msec_int_t) (now - lr->last); ms = ngx_abs(ms); if (ms < 60000) { return; } excess = lr->excess - ctx->rate * ms / 1000; if (excess > 0) { return; } } ngx_queue_remove(q); node = (ngx_rbtree_node_t *) ((u_char *) lr - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } }
static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit) { ngx_int_t excess; ngx_msec_t now, delay, max_delay; ngx_msec_int_t ms; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; excess = *ep; if (excess == 0 || (*limit)->nodelay) { max_delay = 0; } else { ctx = (*limit)->shm_zone->data; max_delay = excess * 1000 / ctx->rate; } while (n--) { ctx = limits[n].shm_zone->data; lr = ctx->node; if (lr == NULL) { continue; } ngx_shmtx_lock(&ctx->shpool->mutex); now = ngx_current_msec; ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } lr->last = now; lr->excess = excess; lr->count--; ngx_shmtx_unlock(&ctx->shpool->mutex); ctx->node = NULL; if (limits[n].nodelay) { continue; } delay = excess * 1000 / ctx->rate; if (delay > max_delay) { max_delay = delay; *ep = excess; *limit = &limits[n]; } } return max_delay; }
static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account) { size_t size; ngx_int_t rc, excess; ngx_msec_t now; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; now = ngx_current_msec; ctx = limit->shm_zone->data; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ lr = (ngx_http_limit_req_node_t *) &node->color; rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len); if (rc == 0) { ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } *ep = excess; if ((ngx_uint_t) excess > limit->burst) { return NGX_BUSY; } if (account) { lr->excess = excess; lr->last = now; return NGX_OK; } lr->count++; ctx->node = lr; return NGX_AGAIN; } node = (rc < 0) ? node->left : node->right; } *ep = 0; size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_req_node_t, data) + key->len; ngx_http_limit_req_expire(ctx, 1); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_http_limit_req_expire(ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "could not allocate node%s", ctx->shpool->log_ctx); return NGX_ERROR; } } node->key = hash; lr = (ngx_http_limit_req_node_t *) &node->color; lr->len = (u_short) key->len; lr->excess = 0; ngx_memcpy(lr->data, key->data, key->len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); if (account) { lr->last = now; lr->count = 0; return NGX_OK; } lr->last = 0; lr->count = 1; ctx->node = lr; return NGX_AGAIN; }
static ngx_int_t ngx_http_push_stream_subscriber_polling_handler(ngx_http_request_t *r, ngx_http_push_stream_requested_channel_t *channels_ids, time_t if_modified_since, ngx_str_t *last_event_id, ngx_flag_t longpolling, ngx_pool_t *temp_pool) { ngx_http_push_stream_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_stream_module); ngx_slab_pool_t *shpool = (ngx_slab_pool_t *)ngx_http_push_stream_shm_zone->shm.addr; ngx_http_push_stream_subscriber_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_push_stream_module); ngx_http_push_stream_requested_channel_t *cur; ngx_http_push_stream_subscriber_t *worker_subscriber; ngx_http_push_stream_channel_t *channel; ngx_http_push_stream_subscription_t *subscription; ngx_str_t *etag = NULL, vv_etag = ngx_null_string; ngx_int_t tag; time_t greater_message_time; ngx_int_t greater_message_tag; ngx_flag_t has_message_to_send = 0; ngx_str_t callback_function_name; if (cf->last_received_message_tag != NULL) { ngx_http_push_stream_complex_value(r, cf->last_received_message_tag, &vv_etag); etag = vv_etag.len ? &vv_etag : NULL; } else { etag = ngx_http_push_stream_get_header(r, &NGX_HTTP_PUSH_STREAM_HEADER_IF_NONE_MATCH); } if (ngx_http_arg(r, NGX_HTTP_PUSH_STREAM_CALLBACK.data, NGX_HTTP_PUSH_STREAM_CALLBACK.len, &callback_function_name) == NGX_OK) { ngx_http_push_stream_unescape_uri(&callback_function_name); if ((ctx->callback = ngx_http_push_stream_get_formatted_chunk(callback_function_name.data, callback_function_name.len, r->pool)) == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate memory for callback function name"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } tag = ((etag != NULL) && ((tag = ngx_atoi(etag->data, etag->len)) != NGX_ERROR)) ? ngx_abs(tag) : -1; greater_message_tag = tag; greater_message_time = if_modified_since = (if_modified_since < 0) ? 0 : if_modified_since; ngx_shmtx_lock(&shpool->mutex); // check if has any message to send cur = channels_ids; while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) { channel = ngx_http_push_stream_find_channel(cur->id, r->connection->log); if (channel == NULL) { // channel not found ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate shared memory for channel %s", cur->id->data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ngx_http_push_stream_has_old_messages_to_send(channel, cur->backtrack_messages, if_modified_since, tag, greater_message_time, greater_message_tag, last_event_id)) { has_message_to_send = 1; if (channel->last_message_time > greater_message_time) { greater_message_time = channel->last_message_time; greater_message_tag = channel->last_message_tag; } else { if ((channel->last_message_time == greater_message_time) && (channel->last_message_tag > greater_message_tag) ) { greater_message_tag = channel->last_message_tag; } } } } if (longpolling && !has_message_to_send) { // long polling mode without messages if ((worker_subscriber = ngx_http_push_stream_subscriber_prepare_request_to_keep_connected(r)) == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_INTERNAL_SERVER_ERROR; } worker_subscriber->longpolling = 1; if (ngx_http_push_stream_registry_subscriber_locked(r, worker_subscriber) == NGX_ERROR) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_INTERNAL_SERVER_ERROR; } // adding subscriber to channel(s) cur = channels_ids; while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) { if ((channel = ngx_http_push_stream_find_channel(cur->id, r->connection->log)) == NULL) { // channel not found ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate shared memory for channel %s", cur->id->data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if ((subscription = ngx_http_push_stream_create_channel_subscription(r, channel, worker_subscriber)) == NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_push_stream_assing_subscription_to_channel_locked(shpool, cur->id, subscription, &worker_subscriber->subscriptions_sentinel, r->connection->log); } ngx_shmtx_unlock(&shpool->mutex); return NGX_DONE; } ngx_shmtx_unlock(&shpool->mutex); // polling or long polling without messages to send ngx_http_push_stream_add_polling_headers(r, greater_message_time, greater_message_tag, temp_pool); if (!has_message_to_send) { // polling subscriber requests get a 304 with their entity tags preserved if don't have new messages. return ngx_http_push_stream_send_only_header_response(r, NGX_HTTP_NOT_MODIFIED, NULL); } // polling with messages or long polling without messages to send r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = -1; 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; } if (ctx->callback != NULL) { ngx_http_push_stream_send_response_text(r, ctx->callback->data, ctx->callback->len, 0); ngx_http_push_stream_send_response_text(r, NGX_HTTP_PUSH_STREAM_CALLBACK_INIT_CHUNK.data, NGX_HTTP_PUSH_STREAM_CALLBACK_INIT_CHUNK.len, 0); } cur = channels_ids; while ((cur = (ngx_http_push_stream_requested_channel_t *) ngx_queue_next(&cur->queue)) != channels_ids) { channel = ngx_http_push_stream_find_channel(cur->id, r->connection->log); if (channel == NULL) { // channel not found ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push stream module: unable to allocate shared memory for channel %s", cur->id->data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_push_stream_send_old_messages(r, channel, cur->backtrack_messages, if_modified_since, tag, greater_message_time, greater_message_tag, last_event_id); } if (ctx->callback != NULL) { ngx_http_push_stream_send_response_text(r, NGX_HTTP_PUSH_STREAM_CALLBACK_END_CHUNK.data, NGX_HTTP_PUSH_STREAM_CALLBACK_END_CHUNK.len, 0); } if (cf->footer_template.len > 0) { ngx_http_push_stream_send_response_text(r, cf->footer_template.data, cf->footer_template.len, 0); } ngx_http_push_stream_send_response_text(r, NGX_HTTP_PUSH_STREAM_LAST_CHUNK.data, NGX_HTTP_PUSH_STREAM_LAST_CHUNK.len, 1); return NGX_OK; }
static ngx_int_t ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) { char *sep; u_char *p, *last; ssize_t n; ngx_fd_t fd; struct tm tm; ngx_str_t noname, *name; ngx_uint_t i, frame_rate_num, frame_rate_denom; ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_codec_ctx_t *codec_ctx; ngx_rtmp_dash_frag_t *f; ngx_rtmp_dash_app_conf_t *dacf; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; static u_char start_time[sizeof("1970-09-28T12:00:00+06:00")]; static u_char end_time[sizeof("1970-09-28T12:00:00+06:00")]; static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); if (dacf == NULL || ctx == NULL || codec_ctx == NULL) { return NGX_ERROR; } if (ctx->id == 0) { ngx_rtmp_dash_write_init_segments(s); } fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: open failed: '%V'", &ctx->playlist_bak); return NGX_ERROR; } #define NGX_RTMP_DASH_MANIFEST_HEADER \ "<?xml version=\"1.0\"?>\n" \ "<MPD\n" \ " type=\"dynamic\"\n" \ " xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n" \ " availabilityStartTime=\"%s\"\n" \ " availabilityEndTime=\"%s\"\n" \ " minimumUpdatePeriod=\"PT%uiS\"\n" \ " minBufferTime=\"PT%uiS\"\n" \ " timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \ " suggestedPresentationDelay=\"PT%uiS\"\n" \ " profiles=\"urn:hbbtv:dash:profile:isoff-live:2012," \ "urn:mpeg:dash:profile:isoff-live:2011\"\n" \ " xmlns:xsi=\"http://www.w3.org/2011/XMLSchema-instance\"\n" \ " xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd\">\n" \ " <Period start=\"PT0S\" id=\"dash\">\n" #define NGX_RTMP_DASH_MANIFEST_VIDEO \ " <AdaptationSet\n" \ " id=\"1\"\n" \ " segmentAlignment=\"true\"\n" \ " maxWidth=\"%ui\"\n" \ " maxHeight=\"%ui\"\n" \ " maxFrameRate=\"%s\">\n" \ " <Representation\n" \ " id=\"%V_H264\"\n" \ " mimeType=\"video/mp4\"\n" \ " codecs=\"avc1.%02uxi%02uxi%02uxi\"\n" \ " width=\"%ui\"\n" \ " height=\"%ui\"\n" \ " frameRate=\"%s\"\n" \ " sar=\"1:1\"\n" \ " startWithSAP=\"1\"\n" \ " bandwidth=\"%ui\">\n" \ " <SegmentTemplate\n" \ " presentationTimeOffset=\"0\"\n" \ " timescale=\"1000\"\n" \ " media=\"%V%s$Time$.m4v\"\n" \ " initialization=\"%V%sinit.m4v\">\n" \ " <SegmentTimeline>\n" #define NGX_RTMP_DASH_MANIFEST_VIDEO_FOOTER \ " </SegmentTimeline>\n" \ " </SegmentTemplate>\n" \ " </Representation>\n" \ " </AdaptationSet>\n" #define NGX_RTMP_DASH_MANIFEST_TIME \ " <S t=\"%uD\" d=\"%uD\"/>\n" #define NGX_RTMP_DASH_MANIFEST_AUDIO \ " <AdaptationSet\n" \ " id=\"2\"\n" \ " segmentAlignment=\"true\">\n" \ " <AudioChannelConfiguration\n" \ " schemeIdUri=\"urn:mpeg:dash:" \ "23003:3:audio_channel_configuration:2011\"\n" \ " value=\"1\"/>\n" \ " <Representation\n" \ " id=\"%V_AAC\"\n" \ " mimeType=\"audio/mp4\"\n" \ " codecs=\"mp4a.%s\"\n" \ " audioSamplingRate=\"%ui\"\n" \ " startWithSAP=\"1\"\n" \ " bandwidth=\"%ui\">\n" \ " <SegmentTemplate\n" \ " presentationTimeOffset=\"0\"\n" \ " timescale=\"1000\"\n" \ " media=\"%V%s$Time$.m4a\"\n" \ " initialization=\"%V%sinit.m4a\">\n" \ " <SegmentTimeline>\n" #define NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER \ " </SegmentTimeline>\n" \ " </SegmentTemplate>\n" \ " </Representation>\n" \ " </AdaptationSet>\n" #define NGX_RTMP_DASH_MANIFEST_FOOTER \ " </Period>\n" \ "</MPD>\n" ngx_libc_localtime(ctx->start_time.sec + ngx_rtmp_dash_get_frag(s, 0)->timestamp / 1000, &tm); *ngx_sprintf(start_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ctx->start_time.gmtoff < 0 ? '-' : '+', ngx_abs(ctx->start_time.gmtoff / 60), ngx_abs(ctx->start_time.gmtoff % 60)) = 0; ngx_libc_localtime(ctx->start_time.sec + (ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp + ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->duration) / 1000, &tm); *ngx_sprintf(end_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ctx->start_time.gmtoff < 0 ? '-' : '+', ngx_abs(ctx->start_time.gmtoff / 60), ngx_abs(ctx->start_time.gmtoff % 60)) = 0; last = buffer + sizeof(buffer); p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, start_time, end_time, (ngx_uint_t) (dacf->fraglen / 1000), (ngx_uint_t) (dacf->fraglen / 1000), (ngx_uint_t) (dacf->fraglen / 500)); n = ngx_write_fd(fd, buffer, p - buffer); ngx_str_null(&noname); name = (dacf->nested ? &noname : &ctx->name); sep = (dacf->nested ? "" : "-"); if (ctx->has_video) { frame_rate_num = (ngx_uint_t) (codec_ctx->frame_rate * 1000.); if (frame_rate_num % 1000 == 0) { *ngx_sprintf(frame_rate, "%ui", frame_rate_num / 1000) = 0; } else { frame_rate_denom = 1000; switch (frame_rate_num) { case 23976: frame_rate_num = 24000; frame_rate_denom = 1001; break; case 29970: frame_rate_num = 30000; frame_rate_denom = 1001; break; case 59940: frame_rate_num = 60000; frame_rate_denom = 1001; break; } *ngx_sprintf(frame_rate, "%ui/%ui", frame_rate_num, frame_rate_denom) = 0; } p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_VIDEO, codec_ctx->width, codec_ctx->height, frame_rate, &ctx->name, codec_ctx->avc_profile, codec_ctx->avc_compat, codec_ctx->avc_level, codec_ctx->width, codec_ctx->height, frame_rate, (ngx_uint_t) (codec_ctx->video_data_rate * 1000), name, sep, name, sep); for (i = 0; i < ctx->nfrags; i++) { f = ngx_rtmp_dash_get_frag(s, i); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, f->timestamp, f->duration); } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VIDEO_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); } if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_AUDIO, &ctx->name, codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? (codec_ctx->aac_sbr ? "40.5" : "40.2") : "6b", codec_ctx->sample_rate, (ngx_uint_t) (codec_ctx->audio_data_rate * 1000), name, sep, name, sep); for (i = 0; i < ctx->nfrags; i++) { f = ngx_rtmp_dash_get_frag(s, i); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, f->timestamp, f->duration); } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); } p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); if (n < 0) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: write failed: '%V'", &ctx->playlist_bak); ngx_close_file(fd); return NGX_ERROR; } ngx_close_file(fd); if (ngx_rtmp_dash_rename_file(ctx->playlist_bak.data, ctx->playlist.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: rename failed: '%V'->'%V'", &ctx->playlist_bak, &ctx->playlist); return NGX_ERROR; } return NGX_OK; }
static ngx_int_t ngx_limit_tcp_lookup(ngx_connection_t *c, ngx_limit_tcp_ctx_t *ctx, ngx_uint_t *ep, ngx_limit_tcp_node_t **rnode) { size_t n; uint32_t hash; ngx_str_t addr; ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_limit_tcp_node_t *lr; addr = c->addr_text; hash = ngx_crc32_short(addr.data, addr.len); node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; rc = -1; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ lr = (ngx_limit_tcp_node_t *) &node->color; rc = ngx_memn2cmp(addr.data, lr->data, addr.len, (size_t) lr->len); if (rc == 0) { *rnode = lr; ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, "limit tcp count %ui %p", lr->count, c); if (ctx->concurrent && lr->count >= ctx->concurrent) { ngx_log_error(NGX_LOG_WARN, c->log, 0, "limit tcp %V over concurrent: %ui", &c->addr_text, lr->count); return NGX_BUSY; } (void) ngx_atomic_fetch_add(&lr->count, 1); if (!ctx->rate) { return NGX_OK; } tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } *ep = excess; if ((ngx_uint_t) excess > ctx->burst) { ngx_log_error(NGX_LOG_WARN, c->log, 0, "limit %V over rate: %i", &c->addr_text, excess); (void) ngx_atomic_fetch_add(&lr->count, -1); return NGX_BUSY; } lr->excess = excess; lr->last = now; if (excess) { return NGX_AGAIN; } return NGX_OK; } node = (rc < 0) ? node->left : node->right; } *ep = 0; n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_limit_tcp_node_t, data) + addr.len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_limit_tcp_expire(c, ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } } tp = ngx_timeofday(); lr = (ngx_limit_tcp_node_t *) &node->color; node->key = hash; lr->len = (u_char) addr.len; lr->excess = 0; lr->count = 1; lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ngx_memcpy(lr->data, addr.data, addr.len); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, "limit tcp new %ui %uV", lr->count, &addr); *rnode = lr; return NGX_OK; }
static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash, u_char *data, size_t len, ngx_uint_t *ep, ngx_uint_t account) { size_t size; ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ctx = limit->shm_zone->data; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ lr = (ngx_http_limit_req_node_t *) &node->color; rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len); if (rc == 0) { ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } *ep = excess; if ((ngx_uint_t) excess > limit->burst) { return NGX_BUSY; } if (account) { lr->excess = excess; lr->last = now; return NGX_OK; } lr->count++; ctx->node = lr; return NGX_AGAIN; } node = (rc < 0) ? node->left : node->right; } *ep = 0; size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_req_node_t, data) + len; ngx_http_limit_req_expire(ctx, 1); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_http_limit_req_expire(ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { return NGX_ERROR; } } node->key = hash; lr = (ngx_http_limit_req_node_t *) &node->color; lr->len = (u_char) len; lr->excess = 0; ngx_memcpy(lr->data, data, len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); if (account) { lr->last = now; lr->count = 0; return NGX_OK; } lr->last = 0; lr->count = 1; ctx->node = lr; return NGX_AGAIN; }
static ngx_int_t ngx_http_ip_behavior_lookup(ngx_http_request_t *r, ngx_uint_t hash, ngx_http_ip_behavior_ctx_t *ctx, ngx_http_ip_behavior_conf_t *ilcf) { size_t size; ngx_int_t rc; ngx_time_t *tp; ngx_msec_t now; ngx_rbtree_node_t *node, *sentinel; ngx_http_ip_behavior_node_t *ibn; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; rc = -1; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ ibn = (ngx_http_ip_behavior_node_t *) &node->color; rc = ngx_memn2cmp(r->connection->addr_text.data, ibn->addr, r->connection->addr_text.len, (size_t) ibn->len); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior lookup is : %i", rc); if (rc == 0) { /* found the node */ /* check expire */ if (ngx_abs((ngx_msec_int_t)(now - ibn->last)) >= ctx->sample_cycle) { /* node is expired, clear counters */ ibn->insensitive = 0; ibn->total = 0; ibn->bad_response = 0; } ngx_queue_remove(&ibn->queue); ngx_queue_insert_head(&ctx->sh->queue, &ibn->queue); ibn->last = now; if (ilcf->type & TYPE_SENSITIVE_URL) { if (!ilcf->sensitive) { ibn->insensitive++; } } else if (ilcf->type & TYPE_BAD_RESPONSE) { /* TODO: support bad response behavior */ } else { /* should never be here */ } /* total can be zero when it grows to big, * so need to reset all the counters */ if (++ibn->total == 0) { ibn->insensitive = 0; ibn->bad_response = 0; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior node reset: %V", &r->connection->addr_text); } if (ibn->total >= (ngx_uint_t)ctx->sample_base) { r->insensitive_percent = (float)ibn->insensitive / (float)ibn->total * 100; r->bad_response_percent = (float)ibn->bad_response / (float)ibn->total * 100; } ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior find node: %V, total: %i, insens: %i," " percent: %i", &r->connection->addr_text, ibn->total, ibn->insensitive, r->insensitive_percent); return NGX_OK; } node = (rc < 0) ? node->left : node->right; } /* create a new node */ size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_ip_behavior_node_t, addr) + r->connection->addr_text.len; node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_http_ip_behavior_expire(ctx, 1); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { return NGX_ERROR; } } node->key = hash; ibn = (ngx_http_ip_behavior_node_t *) &node->color; ibn->len = (u_char) r->connection->addr_text.len; ngx_memcpy(ibn->addr, r->connection->addr_text.data, r->connection->addr_text.len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &ibn->queue); ibn->last = now; ibn->total = 1; ibn->insensitive = !ilcf->sensitive; ibn->bad_response = 0; ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ip behavior new node: %V, total: %i, insens: %i," " percent: %i", &r->connection->addr_text, ibn->total, ibn->insensitive, r->insensitive_percent); return NGX_OK; }
ngx_http_reqstat_rbnode_t * ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val) { size_t size, len; uint32_t hash; ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; ngx_queue_t *q; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_reqstat_ctx_t *ctx; ngx_http_reqstat_rbnode_t *rs; ctx = shm_zone->data; hash = ngx_murmur_hash2(val->data, val->len); node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ngx_shmtx_lock(&ctx->shpool->mutex); while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ rs = (ngx_http_reqstat_rbnode_t *) &node->color; /* len < node->len */ if (val->len < (size_t) rs->len) { node = node->left; continue; } rc = ngx_strncmp(val->data, rs->data, (size_t) rs->len); if (rc == 0) { ms = (ngx_msec_int_t) (now - rs->last_visit); rs->excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000 + 1000; rs->last_visit = now; if (rs->excess > 0) { ngx_queue_remove(&rs->visit); ngx_queue_insert_head(&ctx->sh->visit, &rs->visit); } ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup exist: %*s", rs->len, rs->data); ngx_shmtx_unlock(&ctx->shpool->mutex); return rs; } node = (rc < 0) ? node->left : node->right; } rc = 0; node = NULL; size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_reqstat_rbnode_t, data) + ctx->key_len; if (ctx->alloc_already_fail == 0) { node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ctx->alloc_already_fail = 1; } } if (node == NULL) { /* try to free a vacant node */ q = ngx_queue_last(&ctx->sh->visit); rs = ngx_queue_data(q, ngx_http_reqstat_rbnode_t, visit); ms = (ngx_msec_int_t) (now - rs->last_visit); excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000; ngx_log_debug3(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup try recycle: %*s, %d", rs->len, rs->data, excess); if (excess < 0) { rc = 1; node = (ngx_rbtree_node_t *) ((char *) rs - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_queue_remove(&rs->visit); ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup recycle: %*s", rs->len, rs->data); rs->conn_total = 0; ngx_memzero((void *) &rs->bytes_in, size - offsetof(ngx_rbtree_node_t, color) - offsetof(ngx_http_reqstat_rbnode_t, bytes_in)); } else { ngx_shmtx_unlock(&ctx->shpool->mutex); return NULL; } } node->key = hash; rs = (ngx_http_reqstat_rbnode_t *) &node->color; len = ngx_min(ctx->key_len, (ssize_t) val->len); ngx_memcpy(rs->data, val->data, len); rs->len = len; ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->visit, &rs->visit); if (!rc) { ngx_queue_insert_head(&ctx->sh->queue, &rs->queue); } rs->last_visit = now; rs->excess = 1000; ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup build: %*s", rs->len, rs->data); ngx_shmtx_unlock(&ctx->shpool->mutex); return rs; }
static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf, ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lrp) { ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; ctx = lrcf->shm_zone->data; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ do { lr = (ngx_http_limit_req_node_t *) &node->color; rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len); if (rc == 0) { tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } lr->excess = excess; lr->last = now; *lrp = lr; if ((ngx_uint_t) excess > lrcf->burst) { return NGX_BUSY; } if (excess) { return NGX_AGAIN; } return NGX_OK; } node = (rc < 0) ? node->left : node->right; } while (node != sentinel && hash == node->key); break; } *lrp = NULL; return NGX_DECLINED; }
static ngx_int_t ngx_http_limit_req2_lookup(ngx_http_request_t *r, ngx_http_limit_req2_t *limit_req2, ngx_uint_t hash, ngx_uint_t *ep) { u_char *lr_data, *lr_last; size_t lr_vv_len; ngx_int_t rc, excess; ngx_uint_t i; ngx_time_t *tp; ngx_msec_t now; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_limit_req2_ctx_t *ctx; ngx_http_limit_req2_node_t *lr; ngx_http_variable_value_t *vv; ngx_http_limit_req2_variable_t *lrv; ctx = limit_req2->shm_zone->data; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; rc = -1; lrv = ctx->limit_vars->elts; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ lr = (ngx_http_limit_req2_node_t *) &node->color; lr_data = lr->data; lr_last = lr_data + lr->len; for (i = 0; i < ctx->limit_vars->nelts; i++) { vv = ngx_http_get_indexed_variable(r, lrv[i].index); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit_req2 vv is %i %v node is %s", lrv[i].index, vv, lr_data); lr_vv_len = ngx_min(lr_last - lr_data, vv->len); if ((rc = ngx_memcmp(vv->data, lr_data, lr_vv_len)) != 0) { break; } if (lr_vv_len != vv->len) { rc = 1; break; } /* lr_vv_len == vv->len */ lr_data += lr_vv_len; } if (rc == 0 && lr_last > lr_data) { rc = -1; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit_req2 lookup is : %i, size is %i", rc, ctx->limit_vars->nelts); if (rc == 0) { ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } *ep = excess; if ((ngx_uint_t) excess > limit_req2->burst) { return NGX_BUSY; } lr->excess = excess; lr->last = now; if (excess) { return NGX_AGAIN; } return NGX_OK; } node = (rc < 0) ? node->left : node->right; } *ep = 0; return NGX_DECLINED; }
static ngx_int_t ngx_http_limit_req_lookup(ngx_http_request_t *r, ngx_http_limit_req_limit_t *limit, ngx_uint_t hash, size_t len, ngx_uint_t *ep, ngx_uint_t account) { size_t size; ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lr; u_char *lr_data, *lr_last; ngx_http_variable_value_t *vv; ngx_http_limit_req_variable_t *lrv; size_t lr_vv_len; ngx_uint_t i; ngx_http_limit_req_variable_t *cond; ngx_http_variable_value_t *cond_vv; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ctx = limit->shm_zone->data; cond = &limit->condition; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; rc = -1; lrv = ctx->limit_vars->elts; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ lr = (ngx_http_limit_req_node_t *) &node->color; lr_data = lr->data; lr_last = lr_data + lr->len; for (i = 0; i < ctx->limit_vars->nelts; i++) { vv = ngx_http_get_indexed_variable(r, lrv[i].index); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit vv is %i %v node is %s", lrv[i].index, vv, lr_data); lr_vv_len = ngx_min(lr_last - lr_data, vv->len); if ((rc = ngx_memcmp(vv->data, lr_data, lr_vv_len)) != 0) { break; } if (lr_vv_len != vv->len) { rc = 1; break; } /* lr_vv_len == vv->len */ lr_data += lr_vv_len; } if (rc == 0 && lr_last > lr_data) { rc = -1; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit lookup is : %i, size is %ui", rc, ctx->limit_vars->nelts); if (rc == 0) { if (cond->index != -1) { /* need to check condition */ cond_vv = ngx_http_get_indexed_variable(r, cond->index); if (cond_vv == NULL || cond_vv->not_found) { goto out; } if (cond_vv->len == 0) { goto out; } if (ngx_memcmp(cond_vv->data, "1", 1) != 0) { goto out; } } ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); ms = (ngx_msec_int_t) (now - lr->last); excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; if (excess < 0) { excess = 0; } *ep = excess; if ((ngx_uint_t) excess > limit->burst) { return NGX_BUSY; } if (account) { lr->excess = excess; lr->last = now; return NGX_OK; } lr->count++; ctx->node = lr; out: return NGX_AGAIN; } node = (rc < 0) ? node->left : node->right; } *ep = 0; size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_req_node_t, data) + len; ngx_http_limit_req_expire(ctx, 1); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_http_limit_req_expire(ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "could not allocate node%s", ctx->shpool->log_ctx); return NGX_ERROR; } } node->key = hash; lr = (ngx_http_limit_req_node_t *) &node->color; lr->len = (u_char) len; lr->excess = 0; ngx_http_limit_req_copy_variables(r, (uint32_t *)&hash, ctx, lr); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); if (account) { lr->last = now; lr->count = 0; return NGX_OK; } lr->last = 0; lr->count = 1; ctx->node = lr; return NGX_AGAIN; }