diff options
author | Igor Sysoev <igor@sysoev.ru> | 2006-04-14 09:53:38 +0000 |
---|---|---|
committer | Igor Sysoev <igor@sysoev.ru> | 2006-04-14 09:53:38 +0000 |
commit | 8a2b2fb4fbd1801a5fb70d86384f2c6fd13c32c3 (patch) | |
tree | c0f7531cdc57da852f65d2d02bf11888bba63fe6 /src | |
parent | 2b1b55d025e0444978e1fcf4580217eb391860e0 (diff) | |
download | nginx-release-0.3.38.tar.gz nginx-release-0.3.38.zip |
nginx-0.3.38-RELEASE importrelease-0.3.38
*) Feature: the ngx_http_dav_module.
*) Change: the ngx_http_perl_module optimizations.
Thanks to Sergey Skvortsov.
*) Feature: the ngx_http_perl_module supports the $r->request_body_file
method.
*) Feature: the "client_body_in_file_only" directive.
*) Workaround: now on disk overflow nginx tries to write access logs
once a second only.
Thanks to Anton Yuzhaninov and Maxim Dounin.
*) Bugfix: now the "limit_rate" directive more precisely limits rate if
rate is more than 100 Kbyte/s.
Thanks to ForJest.
*) Bugfix: now the IMAP/POP3 proxy escapes the "\r" and "\n" symbols in
login and password to pass authorization server.
Thanks to Maxim Dounin.
Diffstat (limited to 'src')
26 files changed, 928 insertions, 430 deletions
diff --git a/src/core/nginx.c b/src/core/nginx.c index 64f801205..12a2b8a25 100644 --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -426,7 +426,7 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv) ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); - if (ngx_rename_file((char *) ccf->pid.data, (char *) ccf->oldpid.data) + if (ngx_rename_file(ccf->pid.data, ccf->oldpid.data) != NGX_OK) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, @@ -442,7 +442,7 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv) pid = ngx_execute(cycle, &ctx); if (pid == NGX_INVALID_PID) { - if (ngx_rename_file((char *) ccf->oldpid.data, (char *) ccf->pid.data) + if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data) != NGX_OK) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, diff --git a/src/core/nginx.h b/src/core/nginx.h index 06e46c0c6..f47c36ae2 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VER "nginx/0.3.37" +#define NGINX_VER "nginx/0.3.38" #define NGINX_VAR "NGINX" #define NGX_OLDPID_EXT ".oldbin" diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c index e3e719022..0a2c37190 100644 --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -19,7 +19,7 @@ ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain) if (tf->file.fd == NGX_INVALID_FILE) { rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool, - tf->persistent); + tf->persistent, tf->mode); if (rc == NGX_ERROR || rc == NGX_AGAIN) { return rc; @@ -36,7 +36,7 @@ ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain) ngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool, - ngx_uint_t persistent) + ngx_uint_t persistent, ngx_uint_t mode) { ngx_err_t err; ngx_atomic_uint_t n; @@ -71,11 +71,7 @@ ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool, return NGX_ERROR; } -#if 1 - file->fd = ngx_open_tempfile(file->name.data, persistent); -#else - file->fd = ngx_open_tempfile(file->name.data, 1); -#endif + file->fd = ngx_open_tempfile(file->name.data, persistent, mode); ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, "temp fd:%d", file->fd); diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h index 52c1ae22d..488c10ad6 100644 --- a/src/core/ngx_file.h +++ b/src/core/ngx_file.h @@ -49,13 +49,15 @@ typedef struct { ngx_pool_t *pool; char *warn; + ngx_uint_t mode; + unsigned persistent:1; } ngx_temp_file_t; ssize_t ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain); ngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, - ngx_pool_t *pool, ngx_uint_t persistent); + ngx_pool_t *pool, ngx_uint_t persistent,ngx_uint_t mode); void ngx_create_hashed_filename(ngx_file_t *file, ngx_path_t *path); ngx_int_t ngx_create_path(ngx_file_t *file, ngx_path_t *path); ngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot); diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c index 45110c59e..6581e5904 100644 --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -52,6 +52,7 @@ ngx_http_chunked_header_filter(ngx_http_request_t *r) { if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED || r->headers_out.status == NGX_HTTP_NO_CONTENT + || r->headers_out.status == NGX_HTTP_CREATED || r != r->main) { return ngx_http_next_header_filter(r); diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c new file mode 100644 index 000000000..1b982044f --- /dev/null +++ b/src/http/modules/ngx_http_dav_module.c @@ -0,0 +1,286 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +#define NGX_HTTP_DAV_OFF 2 + +typedef struct { + ngx_uint_t methods; +} ngx_http_dav_loc_conf_t; + + +static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r); +static void ngx_http_dav_put_handler(ngx_http_request_t *r); +static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child); +static ngx_int_t ngx_http_dav_init(ngx_cycle_t *cycle); + + +static ngx_conf_bitmask_t ngx_http_dav_methods_mask[] = { + { ngx_string("off"), NGX_HTTP_DAV_OFF }, + { ngx_string("put"), NGX_HTTP_PUT }, + { ngx_string("delete"), NGX_HTTP_DELETE }, + { ngx_null_string, 0 } +}; + + +static ngx_command_t ngx_http_dav_commands[] = { + + { ngx_string("dav_methods"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_dav_loc_conf_t, methods), + &ngx_http_dav_methods_mask }, + + ngx_null_command +}; + + +ngx_http_module_t ngx_http_dav_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_dav_create_loc_conf, /* create location configuration */ + ngx_http_dav_merge_loc_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_dav_module = { + NGX_MODULE_V1, + &ngx_http_dav_module_ctx, /* module context */ + ngx_http_dav_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + ngx_http_dav_init, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_http_dav_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_str_t path; + ngx_http_dav_loc_conf_t *dlcf; + + /* TODO: Win32 */ + if (r->zero_in_uri) { + return NGX_DECLINED; + } + + dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); + + if (!(r->method & dlcf->methods)) { + return NGX_DECLINED; + } + + switch (r->method) { + + case NGX_HTTP_PUT: + + if (r->uri.data[r->uri.len - 1] == '/') { + return NGX_DECLINED; + } + + r->request_body_in_file_only = 1; + r->request_body_in_persistent_file = 1; + r->request_body_delete_incomplete_file = 1; + r->request_body_file_group_access = 1; + + rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_DONE; + + case NGX_HTTP_DELETE: + + if (r->uri.data[r->uri.len - 1] == '/') { + return NGX_DECLINED; + } + + ngx_http_map_uri_to_path(r, &path, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http delete filename: \"%s\"", path.data); + + if (ngx_delete_file(path.data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", path.data); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_HTTP_NO_CONTENT; + } + + return NGX_DECLINED; +} + + +static void +ngx_http_dav_put_handler(ngx_http_request_t *r) +{ + u_char *location; + ngx_err_t err; + ngx_str_t *temp, path; + ngx_uint_t status; + ngx_file_info_t fi; + ngx_http_core_loc_conf_t *clcf; + + ngx_http_map_uri_to_path(r, &path, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http put filename: \"%s\"", path.data); + + temp = &r->request_body->temp_file->file.name; + + if (ngx_file_info(path.data, &fi) == -1) { + status = NGX_HTTP_CREATED; + + } else { + status = NGX_HTTP_NO_CONTENT; + } + + if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { + goto ok; + } + + err = ngx_errno; + +#if (NGX_WIN32) + + if (err == NGX_EEXIST) { + if (ngx_win32_rename_file(temp, &path, r->pool) != NGX_ERROR) { + + if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { + goto ok; + } + } + + err = ngx_errno; + } + +#endif + + ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, + ngx_rename_file_n " \"%s\" failed", path.data); + + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + +ok: + + if (status == NGX_HTTP_CREATED) { + + r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t)); + if (r->headers_out.location == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (!clcf->alias && clcf->root_lengths == NULL) { + location = path.data + clcf->root.len; + + } else { + location = ngx_palloc(r->pool, r->uri.len); + if (location == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + ngx_memcpy(location, r->uri.data, r->uri.len); + } + + /* + * we do not need to set the r->headers_out.location->hash and + * r->headers_out.location->key fields + */ + + r->headers_out.location->value.len = r->uri.len; + r->headers_out.location->value.data = location; + + } + + r->headers_out.status = status; + r->header_only = 1; + + ngx_http_finalize_request(r, ngx_http_send_header(r)); + return; +} + + +static void * +ngx_http_dav_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_dav_loc_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + /* + * set by ngx_pcalloc(): + * + * conf->methods = 0; + */ + + return conf; +} + + +static char * +ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_dav_loc_conf_t *prev = parent; + ngx_http_dav_loc_conf_t *conf = child; + + ngx_conf_merge_bitmask_value(conf->methods, prev->methods, + (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF)); + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_dav_init(ngx_cycle_t *cycle) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_dav_handler; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_log_module.c b/src/http/modules/ngx_http_log_module.c index 9436e5983..07ac113a4 100644 --- a/src/http/modules/ngx_http_log_module.c +++ b/src/http/modules/ngx_http_log_module.c @@ -23,6 +23,7 @@ typedef struct { typedef struct { ngx_open_file_t *file; + time_t disk_full_time; ngx_array_t *ops; /* array of ngx_http_log_op_t */ } ngx_http_log_t; @@ -253,6 +254,17 @@ ngx_http_log_handler(ngx_http_request_t *r) 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 the log for one second. + */ + + continue; + } + len = 0; op = log[l].ops->elts; for (i = 0; i < log[l].ops->nelts; i++) { @@ -272,7 +284,13 @@ ngx_http_log_handler(ngx_http_request_t *r) if (len > (size_t) (file->last - file->pos)) { - ngx_write_fd(file->fd, file->buffer, file->pos - file->buffer); + if (ngx_write_fd(file->fd, file->buffer, + file->pos - file->buffer) + == -1 + && ngx_errno == NGX_ENOSPC) + { + log[l].disk_full_time = ngx_time(); + } file->pos = file->buffer; } @@ -306,7 +324,11 @@ ngx_http_log_handler(ngx_http_request_t *r) ngx_linefeed(p); - ngx_write_fd(file->fd, line, p - line); + if (ngx_write_fd(file->fd, line, p - line) == -1 + && ngx_errno == NGX_ENOSPC) + { + log[l].disk_full_time = ngx_time(); + } } return NGX_OK; @@ -1017,6 +1039,8 @@ ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } + log->disk_full_time = 0; + if (cf->args->nelts >= 3) { name = value[2]; diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs index 7e2cec30e..735cc377b 100644 --- a/src/http/modules/perl/nginx.xs +++ b/src/http/modules/perl/nginx.xs @@ -13,6 +13,20 @@ #include "XSUB.h" +#define ngx_http_perl_set_request(r) \ + r = INT2PTR(ngx_http_request_t *, SvIV((SV *) SvRV(ST(0)))) + + +#define ngx_http_perl_set_targ(p, len, z) \ + \ + sv_upgrade(TARG, SVt_PV); \ + SvPOK_on(TARG); \ + SvPV_set(TARG, (char *) p); \ + SvLEN_set(TARG, len + z); \ + SvCUR_set(TARG, len); \ + SvFAKE_on(TARG); \ + SvREADONLY_on(TARG); \ + static ngx_int_t ngx_http_perl_sv2str(pTHX_ ngx_http_request_t *r, ngx_str_t *s, SV *sv) @@ -79,15 +93,14 @@ ngx_http_perl_output(ngx_http_request_t *r, ngx_buf_t *b) MODULE = nginx PACKAGE = nginx -int +void send_http_header(r, ...) - nginx r - - PREINIT: + CODE: - SV *sv; + ngx_http_request_t *r; + SV *sv; - CODE: + ngx_http_perl_set_request(r); if (r->headers_out.status == 0) { r->headers_out.status = NGX_HTTP_OK; @@ -99,127 +112,104 @@ send_http_header(r, ...) if (ngx_http_perl_sv2str(aTHX_ r, &r->headers_out.content_type, sv) != NGX_OK) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } } else { if (r->headers_out.content_type.len == 0) { if (ngx_http_set_content_type(r) != NGX_OK) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } } } - RETVAL = ngx_http_send_header(r); + (void) ngx_http_send_header(r); - done: - - OUTPUT: - RETVAL - -int +void header_only(r) - nginx r - CODE: - RETVAL = r->header_only; - - OUTPUT: - RETVAL + dXSTARG; + ngx_http_request_t *r; -# The returning "char *" is more quickly than creating SV, because SV returned -# from XS is never used as permanent storage. Even in simple case: -# "$uri = $r->uri" the SV returned by $r->uri is copied to $uri's SV. + ngx_http_perl_set_request(r); -char * -uri(r, ...) - nginx r + sv_upgrade(TARG, SVt_IV); + sv_setiv(TARG, r->header_only); - CODE: + ST(0) = TARG; - if (items != 1) { - croak("$r->uri(text) is not implemented"); - } - RETVAL = ngx_palloc(r->pool, r->uri.len + 1); - if (RETVAL == NULL) { - XSRETURN_UNDEF; - } +void +uri(r) + CODE: - ngx_cpystrn((u_char *) RETVAL, r->uri.data, r->uri.len + 1); + dXSTARG; + ngx_http_request_t *r; - OUTPUT: - RETVAL + ngx_http_perl_set_request(r); + ngx_http_perl_set_targ(r->uri.data, r->uri.len, 0); + ST(0) = TARG; -char * -args(r, ...) - nginx r +void +args(r) CODE: - if (items != 1) { - croak("$r->args(text) is not implemented"); - } - - RETVAL = ngx_palloc(r->pool, r->args.len + 1); - if (RETVAL == NULL) { - XSRETURN_UNDEF; - } + dXSTARG; + ngx_http_request_t *r; - ngx_cpystrn((u_char *) RETVAL, r->args.data, r->args.len + 1); + ngx_http_perl_set_request(r); + ngx_http_perl_set_targ(r->args.data, r->args.len, 0); - OUTPUT: - RETVAL + ST(0) = TARG; -char * +void request_method(r) - nginx r - CODE: - RETVAL = ngx_palloc(r->pool, r->method_name.len + 1); - if (RETVAL == NULL) { - XSRETURN_UNDEF; - } + dXSTARG; + ngx_http_request_t *r; - ngx_cpystrn((u_char *) RETVAL, r->method_name.data, r->method_name.len + 1); + ngx_http_perl_set_request(r); + ngx_http_perl_set_targ(r->method_name.data, r->method_name.len, 0); - OUTPUT: - RETVAL + ST(0) = TARG; -char * +void remote_addr(r) - nginx r - CODE: - RETVAL = (char *) r->connection->addr_text.data; + dXSTARG; + ngx_http_request_t *r; + + ngx_http_perl_set_request(r); + ngx_http_perl_set_targ(r->connection->addr_text.data, + r->connection->addr_text.len, 1); - OUTPUT: - RETVAL + ST(0) = TARG; -char * +void header_in(r, key) - nginx r - SV *key + CODE: - PREINIT: + dXSTARG; + ngx_http_request_t *r; + SV *key; + u_char *p; + STRLEN len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; - u_char *p; - STRLEN len; - ngx_uint_t i; - ngx_list_part_t *part; - ngx_table_elt_t *header; + ngx_http_perl_set_request(r); - CODE: + key = ST(1); if (SvROK(key) && SvTYPE(SvRV(key)) == SVt_PV) { key = SvRV(key); @@ -248,7 +238,7 @@ header_in(r, key) continue; } - RETVAL = (char *) header[i].value.data; + ngx_http_perl_set_targ(header[i].value.data, header[i].value.len, 0); goto done; } @@ -257,73 +247,80 @@ header_in(r, key) done: - OUTPUT: - RETVAL + ST(0) = TARG; -SV * +void request_body(r) - nginx r - - PREINIT: - - STRLEN len; - ngx_chain_t *cl; - CODE: - len = 0; + dXSTARG; + ngx_http_request_t *r; + size_t len; - for (cl = r->request_body->bufs; cl; cl = cl->next) { - if (cl->buf->in_file) { - XSRETURN_UNDEF; - } + ngx_http_perl_set_request(r); - len += cl->buf->last - cl->buf->pos; + if (r->request_body->temp_file || r->request_body->bufs == NULL) { + XSRETURN_UNDEF; } + len = r->request_body->bufs->buf->last - r->request_body->bufs->buf->pos; + if (len == 0) { XSRETURN_UNDEF; } - RETVAL = newSV(len); + ngx_http_perl_set_targ(r->request_body->bufs->buf->pos, len, 0); + + ST(0) = TARG; - for (cl = r->request_body->bufs; cl; cl = cl->next) { - sv_catpvn(RETVAL, cl->buf->pos, cl->buf->last - cl->buf->pos); + +void +request_body_file(r) + CODE: + + dXSTARG; + ngx_http_request_t *r; + + ngx_http_perl_set_request(r); + + if (r->request_body->temp_file == NULL) { + XSRETURN_UNDEF; } - OUTPUT: - RETVAL + ngx_http_perl_set_targ(r->request_body->temp_file->file.name.data, + r->request_body->temp_file->file.name.len, 1); + ST(0) = TARG; -int + +void header_out(r, key, value) - nginx r - SV *key - SV *value + CODE: - PREINIT: + ngx_http_request_t *r; + SV *key; + SV *value; + ngx_table_elt_t *header; - ngx_table_elt_t *header; + ngx_http_perl_set_request(r); - CODE: + key = ST(1); + value = ST(2); header = ngx_list_push(&r->headers_out.headers); if (header == NULL) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } header->hash = 1; if (ngx_http_perl_sv2str(aTHX_ r, &header->key, key) != NGX_OK) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } if (ngx_http_perl_sv2str(aTHX_ r, &header->value, value) != NGX_OK) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } if (header->key.len == sizeof("Content-Length") - 1 @@ -335,62 +332,51 @@ header_out(r, key, value) r->headers_out.content_length = header; } - RETVAL = NGX_OK; - - done: - - OUTPUT: - RETVAL + XSRETURN_EMPTY; -char * +void filename(r) - nginx r - - PREINIT: + CODE: - ngx_str_t path; + dXSTARG; + ngx_http_request_t *r; ngx_http_perl_ctx_t *ctx; - CODE: + ngx_http_perl_set_request(r); ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module); - if (ctx->filename) { + if (ctx->filename.data) { goto done; } - if (ngx_http_map_uri_to_path(r, &path, 0) == NULL) { + if (ngx_http_map_uri_to_path(r, &ctx->filename, 0) == NULL) { XSRETURN_UNDEF; } - ctx->filename = (char *) path.data; - - sv_setpv(PL_statname, ctx->filename); + ctx->filename.len--; + sv_setpv(PL_statname, (char *) ctx->filename.data); done: - RETVAL = ctx->filename; + ngx_http_perl_set_targ(ctx->filename.data, ctx->filename.len, 1); - OUTPUT: - RETVAL + ST(0) = TARG; -int +void print(r, ...) - nginx r - - PREINIT: - - SV *sv; - int i; - u_char *p; - size_t size; - STRLEN len; - ngx_buf_t *b; - CODE: - RETVAL = NGX_OK; + ngx_http_request_t *r; + SV *sv; + int i; + u_char *p; + size_t size; + STRLEN len; + ngx_buf_t *b; + + ngx_http_perl_set_request(r); if (items == 2) { @@ -410,13 +396,12 @@ print(r, ...) p = (u_char *) SvPV(sv, len); if (len == 0) { - goto done; + XSRETURN_EMPTY; } b = ngx_calloc_buf(r->pool); if (b == NULL) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } b->memory = 1; @@ -451,13 +436,12 @@ print(r, ...) } if (size == 0) { - goto done; + XSRETURN_EMPTY; } b = ngx_create_temp_buf(r->pool, size); if (b == NULL) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } for (i = 1; i < items; i++) { @@ -473,51 +457,49 @@ print(r, ...) out: - RETVAL = ngx_http_perl_output(r, b); - - done: + (void) ngx_http_perl_output(r, b); - OUTPUT: - RETVAL + XSRETURN_EMPTY; -int +void sendfile(r, filename, offset = -1, bytes = 0) - nginx r - char *filename + CODE: + + ngx_http_request_t *r; + char *filename; int offset; size_t bytes; - - PREINIT: - ngx_fd_t fd; ngx_buf_t *b; ngx_file_info_t fi; ngx_pool_cleanup_t *cln; ngx_pool_cleanup_file_t *clnf; - CODE: + ngx_http_perl_set_request(r); + + filename = SvPV_nolen(ST(1)); if (filename == NULL) { croak("sendfile(): NULL filename"); } + offset = items < 3 ? -1 : SvIV(ST(2)); + bytes = items < 4 ? 0 : SvIV(ST(3)); + b = ngx_calloc_buf(r->pool); if (b == NULL) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); if (b->file == NULL) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)); if (cln == NULL) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } fd = ngx_open_file((u_char *) filename, NGX_FILE_RDONLY, NGX_FILE_OPEN); @@ -525,8 +507,7 @@ sendfile(r, filename, offset = -1, bytes = 0) if (fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, ngx_open_file_n " \"%s\" failed", filename); - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } if (offset == -1) { @@ -543,9 +524,7 @@ sendfile(r, filename, offset = -1, bytes = 0) ngx_close_file_n " \"%s\" failed", filename); } - RETVAL = NGX_ERROR; - goto done; - + XSRETURN_EMPTY; } bytes = ngx_file_size(&fi) - offset; @@ -566,53 +545,46 @@ sendfile(r, filename, offset = -1, bytes = 0) b->file->fd = fd; b->file->log = r->connection->log; - RETVAL = ngx_http_perl_output(r, b); + (void) ngx_http_perl_output(r, b); - done: + XSRETURN_EMPTY; - OUTPUT: - RETVAL - -int +void rflush(r) - nginx r - - PREINIT: + CODE: - ngx_buf_t *b; + ngx_http_request_t *r; + ngx_buf_t *b; - CODE: + ngx_http_perl_set_request(r); b = ngx_calloc_buf(r->pool); if (b == NULL) { - RETVAL = NGX_ERROR; - goto done; + XSRETURN_EMPTY; } b->flush = 1; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "$r->rflush"); - RETVAL = ngx_http_perl_output(r, b); + (void) ngx_http_perl_output(r, b); - done: - - OUTPUT: - RETVAL + XSRETURN_EMPTY; void internal_redirect(r, uri) - nginx r - SV *uri - - PREINIT: + CODE: + ngx_http_request_t *r; + SV *uri; ngx_uint_t i; ngx_http_perl_ctx_t *ctx; - CODE: + ngx_http_perl_set_request(r); + + uri = ST(1); ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module); @@ -632,32 +604,35 @@ internal_redirect(r, uri) } -char * +void unescape(r, text, type = 0) - nginx r - SV *text - int type + CODE: - PREINIT: + dXSTARG; + ngx_http_request_t *r; + SV *text; + int type; + u_char *p, *dst, *src; + STRLEN len; - u_char *p, *dst, *src; - STRLEN n; + ngx_http_perl_set_request(r); - CODE: + text = ST(1); - src = (u_char *) SvPV(text, n); + src = (u_char *) SvPV(text, len); - p = ngx_palloc(r->pool, n + 1); + p = ngx_palloc(r->pool, len + 1); if (p == NULL) { XSRETURN_UNDEF; } dst = p; - ngx_unescape_uri(&dst, &src, n, (ngx_uint_t) type); + type = items < 3 ? 0 : SvIV(ST(2)); + + ngx_unescape_uri(&dst, &src, len, (ngx_uint_t) type); *dst = '\0'; - RETVAL = (char *) p; + ngx_http_perl_set_targ(p, dst - p, 1); - OUTPUT: - RETVAL + ST(0) = TARG; diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c index e453dc38b..84de2627a 100644 --- a/src/http/modules/perl/ngx_http_perl_module.c +++ b/src/http/modules/perl/ngx_http_perl_module.c @@ -165,10 +165,14 @@ static ngx_http_ssi_command_t ngx_http_perl_ssi_command = { #endif +static HV *nginx_stash; + static void ngx_http_perl_xs_init(pTHX) { newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__); + + nginx_stash = gv_stashpv("nginx", TRUE); } @@ -182,6 +186,9 @@ ngx_http_perl_handler(ngx_http_request_t *r) return NGX_HTTP_NOT_FOUND; } + r->request_body_in_single_buf = 1; + r->request_body_in_persistent_file = 1; + rc = ngx_http_read_client_request_body(r, ngx_http_perl_handle_request); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { @@ -259,7 +266,7 @@ ngx_http_perl_handle_request(ngx_http_request_t *r) uri.len = 0; } - ctx->filename = NULL; + ctx->filename.data = NULL; ctx->redirect_uri.len = 0; if (uri.len) { @@ -332,7 +339,7 @@ ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, v->not_found = 1; } - ctx->filename = NULL; + ctx->filename.data = NULL; ctx->redirect_uri.len = 0; return rc; @@ -409,7 +416,7 @@ ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx, ngx_http_perl_free_interpreter(pmcf, ctx->perl); - ctx->filename = NULL; + ctx->filename.data = NULL; ctx->redirect_uri.len = 0; ctx->ssi = NULL; @@ -631,8 +638,7 @@ ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r, SV *sub, PUSHMARK(sp); - sv = sv_newmortal(); - sv_setref_pv(sv, "nginx", r); + sv = sv_2mortal(sv_bless(newRV_noinc(newSViv(PTR2IV(r))), nginx_stash)); XPUSHs(sv); if (args) { diff --git a/src/http/modules/perl/ngx_http_perl_module.h b/src/http/modules/perl/ngx_http_perl_module.h index 1bc3852e7..1609c478b 100644 --- a/src/http/modules/perl/ngx_http_perl_module.h +++ b/src/http/modules/perl/ngx_http_perl_module.h @@ -21,8 +21,7 @@ typedef ngx_http_request_t *nginx; typedef struct { PerlInterpreter *perl; - char *filename; - + ngx_str_t filename; ngx_str_t redirect_uri; ngx_str_t redirect_args; @@ -45,7 +44,7 @@ extern ngx_module_t ngx_http_perl_module; #endif -extern void boot_DynaLoader (pTHX_ CV* cv); +extern void boot_DynaLoader(pTHX_ CV* cv); #endif /* _NGX_HTTP_PERL_MODULE_H_INCLUDED_ */ diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 62e172b62..6a08c4b32 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -268,6 +268,13 @@ static ngx_command_t ngx_http_core_commands[] = { offsetof(ngx_http_core_loc_conf_t, client_body_temp_path), (void *) ngx_garbage_collector_temp_handler }, + { ngx_string("client_body_in_file_only"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, client_body_in_file_only), + NULL }, + { ngx_string("sendfile"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -743,6 +750,11 @@ ngx_http_update_location_config(ngx_http_request_t *r) r->connection->sendfile = 0; } + if (clcf->client_body_in_file_only) { + r->request_body_in_file_only = 1; + r->request_body_in_persistent_file = 1; + } + if (r->keepalive && clcf->keepalive_timeout == 0) { r->keepalive = 0; } @@ -1987,6 +1999,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf) lcf->client_body_timeout = NGX_CONF_UNSET_MSEC; lcf->satisfy_any = NGX_CONF_UNSET; lcf->internal = NGX_CONF_UNSET; + lcf->client_body_in_file_only = NGX_CONF_UNSET; lcf->sendfile = NGX_CONF_UNSET; lcf->tcp_nopush = NGX_CONF_UNSET; lcf->tcp_nodelay = NGX_CONF_UNSET; @@ -2151,6 +2164,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->satisfy_any, prev->satisfy_any, 0); ngx_conf_merge_value(conf->internal, prev->internal, 0); + ngx_conf_merge_value(conf->client_body_in_file_only, + prev->client_body_in_file_only, 0); ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0); ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0); ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 0); diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index 6e19ebf8c..681bb27ff 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -229,6 +229,7 @@ struct ngx_http_core_loc_conf_s { ngx_flag_t satisfy_any; /* satisfy_any */ ngx_flag_t internal; /* internal */ + ngx_flag_t client_body_in_file_only; /* client_body_in_file_only */ ngx_flag_t sendfile; /* sendfile */ ngx_flag_t tcp_nopush; /* tcp_nopush */ ngx_flag_t tcp_nodelay; /* tcp_nodelay */ diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c index df40c43f7..9f93bd8f5 100644 --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -193,7 +193,7 @@ int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx, #endif if (retry || (err != NGX_ENOENT && err != NGX_ENOTDIR)) { - ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, ngx_rename_file_n "(\"%s\", \"%s\") failed", temp_file->data, ctx->file.name.data); diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c index d835c43ef..69b624b11 100644 --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -51,7 +51,7 @@ static char ngx_http_server_string[] = "Server: " NGINX_VER CRLF; static ngx_str_t ngx_http_status_lines[] = { ngx_string("200 OK"), - ngx_null_string, /* "201 Created" */ + ngx_string("201 Created"), ngx_null_string, /* "202 Accepted" */ ngx_null_string, /* "203 Non-Authoritative Information" */ ngx_string("204 No Content"), diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 91722881e..c8431774f 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -71,6 +71,9 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) if (m[0] == 'G' && m[1] == 'E' && m[2] == 'T') { r->method = NGX_HTTP_GET; + + } else if (m[0] == 'P' && m[1] == 'U' && m[2] == 'T') { + r->method = NGX_HTTP_PUT; } } else if (p - m == 4) { @@ -85,6 +88,14 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) { r->method = NGX_HTTP_HEAD; } + + } else if (p - m == 6) { + + if (m[0] == 'D' && m[1] == 'E' && m[2] == 'L' + && m[3] == 'E' && m[4] == 'T' && m[5] == 'E') + { + r->method = NGX_HTTP_DELETE; + } } state = sw_spaces_before_uri; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 103db1788..c296c9464 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -32,9 +32,6 @@ static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r); static void ngx_http_writer(ngx_http_request_t *r); static void ngx_http_block_read(ngx_http_request_t *r); -static void ngx_http_read_discarded_body_handler(ngx_http_request_t *r); -static ngx_int_t ngx_http_read_discarded_body(ngx_http_request_t *r); - static void ngx_http_set_keepalive(ngx_http_request_t *r); static void ngx_http_keepalive_handler(ngx_event_t *ev); static void ngx_http_set_lingering_close(ngx_http_request_t *r); @@ -1682,117 +1679,6 @@ ngx_http_block_read(ngx_http_request_t *r) } -ngx_int_t -ngx_http_discard_body(ngx_http_request_t *r) -{ - ssize_t size; - ngx_event_t *rev; - - if (r != r->main) { - return NGX_OK; - } - - rev = r->connection->read; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); - - if (rev->timer_set) { - ngx_del_timer(rev); - } - - if (r->headers_in.content_length_n <= 0) { - return NGX_OK; - } - - r->discard_body = 1; - - size = r->header_in->last - r->header_in->pos; - - if (size) { - if (r->headers_in.content_length_n > size) { - r->headers_in.content_length_n -= size; - - } else { - r->header_in->pos += r->headers_in.content_length_n; - r->headers_in.content_length_n = 0; - return NGX_OK; - } - } - - r->read_event_handler = ngx_http_read_discarded_body_handler; - - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - return ngx_http_read_discarded_body(r); -} - - -static void -ngx_http_read_discarded_body_handler(ngx_http_request_t *r) -{ - ngx_int_t rc; - - rc = ngx_http_read_discarded_body(r); - - if (rc == NGX_AGAIN) { - if (ngx_handle_read_event(r->connection->read, 0) == NGX_ERROR) { - ngx_http_close_request(r, rc); - return; - } - } - - if (rc != NGX_OK) { - ngx_http_close_request(r, rc); - } -} - - -static ngx_int_t -ngx_http_read_discarded_body(ngx_http_request_t *r) -{ - ssize_t size, n; - u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE]; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http read discarded body"); - - if (r->headers_in.content_length_n == 0) { - return NGX_OK; - } - - - size = r->headers_in.content_length_n; - - if (size > NGX_HTTP_DISCARD_BUFFER_SIZE) { - size = NGX_HTTP_DISCARD_BUFFER_SIZE; - } - - n = r->connection->recv(r->connection, buffer, size); - - if (n == NGX_ERROR) { - - r->connection->error = 1; - - /* - * if a client request body is discarded then we already set - * some HTTP response code for client and we can ignore the error - */ - - return NGX_OK; - } - - if (n == NGX_AGAIN) { - return NGX_AGAIN; - } - - r->headers_in.content_length_n -= n; - - return NGX_OK; -} - - static void ngx_http_set_keepalive(ngx_http_request_t *r) { diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 2e4ec22b9..182e57d27 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -19,10 +19,12 @@ #define NGX_HTTP_VERSION_10 1000 #define NGX_HTTP_VERSION_11 1001 -#define NGX_HTTP_UNKNOWN 1 -#define NGX_HTTP_GET 2 -#define NGX_HTTP_HEAD 4 -#define NGX_HTTP_POST 8 +#define NGX_HTTP_UNKNOWN 0x0001 +#define NGX_HTTP_GET 0x0002 +#define NGX_HTTP_HEAD 0x0004 +#define NGX_HTTP_POST 0x0008 +#define NGX_HTTP_PUT 0x0010 +#define NGX_HTTP_DELETE 0x0020 #define NGX_HTTP_CONNECTION_CLOSE 1 #define NGX_HTTP_CONNECTION_KEEP_ALIVE 2 @@ -44,6 +46,7 @@ #define NGX_HTTP_OK 200 +#define NGX_HTTP_CREATED 201 #define NGX_HTTP_NO_CONTENT 204 #define NGX_HTTP_PARTIAL_CONTENT 206 @@ -229,6 +232,7 @@ typedef struct { ngx_chain_t *bufs; ngx_buf_t *buf; size_t rest; + ngx_chain_t *to_write; ngx_http_client_body_handler_pt post_handler; } ngx_http_request_body_t; @@ -375,6 +379,12 @@ struct ngx_http_request_s { unsigned uri_changed:1; unsigned uri_changes:4; + unsigned request_body_in_single_buf:1; + unsigned request_body_in_file_only:1; + unsigned request_body_in_persistent_file:1; + unsigned request_body_delete_incomplete_file:1; + unsigned request_body_file_group_access:1; + unsigned fast_subrequest:1; unsigned low_case_exten:1; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index 48aff74ac..cda7f7d8d 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -12,6 +12,12 @@ static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r); +static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r, + ngx_chain_t *body); +static void ngx_http_finalize_request_body(ngx_http_request_t *r, ngx_int_t rc); +static void ngx_http_read_discarded_body_handler(ngx_http_request_t *r); +static ngx_int_t ngx_http_read_discarded_body(ngx_http_request_t *r); + /* * on completion ngx_http_read_client_request_body() adds to @@ -23,11 +29,10 @@ static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r); ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler) - { - ssize_t size; + ssize_t size, preread; ngx_buf_t *b; - ngx_chain_t *cl; + ngx_chain_t *cl, **next; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; @@ -58,20 +63,25 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, * rb->rest = 0; */ - size = r->header_in->last - r->header_in->pos; + preread = r->header_in->last - r->header_in->pos; - if (size) { + if (preread) { /* there is the pre-read part of the request body */ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http client request body preread %uz", preread); + b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->temporary = 1; - b->start = b->pos = r->header_in->pos; - b->end = b->last = r->header_in->last; + b->start = r->header_in->pos; + b->pos = r->header_in->pos; + b->last = r->header_in->last; + b->end = r->header_in->end; rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { @@ -81,34 +91,70 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, rb->bufs->buf = b; rb->bufs->next = NULL; - if (size >= r->headers_in.content_length_n) { + if (preread >= r->headers_in.content_length_n) { /* the whole request body was pre-read */ r->header_in->pos += r->headers_in.content_length_n; r->request_length += r->headers_in.content_length_n; + if (r->request_body_in_file_only) { + if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + post_handler(r); return NGX_OK; } + /* + * to not consider the body as pipelined request in + * ngx_http_set_keepalive() + */ r->header_in->pos = r->header_in->last; - r->request_length += size; - } + r->request_length += preread; + + rb->rest = r->headers_in.content_length_n - preread; + + if (rb->rest <= (size_t) (b->end - b->last)) { + + /* the whole request body may be placed in r->header_in */ + + rb->buf = b; + + r->read_event_handler = ngx_http_read_client_request_body_handler; + + return ngx_http_do_read_client_request_body(r); + } + + next = &rb->bufs->next; + + } else { + b = NULL; + rb->rest = r->headers_in.content_length_n; + next = &rb->bufs; + } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - rb->rest = r->headers_in.content_length_n - size; + size = clcf->client_body_buffer_size; + size += size >> 2; - if (rb->rest < clcf->client_body_buffer_size - + (clcf->client_body_buffer_size >> 2)) - { + if (rb->rest < (size_t) size) { size = rb->rest; + if (r->request_body_in_single_buf) { + size += preread; + } + } else { size = clcf->client_body_buffer_size; + + /* disable copying buffer for r->request_body_in_single_buf */ + b = NULL; } rb->buf = ngx_create_temp_buf(r->pool, size); @@ -124,11 +170,21 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, cl->buf = rb->buf; cl->next = NULL; - if (rb->bufs) { - rb->bufs->next = cl; + if (b && r->request_body_in_single_buf) { + size = b->last - b->pos; + ngx_memcpy(rb->buf->pos, b->pos, size); + rb->buf->last += size; + + next = &rb->bufs; + } + + *next = cl; + + if (r->request_body_in_file_only || r->request_body_in_single_buf) { + rb->to_write = rb->bufs; } else { - rb->bufs = cl; + rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; } r->read_event_handler = ngx_http_read_client_request_body_handler; @@ -144,14 +200,14 @@ ngx_http_read_client_request_body_handler(ngx_http_request_t *r) if (r->connection->read->timedout) { r->connection->timedout = 1; - ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); + ngx_http_finalize_request_body(r, NGX_HTTP_REQUEST_TIME_OUT); return; } rc = ngx_http_do_read_client_request_body(r); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - ngx_http_finalize_request(r, rc); + ngx_http_finalize_request_body(r, rc); } } @@ -162,7 +218,6 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) size_t size; ssize_t n; ngx_buf_t *b; - ngx_temp_file_t *tf; ngx_connection_t *c; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; @@ -176,36 +231,11 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) for ( ;; ) { if (rb->buf->last == rb->buf->end) { - if (rb->temp_file == NULL) { - tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); - if (tf == NULL) { - return NGX_ERROR; - } - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - tf->file.fd = NGX_INVALID_FILE; - tf->file.log = r->connection->log; - tf->path = clcf->client_body_temp_path; - tf->pool = r->pool; - tf->warn = "a client request body is buffered " - "to a temporary file"; - - rb->temp_file = tf; - - } - - n = ngx_write_chain_to_temp_file(rb->temp_file, - rb->bufs->next ? rb->bufs->next: - rb->bufs); - - /* TODO: n == 0 or not complete and level event */ - - if (n == NGX_ERROR) { + if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - rb->temp_file->offset += n; + rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; rb->buf->last = rb->buf->start; } @@ -265,17 +295,11 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) ngx_del_timer(c->read); } - if (rb->temp_file) { + if (rb->temp_file || r->request_body_in_file_only) { /* save the last part */ - n = ngx_write_chain_to_temp_file(rb->temp_file, - rb->bufs->next ? rb->bufs->next: - rb->bufs); - - /* TODO: n == 0 or not complete and level event */ - - if (n == NGX_ERROR) { + if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -297,7 +321,188 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) } } + if (r->request_body_in_file_only && rb->bufs->next) { + rb->bufs = rb->bufs->next; + } + rb->post_handler(r); return NGX_OK; } + + +static ngx_int_t +ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body) +{ + ssize_t n; + ngx_temp_file_t *tf; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + + rb = r->request_body; + + if (rb->temp_file == NULL) { + tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); + if (tf == NULL) { + return NGX_ERROR; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + tf->file.fd = NGX_INVALID_FILE; + tf->file.log = r->connection->log; + tf->path = clcf->client_body_temp_path; + tf->pool = r->pool; + tf->warn = "a client request body is buffered to a temporary file"; + tf->persistent = r->request_body_in_persistent_file; + + if (r->request_body_file_group_access) { + tf->mode = 0660; + } + + rb->temp_file = tf; + } + + n = ngx_write_chain_to_temp_file(rb->temp_file, body); + + /* TODO: n == 0 or not complete and level event */ + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + rb->temp_file->offset += n; + + return NGX_OK; +} + + +static void +ngx_http_finalize_request_body(ngx_http_request_t *r, ngx_int_t rc) +{ + if (r->request_body->temp_file + && r->request_body_in_persistent_file + && r->request_body_delete_incomplete_file) + { + if (ngx_delete_file(r->request_body->temp_file->file.name.data) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", + r->request_body->temp_file->file.name.data); + } + } + + ngx_http_finalize_request(r, rc); +} + + +ngx_int_t +ngx_http_discard_body(ngx_http_request_t *r) +{ + ssize_t size; + ngx_event_t *rev; + + if (r != r->main) { + return NGX_OK; + } + + rev = r->connection->read; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); + + if (rev->timer_set) { + ngx_del_timer(rev); + } + + if (r->headers_in.content_length_n <= 0) { + return NGX_OK; + } + + r->discard_body = 1; + + size = r->header_in->last - r->header_in->pos; + + if (size) { + if (r->headers_in.content_length_n > size) { + r->headers_in.content_length_n -= size; + + } else { + r->header_in->pos += r->headers_in.content_length_n; + r->headers_in.content_length_n = 0; + return NGX_OK; + } + } + + r->read_event_handler = ngx_http_read_discarded_body_handler; + + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return ngx_http_read_discarded_body(r); +} + + +static void +ngx_http_read_discarded_body_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + + rc = ngx_http_read_discarded_body(r); + + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(r->connection->read, 0) == NGX_ERROR) { + ngx_http_finalize_request(r, rc); + return; + } + } + + if (rc != NGX_OK) { + ngx_http_finalize_request(r, rc); + } +} + + +static ngx_int_t +ngx_http_read_discarded_body(ngx_http_request_t *r) +{ + ssize_t size, n; + u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE]; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http read discarded body"); + + if (r->headers_in.content_length_n == 0) { + return NGX_OK; + } + + + size = r->headers_in.content_length_n; + + if (size > NGX_HTTP_DISCARD_BUFFER_SIZE) { + size = NGX_HTTP_DISCARD_BUFFER_SIZE; + } + + n = r->connection->recv(r->connection, buffer, size); + + if (n == NGX_ERROR) { + + r->connection->error = 1; + + /* + * if a client request body is discarded then we already set + * some HTTP response code for client and we can ignore the error + */ + + return NGX_OK; + } + + if (n == NGX_AGAIN) { + return NGX_AGAIN; + } + + r->headers_in.content_length_n -= n; + + return NGX_OK; +} diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c index 4bb42cdb9..f221276c6 100644 --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -199,7 +199,7 @@ static char error_504_page[] = static ngx_str_t error_pages[] = { - ngx_null_string, /* 204 */ + ngx_null_string, /* 201, 204 */ #define NGX_HTTP_LEVEL_200 1 @@ -320,7 +320,11 @@ ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) } } - if (error == NGX_HTTP_NO_CONTENT) { + if (error == NGX_HTTP_CREATED) { + /* 201 */ + err = 0; + + } else if (error == NGX_HTTP_NO_CONTENT) { /* 204 */ err = 0; diff --git a/src/http/ngx_http_write_filter_module.c b/src/http/ngx_http_write_filter_module.c index 30f1a72e2..5ae24b030 100644 --- a/src/http/ngx_http_write_filter_module.c +++ b/src/http/ngx_http_write_filter_module.c @@ -47,7 +47,7 @@ ngx_module_t ngx_http_write_filter_module = { ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) { - off_t size, sent; + off_t size, sent, to_send; ngx_uint_t last, flush; ngx_chain_t *cl, *ln, **ll, *chain; ngx_connection_t *c; @@ -209,25 +209,34 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) return NGX_ERROR; } + to_send = r->limit_rate * (ngx_time() - r->start_time + 1) - c->sent; + + if (to_send < 0) { + to_send = 0; + } + sent = c->sent; - chain = c->send_chain(c, r->out, r->limit_rate); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http write filter to send %O", to_send); + + chain = c->send_chain(c, r->out, to_send); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter %p", chain); - if (r->limit_rate) { + if (chain == NGX_CHAIN_ERROR) { + c->error = 1; + return NGX_ERROR; + } + + if (to_send) { sent = c->sent - sent; c->write->delayed = 1; ngx_add_timer(r->connection->write, (ngx_msec_t) (sent * 1000 / r->limit_rate)); } - if (chain == NGX_CHAIN_ERROR) { - c->error = 1; - return NGX_ERROR; - } - for (cl = r->out; cl && cl != chain; /* void */) { ln = cl; cl = cl->next; diff --git a/src/imap/ngx_imap_auth_http_module.c b/src/imap/ngx_imap_auth_http_module.c index cedb0d830..8dbbb72b2 100644 --- a/src/imap/ngx_imap_auth_http_module.c +++ b/src/imap/ngx_imap_auth_http_module.c @@ -68,6 +68,8 @@ static void ngx_imap_auth_http_block_read(ngx_event_t *rev); static void ngx_imap_auth_http_dummy_handler(ngx_event_t *ev); static ngx_buf_t *ngx_imap_auth_http_create_request(ngx_imap_session_t *s, ngx_pool_t *pool, ngx_imap_auth_http_conf_t *ahcf); +static ngx_int_t ngx_imap_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, + ngx_str_t *escaped); static void *ngx_imap_auth_http_create_conf(ngx_conf_t *cf); static char *ngx_imap_auth_http_merge_conf(ngx_conf_t *cf, void *parent, @@ -984,12 +986,21 @@ ngx_imap_auth_http_create_request(ngx_imap_session_t *s, ngx_pool_t *pool, { size_t len; ngx_buf_t *b; + ngx_str_t login, passwd; + + if (ngx_imap_auth_http_escape(pool, &s->login, &login) != NGX_OK) { + return NULL; + } + + if (ngx_imap_auth_http_escape(pool, &s->passwd, &passwd) != NGX_OK) { + return NULL; + } len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1 + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1 + sizeof("Auth-Method: plain" CRLF) - 1 - + sizeof("Auth-User: ") - 1 + s->login.len + sizeof(CRLF) - 1 - + sizeof("Auth-Pass: ") - 1 + s->passwd.len + sizeof(CRLF) - 1 + + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1 + + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1 + sizeof("Auth-Protocol: imap" CRLF) - 1 + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN + sizeof(CRLF) - 1 @@ -1016,11 +1027,11 @@ ngx_imap_auth_http_create_request(ngx_imap_session_t *s, ngx_pool_t *pool, sizeof("Auth-Method: plain" CRLF) - 1); b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1); - b->last = ngx_copy(b->last, s->login.data, s->login.len); + b->last = ngx_copy(b->last, login.data, login.len); *b->last++ = CR; *b->last++ = LF; b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1); - b->last = ngx_copy(b->last, s->passwd.data, s->passwd.len); + b->last = ngx_copy(b->last, passwd.data, passwd.len); *b->last++ = CR; *b->last++ = LF; b->last = ngx_cpymem(b->last, "Auth-Protocol: ", @@ -1059,6 +1070,60 @@ ngx_imap_auth_http_create_request(ngx_imap_session_t *s, ngx_pool_t *pool, } +static ngx_int_t +ngx_imap_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped) +{ + u_char ch, *p; + ngx_uint_t i, n; + + n = 0; + + for (i = 0; i < text->len; i++) { + ch = text->data[i]; + + if (ch == CR || ch == LF) { + n++; + } + } + + if (n == 0) { + *escaped = *text; + return NGX_OK; + } + + escaped->len = text->len + n * 2; + + p = ngx_palloc(pool, escaped->len); + if (p == NULL) { + return NGX_ERROR; + } + + escaped->data = p; + + for (i = 0; i < text->len; i++) { + ch = text->data[i]; + + if (ch == CR) { + *p++ = '%'; + *p++ = '0'; + *p++ = 'D'; + continue; + } + + if (ch == LF) { + *p++ = '%'; + *p++ = '0'; + *p++ = 'A'; + continue; + } + + *p++ = ch; + } + + return NGX_OK; +} + + static void * ngx_imap_auth_http_create_conf(ngx_conf_t *cf) { diff --git a/src/os/unix/ngx_errno.h b/src/os/unix/ngx_errno.h index e55078810..dae9571e2 100644 --- a/src/os/unix/ngx_errno.h +++ b/src/os/unix/ngx_errno.h @@ -24,6 +24,7 @@ typedef int ngx_err_t; #define NGX_EEXIST EEXIST #define NGX_ENOTDIR ENOTDIR #define NGX_EINVAL EINVAL +#define NGX_ENOSPC ENOSPC #define NGX_EPIPE EPIPE #define NGX_EAGAIN EAGAIN #define NGX_EINPROGRESS EINPROGRESS diff --git a/src/os/unix/ngx_files.c b/src/os/unix/ngx_files.c index f89147df1..575831390 100644 --- a/src/os/unix/ngx_files.c +++ b/src/os/unix/ngx_files.c @@ -112,11 +112,11 @@ ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) ngx_fd_t -ngx_open_tempfile(u_char *name, ngx_uint_t persistent) +ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t mode) { ngx_fd_t fd; - fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR, 0600); + fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR, mode ? mode : 0600); if (fd != -1 && !persistent) { unlink((const char *) name); diff --git a/src/os/unix/ngx_files.h b/src/os/unix/ngx_files.h index fcf5da4ad..29b649583 100644 --- a/src/os/unix/ngx_files.h +++ b/src/os/unix/ngx_files.h @@ -37,7 +37,8 @@ #define ngx_delete_file_n "unlink()" -ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent); +ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent, + ngx_uint_t mode); #define ngx_open_tempfile_n "open()" @@ -56,7 +57,7 @@ ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce, #define NGX_LINEFEED_SIZE 1 -#define ngx_rename_file rename +#define ngx_rename_file(o, n) rename((const char *) o, (const char *) n) #define ngx_rename_file_n "rename" diff --git a/src/os/win32/ngx_errno.h b/src/os/win32/ngx_errno.h index 46f4382c4..97565760b 100644 --- a/src/os/win32/ngx_errno.h +++ b/src/os/win32/ngx_errno.h @@ -26,6 +26,7 @@ typedef DWORD ngx_err_t; #endif #define NGX_EEXIST ERROR_ALREADY_EXISTS #define NGX_ENOTDIR ERROR_PATH_NOT_FOUND +#define NGX_ENOSPC ERROR_DISK_FULL #define NGX_EPIPE EPIPE #define NGX_EAGAIN WSAEWOULDBLOCK #define NGX_EINPROGRESS WSAEINPROGRESS diff --git a/src/os/win32/ngx_files.h b/src/os/win32/ngx_files.h index 098a90588..beba0a83a 100644 --- a/src/os/win32/ngx_files.h +++ b/src/os/win32/ngx_files.h @@ -48,7 +48,7 @@ ngx_int_t ngx_file_append_mode(ngx_fd_t fd); #define ngx_file_append_mode_n "SetFilePointer()" -#define ngx_open_tempfile(name, persistent) \ +#define ngx_open_tempfile(name, persistent, mode) \ CreateFile((const char *) name, \ GENERIC_READ|GENERIC_WRITE, \ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, \ @@ -74,7 +74,7 @@ ngx_int_t ngx_file_append_mode(ngx_fd_t fd); #define ngx_delete_file_n "DeleteFile()" -#define ngx_rename_file MoveFile +#define ngx_rename_file(o, n) MoveFile((const char *) o, (const char *) n) #define ngx_rename_file_n "MoveFile()" ngx_int_t ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_pool_t *pool); |