static void ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch) { ngx_int_t i; for (i = 0; i < ngx_last_process; i++) { if (i == ngx_process_slot || ngx_processes[i].pid == -1 || ngx_processes[i].channel[0] == -1) { continue; } ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d", ch->slot, ch->pid, ch->fd, i, ngx_processes[i].pid, ngx_processes[i].channel[0]); /* TODO: NGX_AGAIN */ ngx_write_channel(ngx_processes[i].channel[0], ch, sizeof(ngx_channel_t), cycle->log); } }
static ngx_uint_t ngx_http_viewer_cache_node_count(ngx_http_file_cache_t *cache) { ngx_queue_t *q; ngx_uint_t count; ngx_http_file_cache_node_t *fcn; count = 0; ngx_shmtx_lock(&cache->shpool->mutex); for (q = ngx_queue_last(&cache->sh->queue); q != ngx_queue_sentinel(&cache->sh->queue); q = ngx_queue_prev(q)) { fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http viewer file cache: #%d %d %02xd%02xd%02xd%02xd", fcn->count, fcn->exists, fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); count++; } ngx_shmtx_unlock(&cache->shpool->mutex); return count; }
/* 将刚创建的子进程的消息发送给其他所有的子进程,特别是进程的pid * 和进程通信的socket文件描述符以及进程在进程数组中的索引 */ static void ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch) { ngx_int_t i; /* 将ch信息发送给其他的所有存活进程 */ for (i = 0; i < ngx_last_process; i++) { if (i == ngx_process_slot || ngx_processes[i].pid == -1 || ngx_processes[i].channel[0] == -1) { continue; } ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d", ch->slot, ch->pid, ch->fd, i, ngx_processes[i].pid, ngx_processes[i].channel[0]); /* TODO: NGX_AGAIN */ /* 为啥是向其他进程的0通道发送? * 进程的1通道都是用来读取的,0通道都是其他进程用来写的, * 和进程进行通信的 */ ngx_write_channel(ngx_processes[i].channel[0], ch, sizeof(ngx_channel_t), cycle->log); } }
static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type) { ngx_int_t i; ngx_channel_t ch; struct itimerval itv; ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "start worker processes"); ch.command = NGX_CMD_OPEN_CHANNEL; while (n--) { ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL, "worker process", type); ch.pid = ngx_processes[ngx_process_slot].pid; ch.slot = ngx_process_slot; ch.fd = ngx_processes[ngx_process_slot].channel[0]; for (i = 0; i < ngx_last_process; i++) { if (i == ngx_process_slot || ngx_processes[i].pid == -1 || ngx_processes[i].channel[0] == -1) { continue; } ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, "pass channel s:%d pid:" PID_T_FMT " fd:%d to s:%d pid:" PID_T_FMT " fd:%d", ch.slot, ch.pid, ch.fd, i, ngx_processes[i].pid, ngx_processes[i].channel[0]); /* TODO: NGX_AGAIN */ ngx_write_channel(ngx_processes[i].channel[0], &ch, sizeof(ngx_channel_t), cycle->log); } } /* * we have to limit the maximum life time of the worker processes * by 10 days because our millisecond event timer is limited * by 24 days on 32-bit platforms */ itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; itv.it_value.tv_sec = 10 * 24 * 60 * 60; itv.it_value.tv_usec = 0; if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setitimer() failed"); } }
static ngx_inline void ngx_kqueue_dump_event(ngx_log_t *log, struct kevent *kev) { if (kev->ident > 0x8000000 && kev->ident != (unsigned) -1) { ngx_log_debug6(NGX_LOG_DEBUG_EVENT, log, 0, "kevent: %p: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p", (void *) kev->ident, kev->filter, kev->flags, kev->fflags, (int) kev->data, kev->udata); } else { ngx_log_debug6(NGX_LOG_DEBUG_EVENT, log, 0, "kevent: %d: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p", (int) kev->ident, kev->filter, kev->flags, kev->fflags, (int) kev->data, kev->udata); } }
static ngx_int_t ngx_rtmp_access_inet6(ngx_rtmp_session_t *s, u_char *p, ngx_uint_t flag) { ngx_uint_t n; ngx_uint_t i; ngx_rtmp_access_rule6_t *rule6; ngx_rtmp_access_app_conf_t *ascf; ascf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_access_module); rule6 = ascf->rules6.elts; for (i = 0; i < ascf->rules6.nelts; i++) { #if (NGX_DEBUG) { size_t cl, ml, al; u_char ct[NGX_INET6_ADDRSTRLEN]; u_char mt[NGX_INET6_ADDRSTRLEN]; u_char at[NGX_INET6_ADDRSTRLEN]; cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN); ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN); al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN); ngx_log_debug6(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, "access: %*s %*s %*s", cl, ct, ml, mt, al, at); } #endif for (n = 0; n < 16; n++) { if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) { goto next; } } if (flag & rule6[i].flags) { return ngx_rtmp_access_found(s, rule6[i].deny); } next: continue; } return NGX_OK; }
static ngx_int_t ngx_http_access_plus_inet6(ngx_http_request_t *r, ngx_http_access_plus_loc_conf_t *alcf, u_char *p) { ngx_uint_t n; ngx_uint_t i; ngx_http_access_plus_rule6_t *rule6; rule6 = alcf->rules6->elts; for (i = 0; i < alcf->rules6->nelts; i++) { if (!(r->method & rule6[i].methods)) { continue; } #if (NGX_DEBUG) { size_t cl, ml, al; u_char ct[NGX_INET6_ADDRSTRLEN]; u_char mt[NGX_INET6_ADDRSTRLEN]; u_char at[NGX_INET6_ADDRSTRLEN]; cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN); ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN); al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN); ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "access: %*s %*s %*s", cl, ct, ml, mt, al, at); } #endif for (n = 0; n < 16; n++) { if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) { goto next; } } return ngx_http_access_plus_found(r, rule6[i].deny); next: continue; } return NGX_DECLINED; }
static ngx_int_t ngx_stream_access_inet6(ngx_stream_session_t *s, ngx_stream_access_srv_conf_t *ascf, u_char *p) { ngx_uint_t n; ngx_uint_t i; ngx_stream_access_rule6_t *rule6; rule6 = ascf->rules6->elts; for (i = 0; i < ascf->rules6->nelts; i++) { #if (NGX_DEBUG) { size_t cl, ml, al; u_char ct[NGX_INET6_ADDRSTRLEN]; u_char mt[NGX_INET6_ADDRSTRLEN]; u_char at[NGX_INET6_ADDRSTRLEN]; cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN); ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN); al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN); ngx_log_debug6(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "access: %*s %*s %*s", cl, ct, ml, mt, al, at); } #endif for (n = 0; n < 16; n++) { if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) { goto next; } } return ngx_stream_access_found(s, rule6[i].deny); next: continue; } return NGX_DECLINED; }
static ngx_int_t ngx_limit_tcp_inet6(ngx_connection_t *c, ngx_limit_tcp_conf_t *ltcf, u_char *p) { ngx_uint_t n; ngx_uint_t i; ngx_limit_tcp_rule6_t *rule6; rule6 = ltcf->rules6->elts; for (i = 0; i < ltcf->rules6->nelts; i++) { #if (NGX_DEBUG) { size_t cl, ml, al; u_char ct[NGX_INET6_ADDRSTRLEN]; u_char mt[NGX_INET6_ADDRSTRLEN]; u_char at[NGX_INET6_ADDRSTRLEN]; cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN); ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN); al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN); ngx_log_debug6(NGX_LOG_DEBUG_CORE, c->log, 0, "access: %*s %*s %*s", cl, ct, ml, mt, al, at); } #endif for (n = 0; n < 16; n++) { if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) { goto next; } } return (rule6[i].deny ? NGX_BUSY : NGX_DECLINED); next: continue; } return NGX_OK; }
static ngx_int_t ngx_http_cloudrouter_init_upstream_peer(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us) { struct sockaddr_in* sin; ngx_http_hs_main_conf_t *hscf; ngx_http_cloudrouter_peer_t *peer; hs_route_t *route; int tcb_s; hscf = ngx_http_get_module_main_conf(r, ngx_http_hs_module); if (hscf == NULL || hscf->tcb_route_db == NULL) { return NGX_ERROR; } peer = ngx_pcalloc(r->pool, sizeof(ngx_http_cloudrouter_peer_t)); if (peer == NULL) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "NULL peer"); return NGX_ERROR; } peer->route = NULL; peer->socklen = sizeof(struct sockaddr_in); peer->sockaddr = (struct sockaddr*)ngx_pcalloc(r->pool, peer->socklen); if (peer->sockaddr == NULL) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "ALERT: peer->sockaddr alloc failed"); return NGX_ERROR; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "init_peer"); /* fallback = 404 host */ ngx_str_set(&peer->name, "127.0.0.1"); peer->inet_addr = ngx_inet_addr(peer->name.data, peer->name.len); peer->port_n = htons(8404); r->upstream->peer.data = peer; r->upstream->peer.free = ngx_http_cloudrouter_free_upstream_peer; r->upstream->peer.get = ngx_http_cloudrouter_get_upstream_peer; r->upstream->peer.tries = 1; // fetch config for client-supplied Host if (r->headers_in.server.len > 0) { route = (hs_route_t*)tcbdbget(hscf->tcb_route_db, r->headers_in.server.data, r->headers_in.server.len, &tcb_s); } else { route = NULL; } if (route == NULL) { // send user to 404 host ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "CloudRouter: no route matched"); return NGX_OK; } else { /* copy route into peer, managed memory */ peer->route = ngx_pcalloc(r->pool, sizeof(hs_route_t)); if (peer->route==NULL) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "ALERT: alloc failed"); free(route); return NGX_ERROR; } (void)ngx_copy(peer->route, route, sizeof(hs_route_t)); free(route); // gets malloc()'d by TC route = peer->route; peer->name.len = route->di_nlen; peer->name.data = ngx_pcalloc(r->pool, peer->name.len); (void)ngx_copy(peer->name.data, route->di_name, peer->name.len); ngx_shmtx_lock(&ngx_http_cloudrouter_shpool->mutex); ngx_http_cloudrouter_node_t *e = ngx_http_cloudrouter_get_locked(route); if (e && e->timestamp > (r->start_sec - NGX_HTTP_HS_CACHE_TIMEOUT)) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "cache hit"); ngx_http_cloudrouter_remote_t *remote = e->remote; ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "cached values READ: %p:%d:%s[%d],(%uxD,%d)", e, e->timestamp, e->di_name, e->di_nlen, remote->inet_addr, remote->port_n); ngx_http_cloudrouter_set_hostandport(r,peer,e); ngx_shmtx_unlock(&ngx_http_cloudrouter_shpool->mutex); } else { /* entry has expired. * pretend it does not exist, the preconnect handler will overwrite it. */ int current = 0; if (e) { ngx_http_cloudrouter_remote_t *remote = e->remote; while (remote != NULL) { current += remote->current; remote = remote->next; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "CloudRouter: cache entry expired"); } else { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "CloudRouter: no entry in cache"); } ngx_shmtx_unlock(&ngx_http_cloudrouter_shpool->mutex); peer->sendbuf.data = ngx_pcalloc(r->pool, NGX_HTTP_CLOUDROUTER_PRECONNECT_SENDBUFSIZE); if (peer->sendbuf.data == NULL) { ngx_log_error(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "alloc peer->sendbuf.data failed, aborting request"); return NGX_ERROR; } peer->sendbuf.len = ngx_sprintf(peer->sendbuf.data, "BLNC %*s %ud", peer->route->di_nlen, peer->route->di_name, current) - peer->sendbuf.data; ngx_shmtx_unlock(&ngx_http_cloudrouter_shpool->mutex); r->upstream->peer.preconnect = (ngx_event_preconnect_pt)ngx_http_cloudrouter_peer_preconnect; } return NGX_OK; } /* never reached */ }
static void ngx_http_transfer_body(ngx_http_request_t* r) { // already get the whole request body ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "enter ngx_http_transfer_body"); unsigned long int start_time, end_time; start_time = ngx_http_pbmsgpack_get_time_us(); // first, set ctx read request flag to 0 ngx_http_pbmsgpack_transfer_ctx_t* ctx = NULL; ctx = (ngx_http_pbmsgpack_transfer_ctx_t*) ngx_http_get_module_ctx(r, ngx_http_pb_msgpack_transfer_module); if(NULL == ctx) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "[req_body_transfer] unexpected emmpty ctx!"); ngx_http_finalize_request(r,NGX_DONE); return; } ctx->is_waiting_body = 0; // second, process data ngx_chain_t* bufs = r->request_body->bufs; if(NULL == bufs) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request bufs is NULL!"); ngx_http_finalize_request(r,NGX_DONE); return; } //try deal //first , get boundary info from header; char bound[g_max_content_type_size]; int ret = get_multipart_form_header_info(r, ctx->content_type_header, bound); if(0 != ret) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "pb msgpack get_multipart_form_header_info failed!"); ngx_http_finalize_request(r,NGX_DONE); return ; } // check cmd int cmd = ngx_http_pbmsgpack_get_cmd(r); if(cmd <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "illegal cmd is: %d", cmd); ngx_http_finalize_request(r,NGX_DONE); return; } ngx_int_t total_size = 0; ngx_int_t buf_num = 0; ngx_buf_t* temp_buf = NULL; ngx_chain_t* temp_chain = bufs; ctx->pb_buf_size = ctx->msgpack_buf_size = g_pbmsgpack_buf_size; ctx->pb_buf_pos = ctx->msgpack_pos = 0; ctx->pb_buf = (u_char*)ngx_pcalloc(r->pool, g_pbmsgpack_buf_size); // re-use pb buffer, ctx->msgpack_buf = ctx->pb_buf; while(NULL != temp_chain) { temp_buf = temp_chain->buf; if(NULL != temp_buf) { total_size += (int)(temp_buf->last - temp_buf->pos); ++ buf_num; } temp_chain = temp_chain->next; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[req_body_transfer]total requst body size:%d ,bufs:%d", total_size, buf_num); if(total_size >= g_pbmsgpack_buf_size) { //TODO ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "illegal request body size![now:%d][limit:%d]", total_size, g_pbmsgpack_buf_size); ngx_http_finalize_request(r,NGX_DONE); return; } temp_chain = bufs; while(NULL != temp_chain) { temp_buf = temp_chain->buf; if(NULL != temp_buf) { total_size = (int)(temp_buf->last - temp_buf->pos); memcpy(ctx->pb_buf + ctx->pb_buf_pos , temp_buf->pos, total_size); ctx->pb_buf_pos += total_size; } temp_chain = temp_chain->next; } //get the whole request body ctx->pb_buf[ ctx->pb_buf_pos ] = '\0'; int data_start_pos; int data_end_pos; ret = get_multipart_form_info( (char*)ctx->pb_buf, ctx->pb_buf_pos,bound, g_tag_name, data_start_pos, data_end_pos); if(0 != ret) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "pb msgpack get_multipart_form_info failed!"); ngx_http_finalize_request(r,NGX_DONE); return ; } // 节省空间,拷贝两次 // 上面的pos是闭区间 int temp_copy_size = ctx->pb_buf_pos - data_end_pos + 1; int temp_src_copy_pos = data_end_pos; int temp_dst_copy_pos = g_pbmsgpack_buf_size - temp_copy_size ; int raw_data_size = data_end_pos - data_start_pos ; int old_data_size = raw_data_size; memmove(ctx->pb_buf + temp_dst_copy_pos, ctx->pb_buf + temp_src_copy_pos, temp_copy_size ); ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pb msgpack post body start:%d,end:%d,copy:%d,new_copy_pos:%d,old_data:%d,total:%d", data_start_pos, data_end_pos, temp_copy_size, temp_dst_copy_pos, old_data_size, ctx->pb_buf_pos); ret = pkg_pb2msgpack(g_ctx, ctx->pb_buf + data_start_pos, & raw_data_size, g_pbmsgpack_buf_size - temp_copy_size - data_start_pos, cmd, PKG_UPSTREAM); if(0 != ret) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "post body trans failed! pkg_pb2msgpack return failed!"); ngx_http_finalize_request(r,NGX_DONE); return; } temp_src_copy_pos += raw_data_size - old_data_size; memmove(ctx->pb_buf + temp_src_copy_pos, ctx->pb_buf + temp_dst_copy_pos, temp_copy_size); ctx->pb_buf_pos += raw_data_size - old_data_size; ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pb msgpack post body start:%d,end:%d,copy:%d,new_copy_pos:%d,new_data:%d,total:%d", data_start_pos, data_end_pos, temp_copy_size, temp_dst_copy_pos, raw_data_size, ctx->pb_buf_pos); // first , reset the request body info ngx_chain_t* test_chain = ngx_alloc_chain_link(r->pool); ngx_buf_t* test_buf = (ngx_buf_t*)ngx_calloc_buf(r->pool); test_buf->pos = test_buf->start = ctx->pb_buf; test_buf->last = test_buf->end = (u_char*) (ctx->pb_buf + ctx->pb_buf_pos); test_buf->temporary = 1; test_chain->buf = test_buf; test_chain->next = NULL; r->request_body->bufs = test_chain; r->headers_in.content_length_n = ctx->pb_buf_pos; // second , reset the requset header info , include content-length and x_bd_data_type char* str_content_length = (char*)ngx_pcalloc(r->pool, DEFAULT_STR_BUF_SIZE); int str_size = snprintf(str_content_length, DEFAULT_STR_BUF_SIZE, "%u", r->headers_in.content_length_n); r->headers_in.content_length->value.data = (u_char*) str_content_length; r->headers_in.content_length->value.len = str_size; ctx->data_type_header->value = g_msgpack_str; end_time = ngx_http_pbmsgpack_get_time_us(); ctx->transfer_cost = end_time - start_time; ngx_http_finalize_request(r,NGX_DONE); }
ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes) { syslog(LOG_INFO, "[%s:%s:%d]\n", __FILE__, __func__, __LINE__); u_char c, ch, decoded, *p, *u; enum { sw_usual = 0, sw_slash, sw_dot, sw_dot_dot, sw_quoted, sw_quoted_second } state, quoted_state; #if (NGX_SUPPRESS_WARN) decoded = '\0'; quoted_state = sw_usual; #endif state = sw_usual; p = r->uri_start; u = r->uri.data; r->uri_ext = NULL; r->args_start = NULL; ch = *p++; while (p <= r->uri_end) { /* * we use "ch = *p++" inside the cycle, but this operation is safe, * because after the URI there is always at least one character: * the line feed */ ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[%s:%d]s:%d in:'%Xd:%c', out:'%c'", __func__, __LINE__, state, ch, ch, *u); switch (state) { case sw_usual: if (usual[ch >> 5] & (1 << (ch & 0x1f))) { *u++ = ch; ch = *p++; break; } switch(ch) { #if (NGX_WIN32) case '\\': if (u - 2 >= r->uri.data && *(u - 1) == '.' && *(u - 2) != '.') { u--; } r->uri_ext = NULL; if (p == r->uri_start + r->uri.len) { /* * we omit the last "\" to cause redirect because * the browsers do not treat "\" as "/" in relative URL path */ break; } state = sw_slash; *u++ = '/'; break; #endif case '/': #if (NGX_WIN32) if (u - 2 >= r->uri.data && *(u - 1) == '.' && *(u - 2) != '.') { u--; } #endif r->uri_ext = NULL; state = sw_slash; *u++ = ch; break; case '%': quoted_state = state; state = sw_quoted; break; case '?': r->args_start = p; goto args; case '#': goto done; case '.': r->uri_ext = u + 1; *u++ = ch; break; case '+': r->plus_in_uri = 1; /* fall through */ default: *u++ = ch; break; } ch = *p++; break; case sw_slash: if (usual[ch >> 5] & (1 << (ch & 0x1f))) { state = sw_usual; *u++ = ch; ch = *p++; break; } switch(ch) { #if (NGX_WIN32) case '\\': break; #endif case '/': if (!merge_slashes) { *u++ = ch; } break; case '.': state = sw_dot; *u++ = ch; break; case '%': quoted_state = state; state = sw_quoted; break; case '?': r->args_start = p; goto args; case '#': goto done; case '+': r->plus_in_uri = 1; default: state = sw_usual; *u++ = ch; break; } ch = *p++; break; case sw_dot: if (usual[ch >> 5] & (1 << (ch & 0x1f))) { state = sw_usual; *u++ = ch; ch = *p++; break; } switch(ch) { #if (NGX_WIN32) case '\\': #endif case '/': state = sw_slash; u--; break; case '.': state = sw_dot_dot; *u++ = ch; break; case '%': quoted_state = state; state = sw_quoted; break; case '?': r->args_start = p; goto args; case '#': goto done; case '+': r->plus_in_uri = 1; default: state = sw_usual; *u++ = ch; break; } ch = *p++; break; case sw_dot_dot: if (usual[ch >> 5] & (1 << (ch & 0x1f))) { state = sw_usual; *u++ = ch; ch = *p++; break; } switch(ch) { #if (NGX_WIN32) case '\\': #endif case '/': state = sw_slash; u -= 5; for ( ;; ) { if (u < r->uri.data) { return NGX_HTTP_PARSE_INVALID_REQUEST; } if (*u == '/') { u++; break; } u--; } break; case '%': quoted_state = state; state = sw_quoted; break; case '?': r->args_start = p; goto args; case '#': goto done; case '+': r->plus_in_uri = 1; default: state = sw_usual; *u++ = ch; break; } ch = *p++; break; case sw_quoted: r->quoted_uri = 1; if (ch >= '0' && ch <= '9') { decoded = (u_char) (ch - '0'); state = sw_quoted_second; ch = *p++; break; } c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'f') { decoded = (u_char) (c - 'a' + 10); state = sw_quoted_second; ch = *p++; break; } return NGX_HTTP_PARSE_INVALID_REQUEST; case sw_quoted_second: if (ch >= '0' && ch <= '9') { ch = (u_char) ((decoded << 4) + ch - '0'); if (ch == '%' || ch == '#') { state = sw_usual; *u++ = ch; ch = *p++; break; } else if (ch == '\0') { return NGX_HTTP_PARSE_INVALID_REQUEST; } state = quoted_state; break; } c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'f') { ch = (u_char) ((decoded << 4) + c - 'a' + 10); if (ch == '?') { state = sw_usual; *u++ = ch; ch = *p++; break; } else if (ch == '+') { r->plus_in_uri = 1; } state = quoted_state; break; } return NGX_HTTP_PARSE_INVALID_REQUEST; } } done: r->uri.len = u - r->uri.data; if (r->uri_ext) { r->exten.len = u - r->uri_ext; r->exten.data = r->uri_ext; } r->uri_ext = NULL; return NGX_OK; args: while (p < r->uri_end) { if (*p++ != '#') { continue; } r->args.len = p - 1 - r->args_start; r->args.data = r->args_start; r->args_start = NULL; break; } r->uri.len = u - r->uri.data; if (r->uri_ext) { r->exten.len = u - r->uri_ext; r->exten.data = r->uri_ext; } r->uri_ext = NULL; return NGX_OK; }
static void ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n) { char *err; size_t len; ngx_uint_t i, times, ident, qident, flags, code, nqs, nan, qtype, qclass; ngx_queue_t *q; ngx_resolver_qs_t *qs; ngx_resolver_node_t *rn; ngx_resolver_query_t *query; if ((size_t) n < sizeof(ngx_resolver_query_t)) { goto short_response; } query = (ngx_resolver_query_t *) buf; ident = (query->ident_hi << 8) + query->ident_lo; flags = (query->flags_hi << 8) + query->flags_lo; nqs = (query->nqs_hi << 8) + query->nqs_lo; nan = (query->nan_hi << 8) + query->nan_lo; ngx_log_debug6(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver DNS response %ui fl:%04Xui %ui/%ui/%ui/%ui", ident, flags, nqs, nan, (query->nns_hi << 8) + query->nns_lo, (query->nar_hi << 8) + query->nar_lo); if (!(flags & 0x8000)) { ngx_log_error(r->log_level, r->log, 0, "invalid DNS response %ui fl:%04Xui", ident, flags); return; } code = flags & 0x7f; if (code == NGX_RESOLVE_FORMERR) { times = 0; for (q = ngx_queue_head(&r->name_resend_queue); q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100; q = ngx_queue_next(q)) { rn = ngx_queue_data(q, ngx_resolver_node_t, queue); qident = (rn->query[0] << 8) + rn->query[1]; if (qident == ident) { ngx_log_error(r->log_level, r->log, 0, "DNS error (%ui: %s), query id:%ui, name:\"%*s\"", code, ngx_resolver_strerror(code), ident, rn->nlen, rn->name); return; } } goto dns_error; } if (code > NGX_RESOLVE_REFUSED) { goto dns_error; } if (nqs != 1) { err = "invalid number of questions in DNS response"; goto done; } i = sizeof(ngx_resolver_query_t); while (i < (ngx_uint_t) n) { if (buf[i] == '\0') { goto found; } len = buf[i]; i += 1 + len; } goto short_response; found: if (i++ == 0) { err = "zero-length domain name in DNS response"; goto done; } if (i + sizeof(ngx_resolver_qs_t) + nan * (2 + sizeof(ngx_resolver_an_t)) > (ngx_uint_t) n) { goto short_response; } qs = (ngx_resolver_qs_t *) &buf[i]; qtype = (qs->type_hi << 8) + qs->type_lo; qclass = (qs->class_hi << 8) + qs->class_lo; ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver DNS response qt:%ui cl:%ui", qtype, qclass); if (qclass != 1) { ngx_log_error(r->log_level, r->log, 0, "unknown query class %ui in DNS response", qclass); return; } switch (qtype) { case NGX_RESOLVE_A: ngx_resolver_process_a(r, buf, n, ident, code, nan, i + sizeof(ngx_resolver_qs_t)); break; case NGX_RESOLVE_PTR: ngx_resolver_process_ptr(r, buf, n, ident, code, nan); break; default: ngx_log_error(r->log_level, r->log, 0, "unknown query type %ui in DNS response", qtype); return; } return; short_response: err = "short dns response"; done: ngx_log_error(r->log_level, r->log, 0, err); return; dns_error: ngx_log_error(r->log_level, r->log, 0, "DNS error (%ui: %s), query id:%ui", code, ngx_resolver_strerror(code), ident); return; }
static ngx_int_t ngx_http_touch_handler(ngx_http_request_t *r) { //main config ngx_http_upstream_main_conf_t * umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); //server config //ngx_http_upstream_srv_conf_t * umsf = r->srv_conf[ngx_http_upstream_module.ctx_index]; //location config //ngx_http_core_loc_conf_t * hclf = (*(r->loc_conf)); ngx_http_upstream_srv_conf_t **uscfp, *uscf; ngx_uint_t i, j, len; u_char *p, *b; ngx_chain_t *cl; ngx_http_upstream_server_t *us; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_touch_handler"); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "umcfaddress=%d", umcf); if (umcf == NULL || umcf->upstreams.nelts <= 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "NGX_HTTP_NO_CONTENT"); return NGX_HTTP_NO_CONTENT; } //response content buffer length len = 1024 * 16; p = b = ngx_palloc(r->pool, len); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "umcf->upstreams.nelts=%ui\n", umcf->upstreams.nelts); ngx_http_variable_value_t *upstreamname, *servername; ngx_uint_t hash; hash = ngx_hash_key(arg_upstream.data, arg_upstream.len); upstreamname = ngx_http_get_variable(r, &arg_upstream, hash); hash = ngx_hash_key(arg_server.data, arg_server.len); servername = ngx_http_get_variable(r, &arg_server, hash); p = ngx_slprintf(p, b + len, "Worker id: %P\n", ngx_pid); uscfp = umcf->upstreams.elts; for (i = 0; i < umcf->upstreams.nelts; i++) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "i=%d", i); uscf = uscfp[i]; // ngx_slprintf(start, last, fmt, args) p = ngx_slprintf(p, b + len, "upstream name: %V\n", &uscf->host); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "upstream name:%V", &uscf->host); if(uscf->servers != NULL && uscf->servers->nelts > 0) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "uscf->servers->nelts = %ui", uscf->servers->nelts); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "uscf->servers->size = %ui", uscf->servers->size); for (j = 0; j < uscf->servers->nelts; j++) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "j=%d", j); //us = (ngx_http_upstream_server_t *)(uscf->servers->elts + j * uscf->servers->size); us = (ngx_http_upstream_server_t *)uscf->servers->elts + j; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "us=%d", us); if (us != NULL) { if (upstreamname && upstreamname->not_found == 0 && servername && servername->not_found == 0 && ngx_strncmp(upstreamname->data, uscf->host.data, upstreamname->len) == 0 && ngx_strncmp(servername->data, us->addrs->name.data, servername->len) == 0) { ngx_http_touch_set_upstream_server(r, us, uscf); } ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "usaddress=%d, weight=%d, max_fails=%d, fail_timeout=%d, down=%d, backup=%d", us, us->weight, us->max_fails, us->fail_timeout, us->down, us->backup); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "server name=%V", &us->addrs->name); if (us->addrs != NULL) { // socket to string // parameters :sockaddr, start, max_length, port print? p += ngx_sock_ntop(us->addrs->sockaddr, p, b - p + len, 1); } p = ngx_slprintf(p, b + len, " weight=%d, max_fails=%d, fail_timeout=%d, down=%d, backup=%d\n", us->weight, us->max_fails, us->fail_timeout, us->down, us->backup); } } } } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 1, "ngx_alloc_chain_link"); cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 1, "ngx_alloc_chain_link error"); return NGX_ERROR; } cl->next = NULL; cl->buf = ngx_calloc_buf(r->pool); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 1, "ngx_calloc_buf"); if (cl->buf == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 1, "ngx_calloc_buf error"); return NGX_ERROR; } cl->buf->pos = b; cl->buf->last = p; cl->buf->last_buf = 1;/* this is last , and there will be no more buffers in the request */ cl->buf->memory = 1; /* content is in read-only memory */ /* (i.e., filters should copy it rather than rewrite in place) */ r->headers_out.content_length_n = p - b; r->headers_out.status = NGX_HTTP_OK; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 1, "ngx_http_send_header(r)"); if (ngx_http_send_header(r) != NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 1, "ngx_http_send_header(r) error"); return NGX_ERROR; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 1, "ngx_http_output_filter"); return ngx_http_output_filter(r, cl); }
static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache) { u_char *name, *p; size_t len; time_t now, wait; ngx_path_t *path; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire"); path = cache->path; len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; name = ngx_alloc(len + 1, ngx_cycle->log); if (name == NULL) { return 10; } ngx_memcpy(name, path->name.data, path->name.len); now = ngx_time(); ngx_shmtx_lock(&cache->shpool->mutex); for ( ;; ) { if (ngx_queue_empty(&cache->sh->queue)) { wait = 10; break; } q = ngx_queue_last(&cache->sh->queue); fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); wait = fcn->expire - now; if (wait > 0) { wait = wait > 10 ? 10 : wait; break; } ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire: #%d %d %02xd%02xd%02xd%02xd", fcn->count, fcn->exists, fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); if (fcn->count) { p = ngx_hex_dump(key, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t)); len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t); (void) ngx_hex_dump(p, fcn->key, len); /* * abnormally exited workers may leave locked cache entries, * and although it may be safe to remove them completely, * we prefer to remove them from inactive queue and rbtree * only, and to allow other leaks */ ngx_queue_remove(q); ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "ignore long locked inactive cache entry %*s, count:%d", 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count); continue; } if (!fcn->exists) { ngx_queue_remove(q); ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); ngx_slab_free_locked(cache->shpool, fcn); continue; } ngx_http_file_cache_delete(cache, q, name); } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_free(name); return wait; }
ngx_int_t ngx_rtmp_aggregate_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { uint32_t base_time, timestamp, prev_size; size_t len; ngx_int_t first; u_char *last; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl, *next; ngx_rtmp_header_t ch; ch = *h; first = 1; base_time = 0; while (in) { if (ngx_rtmp_fetch_uint8(&in, &ch.type) != NGX_OK) { return NGX_OK; } if (ngx_rtmp_fetch_uint32(&in, &ch.mlen, 3) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_fetch_uint32(&in, ×tamp, 3) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_fetch_uint8(&in, (uint8_t *) ×tamp + 3) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_fetch_uint32(&in, &ch.msid, 3) != NGX_OK) { return NGX_ERROR; } if (first) { base_time = timestamp; first = 0; } ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "RTMP aggregate %s (%d) len=%uD time=%uD (+%D) msid=%uD", ngx_rtmp_message_type(ch.type), (ngx_int_t) ch.type, ch.mlen, ch.timestamp, timestamp - base_time, ch.msid); /* limit chain */ len = 0; cl = in; while (cl) { b = cl->buf; len += (b->last - b->pos); if (len > ch.mlen) { break; } cl = cl->next; } if (cl == NULL) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "RTMP error parsing aggregate"); return NGX_ERROR; } next = cl->next; cl->next = NULL; b = cl->buf; last = b->last; b->last -= (len - ch.mlen); /* handle aggregated message */ ch.timestamp = h->timestamp + timestamp - base_time; rc = ngx_rtmp_receive_message(s, &ch, in); /* restore chain before checking the result */ in = cl; in->next = next; b->pos = b->last; b->last = last; if (rc != NGX_OK) { return rc; } /* read 32-bit previous tag size */ if (ngx_rtmp_fetch_uint32(&in, &prev_size, 4) != NGX_OK) { return NGX_OK; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "RTMP aggregate prev_size=%uD", prev_size); } return NGX_OK; }
static ngx_int_t ngx_http_brotli_filter_compress(ngx_http_request_t *r, ngx_http_brotli_ctx_t *ctx) { int rc; ngx_buf_t *b; ngx_chain_t *cl; ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "brotli in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", ctx->bstream.next_in, ctx->bstream.next_out, ctx->bstream.avail_in, ctx->bstream.avail_out, ctx->flush, ctx->redo); rc = brotli_compress(&ctx->bstream, ctx->flush); if (rc != B_OK && rc != B_STREAM_END && rc != B_BUF_ERROR) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "compress() failed: %d, %d", ctx->flush, rc); return NGX_ERROR; } ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "brotli out: ni:%p no:%p ai:%ud ao:%ud rc:%d", ctx->bstream.next_in, ctx->bstream.next_out, ctx->bstream.avail_in, ctx->bstream.avail_out, rc); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "brotli in_buf:%p pos:%p", ctx->in_buf, ctx->in_buf->pos); if (ctx->bstream.next_in) { ctx->in_buf->pos = ctx->bstream.next_in; if (ctx->bstream.avail_in == 0) { ctx->bstream.next_in = NULL; } } ctx->out_buf->last = ctx->bstream.next_out; if (ctx->bstream.avail_out == 0) { /* brotli wants to output some more compressed data */ cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = ctx->out_buf; cl->next = NULL; *ctx->last_out = cl; ctx->last_out = &cl->next; ctx->redo = 1; return NGX_AGAIN; } ctx->redo = 0; if (ctx->flush == B_SYNC_FLUSH) { ctx->flush = B_NO_FLUSH; cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } b = ctx->out_buf; if (ngx_buf_size(b) == 0) { b = ngx_calloc_buf(ctx->request->pool); if (b == NULL) { return NGX_ERROR; } } else { ctx->bstream.avail_out = 0; } b->flush = 1; cl->buf = b; cl->next = NULL; *ctx->last_out = cl; ctx->last_out = &cl->next; r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; return NGX_OK; } if (rc == B_STREAM_END) { if (ngx_http_brotli_filter_end(r, ctx) != NGX_OK) { return NGX_ERROR; } return NGX_OK; } return NGX_AGAIN; }
static ngx_int_t ngx_http_gunzip_filter_inflate(ngx_http_request_t *r, ngx_http_gunzip_ctx_t *ctx) { int rc; ngx_buf_t *b; ngx_chain_t *cl; ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "inflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", ctx->zstream.next_in, ctx->zstream.next_out, ctx->zstream.avail_in, ctx->zstream.avail_out, ctx->flush, ctx->redo); rc = inflate(&ctx->zstream, ctx->flush); if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "inflate() failed: %d, %d", ctx->flush, rc); return NGX_ERROR; } ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", ctx->zstream.next_in, ctx->zstream.next_out, ctx->zstream.avail_in, ctx->zstream.avail_out, rc); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "gunzip in_buf:%p pos:%p", ctx->in_buf, ctx->in_buf->pos); if (ctx->zstream.next_in) { ctx->in_buf->pos = ctx->zstream.next_in; if (ctx->zstream.avail_in == 0) { ctx->zstream.next_in = NULL; } } ctx->out_buf->last = ctx->zstream.next_out; if (ctx->zstream.avail_out == 0) { /* zlib wants to output some more data */ cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = ctx->out_buf; cl->next = NULL; *ctx->last_out = cl; ctx->last_out = &cl->next; ctx->redo = 1; return NGX_AGAIN; } ctx->redo = 0; if (ctx->flush == Z_SYNC_FLUSH) { ctx->flush = Z_NO_FLUSH; cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } b = ctx->out_buf; if (ngx_buf_size(b) == 0) { b = ngx_calloc_buf(ctx->request->pool); if (b == NULL) { return NGX_ERROR; } } else { ctx->zstream.avail_out = 0; } b->flush = 1; cl->buf = b; cl->next = NULL; *ctx->last_out = cl; ctx->last_out = &cl->next; return NGX_OK; } if (ctx->flush == Z_FINISH && ctx->zstream.avail_in == 0) { if (rc != Z_STREAM_END) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "inflate() returned %d on response end", rc); return NGX_ERROR; } if (ngx_http_gunzip_filter_inflate_end(r, ctx) != NGX_OK) { return NGX_ERROR; } return NGX_OK; } if (rc == Z_STREAM_END && ctx->zstream.avail_in > 0) { rc = inflateReset(&ctx->zstream); if (rc != Z_OK) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "inflateReset() failed: %d", rc); return NGX_ERROR; } ctx->redo = 1; return NGX_AGAIN; } if (ctx->in == NULL) { b = ctx->out_buf; if (ngx_buf_size(b) == 0) { return NGX_OK; } cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } ctx->zstream.avail_out = 0; cl->buf = b; cl->next = NULL; *ctx->last_out = cl; ctx->last_out = &cl->next; return NGX_OK; } return NGX_AGAIN; }
/* * 强制删除一个cache节点 */ static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache) { u_char *name; size_t len; time_t wait; ngx_uint_t tries; ngx_path_t *path; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache forced expire"); path = cache->path; len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; // 文件的完整路径 // len + 1: 文件名包括"\0" name = ngx_alloc(len + 1, ngx_cycle->log); if (name == NULL) { return 10; } ngx_memcpy(name, path->name.data, path->name.len); wait = 10; tries = 20; ngx_shmtx_lock(&cache->shpool->mutex); for (q = ngx_queue_last(&cache->sh->queue); q != ngx_queue_sentinel(&cache->sh->queue); q = ngx_queue_prev(q)) { fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd", fcn->count, fcn->exists, fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); // cache节点引用计数等于0时,直接删除文件 if (fcn->count == 0) { ngx_http_file_cache_delete(cache, q, name); wait = 0; } else { // 尝试20次 if (--tries) { continue; } wait = 1; } break; } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_free(name); return wait; }
static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache) { u_char *name; size_t len; time_t wait; ngx_uint_t tries; ngx_path_t *path; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache forced expire"); path = cache->path; len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; name = ngx_alloc(len + 1, ngx_cycle->log); if (name == NULL) { return 10; } ngx_memcpy(name, path->name.data, path->name.len); wait = 10; tries = 0; ngx_shmtx_lock(&cache->shpool->mutex); for (q = ngx_queue_last(&cache->sh->queue); q != ngx_queue_sentinel(&cache->sh->queue); q = ngx_queue_prev(q)) { fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd", fcn->count, fcn->exists, fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); if (fcn->count) { if (tries++ < 20) { continue; } wait = 1; break; } if (!fcn->exists) { ngx_queue_remove(q); ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); ngx_slab_free_locked(cache->shpool, fcn); break; } ngx_http_file_cache_delete(cache, q, name); break; } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_free(name); return wait; }
/* ngx_http_file_cache_expire,使用了LRU,也就是队列最尾端保存的是最长时间没有被使用的,并且这个函数返回的就是一个wait值 */ static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache) { u_char *name, *p; size_t len; time_t now, wait; ngx_path_t *path; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire"); path = cache->path; len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; // name = ngx_alloc(len + 1, ngx_cycle->log); if (name == NULL) { return 10; } // 拷贝路径名 -- cache存放的目录,不包括子目录 ngx_memcpy(name, path->name.data, path->name.len); now = ngx_time(); ngx_shmtx_lock(&cache->shpool->mutex); // 共享内存区上锁 for ( ;; ) { // 队列空,直接返回 if (ngx_queue_empty(&cache->sh->queue)) { wait = 10; break; } // 在队列的尾部取出一个节点,检查是否已经过期 q = ngx_queue_last(&cache->sh->queue); fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); wait = fcn->expire - now; if (wait > 0) { // wait大于0说明未过期 wait = wait > 10 ? 10 : wait; break; } // 此节点已经过期 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire: #%d %d %02xd%02xd%02xd%02xd", fcn->count, fcn->exists, fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); if (fcn->count == 0) { ngx_http_file_cache_delete(cache, q, name); continue; } if (fcn->deleting) { wait = 1; break; } // 转换文件名到acsii格式 p = ngx_hex_dump(key, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t)); len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t); (void) ngx_hex_dump(p, fcn->key, len); /* * abnormally exited workers may leave locked cache entries, * and although it may be safe to remove them completely, * we prefer to just move them to the top of the inactive queue */ // 将节点放入队列的头部 ngx_queue_remove(q); // 重新计算过期时间 fcn->expire = ngx_time() + cache->inactive; ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "ignore long locked inactive cache entry %*s, count:%d", 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count); } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_free(name); return wait; }
static ngx_int_t ngx_rtmp_cmd_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v) { ngx_rtmp_header_t h; static double trans; static int bfalse; static ngx_rtmp_amf_elt_t out_inf[] = { { NGX_RTMP_AMF_STRING, ngx_string("code"), "NetStream.Play.Reset", 0 }, { NGX_RTMP_AMF_STRING, ngx_string("level"), "status", 0 }, { NGX_RTMP_AMF_STRING, ngx_string("description"), "Playing and resetting.", 0 }, }; static ngx_rtmp_amf_elt_t out_elts[] = { { NGX_RTMP_AMF_STRING, ngx_null_string, "onStatus", 0 }, { NGX_RTMP_AMF_NUMBER, ngx_null_string, &trans, 0 }, { NGX_RTMP_AMF_NULL, ngx_null_string, NULL, 0 }, { NGX_RTMP_AMF_OBJECT, ngx_null_string, out_inf, sizeof(out_inf) }, }; static ngx_rtmp_amf_elt_t out2_inf[] = { { NGX_RTMP_AMF_STRING, ngx_string("code"), "NetStream.Play.Start", 0 }, { NGX_RTMP_AMF_STRING, ngx_string("level"), "status", 0 }, { NGX_RTMP_AMF_STRING, ngx_string("description"), "Started playing.", 0 }, }; static ngx_rtmp_amf_elt_t out2_elts[] = { { NGX_RTMP_AMF_STRING, ngx_null_string, "onStatus", 0 }, { NGX_RTMP_AMF_NUMBER, ngx_null_string, &trans, 0 }, { NGX_RTMP_AMF_NULL, ngx_null_string, NULL, 0 }, { NGX_RTMP_AMF_OBJECT, ngx_null_string, out2_inf, sizeof(out2_inf) }, }; static ngx_rtmp_amf_elt_t out3_elts[] = { { NGX_RTMP_AMF_STRING, ngx_null_string, "|RtmpSampleAccess", 0 }, { NGX_RTMP_AMF_BOOLEAN, ngx_null_string, &bfalse, 0 }, { NGX_RTMP_AMF_BOOLEAN, ngx_null_string, &bfalse, 0 }, }; static ngx_rtmp_amf_elt_t out4_inf[] = { { NGX_RTMP_AMF_STRING, ngx_string("code"), "NetStream.Data.Start", 0 }, }; static ngx_rtmp_amf_elt_t out4_elts[] = { { NGX_RTMP_AMF_STRING, ngx_null_string, "onStatus", 0 }, { NGX_RTMP_AMF_OBJECT, ngx_null_string, out4_inf, sizeof(out4_inf) }, }; ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: name='%s' args='%s' start=%uD duration=%uD " "reset=%d silent=%d", v->name, v->args, (uint32_t)v->start, (uint32_t)v->duration, v->reset, v->silent); if (v->silent) { return NGX_OK; } /* send onStatus reply */ memset(&h, 0, sizeof(h)); h.type = NGX_RTMP_MSG_AMF_CMD; h.csid = NGX_RTMP_CMD_CSID_AMF; h.msid = NGX_RTMP_CMD_MSID; if (ngx_rtmp_send_amf(s, &h, out_elts, sizeof(out_elts) / sizeof(out_elts[0])) != NGX_OK) { return NGX_ERROR; } /* send sample access meta message FIXME */ if (ngx_rtmp_send_amf(s, &h, out2_elts, sizeof(out2_elts) / sizeof(out2_elts[0])) != NGX_OK) { return NGX_ERROR; } /* send data start meta message */ h.type = NGX_RTMP_MSG_AMF_META; if (ngx_rtmp_send_amf(s, &h, out3_elts, sizeof(out3_elts) / sizeof(out3_elts[0])) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_send_amf(s, &h, out4_elts, sizeof(out4_elts) / sizeof(out4_elts[0])) != NGX_OK) { return NGX_ERROR; } return NGX_OK; }
static int ngx_http_lua_ngx_re_sub_helper(lua_State *L, unsigned global) { ngx_http_lua_regex_t *re; ngx_http_request_t *r; ngx_str_t subj; ngx_str_t pat; ngx_str_t opts; ngx_str_t tpl; ngx_http_lua_main_conf_t *lmcf = NULL; ngx_pool_t *pool, *old_pool; ngx_lua_regex_compile_t re_comp; const char *msg; ngx_int_t rc; ngx_uint_t n; ngx_int_t i; int nargs; int *cap = NULL; int ovecsize; int type; unsigned func; int offset; size_t count; luaL_Buffer luabuf; ngx_int_t flags; u_char *p; u_char errstr[NGX_MAX_CONF_ERRSTR + 1]; pcre_extra *sd = NULL; ngx_http_lua_complex_value_t *ctpl = NULL; ngx_http_lua_compile_complex_value_t ccv; nargs = lua_gettop(L); if (nargs != 3 && nargs != 4) { return luaL_error(L, "expecting three or four arguments, but got %d", nargs); } lua_pushlightuserdata(L, &ngx_http_lua_request_key); lua_rawget(L, LUA_GLOBALSINDEX); r = lua_touserdata(L, -1); lua_pop(L, 1); if (r == NULL) { return luaL_error(L, "no request object found"); } subj.data = (u_char *) luaL_checklstring(L, 1, &subj.len); pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); func = 0; type = lua_type(L, 3); switch (type) { case LUA_TFUNCTION: func = 1; tpl.len = 0; tpl.data = (u_char *) ""; break; case LUA_TNUMBER: case LUA_TSTRING: tpl.data = (u_char *) lua_tolstring(L, 3, &tpl.len); break; default: msg = lua_pushfstring(L, "string, number, or function expected, " "got %s", lua_typename(L, type)); return luaL_argerror(L, 3, msg); } ngx_memzero(&re_comp, sizeof(ngx_lua_regex_compile_t)); if (nargs == 4) { opts.data = (u_char *) luaL_checklstring(L, 4, &opts.len); lua_pop(L, 1); } else { /* nargs == 3 */ opts.data = (u_char *) ""; opts.len = 0; } /* stack: subj regex repl */ re_comp.options = 0; flags = ngx_http_lua_ngx_re_parse_opts(L, &re_comp, &opts, 4); if (flags & NGX_LUA_RE_COMPILE_ONCE) { lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); pool = lmcf->pool; dd("server pool %p", lmcf->pool); lua_pushlightuserdata(L, &ngx_http_lua_regex_cache_key); lua_rawget(L, LUA_REGISTRYINDEX); /* table */ lua_pushliteral(L, "s"); lua_pushinteger(L, tpl.len); lua_pushliteral(L, ":"); lua_pushvalue(L, 2); if (tpl.len != 0) { lua_pushvalue(L, 3); } dd("options size: %d", (int) sizeof(re_comp.options)); lua_pushlstring(L, (char *) &re_comp.options, sizeof(re_comp.options)); /* table regex opts */ if (tpl.len == 0) { lua_concat(L, 5); /* table key */ } else { lua_concat(L, 6); /* table key */ } lua_pushvalue(L, -1); /* table key key */ dd("regex cache key: %.*s", (int) (pat.len + sizeof(re_comp.options)), lua_tostring(L, -1)); lua_rawget(L, -3); /* table key re */ re = lua_touserdata(L, -1); lua_pop(L, 1); /* table key */ if (re) { ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua regex cache hit for sub regex \"%s\" with options " "\"%s\" and replace \"%s\"", pat.data, opts.data, func ? (u_char *) "<func>" : tpl.data); lua_pop(L, 2); dd("restoring regex %p, ncaptures %d, captures %p", re->regex, re->ncaptures, re->captures); re_comp.regex = re->regex; sd = re->regex_sd; re_comp.captures = re->ncaptures; cap = re->captures; ctpl = re->replace; if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; } else { ovecsize = (re->ncaptures + 1) * 3; } goto exec; } ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua regex cache miss for %ssub regex \"%s\" with options " "\"%s\" and replace \"%s\"", global ? "g" : "", pat.data, opts.data, func ? (u_char *) "<func>" : tpl.data); if (lmcf->regex_cache_entries >= lmcf->regex_cache_max_entries) { if (lmcf->regex_cache_entries == lmcf->regex_cache_max_entries) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "lua exceeding regex cache max entries (%i)", lmcf->regex_cache_max_entries); lmcf->regex_cache_entries++; } pool = r->pool; flags &= ~NGX_LUA_RE_COMPILE_ONCE; } } else { pool = r->pool; } re_comp.pattern = pat; re_comp.err.len = NGX_MAX_CONF_ERRSTR; re_comp.err.data = errstr; re_comp.pool = pool; dd("compiling regex"); ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua compiling %ssub regex \"%s\" with options \"%s\" " "(compile once: %d) (dfa mode: %d) (jit mode: %d)", global ? "g" : "", pat.data, opts.data, (flags & NGX_LUA_RE_COMPILE_ONCE) != 0, (flags & NGX_LUA_RE_MODE_DFA) != 0, (flags & NGX_LUA_RE_MODE_JIT) != 0); old_pool = ngx_http_lua_pcre_malloc_init(pool); rc = ngx_lua_regex_compile(&re_comp); ngx_http_lua_pcre_malloc_done(old_pool); if (rc != NGX_OK) { dd("compile failed"); re_comp.err.data[re_comp.err.len] = '\0'; msg = lua_pushfstring(L, "failed to compile regex \"%s\": %s", pat.data, re_comp.err.data); return luaL_argerror(L, 2, msg); } #if LUA_HAVE_PCRE_JIT if (flags & NGX_LUA_RE_MODE_JIT) { old_pool = ngx_http_lua_pcre_malloc_init(pool); sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg); ngx_http_lua_pcre_malloc_done(old_pool); # if (NGX_DEBUG) dd("sd = %p", sd); if (msg != NULL) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre study failed with PCRE_STUDY_JIT_COMPILE: %s (%p)", msg, sd); } if (sd != NULL) { int jitted; old_pool = ngx_http_lua_pcre_malloc_init(pool); pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted); ngx_http_lua_pcre_malloc_done(old_pool); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre JIT compiling result: %d", jitted); } # endif /* NGX_DEBUG */ } else { old_pool = ngx_http_lua_pcre_malloc_init(pool); sd = pcre_study(re_comp.regex, 0, &msg); ngx_http_lua_pcre_malloc_done(old_pool); # if (NGX_DEBUG) dd("sd = %p", sd); if (msg != NULL) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre_study failed with PCRE_STUDY_JIT_COMPILE: %s (%p)", msg, sd); } # endif /* NGX_DEBUG */ } #else /* LUA_HAVE_PCRE_JIT */ if (flags & NGX_LUA_RE_MODE_JIT) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "your pcre build does not have JIT support and " "the \"j\" regex option is ignored"); } #endif /* LUA_HAVE_PCRE_JIT */ dd("compile done, captures %d", re_comp.captures); if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; } else { ovecsize = (re_comp.captures + 1) * 3; } cap = ngx_palloc(pool, ovecsize * sizeof(int)); if (cap == NULL) { flags &= ~NGX_LUA_RE_COMPILE_ONCE; msg = "out of memory"; goto error; } if (func) { ctpl = NULL; } else { ctpl = ngx_palloc(pool, sizeof(ngx_http_lua_complex_value_t)); if (ctpl == NULL) { flags &= ~NGX_LUA_RE_COMPILE_ONCE; msg = "out of memory"; goto error; } if ((flags & NGX_LUA_RE_COMPILE_ONCE) && tpl.len != 0) { /* copy the string buffer pointed to by tpl.data from Lua VM */ p = ngx_palloc(pool, tpl.len + 1); if (p == NULL) { flags &= ~NGX_LUA_RE_COMPILE_ONCE; msg = "out of memory"; goto error; } ngx_memcpy(p, tpl.data, tpl.len); p[tpl.len] = '\0'; tpl.data = p; } ngx_memzero(&ccv, sizeof(ngx_http_lua_compile_complex_value_t)); ccv.pool = pool; ccv.log = r->connection->log; ccv.value = &tpl; ccv.complex_value = ctpl; if (ngx_http_lua_compile_complex_value(&ccv) != NGX_OK) { ngx_pfree(pool, cap); ngx_pfree(pool, ctpl); if ((flags & NGX_LUA_RE_COMPILE_ONCE) && tpl.len != 0) { ngx_pfree(pool, tpl.data); } if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } ngx_pfree(pool, re_comp.regex); return luaL_error(L, "bad template for substitution: \"%s\"", lua_tostring(L, 3)); } } if (flags & NGX_LUA_RE_COMPILE_ONCE) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua saving compiled sub regex (%d captures) into the cache " "(entries %i)", re_comp.captures, lmcf ? lmcf->regex_cache_entries : 0); re = ngx_palloc(pool, sizeof(ngx_http_lua_regex_t)); if (re == NULL) { return luaL_error(L, "out of memory"); } dd("saving regex %p, ncaptures %d, captures %p", re_comp.regex, re_comp.captures, cap); re->regex = re_comp.regex; re->regex_sd = sd; re->ncaptures = re_comp.captures; re->captures = cap; re->replace = ctpl; lua_pushlightuserdata(L, re); /* table key value */ lua_rawset(L, -3); /* table */ lua_pop(L, 1); if (lmcf) { lmcf->regex_cache_entries++; } } exec: count = 0; offset = 0; for (;;) { if (subj.len == 0) { break; } if (flags & NGX_LUA_RE_MODE_DFA) { #if LUA_HAVE_PCRE_DFA int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; rc = ngx_http_lua_regex_dfa_exec(re_comp.regex, sd, &subj, offset, cap, ovecsize, ws, NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT); #else /* LUA_HAVE_PCRE_DFA */ msg = "at least pcre 6.0 is required for the DFA mode"; goto error; #endif /* LUA_HAVE_PCRE_DFA */ } else { rc = ngx_http_lua_regex_exec(re_comp.regex, sd, &subj, offset, cap, ovecsize); } if (rc == NGX_REGEX_NO_MATCHED) { break; } if (rc < 0) { msg = lua_pushfstring(L, ngx_regex_exec_n " failed: %d on \"%s\" " "using \"%s\"", (int) rc, subj.data, pat.data); goto error; } if (rc == 0) { if (flags & NGX_LUA_RE_MODE_DFA) { rc = 1; } else { msg = "capture size too small"; goto error; } } dd("rc = %d", (int) rc); count++; if (count == 1) { luaL_buffinit(L, &luabuf); } if (func) { lua_pushvalue(L, -1); lua_createtable(L, rc - 1 /* narr */, 1 /* nrec */); for (i = 0, n = 0; i < rc; i++, n += 2) { dd("capture %d: %d %d", (int) i, cap[n], cap[n + 1]); if (cap[n] < 0) { lua_pushnil(L); } else { lua_pushlstring(L, (char *) &subj.data[cap[n]], cap[n + 1] - cap[n]); dd("pushing capture %s at %d", lua_tostring(L, -1), (int) i); } lua_rawseti(L, -2, (int) i); } dd("stack size at call: %d", lua_gettop(L)); lua_call(L, 1 /* nargs */, 1 /* nresults */); type = lua_type(L, -1); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: tpl.data = (u_char *) lua_tolstring(L, -1, &tpl.len); break; default: msg = lua_pushfstring(L, "string or number expected to be " "returned by the replace function, got %s", lua_typename(L, type)); return luaL_argerror(L, 3, msg); } luaL_addlstring(&luabuf, (char *) &subj.data[offset], cap[0] - offset); luaL_addlstring(&luabuf, (char *) tpl.data, tpl.len); lua_pop(L, 1); offset = cap[1]; if (global) { continue; } break; } rc = ngx_http_lua_complex_value(r, &subj, offset, rc, cap, ctpl, &luabuf); if (rc != NGX_OK) { msg = lua_pushfstring(L, "failed to eval the template for " "replacement: \"%s\"", tpl.data); goto error; } offset = cap[1]; if (global) { continue; } break; } if (count == 0) { dd("no match, just the original subject"); lua_settop(L, 1); } else { if (offset != (int) subj.len) { dd("adding trailer: %s (len %d)", &subj.data[offset], (int) (subj.len - offset)); luaL_addlstring(&luabuf, (char *) &subj.data[offset], subj.len - offset); } luaL_pushresult(&luabuf); dd("the dst string: %s", lua_tostring(L, -1)); } if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } if (re_comp.regex) { ngx_pfree(pool, re_comp.regex); } if (ctpl) { ngx_pfree(pool, ctpl); } if (cap) { ngx_pfree(pool, cap); } } lua_pushinteger(L, count); return 2; error: if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } if (re_comp.regex) { ngx_pfree(pool, re_comp.regex); } if (ctpl) { ngx_pfree(pool, ctpl); } if (cap) { ngx_pfree(pool, cap); } } return luaL_error(L, msg); }