void ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data, ngx_uint_t state) { ngx_http_upstream_rr_peer_data_t *rrp = data; time_t now; ngx_http_upstream_rr_peer_t *peer; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "free rr peer %ui %ui", pc->tries, state); if (state == 0 && pc->tries == 0) { return; } /* TODO: NGX_PEER_KEEPALIVE */ if (rrp->peers->single) { pc->tries = 0; return; } peer = &rrp->peers->peer[rrp->current]; if (state & NGX_PEER_FAILED) { now = ngx_time(); /* ngx_lock_mutex(rrp->peers->mutex); */ peer->fails++; peer->accessed = now; peer->checked = now; if (peer->max_fails) { peer->effective_weight -= peer->weight / peer->max_fails; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "free rr peer failed: %ui %i", rrp->current, peer->effective_weight); if (peer->effective_weight < 0) { peer->effective_weight = 0; } /* ngx_unlock_mutex(rrp->peers->mutex); */ } else { /* mark peer live if check passed */ if (peer->accessed < peer->checked) { peer->fails = 0; } } if (pc->tries) { pc->tries--; } /* ngx_unlock_mutex(rrp->peers->mutex); */ }
static ngx_int_t ngx_http_session_handler(ngx_http_request_t *r) { ngx_http_session_conf_t *sscf; ngx_http_session_t *session; ngx_http_session_list_t *session_list; ngx_str_t cookie; ngx_int_t ret; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "session handler begin"); sscf = ngx_http_get_module_loc_conf(r, ngx_http_session_module); if (!sscf->enabled) { return NGX_DECLINED; } if (ngx_http_session_request_cleanup_init(r) == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (sscf->session_show_enabled || ngx_http_session_is_favico(r)) { ngx_http_session_set_bypass(r); ngx_http_session_clr_found(r); ngx_http_session_clr_create(r); return NGX_DECLINED; } memset(&cookie, 0, sizeof(ngx_str_t)); ret = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &sscf->keyword, &cookie); if (ret == NGX_DECLINED || cookie.len == 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "new session, create one when response"); ngx_http_session_clr_found(r); ngx_http_session_set_create(r); ngx_http_session_set_location_handler(r); /* return NGX_OK to jump over other modules in NS layer */ return NGX_OK; } session_list = ngx_http_session_shm_zone->data; ngx_shmtx_lock(&session_list->shpool->mutex); session = __ngx_http_session_search(r, &cookie); if (!session) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "session time out!"); ngx_http_session_clr_found(r); ngx_http_session_set_create(r); ngx_http_session_set_location_handler(r); ngx_shmtx_unlock(&session_list->shpool->mutex); return NGX_OK; } else { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "find a session"); ngx_http_session_set_found(r); ngx_http_session_clr_create(r); ngx_http_session_set_request_session(r, session); __ngx_http_session_get_ref(r); /* reset timer */ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "reset timer: %p, timeout: %d, ref: %d\n", session, session->timeout, session->ref); session->reset = 1; session->timeout = sscf->timeout; session->est = ngx_time(); if (!ngx_queue_empty(&session->redirect_queue_node)) { ngx_queue_remove(&session->redirect_queue_node); ngx_queue_init(&session->redirect_queue_node); session_list->redirect_num--; if (session->ev.timer_set) { ngx_del_timer(&session->ev); } } __ngx_http_session_insert_to_new_chain(session_list, session); } ngx_shmtx_unlock(&session_list->shpool->mutex); /*In blacklist*/ if (!ngx_http_session_test_bypass(r) && session->bl_timeout > ngx_time()) { return NGX_ERROR; } return NGX_DECLINED; }
static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker) { sigset_t set; uint64_t cpu_affinity; ngx_int_t n; ngx_uint_t i; struct rlimit rlmt; ngx_core_conf_t *ccf; ngx_listening_t *ls; if (ngx_set_environment(cycle, NULL) == NULL) { /* fatal */ exit(2); } ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); if (worker >= 0 && ccf->priority != 0) { if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setpriority(%d) failed", ccf->priority); } } if (ccf->rlimit_nofile != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile; rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile; if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_NOFILE, %i) failed", ccf->rlimit_nofile); } } if (ccf->rlimit_core != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_core; rlmt.rlim_max = (rlim_t) ccf->rlimit_core; if (setrlimit(RLIMIT_CORE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_CORE, %O) failed", ccf->rlimit_core); } } #ifdef RLIMIT_SIGPENDING if (ccf->rlimit_sigpending != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending; rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending; if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_SIGPENDING, %i) failed", ccf->rlimit_sigpending); } } #endif if (geteuid() == 0) { if (setgid(ccf->group) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setgid(%d) failed", ccf->group); /* fatal */ exit(2); } if (initgroups(ccf->username, ccf->group) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "initgroups(%s, %d) failed", ccf->username, ccf->group); } if (setuid(ccf->user) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setuid(%d) failed", ccf->user); /* fatal */ exit(2); } } if (worker >= 0) { cpu_affinity = ngx_get_cpu_affinity(worker); if (cpu_affinity) { ngx_setaffinity(cpu_affinity, cycle->log); } } #if (NGX_HAVE_PR_SET_DUMPABLE) /* allow coredump after setuid() in Linux 2.4.x */ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "prctl(PR_SET_DUMPABLE) failed"); } #endif if (ccf->working_directory.len) { if (chdir((char *) ccf->working_directory.data) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "chdir(\"%s\") failed", ccf->working_directory.data); /* fatal */ exit(2); } } sigemptyset(&set); if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "sigprocmask() failed"); } srandom((ngx_pid << 16) ^ ngx_time()); /* * disable deleting previous events for the listening sockets because * in the worker processes there are no events at all at this point */ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { ls[i].previous = NULL; } for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->init_process) { if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) { /* fatal */ exit(2); } } } for (n = 0; n < ngx_last_process; n++) { if (ngx_processes[n].pid == -1) { continue; } if (n == ngx_process_slot) { continue; } if (ngx_processes[n].channel[1] == -1) { continue; } if (close(ngx_processes[n].channel[1]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "close() channel failed"); } } if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "close() channel failed"); } #if 0 ngx_last_process = 0; #endif if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, ngx_channel_handler) == NGX_ERROR) { /* fatal */ exit(2); } }
ngx_int_t ngx_tcp_log_handler(ngx_tcp_session_t *s) { u_char *line, *p; size_t len; ngx_uint_t l; ngx_connection_t *c; ngx_tcp_log_t *log; ngx_open_file_t *file; #if (nginx_version) >= 1003010 ngx_tcp_log_buf_t *buffer; #endif ngx_tcp_log_srv_conf_t *lscf; ngx_tcp_core_srv_conf_t *cscf; ngx_log_debug0(NGX_LOG_DEBUG_TCP, s->connection->log, 0, "tcp access log handler"); cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module); lscf = cscf->access_log; if (lscf->off) { return NGX_OK; } c = s->connection; log = lscf->logs->elts; for (l = 0; l < lscf->logs->nelts; l++) { if (ngx_time() == log[l].disk_full_time) { /* * on FreeBSD writing to a full filesystem with enabled softupdates * may block process for much longer time than writing to non-full * filesystem, so we skip writing to a log for one second */ continue; } len = 0; /* Calculate the length */ len += sizeof("1970/09/28 12:00:00"); /* log time */ len += NGX_INT64_LEN + 2; /* [ngx_pid] */ len += c->addr_text.len + 1; /* client address */ len += s->addr_text->len + 1; /* this session address */ len += sizeof("1970/09/28 12:00:00"); /* accept time */ len += sizeof("255.255.255.255:65536"); /* upstream address */ len += NGX_OFF_T_LEN + 1; /* read bytes from client */ len += NGX_OFF_T_LEN + 1; /* write bytes to client */ len += NGX_LINEFEED_SIZE; file = log[l].file; #if (nginx_version) >= 1003010 if (file && file->data) { buffer = file->data; if (len > (size_t) (buffer->last - buffer->pos)) { ngx_tcp_log_write(s, &log[l], buffer->start, buffer->pos - buffer->start); buffer->pos = buffer->start; } if (len <= (size_t) (buffer->last - buffer->pos)) { p = buffer->pos; p = ngx_tcp_log_fill(s, p); buffer->pos = p; continue; } } #else if (file && file->buffer) { if (len > (size_t) (file->last - file->pos)) { ngx_tcp_log_write(s, &log[l], file->buffer, file->pos - file->buffer); file->pos = file->buffer; } if (len <= (size_t) (file->last - file->pos)) { p = file->pos; p = ngx_tcp_log_fill(s, p); file->pos = p; continue; } } #endif line = ngx_pnalloc(s->pool, len); if (line == NULL) { return NGX_ERROR; } p = line; p = ngx_tcp_log_fill(s, p); ngx_tcp_log_write(s, &log[l], line, p - line); } return NGX_OK; }
static ngx_int_t ngx_http_secure_token_handler(ngx_http_request_t *r) { ngx_http_secure_token_loc_conf_t *stlcf; ngx_http_secure_token_ctx_t *stctx; ngx_http_complex_value_t *cv; ngx_md5_t md5; ngx_str_t val; ngx_int_t rc; ngx_uint_t i; size_t adjust, len; u_char hex[32]; stlcf = ngx_http_get_module_loc_conf(r, ngx_http_secure_token_module); if (!stlcf->enable) { return NGX_DECLINED; } if (stlcf->key.len == 0 || stlcf->md5 == NULL || stlcf->input == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } stctx = ngx_pcalloc(r->pool, sizeof(ngx_http_secure_token_ctx_t)); if (stctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_http_set_ctx(r, stctx, ngx_http_secure_token_module); rc = ngx_http_secure_token_parse_input(r); if (rc != NGX_OK) { return rc; } if (stctx->expire_time < ngx_time()) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "secure token: token expired: input: %d now: %d", stctx->expire_time, ngx_time()); return NGX_HTTP_FORBIDDEN; } if (stctx->access.len > 0) { adjust = stctx->access.data[stctx->access.len - 1] == '*' ? 1 : 0; len = r->args.data ? (size_t) (r->args.data - r->unparsed_uri.data) - 1 : r->unparsed_uri.len; if (len < stctx->access.len - adjust || ngx_strncmp(r->unparsed_uri.data, stctx->access.data, stctx->access.len - adjust) != 0) { ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "secure token: access mismatch: input: %V uri: %*s", &stctx->access, len, r->unparsed_uri.data); return NGX_HTTP_FORBIDDEN; } } cv = stlcf->md5->elts; for (i = 0; i < stlcf->md5->nelts; i++) { if (ngx_http_complex_value(r, &cv[i], &val) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (val.len == 0) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_md5_init(&md5); ngx_md5_update(&md5, val.data, val.len); ngx_md5_final(stctx->md5, &md5); #if (NGX_DEBUG) (void) ngx_hex_dump(hex, stctx->md5, 16); #endif ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "secure token: md5/%d: %*s", i, 32, hex); } #if (!NGX_DEBUG) (void) ngx_hex_dump(hex, stctx->md5, 16); #endif if (ngx_strncasecmp(hex, stctx->token.data, 32)) { ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "secure token: token mismatch: input: %V expected: %*s", &stctx->token, 32, hex); return NGX_HTTP_FORBIDDEN; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "secure token: OK"); return NGX_OK; }
void ngx_http_discarded_request_body_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_msec_t timer; ngx_event_t *rev; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; c = r->connection; rev = c->read; if (rev->timedout) { c->timedout = 1; c->error = 1; ngx_http_finalize_request(r, NGX_ERROR); return; } if (r->lingering_time) { timer = (ngx_msec_t) (r->lingering_time - ngx_time()); if (timer <= 0) { r->discard_body = 0; r->lingering_close = 0; ngx_http_finalize_request(r, NGX_ERROR); return; } } else { timer = 0; } rc = ngx_http_read_discarded_request_body(r); if (rc == NGX_OK) { r->discard_body = 0; r->lingering_close = 0; ngx_http_finalize_request(r, NGX_DONE); return; } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { c->error = 1; ngx_http_finalize_request(r, NGX_ERROR); return; } /* rc == NGX_AGAIN */ if (ngx_handle_read_event(rev, 0) != NGX_OK) { c->error = 1; ngx_http_finalize_request(r, NGX_ERROR); return; } if (timer) { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); timer *= 1000; if (timer > clcf->lingering_timeout) { timer = clcf->lingering_timeout; } ngx_add_timer(rev, timer); } }
void ngx_pipe_do_rollback(ngx_cycle_t *cycle, ngx_pipe_rollback_conf_t *rbcf) { int fd; struct flock lock; int ret; ngx_int_t i; ngx_file_info_t sb; ngx_int_t need_do = 0; fd = ngx_open_file(rbcf->logname, NGX_FILE_RDWR, NGX_FILE_OPEN, 0); if (fd < 0) { //open lock file failed just no need rollback return; } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; ret = fcntl(fd, F_SETLKW, &lock); if (ret < 0) { ngx_close_file(fd); //lock failed just no need rollback return; } //check time if (rbcf->interval >= 0) { if (ngx_file_info(rbcf->backup[0], &sb) == -1) { need_do = 1; ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "need rollback [%s]: cannot open backup", rbcf->logname); } else if (sb.st_ctime / rbcf->interval < ngx_time() / rbcf->interval) { need_do = 1; ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "need rollback [%s]: time on [%d] [%d]", rbcf->logname, sb.st_ctime, rbcf->time_now); } else { ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "no need rollback [%s]: time not on [%d] [%d]", rbcf->logname, sb.st_ctime, rbcf->time_now); } } else { ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "no need check rollback [%s] time: no interval", rbcf->logname); } //check size if (rbcf->log_max_size > 0) { if (ngx_file_info(rbcf->logname, &sb) == 0 && (sb.st_size >= rbcf->log_max_size)) { need_do = 1; ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "need rollback [%s]: size on [%d]", rbcf->logname, sb.st_size); } else { ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "no need rollback [%s]: size not on", rbcf->logname); } } else { ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "no need check rollback [%s] size: no max size", rbcf->logname); } if (need_do) { for (i = 1; i < rbcf->backup_num; i++) { ngx_rename_file(rbcf->backup[rbcf->backup_num - i - 1], rbcf->backup[rbcf->backup_num - i]); } if (ngx_rename_file(rbcf->logname, rbcf->backup[0]) < 0) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "rname %s to %s failed", rbcf->logname, rbcf->backup[0]); } else { ngx_log_error(NGX_LOG_WARN, cycle->log, 0, "rollback [%s] success", rbcf->logname); } } ngx_close_file(fd); }
ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, ngx_open_file_info_t *of, ngx_pool_t *pool) { time_t now; uint32_t hash; ngx_int_t rc; ngx_file_info_t fi; ngx_pool_cleanup_t *cln; ngx_cached_open_file_t *file; ngx_pool_cleanup_file_t *clnf; ngx_open_file_cache_cleanup_t *ofcln; of->fd = NGX_INVALID_FILE; of->err = 0; if (cache == NULL) { if (of->test_only) { if (ngx_file_info_wrapper(name, of, &fi, pool->log) == NGX_FILE_ERROR) { return NGX_ERROR; } of->uniq = ngx_file_uniq(&fi); of->mtime = ngx_file_mtime(&fi); of->size = ngx_file_size(&fi); of->fs_size = ngx_file_fs_size(&fi); of->is_dir = ngx_is_dir(&fi); of->is_file = ngx_is_file(&fi); of->is_link = ngx_is_link(&fi); of->is_exec = ngx_is_exec(&fi); return NGX_OK; } cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); if (cln == NULL) { return NGX_ERROR; } rc = ngx_open_and_stat_file(name, of, pool->log); if (rc == NGX_OK && !of->is_dir) { cln->handler = ngx_pool_cleanup_file; clnf = cln->data; clnf->fd = of->fd; clnf->name = name->data; clnf->log = pool->log; } return rc; } cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t)); if (cln == NULL) { return NGX_ERROR; } now = ngx_time(); hash = ngx_crc32_long(name->data, name->len); file = ngx_open_file_lookup(cache, name, hash); if (file) { file->uses++; ngx_queue_remove(&file->queue); if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) { /* file was not used often enough to keep open */ rc = ngx_open_and_stat_file(name, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } goto add_event; } if (file->use_event || (file->event == NULL && (of->uniq == 0 || of->uniq == file->uniq) && now - file->created < of->valid #if (NGX_HAVE_OPENAT) && of->disable_symlinks == file->disable_symlinks && of->disable_symlinks_from == file->disable_symlinks_from #endif )) { if (file->err == 0) { of->fd = file->fd; of->uniq = file->uniq; of->mtime = file->mtime; of->size = file->size; of->is_dir = file->is_dir; of->is_file = file->is_file; of->is_link = file->is_link; of->is_exec = file->is_exec; of->is_directio = file->is_directio; if (!file->is_dir) { file->count++; ngx_open_file_add_event(cache, file, of, pool->log); } } else { of->err = file->err; #if (NGX_HAVE_OPENAT) of->failed = file->disable_symlinks ? ngx_openat_file_n : ngx_open_file_n; #else of->failed = ngx_open_file_n; #endif } goto found; } ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0, "retest open file: %s, fd:%d, c:%d, e:%d", file->name, file->fd, file->count, file->err); if (file->is_dir) { /* * chances that directory became file are very small * so test_dir flag allows to use a single syscall * in ngx_file_info() instead of three syscalls */ of->test_dir = 1; } of->fd = file->fd; of->uniq = file->uniq; rc = ngx_open_and_stat_file(name, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } if (of->is_dir) { if (file->is_dir || file->err) { goto update; } /* file became directory */ } else if (of->err == 0) { /* file */ if (file->is_dir || file->err) { goto add_event; } if (of->uniq == file->uniq) { if (file->event) { file->use_event = 1; } of->is_directio = file->is_directio; goto update; } /* file was changed */ } else { /* error to cache */ if (file->err || file->is_dir) { goto update; } /* file was removed, etc. */ } if (file->count == 0) { ngx_open_file_del_event(file); if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%V\" failed", name); } goto add_event; } ngx_rbtree_delete(&cache->rbtree, &file->node); cache->current--; file->close = 1; goto create; } /* not found */ rc = ngx_open_and_stat_file(name, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } create: if (cache->current >= cache->max) { ngx_expire_old_cached_files(cache, 0, pool->log); } file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log); if (file == NULL) { goto failed; } file->name = ngx_alloc(name->len + 1, pool->log); if (file->name == NULL) { ngx_free(file); file = NULL; goto failed; } ngx_cpystrn(file->name, name->data, name->len + 1); file->node.key = hash; ngx_rbtree_insert(&cache->rbtree, &file->node); cache->current++; file->uses = 1; file->count = 0; file->use_event = 0; file->event = NULL; add_event: ngx_open_file_add_event(cache, file, of, pool->log); update: file->fd = of->fd; file->err = of->err; #if (NGX_HAVE_OPENAT) file->disable_symlinks = of->disable_symlinks; file->disable_symlinks_from = of->disable_symlinks_from; #endif if (of->err == 0) { file->uniq = of->uniq; file->mtime = of->mtime; file->size = of->size; file->close = 0; file->is_dir = of->is_dir; file->is_file = of->is_file; file->is_link = of->is_link; file->is_exec = of->is_exec; file->is_directio = of->is_directio; if (!of->is_dir) { file->count++; } } file->created = now; found: file->accessed = now; ngx_queue_insert_head(&cache->expire_queue, &file->queue); ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0, "cached open file: %s, fd:%d, c:%d, e:%d, u:%d", file->name, file->fd, file->count, file->err, file->uses); if (of->err == 0) { if (!of->is_dir) { cln->handler = ngx_open_file_cleanup; ofcln = cln->data; ofcln->cache = cache; ofcln->file = file; ofcln->min_uses = of->min_uses; ofcln->log = pool->log; } return NGX_OK; } return NGX_ERROR; failed: if (file) { ngx_rbtree_delete(&cache->rbtree, &file->node); cache->current--; if (file->count == 0) { if (file->fd != NGX_INVALID_FILE) { if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file->name); } } ngx_free(file->name); ngx_free(file); } else { file->close = 1; } } if (of->fd != NGX_INVALID_FILE) { if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%V\" failed", name); } } return NGX_ERROR; }
static ngx_int_t ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, ngx_uint_t do_write) { off_t *received, limit; size_t size, limit_rate; ssize_t n; ngx_buf_t *b; ngx_uint_t flags; ngx_msec_t delay; ngx_connection_t *c, *pc, *src, *dst; ngx_log_handler_pt handler; ngx_stream_upstream_t *u; ngx_stream_proxy_srv_conf_t *pscf; u = s->upstream; c = s->connection; pc = u->connected ? u->peer.connection : NULL; pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); if (from_upstream) { src = pc; dst = c; b = &u->upstream_buf; limit_rate = pscf->download_rate; received = &u->received; } else { src = c; dst = pc; b = &u->downstream_buf; limit_rate = pscf->upload_rate; received = &s->received; } for ( ;; ) { if (do_write) { size = b->last - b->pos; if (size && dst && dst->write->ready) { n = dst->send(dst, b->pos, size); if (n == NGX_ERROR) { ngx_stream_proxy_finalize(s, NGX_DECLINED); return NGX_ERROR; } if (n > 0) { b->pos += n; if (b->pos == b->last) { b->pos = b->start; b->last = b->start; } } } } size = b->end - b->last; if (size && src->read->ready && !src->read->delayed) { if (limit_rate) { limit = (off_t) limit_rate * (ngx_time() - u->start_sec + 1) - *received; if (limit <= 0) { src->read->delayed = 1; delay = (ngx_msec_t) (- limit * 1000 / limit_rate + 1); ngx_add_timer(src->read, delay); break; } if ((off_t) size > limit) { size = (size_t) limit; } } n = src->recv(src, b->last, size); if (n == NGX_AGAIN || n == 0) { break; } if (n > 0) { if (limit_rate) { delay = (ngx_msec_t) (n * 1000 / limit_rate); if (delay > 0) { src->read->delayed = 1; ngx_add_timer(src->read, delay); } } *received += n; b->last += n; do_write = 1; continue; } if (n == NGX_ERROR) { src->read->eof = 1; } } break; } if (src->read->eof && (b->pos == b->last || (dst && dst->read->eof))) { handler = c->log->handler; c->log->handler = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "%s disconnected" ", bytes from/to client:%O/%O" ", bytes from/to upstream:%O/%O", from_upstream ? "upstream" : "client", s->received, c->sent, u->received, pc ? pc->sent : 0); c->log->handler = handler; ngx_stream_proxy_finalize(s, NGX_OK); return NGX_DONE; } flags = src->read->eof ? NGX_CLOSE_EVENT : 0; if (ngx_handle_read_event(src->read, flags) != NGX_OK) { ngx_stream_proxy_finalize(s, NGX_ERROR); return NGX_ERROR; } if (dst) { if (ngx_handle_write_event(dst->write, 0) != NGX_OK) { ngx_stream_proxy_finalize(s, NGX_ERROR); return NGX_ERROR; } if (!c->read->delayed && !pc->read->delayed) { ngx_add_timer(c->write, pscf->timeout); } else if (c->write->timer_set) { ngx_del_timer(c->write); } } return NGX_OK; }
ngx_int_t ngx_http_log_handler(ngx_http_request_t *r) { u_char *line, *p; size_t len; ngx_uint_t i, l; ngx_http_log_t *log; ngx_open_file_t *file; ngx_http_log_op_t *op; ngx_http_log_loc_conf_t *lcf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http log handler"); lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module); if (lcf->off) { return NGX_OK; } log = lcf->logs->elts; for (l = 0; l < lcf->logs->nelts; l++) { if (ngx_time() == log[l].disk_full_time) { /* * on FreeBSD writing to a full filesystem with enabled softupdates * may block process for much longer time than writing to non-full * filesystem, so we skip writing to a log for one second */ continue; } ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes); len = 0; op = log[l].format->ops->elts; for (i = 0; i < log[l].format->ops->nelts; i++) { if (op[i].len == 0) { len += op[i].getlen(r, op[i].data); } else { len += op[i].len; } } len += NGX_LINEFEED_SIZE; file = log[l].file; if (file && file->buffer) { if (len > (size_t) (file->last - file->pos)) { ngx_http_log_write(r, &log[l], file->buffer, file->pos - file->buffer); file->pos = file->buffer; } if (len <= (size_t) (file->last - file->pos)) { p = file->pos; for (i = 0; i < log[l].format->ops->nelts; i++) { p = op[i].run(r, p, &op[i]); } ngx_linefeed(p); file->pos = p; continue; } } line = ngx_pnalloc(r->pool, len); if (line == NULL) { return NGX_ERROR; } p = line; for (i = 0; i < log[l].format->ops->nelts; i++) { p = op[i].run(r, p, &op[i]); } ngx_linefeed(p); ngx_http_log_write(r, &log[l], line, p - line); } return NGX_OK; }
ngx_flag_t ngx_buffer_cache_store_gather( ngx_buffer_cache_t* cache, u_char* key, ngx_str_t* buffers, size_t buffer_count) { ngx_buffer_cache_entry_t* entry; ngx_buffer_cache_sh_t *sh = cache->sh; ngx_str_t* cur_buffer; ngx_str_t* last_buffer; size_t buffer_size; uint32_t hash; uint32_t evictions; u_char* target_buffer; hash = ngx_crc32_short(key, BUFFER_CACHE_KEY_SIZE); ngx_shmtx_lock(&cache->shpool->mutex); if (sh->reset) { // a previous store operation was killed in progress, need to reset the cache // since the data structures may be corrupt. we can only reset the cache after // the access time expires since other processes may still be reading from / // writing to the cache if (ngx_time() < sh->access_time + CACHE_LOCK_EXPIRATION) { ngx_shmtx_unlock(&cache->shpool->mutex); return 0; } // reset the cache, leave the reset flag enabled ngx_buffer_cache_reset(sh); // update stats sh->stats.reset++; } else { // remove expired entries if (cache->expiration) { for (evictions = MAX_EVICTIONS_PER_STORE; evictions > 0; evictions--) { if (!ngx_buffer_cache_free_oldest_entry(sh, cache->expiration)) { break; } } } // make sure the entry does not already exist entry = ngx_buffer_cache_rbtree_lookup(&sh->rbtree, key, hash); if (entry != NULL) { sh->stats.store_exists++; ngx_shmtx_unlock(&cache->shpool->mutex); return 0; } // enable the reset flag before we start making any changes sh->reset = 1; } // allocate a new entry entry = ngx_buffer_cache_get_free_entry(sh); if (entry == NULL) { goto error; } // calculate the buffer size last_buffer = buffers + buffer_count; buffer_size = 0; for (cur_buffer = buffers; cur_buffer < last_buffer; cur_buffer++) { buffer_size += cur_buffer->len; } // allocate a buffer to hold the data target_buffer = ngx_buffer_cache_get_free_buffer(sh, buffer_size + 1); if (target_buffer == NULL) { goto error; } // initialize the entry entry->state = CES_ALLOCATED; entry->ref_count = 1; entry->node.key = hash; memcpy(entry->key, key, BUFFER_CACHE_KEY_SIZE); entry->start_offset = target_buffer; entry->buffer_size = buffer_size; // update the write position sh->buffers_write = target_buffer; // move from free_queue to used_queue ngx_queue_remove(&entry->queue_node); ngx_queue_insert_tail(&sh->used_queue, &entry->queue_node); // insert to rbtree ngx_rbtree_insert(&sh->rbtree, &entry->node); // update stats sh->stats.store_ok++; sh->stats.store_bytes += buffer_size; // Note: the memcpy is performed after releasing the lock to avoid holding the lock for a long time // setting the access time of the entry and cache prevents it from being freed sh->access_time = entry->access_time = ngx_time(); entry->write_time = ngx_time(); sh->reset = 0; ngx_shmtx_unlock(&cache->shpool->mutex); for (cur_buffer = buffers; cur_buffer < last_buffer; cur_buffer++) { target_buffer = ngx_copy(target_buffer, cur_buffer->data, cur_buffer->len); } *target_buffer = '\0'; // Note: no need to obtain the lock since state is ngx_atomic_t entry->state = CES_READY; (void)ngx_atomic_fetch_add(&entry->ref_count, -1); return 1; error: sh->stats.store_err++; sh->reset = 0; ngx_shmtx_unlock(&cache->shpool->mutex); return 0; }
// 丢弃请求体读事件处理,在epoll里加入读事件和handler // 这时epoll通知socket上有数据可以读取 // ngx_http_read_discarded_request_body ok表示数据已经读完 // 传递done给ngx_http_finalize_request,并不是真正结束请求 // 因为有引用计数器r->count,所以在ngx_http_close_request里只是减1的效果 void ngx_http_discarded_request_body_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_msec_t timer; ngx_event_t *rev; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; // 获取读事件相关的连接对象和请求对象 c = r->connection; rev = c->read; // 检查超时,使用的是lingering_timeout // 普通的丢弃不会进入这里 // 用在keepalive,见ngx_http_set_keepalive if (rev->timedout) { c->timedout = 1; c->error = 1; ngx_http_finalize_request(r, NGX_ERROR); return; } // 设置延时关闭时间,那么就会设置超时时间timer // 如果是一开始就丢弃请求体,那么就不会走这里, timer=0 if (r->lingering_time) { // 计算当前事件,是否要关闭 timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time(); // 延时关闭时间已到,不需要再接收数据了 // 清除标志,调用ngx_http_finalize_request结束请求 if ((ngx_msec_int_t) timer <= 0) { r->discard_body = 0; r->lingering_close = 0; ngx_http_finalize_request(r, NGX_ERROR); return; } } else { timer = 0; } // 这时epoll通知socket上有数据可以读取 // 读取请求体数据并丢弃 // 使用固定的4k缓冲区接受丢弃的数据 // 一直读数据并解析,检查content_length_n,如果无数据可读就返回NGX_AGAIN rc = ngx_http_read_discarded_request_body(r); // ok表示数据已经读完 // 传递done给ngx_http_finalize_request,并不是真正结束请求 // 因为有引用计数器r->count,所以在ngx_http_close_request里只是减1的效果 if (rc == NGX_OK) { r->discard_body = 0; r->lingering_close = 0; ngx_http_finalize_request(r, NGX_DONE); return; } // 出错 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { c->error = 1; ngx_http_finalize_request(r, NGX_ERROR); return; } /* rc == NGX_AGAIN */ // again则需要再次加入epoll事件,等有数据来再次进入 // rev的handler不变,直接加入 // 注意,读事件的handler实际上是ngx_http_request_handler // 但最终会调用r->read_event_handler,即本函数 if (ngx_handle_read_event(rev, 0) != NGX_OK) { c->error = 1; ngx_http_finalize_request(r, NGX_ERROR); return; } // 如果是一开始就丢弃请求体,那么就不会走这里, timer=0 if (timer) { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); timer *= 1000; if (timer > clcf->lingering_timeout) { timer = clcf->lingering_timeout; } ngx_add_timer(rev, timer); } }
// worker进程,cachemanager进程和cacheloader进程的初始化函数 static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker) { sigset_t set; uint64_t cpu_affinity; ngx_int_t n; ngx_uint_t i; struct rlimit rlmt; ngx_core_conf_t *ccf; ngx_listening_t *ls; if (ngx_set_environment(cycle, NULL) == NULL) { /* fatal */ exit(2); } ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); // 设置worker进程的nice值,cachemanager cacheloader进程不会设置 if (worker >= 0 && ccf->priority != 0) { if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setpriority(%d) failed", ccf->priority); } } // 设置进程最多可以打开的fd数量 if (ccf->rlimit_nofile != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile; rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile; if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_NOFILE, %i) failed", ccf->rlimit_nofile); } } // 设置这个进程独立于系统的coredump属性 if (ccf->rlimit_core != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_core; rlmt.rlim_max = (rlim_t) ccf->rlimit_core; if (setrlimit(RLIMIT_CORE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_CORE, %O) failed", ccf->rlimit_core); } } #ifdef RLIMIT_SIGPENDING if (ccf->rlimit_sigpending != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending; rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending; if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_SIGPENDING, %i) failed", ccf->rlimit_sigpending); } } #endif // 如果设置了user指令,而且使用root权限启动会在这里改变进程所属的用户和组 if (geteuid() == 0) { if (setgid(ccf->group) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setgid(%d) failed", ccf->group); /* fatal */ exit(2); } if (initgroups(ccf->username, ccf->group) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "initgroups(%s, %d) failed", ccf->username, ccf->group); } if (setuid(ccf->user) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setuid(%d) failed", ccf->user); /* fatal */ exit(2); } } if (worker >= 0) { // 获取这个worker对应的CPU号 cpu_affinity = ngx_get_cpu_affinity(worker); // 绑定这个worker对应的CPU。 if (cpu_affinity) { ngx_setaffinity(cpu_affinity, cycle->log); } } #if (NGX_HAVE_PR_SET_DUMPABLE) /* allow coredump after setuid() in Linux 2.4.x */ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "prctl(PR_SET_DUMPABLE) failed"); } #endif // 设置工作目录 if (ccf->working_directory.len) { if (chdir((char *) ccf->working_directory.data) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "chdir(\"%s\") failed", ccf->working_directory.data); /* fatal */ exit(2); } } sigemptyset(&set); // 把父进程设置为阻塞的信号重新设置为可接收状态。 if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "sigprocmask() failed"); } // 设置随机数种子。 srandom((ngx_pid << 16) ^ ngx_time()); /* * disable deleting previous events for the listening sockets because * in the worker processes there are no events at all at this point */ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { ls[i].previous = NULL; } // 调用每个模块的init_process函数 for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->init_process) { if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) { /* fatal */ exit(2); } } } // 关闭其他子进程与maser进程通信的unix套接字 for (n = 0; n < ngx_last_process; n++) { if (ngx_processes[n].pid == -1) { continue; } if (n == ngx_process_slot) { continue; } if (ngx_processes[n].channel[1] == -1) { continue; } if (close(ngx_processes[n].channel[1]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "close() channel failed"); } } // 关闭unix套接字对的父进程端套接字 if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "close() channel failed"); } #if 0 ngx_last_process = 0; #endif // 将这个进程接收父进程消息套接字的接收事件放到事件模型中, // 并将回调函数设置为ngx_channel_handler if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, ngx_channel_handler) == NGX_ERROR) { /* fatal */ exit(2); } }
static ngx_http_upstream_rr_peer_t * ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp) { time_t now; uintptr_t m; ngx_int_t total; ngx_uint_t i, n; ngx_http_upstream_rr_peer_t *peer, *best; #if (NGX_HTTP_PERSISTENCE) ngx_int_t persist_index; persist_index = ngx_http_upstream_ps_get(rrp->request, rrp->peers->number, rrp->group); #endif now = ngx_time(); best = NULL; total = 0; for (i = 0; i < rrp->peers->number; i++) { #if (NGX_HTTP_PERSISTENCE) if(persist_index >= 0) { i = persist_index; } #endif n = i / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); if (rrp->tried[n] & m) { #if (NGX_HTTP_PERSISTENCE) if(persist_index >= 0) { persist_index = -1; i = 0; } #endif continue; } peer = &rrp->peers->peer[i]; if (peer->down) { #if (NGX_HTTP_PERSISTENCE) if(persist_index >= 0) { persist_index = -1; i = 0; } #endif continue; } #if (NGX_HTTP_UPSTREAM_CHECK) if (ngx_http_upstream_check_peer_down(peer->check_index)) { continue; } #endif if (peer->max_fails && peer->fails >= peer->max_fails && now - peer->checked <= peer->fail_timeout) { #if (NGX_HTTP_PERSISTENCE) if(persist_index >= 0) { persist_index = -1; i = 0; } #endif continue; } #if (NGX_HTTP_PERSISTENCE) if(persist_index >= 0) { best = peer; break; } #endif peer->current_weight += peer->effective_weight; total += peer->effective_weight; if (peer->effective_weight < peer->weight) { peer->effective_weight++; } if (best == NULL || peer->current_weight > best->current_weight) { best = peer; } } if (best == NULL) { return NULL; } i = best - &rrp->peers->peer[0]; rrp->current = i; n = i / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); rrp->tried[n] |= m; best->current_weight -= total; if (now - best->checked > best->fail_timeout) { best->checked = now; } #if (NGX_HTTP_PERSISTENCE) ngx_http_upstream_ps_set(rrp->request, rrp->current, rrp->group); #endif return best; }
static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) { uint32_t hash; in_addr_t addr, *addrs; ngx_int_t rc; ngx_uint_t naddrs; ngx_resolver_ctx_t *next; ngx_resolver_node_t *rn; hash = ngx_crc32_short(ctx->name.data, ctx->name.len); rn = ngx_resolver_lookup_name(r, &ctx->name, hash); if (rn) { if (rn->valid >= ngx_time()) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); ngx_queue_remove(&rn->queue); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); naddrs = rn->naddrs; if (naddrs) { /* NGX_RESOLVE_A answer */ if (naddrs != 1) { addr = 0; addrs = ngx_resolver_dup(r, rn->u.addrs, naddrs * sizeof(in_addr_t)); if (addrs == NULL) { return NGX_ERROR; } } else { addr = rn->u.addr; addrs = NULL; } ctx->next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ do { ctx->state = NGX_OK; ctx->naddrs = naddrs; ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; ctx->addr = addr; next = ctx->next; ctx->handler(ctx); ctx = next; } while (ctx); if (addrs) { ngx_resolver_free(r, addrs); } return NGX_OK; } /* NGX_RESOLVE_CNAME */ if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) { ctx->name.len = rn->cnlen; ctx->name.data = rn->u.cname; return ngx_resolve_name_locked(r, ctx); } ctx->next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ do { ctx->state = NGX_RESOLVE_NXDOMAIN; next = ctx->next; ctx->handler(ctx); ctx = next; } while (ctx); return NGX_OK; } if (rn->waiting) { ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; return NGX_AGAIN; } ngx_queue_remove(&rn->queue); /* lock alloc mutex */ ngx_resolver_free_locked(r, rn->query); rn->query = NULL; if (rn->cnlen) { ngx_resolver_free_locked(r, rn->u.cname); } if (rn->naddrs > 1) { ngx_resolver_free_locked(r, rn->u.addrs); } /* unlock alloc mutex */ } else { rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); if (rn == NULL) { return NGX_ERROR; } rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len); if (rn->name == NULL) { ngx_resolver_free(r, rn); return NGX_ERROR; } rn->node.key = hash; rn->nlen = (u_short) ctx->name.len; rn->query = NULL; ngx_rbtree_insert(&r->name_rbtree, &rn->node); } rc = ngx_resolver_create_name_query(rn, ctx); if (rc == NGX_ERROR) { goto failed; } if (rc == NGX_DECLINED) { ngx_rbtree_delete(&r->name_rbtree, &rn->node); ngx_resolver_free(r, rn->query); ngx_resolver_free(r, rn->name); ngx_resolver_free(r, rn); ctx->state = NGX_RESOLVE_NXDOMAIN; ctx->handler(ctx); return NGX_OK; } if (ngx_resolver_send_query(r, rn) != NGX_OK) { goto failed; } if (ctx->event == NULL) { ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); if (ctx->event == NULL) { goto failed; } ctx->event->handler = ngx_resolver_timeout_handler; ctx->event->data = ctx; ctx->event->log = r->log; ctx->ident = -1; ngx_add_timer(ctx->event, ctx->timeout); } if (ngx_queue_empty(&r->name_resend_queue)) { ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); } rn->expire = ngx_time() + r->resend_timeout; ngx_queue_insert_head(&r->name_resend_queue, &rn->queue); rn->cnlen = 0; rn->naddrs = 0; rn->valid = 0; rn->waiting = ctx; ctx->state = NGX_AGAIN; return NGX_AGAIN; failed: ngx_rbtree_delete(&r->name_rbtree, &rn->node); if (rn->query) { ngx_resolver_free(r, rn->query); } ngx_resolver_free(r, rn->name); ngx_resolver_free(r, rn); return NGX_ERROR; }
static void ngx_stream_proxy_handler(ngx_stream_session_t *s) { u_char *p; ngx_connection_t *c; ngx_stream_upstream_t *u; ngx_stream_proxy_srv_conf_t *pscf; ngx_stream_upstream_srv_conf_t *uscf; c = s->connection; pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "proxy connection handler"); u = ngx_pcalloc(c->pool, sizeof(ngx_stream_upstream_t)); if (u == NULL) { ngx_stream_proxy_finalize(s, NGX_ERROR); return; } s->upstream = u; s->log_handler = ngx_stream_proxy_log_error; u->peer.log = c->log; u->peer.log_error = NGX_ERROR_ERR; u->peer.local = pscf->local; uscf = pscf->upstream; if (uscf->peer.init(s, uscf) != NGX_OK) { ngx_stream_proxy_finalize(s, NGX_ERROR); return; } u->peer.start_time = ngx_current_msec; if (pscf->next_upstream_tries && u->peer.tries > pscf->next_upstream_tries) { u->peer.tries = pscf->next_upstream_tries; } u->proxy_protocol = pscf->proxy_protocol; u->start_sec = ngx_time(); p = ngx_pnalloc(c->pool, pscf->buffer_size); if (p == NULL) { ngx_stream_proxy_finalize(s, NGX_ERROR); return; } u->downstream_buf.start = p; u->downstream_buf.end = p + pscf->buffer_size; u->downstream_buf.pos = p; u->downstream_buf.last = p; c->write->handler = ngx_stream_proxy_downstream_handler; c->read->handler = ngx_stream_proxy_downstream_handler; if (u->proxy_protocol #if (NGX_STREAM_SSL) && pscf->ssl == NULL #endif && pscf->buffer_size >= NGX_PROXY_PROTOCOL_MAX_HEADER) { /* optimization for a typical case */ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream proxy send PROXY protocol header"); p = ngx_proxy_protocol_write(c, u->downstream_buf.last, u->downstream_buf.end); if (p == NULL) { ngx_stream_proxy_finalize(s, NGX_ERROR); return; } u->downstream_buf.last = p; u->proxy_protocol = 0; } if (ngx_stream_proxy_process(s, 0, 0) != NGX_OK) { return; } ngx_stream_proxy_connect(s); }
ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx) { u_char *name; ngx_resolver_t *r; ngx_resolver_node_t *rn; r = ctx->resolver; ctx->addr = ntohl(ctx->addr); /* lock addr mutex */ rn = ngx_resolver_lookup_addr(r, ctx->addr); if (rn) { if (rn->valid >= ngx_time()) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); ngx_queue_remove(&rn->queue); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue); name = ngx_resolver_dup(r, rn->name, rn->nlen); if (name == NULL) { goto failed; } ctx->name.len = rn->nlen; ctx->name.data = name; /* unlock addr mutex */ ctx->state = NGX_OK; ctx->handler(ctx); ngx_resolver_free(r, name); return NGX_OK; } if (rn->waiting) { ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; /* unlock addr mutex */ return NGX_OK; } ngx_queue_remove(&rn->queue); ngx_resolver_free(r, rn->query); rn->query = NULL; } else { rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); if (rn == NULL) { goto failed; } rn->node.key = ctx->addr; rn->query = NULL; ngx_rbtree_insert(&r->addr_rbtree, &rn->node); } if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) { goto failed; } if (ngx_resolver_send_query(r, rn) != NGX_OK) { goto failed; } ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); if (ctx->event == NULL) { goto failed; } ctx->event->handler = ngx_resolver_timeout_handler; ctx->event->data = ctx; ctx->event->log = r->log; ctx->ident = -1; ngx_add_timer(ctx->event, ctx->timeout); if (ngx_queue_empty(&r->addr_resend_queue)) { ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); } rn->expire = ngx_time() + r->resend_timeout; ngx_queue_insert_head(&r->addr_resend_queue, &rn->queue); rn->cnlen = 0; rn->naddrs = 0; rn->name = NULL; rn->nlen = 0; rn->valid = 0; rn->waiting = ctx; /* unlock addr mutex */ ctx->state = NGX_AGAIN; return NGX_OK; failed: if (rn) { ngx_rbtree_delete(&r->addr_rbtree, &rn->node); if (rn->query) { ngx_resolver_free(r, rn->query); } ngx_resolver_free(r, rn); } /* unlock addr mutex */ if (ctx->event) { ngx_resolver_free(r, ctx->event); } ngx_resolver_free(r, ctx); return NGX_ERROR; }
static ngx_int_t ngx_http_secure_cookie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { u_char hash_buf[16], md5_buf[16], time_buf[64]; u_char *p, *last; ngx_str_t val, hash, time_dst, time_src; time_t expires; ngx_md5_t md5; ngx_http_secure_cookie_conf_t *conf; conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_cookie_module); if (conf->variable == NULL || conf->md5 == NULL) { goto not_found; } if (ngx_http_complex_value(r, conf->variable, &val) != NGX_OK) { return NGX_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "secure cookie: \"%V\"", &val); last = val.data + val.len; p = ngx_strlchr(val.data, last, ','); expires = 0; if (p) { val.len = p++ - val.data; time_src.data = p; time_src.len = last - p; if (time_src.len > 64) { goto not_found; } time_dst.data = time_buf; time_dst.len = 64; if (ngx_decode_base64(&time_dst, &time_src) != NGX_OK) { goto not_found; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "secure time: \"%V\"", &time_dst); expires = ngx_http_parse_time(time_dst.data, time_dst.len); if (expires <= 0) { goto not_found; } } if (val.len > 24) { goto not_found; } hash.len = 16; hash.data = hash_buf; if (ngx_decode_base64(&hash, &val) != NGX_OK) { goto not_found; } if (hash.len != 16) { goto not_found; } if (ngx_http_complex_value(r, conf->md5, &val) != NGX_OK) { return NGX_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "secure cookie md5: \"%V\"", &val); ngx_md5_init(&md5); ngx_md5_update(&md5, val.data, val.len); ngx_md5_final(md5_buf, &md5); if (ngx_memcmp(hash_buf, md5_buf, 16) != 0) { goto not_found; } v->data = (u_char *) ((expires && expires < ngx_time()) ? "0" : "1"); v->len = 1; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; return NGX_OK; not_found: v->not_found = 1; return NGX_OK; }
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, va_list args) #endif { #if (NGX_HAVE_VARIADIC_MACROS) va_list args; #endif u_char *p, *last, *msg; ssize_t n; ngx_uint_t wrote_stderr, debug_connection; u_char errstr[NGX_MAX_ERROR_STR]; last = errstr + NGX_MAX_ERROR_STR; p = ngx_cpymem(errstr, ngx_cached_err_log_time.data, ngx_cached_err_log_time.len); p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]); /* pid#tid */ p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ", ngx_log_pid, ngx_log_tid); if (log->connection) { p = ngx_slprintf(p, last, "*%uA ", log->connection); } msg = p; #if (NGX_HAVE_VARIADIC_MACROS) va_start(args, fmt); p = ngx_vslprintf(p, last, fmt, args); va_end(args); #else p = ngx_vslprintf(p, last, fmt, args); #endif if (err) { p = ngx_log_errno(p, last, err); } if (level != NGX_LOG_DEBUG && log->handler) { p = log->handler(log, p, last - p); } if (p > last - NGX_LINEFEED_SIZE) { p = last - NGX_LINEFEED_SIZE; } ngx_linefeed(p); wrote_stderr = 0; debug_connection = (log->log_level & NGX_LOG_DEBUG_CONNECTION) != 0; while (log) { if (log->log_level < level && !debug_connection) { break; } if (log->writer) { log->writer(log, level, errstr, p - errstr); goto next; } if (ngx_time() == log->disk_full_time) { /* * on FreeBSD writing to a full filesystem with enabled softupdates * may block process for much longer time than writing to non-full * filesystem, so we skip writing to a log for one second */ goto next; } n = ngx_write_fd(log->file->fd, errstr, p - errstr); if (n == -1 && ngx_errno == NGX_ENOSPC) { log->disk_full_time = ngx_time(); } if (log->file->fd == ngx_stderr) { wrote_stderr = 1; } next: log = log->next; } if (!ngx_use_stderr || level > NGX_LOG_WARN || wrote_stderr) { return; } msg -= (7 + err_levels[level].len + 3); (void) ngx_sprintf(msg, "nginx: [%V] ", &err_levels[level]); (void) ngx_write_console(ngx_stderr, msg, p - msg); }
ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) { off_t size, sent, nsent, limit; ngx_uint_t last, flush; ngx_msec_t delay; ngx_chain_t *cl, *ln, **ll, *chain; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; c = r->connection; if (c->error) { return NGX_ERROR; } size = 0; flush = 0; last = 0; ll = &r->out; /* find the size, the flush point and the last link of the saved chain */ for (cl = r->out; cl; cl = cl->next) { ll = &cl->next; ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, "write old buf t:%d f:%d %p, pos %p, size: %z " "file: %O, size: %z", cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); #if 1 if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "zero size buf in writer " "t:%d r:%d f:%d %p %p-%p %p %O-%O", cl->buf->temporary, cl->buf->recycled, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last, cl->buf->file, cl->buf->file_pos, cl->buf->file_last); ngx_debug_point(); return NGX_ERROR; } #endif size += ngx_buf_size(cl->buf); if (cl->buf->flush || cl->buf->recycled) { flush = 1; } if (cl->buf->last_buf) { last = 1; } } /* add the new chain to the existent one */ for (ln = in; ln; ln = ln->next) { cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = ln->buf; *ll = cl; ll = &cl->next; ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, "write new buf t:%d f:%d %p, pos %p, size: %z " "file: %O, size: %z", cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); #if 1 if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "zero size buf in writer " "t:%d r:%d f:%d %p %p-%p %p %O-%O", cl->buf->temporary, cl->buf->recycled, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last, cl->buf->file, cl->buf->file_pos, cl->buf->file_last); ngx_debug_point(); return NGX_ERROR; } #endif size += ngx_buf_size(cl->buf); if (cl->buf->flush || cl->buf->recycled) { flush = 1; } if (cl->buf->last_buf) { last = 1; } } *ll = NULL; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter: l:%d f:%d s:%O", last, flush, size); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); /* * avoid the output if there are no last buf, no flush point, * there are the incoming bufs and the size of all bufs * is smaller than "postpone_output" directive */ if (!last && !flush && in && size < (off_t) clcf->postpone_output) { return NGX_OK; } if (c->write->delayed) { c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; } if (size == 0 && !(c->buffered & NGX_LOWLEVEL_BUFFERED)) { if (last || flush) { for (cl = r->out; cl; /* void */) { ln = cl; cl = cl->next; ngx_free_chain(r->pool, ln); } r->out = NULL; c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; return NGX_OK; } ngx_log_error(NGX_LOG_ALERT, c->log, 0, "the http output chain is empty"); ngx_debug_point(); return NGX_ERROR; } if (r->limit_rate) { if (r->limit_rate_after == 0) { r->limit_rate_after = clcf->limit_rate_after; } limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1) - (c->sent - r->limit_rate_after); if (limit <= 0) { c->write->delayed = 1; ngx_add_timer(c->write, (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1)); c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; } if (clcf->sendfile_max_chunk && (off_t) clcf->sendfile_max_chunk < limit) { limit = clcf->sendfile_max_chunk; } } else { limit = clcf->sendfile_max_chunk; } sent = c->sent; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter limit %O", limit); chain = c->send_chain(c, r->out, limit); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter %p", chain); if (chain == NGX_CHAIN_ERROR) { c->error = 1; return NGX_ERROR; } if (r->limit_rate) { nsent = c->sent; if (r->limit_rate_after) { sent -= r->limit_rate_after; if (sent < 0) { sent = 0; } nsent -= r->limit_rate_after; if (nsent < 0) { nsent = 0; } } delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate); if (delay > 0) { limit = 0; c->write->delayed = 1; ngx_add_timer(c->write, delay); } } if (limit && c->write->ready && c->sent - sent >= limit - (off_t) (2 * ngx_pagesize)) { c->write->delayed = 1; ngx_add_timer(c->write, 1); } for (cl = r->out; cl && cl != chain; /* void */) { ln = cl; cl = cl->next; ngx_free_chain(r->pool, ln); } r->out = chain; if (chain) { c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; } c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) { return NGX_AGAIN; } return NGX_OK; }
static ngx_int_t ngx_http_session_sticky_get_cookie(ngx_http_request_t *r) { time_t now; u_char *p, *v, *vv, *st, *last, *end; ngx_int_t diff, delimiter, legal, rc; ngx_str_t *cookie; ngx_uint_t i; ngx_table_elt_t **cookies; ngx_http_ss_ctx_t *ctx; ngx_http_upstream_ss_srv_conf_t *sscf; enum { pre_key = 0, key, pre_equal, pre_value, value } state; legal = 1; ctx = ngx_http_get_module_ctx(r, ngx_http_upstream_session_sticky_module); sscf = ctx->sscf; ctx->tries = 1; p = NULL; cookie = NULL; now = ngx_time(); cookies = (ngx_table_elt_t **) r->headers_in.cookies.elts; for (i = 0; i < r->headers_in.cookies.nelts; i++) { cookie = &cookies[i]->value; p = ngx_strnstr(cookie->data, (char *) sscf->cookie.data, cookie->len); if (p == NULL) { continue; } if (*(p + sscf->cookie.len) == ' ' || *(p + sscf->cookie.len) == '=') { break; } } if (i >= r->headers_in.cookies.nelts) { goto not_found; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "session sticky cookie: \"%V\"", &cookies[i]->value); st = p; v = p + sscf->cookie.len + 1; last = cookie->data + cookie->len; state = 0; while (p < last) { switch (state) { case pre_key: if (*p == ';') { goto not_found; } else if (!is_space(*p)) { state = key; } break; case key: if (is_space(*p)) { state = pre_equal; } else if (*p == '=') { state = pre_value; } break; case pre_equal: if (*p == '=') { state = pre_value; } else if (!is_space(*p)) { goto not_found; } break; case pre_value: if (!is_space(*p)) { state = value; v = p--; } break; case value: if (*p == ';') { end = p + 1; goto success; } if (p + 1 == last) { end = last; p++; goto success; } break; default: break; } p++; } not_found: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "session sticky [firstseen]"); ctx->frist = 1; ctx->sid.len = 0; ctx->sid.data = NULL; ctx->firstseen = now; ctx->lastseen = now; ngx_http_session_sticky_tmtoa(r, &ctx->s_lastseen, ctx->lastseen); ngx_http_session_sticky_tmtoa(r, &ctx->s_firstseen, ctx->firstseen); if (ctx->s_lastseen.data == NULL || ctx->s_firstseen.data == NULL) { return NGX_ERROR; } return NGX_OK; success: if (sscf->flag & NGX_HTTP_SESSION_STICKY_PREFIX) { for (vv = v; vv < p; vv++) { if (*vv == '~') { end = vv + 1; break; } } if (vv >= p) { goto not_found; } st = v; } else { vv = p; } if ((sscf->flag & NGX_HTTP_SESSION_STICKY_INSERT) && sscf->maxidle != NGX_CONF_UNSET) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "session_sticky mode [insert]"); delimiter = 0; for (p = v; p < vv; p++) { if (*p == NGX_HTTP_SESSION_STICKY_DELIMITER) { delimiter++; if (delimiter == 1) { ctx->sid.len = p - v; ctx->sid.data = ngx_pnalloc(r->pool, ctx->sid.len); if (ctx->sid.data == NULL) { return NGX_ERROR; } ngx_memcpy(ctx->sid.data, v, ctx->sid.len); v = p + 1; } else if(delimiter == 2) { ctx->s_lastseen.len = p - v; ctx->s_lastseen.data = ngx_pnalloc(r->pool, ctx->s_lastseen.len); if (ctx->s_lastseen.data == NULL) { return NGX_ERROR; } ngx_memcpy(ctx->s_lastseen.data, v, ctx->s_lastseen.len); v = p + 1; break; } else { legal = 0; goto finish; } } } if (p >= vv || v >= vv) { legal = 0; goto finish; } ctx->s_firstseen.len = vv - v; ctx->s_firstseen.data = ngx_pnalloc(r->pool, ctx->s_firstseen.len); if (ctx->s_firstseen.data == NULL) { return NGX_ERROR; } ngx_memcpy(ctx->s_firstseen.data, v, ctx->s_firstseen.len); ctx->firstseen = ngx_atotm(ctx->s_firstseen.data, ctx->s_firstseen.len); ctx->lastseen = ngx_atotm(ctx->s_lastseen.data, ctx->s_lastseen.len); if (ctx->firstseen == NGX_ERROR || ctx->lastseen == NGX_ERROR) { legal = 0; goto finish; } if (ctx->sid.len != 0) { diff = (ngx_int_t) (now - ctx->lastseen); if (diff > ctx->sscf->maxidle || diff < -86400) { legal = 0; goto finish; } diff = (ngx_int_t) (now - ctx->firstseen); if (diff > ctx->sscf->maxlife || diff < -86400) { legal = 0; goto finish; } } ngx_http_session_sticky_tmtoa(r, &ctx->s_lastseen, now); } else { ctx->sid.len = vv - v; ctx->sid.data = ngx_pnalloc(r->pool, ctx->sid.len); if (ctx->sid.data == NULL) { return NGX_ERROR; } ngx_memcpy(ctx->sid.data, v, ctx->sid.len); } finish: if (sscf->flag & (NGX_HTTP_SESSION_STICKY_PREFIX | NGX_HTTP_SESSION_STICKY_INDIRECT)) { cookie->len -= (end - st); if (cookie->len == 0) { rc = ngx_list_delete(&r->headers_in.headers, cookies[i]); if (rc != NGX_OK) { return NGX_ERROR; } } while (end < last) { *st++ = *end++; } } if (legal == 0) { goto not_found; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "session sticky sid [%V]", &ctx->sid); return NGX_OK; }
static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx) { #if OPENSSL_VERSION_NUMBER >= 0x0090707fL const #endif u_char *p; int n; size_t len; ngx_str_t response; X509_STORE *store; STACK_OF(X509) *chain; OCSP_CERTID *id; OCSP_RESPONSE *ocsp; OCSP_BASICRESP *basic; ngx_ssl_stapling_t *staple; ASN1_GENERALIZEDTIME *thisupdate, *nextupdate; staple = ctx->data; ocsp = NULL; basic = NULL; id = NULL; if (ctx->code != 200) { goto error; } /* check the response */ len = ctx->response->last - ctx->response->pos; p = ctx->response->pos; ocsp = d2i_OCSP_RESPONSE(NULL, &p, len); if (ocsp == NULL) { ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, "d2i_OCSP_RESPONSE() failed"); goto error; } n = OCSP_response_status(ocsp); if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) { ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "OCSP response not successful (%d: %s)", n, OCSP_response_status_str(n)); goto error; } basic = OCSP_response_get1_basic(ocsp); if (basic == NULL) { ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, "OCSP_response_get1_basic() failed"); goto error; } store = SSL_CTX_get_cert_store(staple->ssl_ctx); if (store == NULL) { ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, "SSL_CTX_get_cert_store() failed"); goto error; } #if OPENSSL_VERSION_NUMBER >= 0x10001000L SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain); #else chain = staple->ssl_ctx->extra_certs; #endif if (OCSP_basic_verify(basic, chain, store, staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY) != 1) { ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, "OCSP_basic_verify() failed"); goto error; } id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer); if (id == NULL) { ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, "OCSP_cert_to_id() failed"); goto error; } if (OCSP_resp_find_status(basic, id, &n, NULL, NULL, &thisupdate, &nextupdate) != 1) { ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "certificate status not found in the OCSP response"); goto error; } if (n != V_OCSP_CERTSTATUS_GOOD) { ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "certificate status \"%s\" in the OCSP response", OCSP_cert_status_str(n)); goto error; } if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) { ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, "OCSP_check_validity() failed"); goto error; } OCSP_CERTID_free(id); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(ocsp); /* copy the response to memory not in ctx->pool */ response.len = len; response.data = ngx_alloc(response.len, ctx->log); if (response.data == NULL) { goto done; } ngx_memcpy(response.data, ctx->response->pos, response.len); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp response, %s, %uz", OCSP_cert_status_str(n), response.len); if (staple->staple.data) { ngx_free(staple->staple.data); } staple->staple = response; done: staple->loading = 0; staple->valid = ngx_time() + 3600; /* ssl_stapling_valid */ ngx_ssl_ocsp_done(ctx); return; error: staple->loading = 0; staple->valid = ngx_time() + 300; /* ssl_stapling_err_valid */ if (id) { OCSP_CERTID_free(id); } if (basic) { OCSP_BASICRESP_free(basic); } if (ocsp) { OCSP_RESPONSE_free(ocsp); } ngx_ssl_ocsp_done(ctx); }
static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf) { size_t len; time_t now, expires_time, max_age; ngx_uint_t i; ngx_table_elt_t *expires, *cc, **ccp; expires = r->headers_out.expires; if (expires == NULL) { expires = ngx_list_push(&r->headers_out.headers); if (expires == NULL) { return NGX_ERROR; } r->headers_out.expires = expires; expires->hash = 1; ngx_str_set(&expires->key, "Expires"); } len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT"); expires->value.len = len - 1; ccp = r->headers_out.cache_control.elts; if (ccp == NULL) { if (ngx_array_init(&r->headers_out.cache_control, r->pool, 1, sizeof(ngx_table_elt_t *)) != NGX_OK) { return NGX_ERROR; } ccp = ngx_array_push(&r->headers_out.cache_control); if (ccp == NULL) { return NGX_ERROR; } cc = ngx_list_push(&r->headers_out.headers); if (cc == NULL) { return NGX_ERROR; } cc->hash = 1; ngx_str_set(&cc->key, "Cache-Control"); *ccp = cc; } else { for (i = 1; i < r->headers_out.cache_control.nelts; i++) { ccp[i]->hash = 0; } cc = ccp[0]; } if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) { expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT"; ngx_str_set(&cc->value, "no-cache"); return NGX_OK; } if (conf->expires == NGX_HTTP_EXPIRES_MAX) { expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT"; /* 10 years */ ngx_str_set(&cc->value, "max-age=315360000"); return NGX_OK; } expires->value.data = ngx_pnalloc(r->pool, len); if (expires->value.data == NULL) { return NGX_ERROR; } if (conf->expires_time == 0 && conf->expires != NGX_HTTP_EXPIRES_DAILY) { ngx_memcpy(expires->value.data, ngx_cached_http_time.data, ngx_cached_http_time.len + 1); ngx_str_set(&cc->value, "max-age=0"); return NGX_OK; } now = ngx_time(); if (conf->expires == NGX_HTTP_EXPIRES_DAILY) { expires_time = ngx_next_time(conf->expires_time); max_age = expires_time - now; } else if (conf->expires == NGX_HTTP_EXPIRES_ACCESS || r->headers_out.last_modified_time == -1) { expires_time = now + conf->expires_time; max_age = conf->expires_time; } else { expires_time = r->headers_out.last_modified_time + conf->expires_time; max_age = expires_time - now; } ngx_http_time(expires->value.data, expires_time); if (conf->expires_time < 0 || max_age < 0) { ngx_str_set(&cc->value, "no-cache"); return NGX_OK; } cc->value.data = ngx_pnalloc(r->pool, sizeof("max-age=") + NGX_TIME_T_LEN + 1); if (cc->value.data == NULL) { return NGX_ERROR; } cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", max_age) - cc->value.data; return NGX_OK; }
ngx_int_t ngx_http_srcache_response_no_cache(ngx_http_request_t *r, ngx_http_srcache_loc_conf_t *conf, ngx_http_srcache_ctx_t *ctx) { ngx_table_elt_t **ccp; ngx_table_elt_t *h; ngx_uint_t i; u_char *p, *last; ngx_int_t n; time_t expires; dd("checking response cache control settings"); ccp = r->headers_out.cache_control.elts; if (ccp == NULL) { goto check_expires; } for (i = 0; i < r->headers_out.cache_control.nelts; i++) { if (!ccp[i]->hash) { continue; } p = ccp[i]->value.data; last = p + ccp[i]->value.len; if (!conf->store_private && ngx_strlcasestrn(p, last, (u_char *) "private", 7 - 1) != NULL) { return NGX_OK; } if (!conf->store_no_store && ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) != NULL) { return NGX_OK; } if (!conf->store_no_cache && ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL) { return NGX_OK; } if (ctx->valid_sec != 0) { continue; } p = ngx_strlcasestrn(p, last, (u_char *) "max-age=", 8 - 1); if (p == NULL) { continue; } n = 0; for (p += 8; p < last; p++) { if (*p == ',' || *p == ';' || *p == ' ') { break; } if (*p >= '0' && *p <= '9') { n = n * 10 + *p - '0'; continue; } return NGX_OK; } if (n == 0) { return NGX_OK; } ctx->valid_sec = ngx_time() + n; } check_expires: dd("valid_sec after processing cache-control: %d", (int) ctx->valid_sec); if (ctx->valid_sec == 0) { h = r->headers_out.expires; dd("expires header: %p", h); if (h != NULL && h->hash != 0) { expires = ngx_http_parse_time(h->value.data, h->value.len); if (expires == NGX_ERROR || expires <= ngx_time()) { return NGX_OK; } ctx->valid_sec = expires; } } return NGX_DECLINED; }
static ngx_int_t ngx_http_session_insert(ngx_http_request_t *r, ngx_str_t *cookie) { ngx_http_session_list_t *session_list; ngx_http_session_t *session, *tmp; ngx_http_session_t *redirect; ngx_int_t hash; ngx_http_session_conf_t *sscf; u_char file[64]; ngx_queue_t *head; ngx_queue_t *q; sscf = ngx_http_get_module_loc_conf(r, ngx_http_session_module); session_list = ngx_http_session_shm_zone->data; head = &session_list->redirect_queue_head; if (session_list->redirect_num >= NGX_HTTP_SESSION_DEFAULT_NUMBER/10) { q = ngx_queue_head(head); redirect = ngx_queue_data(q, ngx_http_session_t, redirect_queue_node); __ngx_http_session_delete(redirect); } ngx_shmtx_lock(&session_list->shpool->mutex); session = ngx_slab_alloc_locked(session_list->shpool, sizeof(ngx_http_session_t)); if (session == NULL) { ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "slab alloc failed"); ngx_shmtx_unlock(&session_list->shpool->mutex); return NGX_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "session create: %p\n", session); ngx_queue_init(&session->redirect_queue_node); memset(session, 0, sizeof(ngx_http_session_t)); memcpy(session->id, cookie->data, cookie->len); /* hang session to hash table */ hash = ngx_http_session_cookie_hash(r, cookie); if (session_list->sessions[hash]) { tmp = session_list->sessions[hash]; while (tmp->next) { tmp = tmp->next; } tmp->next = session; session->prev = tmp; session->next = NULL; } else { session_list->sessions[hash] = session; session->next = NULL; session->prev = NULL; session->slot = (void **)(&(session_list->sessions[hash])); } memset(file, 0, 64); sprintf((char *)file, "/var/tmp/%s", session->id); if (ngx_shmtx_create(&session->mutex, (void *)&session->lock, file) != NGX_OK) { return NGX_ERROR; } session->timeout = sscf->redirect_timeout; session->est = ngx_time(); session->ev.handler = ngx_http_session_timeout_handler; session->ev.data = session; session->ev.log = session_list->log; ngx_add_timer(&session->ev, session->timeout); ngx_queue_insert_tail(head, &session->redirect_queue_node); session_list->redirect_num++; ngx_shmtx_unlock(&session_list->shpool->mutex); return NGX_OK; }
/** * @param [in] log 日志对象 * @return int NGX_OK|NGX_ERROR * * 初始化系统相关变量 * 如内存页面大小ngx_pagesize ngx_cacheline_size ngx_max_sockets等 * * \file ngx_os.h 申明原型 */ ngx_int_t ngx_os_init(ngx_log_t *log) { ngx_uint_t n; #if (NGX_HAVE_OS_SPECIFIC_INIT) /** * \file ngx_linux_init.c * OS指定的初始化:初始化内核名称和其它信息,设置全局变量ngx_os_io,后续用于I/O操作基础(包括网络I/O) */ if (ngx_os_specific_init(log) != NGX_OK) { return NGX_ERROR; } #endif /** * \file ngx_setproctitle.h|c * 移动**environ到堆上,为设置进程标题做准备 */ if (ngx_init_setproctitle(log) != NGX_OK) { return NGX_ERROR; } /** * \file ngx_alloc.h|c * os页大小 x86为4096 */ ngx_pagesize = getpagesize(); //os页大小 x86为4096 /** * \file ../../../objs/ngx_auto_config.h * #define NGX_CPU_CACHE_LINE 64 * 主要用于内存池对齐分配。即本机cpu的cache line为64,内存池起始地址也要是64的倍数 */ ngx_cacheline_size = NGX_CPU_CACHE_LINE; //slab用到,计算要多少个cache line填满一页 2^12=4096 ngx_pagesize_shift=12 for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ } #if (NGX_HAVE_SC_NPROCESSORS_ONLN) if (ngx_ncpu == 0) { ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN); //cpu实际个数,配置文件worker_processes } #endif if (ngx_ncpu < 1) { ngx_ncpu = 1; } /** * \file ../../core/cpuinfo.c * 调用汇编代码,获取cpu信息,主要用于根据实际cpu信息设置ngx_cacheline_size的值 */ ngx_cpuinfo(); if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) { //进程可打开最大文件描述符上限 ngx_log_error(NGX_LOG_ALERT, log, errno, "getrlimit(RLIMIT_NOFILE) failed"); return NGX_ERROR; } ngx_max_sockets = (ngx_int_t) rlmt.rlim_cur; //打开socket描述符最大数量 //socket是否可阻塞设置开关 #if (NGX_HAVE_INHERITED_NONBLOCK || NGX_HAVE_ACCEPT4) ngx_inherited_nonblocking = 1; #else ngx_inherited_nonblocking = 0; //TODO 我的系统为0? #endif srandom(ngx_time()); //设置random函数的种子 return NGX_OK; }
static ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) { off_t limit; ssize_t n, size; ngx_int_t rc; ngx_buf_t *b; ngx_msec_t delay; ngx_chain_t *chain, *cl, *ln; if (p->upstream_eof || p->upstream_error || p->upstream_done) { return NGX_OK; } #if (NGX_THREADS) if (p->aio) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe read upstream: aio"); return NGX_AGAIN; } #endif ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe read upstream: %d", p->upstream->read->ready); for ( ;; ) { if (p->upstream_eof || p->upstream_error || p->upstream_done) { break; } if (p->preread_bufs == NULL && !p->upstream->read->ready) { break; } if (p->preread_bufs) { /* use the pre-read bufs if they exist */ chain = p->preread_bufs; p->preread_bufs = NULL; n = p->preread_size; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe preread: %z", n); if (n) { p->read = 1; } } else { #if (NGX_HAVE_KQUEUE) /* * kqueue notifies about the end of file or a pending error. * This test allows not to allocate a buf on these conditions * and not to call c->recv_chain(). */ if (p->upstream->read->available == 0 && p->upstream->read->pending_eof) { p->upstream->read->ready = 0; p->upstream->read->eof = 1; p->upstream_eof = 1; p->read = 1; if (p->upstream->read->kq_errno) { p->upstream->read->error = 1; p->upstream_error = 1; p->upstream_eof = 0; ngx_log_error(NGX_LOG_ERR, p->log, p->upstream->read->kq_errno, "kevent() reported that upstream " "closed connection"); } break; } #endif if (p->limit_rate) { if (p->upstream->read->delayed) { break; } limit = (off_t) p->limit_rate * (ngx_time() - p->start_sec + 1) - p->read_length; if (limit <= 0) { p->upstream->read->delayed = 1; delay = (ngx_msec_t) (- limit * 1000 / p->limit_rate + 1); ngx_add_timer(p->upstream->read, delay); break; } } else { limit = 0; } if (p->free_raw_bufs) { /* use the free bufs if they exist */ chain = p->free_raw_bufs; if (p->single_buf) { p->free_raw_bufs = p->free_raw_bufs->next; chain->next = NULL; } else { p->free_raw_bufs = NULL; } } else if (p->allocated < p->bufs.num) { /* allocate a new buf if it's still allowed */ b = ngx_create_temp_buf(p->pool, p->bufs.size); if (b == NULL) { return NGX_ABORT; } p->allocated++; chain = ngx_alloc_chain_link(p->pool); if (chain == NULL) { return NGX_ABORT; } chain->buf = b; chain->next = NULL; } else if (!p->cacheable && p->downstream->data == p->output_ctx && p->downstream->write->ready && !p->downstream->write->delayed) { /* * if the bufs are not needed to be saved in a cache and * a downstream is ready then write the bufs to a downstream */ p->upstream_blocked = 1; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe downstream ready"); break; } else if (p->cacheable || p->temp_file->offset < p->max_temp_file_size) { /* * if it is allowed, then save some bufs from p->in * to a temporary file, and add them to a p->out chain */ rc = ngx_event_pipe_write_chain_to_temp_file(p); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe temp offset: %O", p->temp_file->offset); if (rc == NGX_BUSY) { break; } if (rc != NGX_OK) { return rc; } chain = p->free_raw_bufs; if (p->single_buf) { p->free_raw_bufs = p->free_raw_bufs->next; chain->next = NULL; } else { p->free_raw_bufs = NULL; } } else { /* there are no bufs to read in */ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, "no pipe bufs to read in"); break; } n = p->upstream->recv_chain(p->upstream, chain, limit); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe recv chain: %z", n); if (p->free_raw_bufs) { chain->next = p->free_raw_bufs; } p->free_raw_bufs = chain; if (n == NGX_ERROR) { p->upstream_error = 1; break; } if (n == NGX_AGAIN) { if (p->single_buf) { ngx_event_pipe_remove_shadow_links(chain->buf); } break; } p->read = 1; if (n == 0) { p->upstream_eof = 1; break; } } delay = p->limit_rate ? (ngx_msec_t) n * 1000 / p->limit_rate : 0; p->read_length += n; cl = chain; p->free_raw_bufs = NULL; while (cl && n > 0) { ngx_event_pipe_remove_shadow_links(cl->buf); size = cl->buf->end - cl->buf->last; if (n >= size) { cl->buf->last = cl->buf->end; /* STUB */ cl->buf->num = p->num++; if (p->input_filter(p, cl->buf) == NGX_ERROR) { return NGX_ABORT; } n -= size; ln = cl; cl = cl->next; ngx_free_chain(p->pool, ln); } else { cl->buf->last += n; n = 0; } } if (cl) { for (ln = cl; ln->next; ln = ln->next) { /* void */ } ln->next = p->free_raw_bufs; p->free_raw_bufs = cl; } if (delay > 0) { p->upstream->read->delayed = 1; ngx_add_timer(p->upstream->read, delay); break; } } #if (NGX_DEBUG) for (cl = p->busy; cl; cl = cl->next) { ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe buf busy s:%d t:%d f:%d " "%p, pos %p, size: %z " "file: %O, size: %O", (cl->buf->shadow ? 1 : 0), cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); } for (cl = p->out; cl; cl = cl->next) { ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe buf out s:%d t:%d f:%d " "%p, pos %p, size: %z " "file: %O, size: %O", (cl->buf->shadow ? 1 : 0), cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); } for (cl = p->in; cl; cl = cl->next) { ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe buf in s:%d t:%d f:%d " "%p, pos %p, size: %z " "file: %O, size: %O", (cl->buf->shadow ? 1 : 0), cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); } for (cl = p->free_raw_bufs; cl; cl = cl->next) { ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe buf free s:%d t:%d f:%d " "%p, pos %p, size: %z " "file: %O, size: %O", (cl->buf->shadow ? 1 : 0), cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe length: %O", p->length); #endif if (p->free_raw_bufs && p->length != -1) { cl = p->free_raw_bufs; if (cl->buf->last - cl->buf->pos >= p->length) { p->free_raw_bufs = cl->next; /* STUB */ cl->buf->num = p->num++; if (p->input_filter(p, cl->buf) == NGX_ERROR) { return NGX_ABORT; } ngx_free_chain(p->pool, cl); } } if (p->length == 0) { p->upstream_done = 1; p->read = 1; } if ((p->upstream_eof || p->upstream_error) && p->free_raw_bufs) { /* STUB */ p->free_raw_bufs->buf->num = p->num++; if (p->input_filter(p, p->free_raw_bufs->buf) == NGX_ERROR) { return NGX_ABORT; } p->free_raw_bufs = p->free_raw_bufs->next; if (p->free_bufs && p->buf_to_file == NULL) { for (cl = p->free_raw_bufs; cl; cl = cl->next) { if (cl->buf->shadow == NULL) { ngx_pfree(p->pool, cl->buf->start); } } } } if (p->cacheable && (p->in || p->buf_to_file)) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe write chain"); rc = ngx_event_pipe_write_chain_to_temp_file(p); if (rc != NGX_OK) { return rc; } } return NGX_OK; }
static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans) { char *err; u_char *cname; size_t len; int32_t ttl; uint32_t hash; in_addr_t addr, *addrs; ngx_str_t name; ngx_uint_t qtype, qident, naddrs, a, i, n, start; ngx_resolver_an_t *an; ngx_resolver_ctx_t *ctx, *next; ngx_resolver_node_t *rn; if (ngx_resolver_copy(r, &name, buf, &buf[12], &buf[last]) != NGX_OK) { return; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name); hash = ngx_crc32_short(name.data, name.len); /* lock name mutex */ rn = ngx_resolver_lookup_name(r, &name, hash); if (rn == NULL || rn->query == NULL) { ngx_log_error(r->log_level, r->log, 0, "unexpected response for %V", &name); goto failed; } qident = (rn->query[0] << 8) + rn->query[1]; if (ident != qident) { ngx_log_error(r->log_level, r->log, 0, "wrong ident %ui response for %V, expect %ui", ident, &name, qident); goto failed; } ngx_resolver_free(r, name.data); if (code == 0 && nan == 0) { code = 3; /* NXDOMAIN */ } if (code) { next = rn->waiting; rn->waiting = NULL; ngx_queue_remove(&rn->queue); ngx_rbtree_delete(&r->name_rbtree, &rn->node); ngx_resolver_free_node(r, rn); /* unlock name mutex */ while (next) { ctx = next; ctx->state = code; next = ctx->next; ctx->handler(ctx); } return; } i = ans; naddrs = 0; addr = 0; addrs = NULL; cname = NULL; qtype = 0; ttl = 0; for (a = 0; a < nan; a++) { start = i; while (i < last) { if (buf[i] & 0xc0) { i += 2; goto found; } if (buf[i] == 0) { i++; goto test_length; } i += 1 + buf[i]; } goto short_response; test_length: if (i - start < 2) { err = "invalid name in dns response"; goto invalid; } found: if (i + sizeof(ngx_resolver_an_t) >= last) { goto short_response; } an = (ngx_resolver_an_t *) &buf[i]; qtype = (an->type_hi << 8) + an->type_lo; len = (an->len_hi << 8) + an->len_lo; ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16) + (an->ttl[2] << 8) + (an->ttl[3]); if (ttl < 0) { ttl = 0; } if (qtype == NGX_RESOLVE_A) { i += sizeof(ngx_resolver_an_t); if (i + len > last) { goto short_response; } addr = htonl((buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + (buf[i + 3])); naddrs++; i += len; } else if (qtype == NGX_RESOLVE_CNAME) { cname = &buf[i] + sizeof(ngx_resolver_an_t); i += sizeof(ngx_resolver_an_t) + len; } else if (qtype == NGX_RESOLVE_DNAME) { i += sizeof(ngx_resolver_an_t) + len; } else { ngx_log_error(r->log_level, r->log, 0, "unexpected qtype %ui", qtype); } } ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver naddrs:%ui cname:%p ttl:%d", naddrs, cname, ttl); if (naddrs) { if (naddrs == 1) { rn->u.addr = addr; } else { addrs = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t)); if (addrs == NULL) { return; } n = 0; i = ans; for (a = 0; a < nan; a++) { for ( ;; ) { if (buf[i] & 0xc0) { i += 2; goto ok; } if (buf[i] == 0) { i++; goto ok; } i += 1 + buf[i]; } ok: an = (ngx_resolver_an_t *) &buf[i]; qtype = (an->type_hi << 8) + an->type_lo; len = (an->len_hi << 8) + an->len_lo; i += sizeof(ngx_resolver_an_t); if (qtype == NGX_RESOLVE_A) { addrs[n++] = htonl((buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + (buf[i + 3])); if (n == naddrs) { break; } } i += len; } rn->u.addrs = addrs; addrs = ngx_resolver_dup(r, rn->u.addrs, naddrs * sizeof(in_addr_t)); if (addrs == NULL) { return; } } rn->naddrs = (u_short) naddrs; ngx_queue_remove(&rn->queue); rn->valid = ngx_time() + (r->valid ? r->valid : ttl); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ while (next) { ctx = next; ctx->state = NGX_OK; ctx->naddrs = naddrs; ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; ctx->addr = addr; next = ctx->next; ctx->handler(ctx); } if (naddrs > 1) { ngx_resolver_free(r, addrs); } return; } else if (cname) { /* CNAME only */ if (ngx_resolver_copy(r, &name, buf, cname, &buf[last]) != NGX_OK) { return; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver cname:\"%V\"", &name); ngx_queue_remove(&rn->queue); rn->cnlen = (u_short) name.len; rn->u.cname = name.data; rn->valid = ngx_time() + (r->valid ? r->valid : ttl); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); ctx = rn->waiting; rn->waiting = NULL; if (ctx) { ctx->name = name; (void) ngx_resolve_name_locked(r, ctx); } return; } ngx_log_error(r->log_level, r->log, 0, "no A or CNAME types in DNS responses, unknown query type: %ui", qtype); return; short_response: err = "short dns response"; invalid: /* unlock name mutex */ ngx_log_error(r->log_level, r->log, 0, err); return; failed: /* unlock name mutex */ ngx_resolver_free(r, name.data); return; }
static ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data) { ngx_http_upstream_ip_hash_peer_data_t *iphp = data; //本来data指向的是ngx_http_upstream_ip_hash_peer_data_t->rrp,因为rrp是该结构中的第一个成员,因此也就直接可以获取该结构,所以rrp必须是第一个成员 time_t now; ngx_int_t w; uintptr_t m; ngx_uint_t i, n, p, hash; ngx_http_upstream_rr_peer_t *peer; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "get ip hash peer, try: %ui", pc->tries); /* TODO: cached */ ngx_http_upstream_rr_peers_wlock(iphp->rrp.peers); //如果失败次数太多,或者只有一个后端服务,那么直接做RR选择 if (iphp->tries > 20 || iphp->rrp.peers->single) { ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers); return iphp->get_rr_peer(pc, &iphp->rrp); } now = ngx_time(); pc->cached = 0; pc->connection = NULL; hash = iphp->hash; for ( ;; ) { //计算IP的hash值 /* 1)由IP计算哈希值的算法如下, 其中公式中hash初始值为89,iphp->addr[i]表示客户端的IP, 通过三次哈希计算得出一个IP的哈希值: for (i = 0; i < 3; i++) { hash = (hash * 113 + iphp->addr[i]) % 6271; } 2)在选择下一个server时,ip_hash的选择策略是这样的: 它在上一次哈希值的基础上,再次哈希,就会得到一个全新的哈希值,再根据哈希值选择另外一个后台的服务器。 哈希算法仍然是 for (i = 0; i < 3; i++) { hash = (hash * 113 + iphp->addr[i]) % 6271; } */ for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) { //iphp->hash默认89,如果是同一个客户端来的请求,则下面计算出的hash肯定相同 //113质数,可以让哈希结果更散列 hash = (hash * 113 + iphp->addr[i]) % 6271; //根据IP地址的前三位和 } w = hash % iphp->rrp.peers->total_weight; peer = iphp->rrp.peers->peer; p = 0; //根据哈希结果得到被选中的后端服务器 while (w >= peer->weight) { w -= peer->weight; peer = peer->next; p++; } //服务器对应在位图中的位置计算 n = p / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); if (iphp->rrp.tried[n] & m) { goto next; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "get ip hash peer, hash: %ui %04XA", p, m); if (peer->down) { goto next; } /* fail_timeout时间内访问后端出现错误的次数大于等于max_fails,则认为该服务器不可用,那么如果不可用了,后端该服务器有恢复了怎么判断检测呢? 答:当这个fail_timeout时间段过了后,会重置peer->checked,那么有可以试探该服务器了,参考ngx_http_upstream_get_peer //checked用来检测时间,例如某个时间段fail_timeout这段时间后端失效了,那么这个fail_timeout过了后,也可以试探使用该服务器 */ if (peer->max_fails && peer->fails >= peer->max_fails && now - peer->checked <= peer->fail_timeout) //失败次数已达上限 { goto next; } break; next: /* 在这种ip_hash策略,如果一个后台服务器不能提供提服务(连接超时或读超时),该服务器的失败次数就会加一,当一个服务器的失败次 数达到max_fails所设置的值,就会在fail_timeout所设置的时间段内不能对外提供服务,这点和RR是一致的。 如果当前server不能提供服务,就会根据当前的哈希值再哈希出一个新哈希值,选择另一个服务器继续尝试,尝试的最大次是upstream中 server的个数,如果server的个数超过20,也就是要最大尝试次数在20次以上,当尝试次数达到20次,仍然找不到一个合适的服务器, ip_hah策略不再尝试ip哈希值来选择server, 而在剩余的尝试中,它会转而使用RR的策略,使用轮循的方法,选择新的server。 */ if (++iphp->tries > 20) {//已经尝试了20个后端服务器都还没找到一个可用的服务器,则直接在剩余的服务器中采用轮询算法 ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers); return iphp->get_rr_peer(pc, &iphp->rrp); } } //当前服务索引 iphp->rrp.current = peer; //服务器地址及名字保存 pc->sockaddr = peer->sockaddr; pc->socklen = peer->socklen; pc->name = &peer->name; peer->conns++; if (now - peer->checked > peer->fail_timeout) { peer->checked = now; } ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers); iphp->rrp.tried[n] |= m; //位图更新 iphp->hash = hash;//保留种子,使下次get_ip_hash_peer的时候能够选到同一个peer上 return NGX_OK; }
static ngx_http_upstream_rr_peer_t * ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp) { time_t now; uintptr_t m; ngx_int_t total; ngx_uint_t i, n; ngx_http_upstream_rr_peer_t *peer, *best; now = ngx_time(); best = NULL; total = 0; for (i = 0; i < rrp->peers->number; i++) { n = i / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); if (rrp->tried[n] & m) { continue; } peer = &rrp->peers->peer[i]; if (peer->down) { continue; } if (peer->max_fails && peer->fails >= peer->max_fails && now - peer->checked <= peer->fail_timeout) { continue; } peer->current_weight += peer->effective_weight; total += peer->effective_weight; if (peer->effective_weight < peer->weight) { peer->effective_weight++; } if (best == NULL || peer->current_weight > best->current_weight) { best = peer; } } if (best == NULL) { return NULL; } i = best - &rrp->peers->peer[0]; rrp->current = i; n = i / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); rrp->tried[n] |= m; best->current_weight -= total; best->checked = now; return best; }