Mercurial > hg > nginx-quic
view src/os/unix/ngx_files.c @ 8743:5d09596909c6 stable-1.20
Upstream: fixed timeouts with gRPC, SSL and select (ticket #2229).
With SSL it is possible that an established connection is ready for
reading after the handshake. Further, events might be already disabled
in case of level-triggered event methods. If this happens and
ngx_http_upstream_send_request() blocks waiting for some data from
the upstream, such as flow control in case of gRPC, the connection
will time out due to no read events on the upstream connection.
Fix is to explicitly check the c->read->ready flag if sending request
blocks and post a read event if it is set.
Note that while it is possible to modify ngx_ssl_handshake() to keep
read events active, this won't completely resolve the issue, since
there can be data already received during the SSL handshake
(see 573bd30e46b4).
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Fri, 20 Aug 2021 03:53:56 +0300 |
parents | ccb5ff87ab3e |
children |
line wrap: on
line source
/* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include <ngx_core.h> #if (NGX_THREADS) #include <ngx_thread_pool.h> static void ngx_thread_read_handler(void *data, ngx_log_t *log); static void ngx_thread_write_chain_to_file_handler(void *data, ngx_log_t *log); #endif static ngx_chain_t *ngx_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *cl); static ssize_t ngx_writev_file(ngx_file_t *file, ngx_iovec_t *vec, off_t offset); #if (NGX_HAVE_FILE_AIO) ngx_uint_t ngx_file_aio = 1; #endif ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) { ssize_t n; ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, "read: %d, %p, %uz, %O", file->fd, buf, size, offset); #if (NGX_HAVE_PREAD) n = pread(file->fd, buf, size, offset); if (n == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "pread() \"%s\" failed", file->name.data); return NGX_ERROR; } #else if (file->sys_offset != offset) { if (lseek(file->fd, offset, SEEK_SET) == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() \"%s\" failed", file->name.data); return NGX_ERROR; } file->sys_offset = offset; } n = read(file->fd, buf, size); if (n == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "read() \"%s\" failed", file->name.data); return NGX_ERROR; } file->sys_offset += n; #endif file->offset += n; return n; } #if (NGX_THREADS) typedef struct { ngx_fd_t fd; ngx_uint_t write; /* unsigned write:1; */ u_char *buf; size_t size; ngx_chain_t *chain; off_t offset; size_t nbytes; ngx_err_t err; } ngx_thread_file_ctx_t; ssize_t ngx_thread_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset, ngx_pool_t *pool) { ngx_thread_task_t *task; ngx_thread_file_ctx_t *ctx; ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, "thread read: %d, %p, %uz, %O", file->fd, buf, size, offset); task = file->thread_task; if (task == NULL) { task = ngx_thread_task_alloc(pool, sizeof(ngx_thread_file_ctx_t)); if (task == NULL) { return NGX_ERROR; } file->thread_task = task; } ctx = task->ctx; if (task->event.complete) { task->event.complete = 0; if (ctx->write) { ngx_log_error(NGX_LOG_ALERT, file->log, 0, "invalid thread call, read instead of write"); return NGX_ERROR; } if (ctx->err) { ngx_log_error(NGX_LOG_CRIT, file->log, ctx->err, "pread() \"%s\" failed", file->name.data); return NGX_ERROR; } return ctx->nbytes; } task->handler = ngx_thread_read_handler; ctx->write = 0; ctx->fd = file->fd; ctx->buf = buf; ctx->size = size; ctx->offset = offset; if (file->thread_handler(task, file) != NGX_OK) { return NGX_ERROR; } return NGX_AGAIN; } #if (NGX_HAVE_PREAD) static void ngx_thread_read_handler(void *data, ngx_log_t *log) { ngx_thread_file_ctx_t *ctx = data; ssize_t n; ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "thread read handler"); n = pread(ctx->fd, ctx->buf, ctx->size, ctx->offset); if (n == -1) { ctx->err = ngx_errno; } else { ctx->nbytes = n; ctx->err = 0; } #if 0 ngx_time_update(); #endif ngx_log_debug4(NGX_LOG_DEBUG_CORE, log, 0, "pread: %z (err: %d) of %uz @%O", n, ctx->err, ctx->size, ctx->offset); } #else #error pread() is required! #endif #endif /* NGX_THREADS */ ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) { ssize_t n, written; ngx_err_t err; ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, "write: %d, %p, %uz, %O", file->fd, buf, size, offset); written = 0; #if (NGX_HAVE_PWRITE) for ( ;; ) { n = pwrite(file->fd, buf + written, size, offset); if (n == -1) { err = ngx_errno; if (err == NGX_EINTR) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err, "pwrite() was interrupted"); continue; } ngx_log_error(NGX_LOG_CRIT, file->log, err, "pwrite() \"%s\" failed", file->name.data); return NGX_ERROR; } file->offset += n; written += n; if ((size_t) n == size) { return written; } offset += n; size -= n; } #else if (file->sys_offset != offset) { if (lseek(file->fd, offset, SEEK_SET) == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() \"%s\" failed", file->name.data); return NGX_ERROR; } file->sys_offset = offset; } for ( ;; ) { n = write(file->fd, buf + written, size); if (n == -1) { err = ngx_errno; if (err == NGX_EINTR) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err, "write() was interrupted"); continue; } ngx_log_error(NGX_LOG_CRIT, file->log, err, "write() \"%s\" failed", file->name.data); return NGX_ERROR; } file->sys_offset += n; file->offset += n; written += n; if ((size_t) n == size) { return written; } size -= n; } #endif } ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access) { ngx_fd_t fd; fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR, access ? access : 0600); if (fd != -1 && !persistent) { (void) unlink((const char *) name); } return fd; } ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset, ngx_pool_t *pool) { ssize_t total, n; ngx_iovec_t vec; struct iovec iovs[NGX_IOVS_PREALLOCATE]; /* use pwrite() if there is the only buf in a chain */ if (cl->next == NULL) { return ngx_write_file(file, cl->buf->pos, (size_t) (cl->buf->last - cl->buf->pos), offset); } total = 0; vec.iovs = iovs; vec.nalloc = NGX_IOVS_PREALLOCATE; do { /* create the iovec and coalesce the neighbouring bufs */ cl = ngx_chain_to_iovec(&vec, cl); /* use pwrite() if there is the only iovec buffer */ if (vec.count == 1) { n = ngx_write_file(file, (u_char *) iovs[0].iov_base, iovs[0].iov_len, offset); if (n == NGX_ERROR) { return n; } return total + n; } n = ngx_writev_file(file, &vec, offset); if (n == NGX_ERROR) { return n; } offset += n; total += n; } while (cl); return total; } static ngx_chain_t * ngx_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *cl) { size_t total, size; u_char *prev; ngx_uint_t n; struct iovec *iov; iov = NULL; prev = NULL; total = 0; n = 0; for ( /* void */ ; cl; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } size = cl->buf->last - cl->buf->pos; if (prev == cl->buf->pos) { iov->iov_len += size; } else { if (n == vec->nalloc) { break; } iov = &vec->iovs[n++]; iov->iov_base = (void *) cl->buf->pos; iov->iov_len = size; } prev = cl->buf->pos + size; total += size; } vec->count = n; vec->size = total; return cl; } static ssize_t ngx_writev_file(ngx_file_t *file, ngx_iovec_t *vec, off_t offset) { ssize_t n; ngx_err_t err; ngx_log_debug3(NGX_LOG_DEBUG_CORE, file->log, 0, "writev: %d, %uz, %O", file->fd, vec->size, offset); #if (NGX_HAVE_PWRITEV) eintr: n = pwritev(file->fd, vec->iovs, vec->count, offset); if (n == -1) { err = ngx_errno; if (err == NGX_EINTR) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err, "pwritev() was interrupted"); goto eintr; } ngx_log_error(NGX_LOG_CRIT, file->log, err, "pwritev() \"%s\" failed", file->name.data); return NGX_ERROR; } if ((size_t) n != vec->size) { ngx_log_error(NGX_LOG_CRIT, file->log, 0, "pwritev() \"%s\" has written only %z of %uz", file->name.data, n, vec->size); return NGX_ERROR; } #else if (file->sys_offset != offset) { if (lseek(file->fd, offset, SEEK_SET) == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() \"%s\" failed", file->name.data); return NGX_ERROR; } file->sys_offset = offset; } eintr: n = writev(file->fd, vec->iovs, vec->count); if (n == -1) { err = ngx_errno; if (err == NGX_EINTR) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err, "writev() was interrupted"); goto eintr; } ngx_log_error(NGX_LOG_CRIT, file->log, err, "writev() \"%s\" failed", file->name.data); return NGX_ERROR; } if ((size_t) n != vec->size) { ngx_log_error(NGX_LOG_CRIT, file->log, 0, "writev() \"%s\" has written only %z of %uz", file->name.data, n, vec->size); return NGX_ERROR; } file->sys_offset += n; #endif file->offset += n; return n; } #if (NGX_THREADS) ssize_t ngx_thread_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset, ngx_pool_t *pool) { ngx_thread_task_t *task; ngx_thread_file_ctx_t *ctx; ngx_log_debug3(NGX_LOG_DEBUG_CORE, file->log, 0, "thread write chain: %d, %p, %O", file->fd, cl, offset); task = file->thread_task; if (task == NULL) { task = ngx_thread_task_alloc(pool, sizeof(ngx_thread_file_ctx_t)); if (task == NULL) { return NGX_ERROR; } file->thread_task = task; } ctx = task->ctx; if (task->event.complete) { task->event.complete = 0; if (!ctx->write) { ngx_log_error(NGX_LOG_ALERT, file->log, 0, "invalid thread call, write instead of read"); return NGX_ERROR; } if (ctx->err || ctx->nbytes == 0) { ngx_log_error(NGX_LOG_CRIT, file->log, ctx->err, "pwritev() \"%s\" failed", file->name.data); return NGX_ERROR; } file->offset += ctx->nbytes; return ctx->nbytes; } task->handler = ngx_thread_write_chain_to_file_handler; ctx->write = 1; ctx->fd = file->fd; ctx->chain = cl; ctx->offset = offset; if (file->thread_handler(task, file) != NGX_OK) { return NGX_ERROR; } return NGX_AGAIN; } static void ngx_thread_write_chain_to_file_handler(void *data, ngx_log_t *log) { ngx_thread_file_ctx_t *ctx = data; #if (NGX_HAVE_PWRITEV) off_t offset; ssize_t n; ngx_err_t err; ngx_chain_t *cl; ngx_iovec_t vec; struct iovec iovs[NGX_IOVS_PREALLOCATE]; vec.iovs = iovs; vec.nalloc = NGX_IOVS_PREALLOCATE; cl = ctx->chain; offset = ctx->offset; ctx->nbytes = 0; ctx->err = 0; do { /* create the iovec and coalesce the neighbouring bufs */ cl = ngx_chain_to_iovec(&vec, cl); eintr: n = pwritev(ctx->fd, iovs, vec.count, offset); if (n == -1) { err = ngx_errno; if (err == NGX_EINTR) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, err, "pwritev() was interrupted"); goto eintr; } ctx->err = err; return; } if ((size_t) n != vec.size) { ctx->nbytes = 0; return; } ctx->nbytes += n; offset += n; } while (cl); #else ctx->err = NGX_ENOSYS; return; #endif } #endif /* NGX_THREADS */ ngx_int_t ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s) { struct timeval tv[2]; tv[0].tv_sec = ngx_time(); tv[0].tv_usec = 0; tv[1].tv_sec = s; tv[1].tv_usec = 0; if (utimes((char *) name, tv) != -1) { return NGX_OK; } return NGX_ERROR; } ngx_int_t ngx_create_file_mapping(ngx_file_mapping_t *fm) { fm->fd = ngx_open_file(fm->name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (fm->fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, ngx_open_file_n " \"%s\" failed", fm->name); return NGX_ERROR; } if (ftruncate(fm->fd, fm->size) == -1) { ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, "ftruncate() \"%s\" failed", fm->name); goto failed; } fm->addr = mmap(NULL, fm->size, PROT_READ|PROT_WRITE, MAP_SHARED, fm->fd, 0); if (fm->addr != MAP_FAILED) { return NGX_OK; } ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, "mmap(%uz) \"%s\" failed", fm->size, fm->name); failed: if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, ngx_close_file_n " \"%s\" failed", fm->name); } return NGX_ERROR; } void ngx_close_file_mapping(ngx_file_mapping_t *fm) { if (munmap(fm->addr, fm->size) == -1) { ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, "munmap(%uz) \"%s\" failed", fm->size, fm->name); } if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, ngx_close_file_n " \"%s\" failed", fm->name); } } ngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir) { dir->dir = opendir((const char *) name->data); if (dir->dir == NULL) { return NGX_ERROR; } dir->valid_info = 0; return NGX_OK; } ngx_int_t ngx_read_dir(ngx_dir_t *dir) { dir->de = readdir(dir->dir); if (dir->de) { #if (NGX_HAVE_D_TYPE) dir->type = dir->de->d_type; #else dir->type = 0; #endif return NGX_OK; } return NGX_ERROR; } ngx_int_t ngx_open_glob(ngx_glob_t *gl) { int n; n = glob((char *) gl->pattern, 0, NULL, &gl->pglob); if (n == 0) { return NGX_OK; } #ifdef GLOB_NOMATCH if (n == GLOB_NOMATCH && gl->test) { return NGX_OK; } #endif return NGX_ERROR; } ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name) { size_t count; #ifdef GLOB_NOMATCH count = (size_t) gl->pglob.gl_pathc; #else count = (size_t) gl->pglob.gl_matchc; #endif if (gl->n < count) { name->len = (size_t) ngx_strlen(gl->pglob.gl_pathv[gl->n]); name->data = (u_char *) gl->pglob.gl_pathv[gl->n]; gl->n++; return NGX_OK; } return NGX_DONE; } void ngx_close_glob(ngx_glob_t *gl) { globfree(&gl->pglob); } ngx_err_t ngx_trylock_fd(ngx_fd_t fd) { struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; if (fcntl(fd, F_SETLK, &fl) == -1) { return ngx_errno; } return 0; } ngx_err_t ngx_lock_fd(ngx_fd_t fd) { struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; if (fcntl(fd, F_SETLKW, &fl) == -1) { return ngx_errno; } return 0; } ngx_err_t ngx_unlock_fd(ngx_fd_t fd) { struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_UNLCK; fl.l_whence = SEEK_SET; if (fcntl(fd, F_SETLK, &fl) == -1) { return ngx_errno; } return 0; } #if (NGX_HAVE_POSIX_FADVISE) && !(NGX_HAVE_F_READAHEAD) ngx_int_t ngx_read_ahead(ngx_fd_t fd, size_t n) { int err; err = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); if (err == 0) { return 0; } ngx_set_errno(err); return NGX_FILE_ERROR; } #endif #if (NGX_HAVE_O_DIRECT) ngx_int_t ngx_directio_on(ngx_fd_t fd) { int flags; flags = fcntl(fd, F_GETFL); if (flags == -1) { return NGX_FILE_ERROR; } return fcntl(fd, F_SETFL, flags | O_DIRECT); } ngx_int_t ngx_directio_off(ngx_fd_t fd) { int flags; flags = fcntl(fd, F_GETFL); if (flags == -1) { return NGX_FILE_ERROR; } return fcntl(fd, F_SETFL, flags & ~O_DIRECT); } #endif #if (NGX_HAVE_STATFS) size_t ngx_fs_bsize(u_char *name) { struct statfs fs; if (statfs((char *) name, &fs) == -1) { return 512; } if ((fs.f_bsize % 512) != 0) { return 512; } #if (NGX_LINUX) if ((size_t) fs.f_bsize > ngx_pagesize) { return 512; } #endif return (size_t) fs.f_bsize; } off_t ngx_fs_available(u_char *name) { struct statfs fs; if (statfs((char *) name, &fs) == -1) { return NGX_MAX_OFF_T_VALUE; } return (off_t) fs.f_bavail * fs.f_bsize; } #elif (NGX_HAVE_STATVFS) size_t ngx_fs_bsize(u_char *name) { struct statvfs fs; if (statvfs((char *) name, &fs) == -1) { return 512; } if ((fs.f_frsize % 512) != 0) { return 512; } #if (NGX_LINUX) if ((size_t) fs.f_frsize > ngx_pagesize) { return 512; } #endif return (size_t) fs.f_frsize; } off_t ngx_fs_available(u_char *name) { struct statvfs fs; if (statvfs((char *) name, &fs) == -1) { return NGX_MAX_OFF_T_VALUE; } return (off_t) fs.f_bavail * fs.f_frsize; } #else size_t ngx_fs_bsize(u_char *name) { return 512; } off_t ngx_fs_available(u_char *name) { return NGX_MAX_OFF_T_VALUE; } #endif