# HG changeset patch # User Igor Sysoev # Date 1145008418 0 # Node ID f971949ffb585d400e0f15508a56232a0f897c80 # Parent 5c60f5f0887dddf498da7b7d3a7699160c21a48b nginx-0.3.38-RELEASE import *) 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. diff --git a/auto/cc/msvc b/auto/cc/msvc --- a/auto/cc/msvc +++ b/auto/cc/msvc @@ -116,19 +116,25 @@ ngx_binout="-Fe" ngx_objext="obj" ngx_binext=".exe" -# Borland make -#ngx_long_start='@&&| -# ' -#ngx_long_end='|' +if [ "$BMAKE" = nmake ]; then + # MS nmake -# MS nmake -ngx_long_start='@<< + ngx_long_start='@<< ' -ngx_long_end='<<' -ngx_long_regex_cont=' \ + ngx_long_end='<<' + ngx_long_regex_cont=' \ + ' + ngx_long_cont=' ' -ngx_long_cont=' - ' + +else + # Borland make + + ngx_long_start='@&&| + ' + ngx_long_end='|' +fi + # MSVC understand / in path #ngx_regex_dirsep='\\' #ngx_dirsep="\\" diff --git a/auto/make b/auto/make --- a/auto/make +++ b/auto/make @@ -22,7 +22,7 @@ LINK = $LINK END -if [ "$CC" = wcl386 ]; then +if [ "$BMAKE" = wmake ]; then echo MAKE = wmake >> $NGX_MAKEFILE ngx_regex_cont=' ' diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -127,6 +127,11 @@ fi HTTP_MODULES="$HTTP_MODULES $HTTP_STATIC_MODULE" +if [ $HTTP_DAV = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_DAV_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_DAV_SRCS" +fi + if [ $HTTP_AUTOINDEX = YES ]; then have=NGX_HTTP_AUTOINDEX . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_AUTOINDEX_MODULE" diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -53,6 +53,7 @@ HTTP_SSI=YES HTTP_POSTPONE=NO HTTP_REALIP=NO HTTP_ADDITION=NO +HTTP_DAV=NO HTTP_ACCESS=YES HTTP_AUTH_BASIC=YES HTTP_USERID=YES @@ -141,6 +142,8 @@ do --with-http_ssl_module) HTTP_SSL=YES ;; --with-http_realip_module) HTTP_REALIP=YES ;; --with-http_addition_module) HTTP_ADDITION=YES ;; + --with-http_dav_module) HTTP_DAV=YES ;; + --without-http_charset_module) HTTP_CHARSET=NO ;; --without-http_gzip_module) HTTP_GZIP=NO ;; --without-http_ssi_module) HTTP_SSI=NO ;; diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -323,6 +323,10 @@ HTTP_ADDITION_FILTER_MODULE=ngx_http_add HTTP_ADDITION_SRCS=src/http/modules/ngx_http_addition_filter_module.c +HTTP_DAV_MODULE=ngx_http_dav_module +HTTP_DAV_SRCS=src/http/modules/ngx_http_dav_module.c + + HTTP_ACCESS_MODULE=ngx_http_access_module HTTP_ACCESS_SRCS=src/http/modules/ngx_http_access_module.c diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -9,6 +9,87 @@ nginx changelog + + + + +модуль ngx_http_dav_module. + + +the ngx_http_dav_module. + + + + + +оптимизация модуля ngx_http_perl_module.
+Спасибо Сергею Скворцову. +
+ +the ngx_http_perl_module optimizations.
+Thanks to Sergey Skvortsov. +
+
+ + + +модуль ngx_http_perl_module поддерживает метод $r->request_body_file. + + +the ngx_http_perl_module supports the $r->request_body_file method. + + + + + +директива client_body_in_file_only. + + +the "client_body_in_file_only" directive. + + + + + +теперь при переполнении диска nginx пытается писать access_log'и только +раз в секунду.
+Спасибо Антону Южанинову и Максиму Дунину. +
+ +no on disk overflow nginx tries to write access logs once a second only.
+Thanks to Anton Yuzhaninov and Maxim Dounin. +
+
+ + + +теперь директива limit_rate точнее ограничивает скорость при значениях +больше 100 Kbyte/s.
+Спасибо ForJest. +
+ +now the "limit_rate" directive more precisely limits rate if rate is more +than 100 Kbyte/s.
+Thanks to ForJest. +
+
+ + + +IMAP/POP3 прокси теперь передаёт серверу авторизации символы "\r" и "\n" +в логине и пароле в закодированном виде.
+Спасибо Максиму Дунину. +
+ +now the IMAP/POP3 proxy escapes the "\r" and "\n" symbols in login and +password to pass authorization server.
+Thanks to Maxim Dounin. +
+
+ +
+ + @@ -269,11 +350,12 @@ the EVFILER_TIMER support in MacOSX 10.4 -обход ошибки обработки миллисекундных таймаутов kqueue в 64-битном ядре MacOSX. +обход ошибки обработки миллисекундных таймаутов kqueue в 64-битном ядре +MacOSX.
Спасибо Андрею Нигматулину.
-for MacOSX 64-bit kernel kqueue millisecond timeout bug. +for MacOSX 64-bit kernel kqueue millisecond timeout bug.
Thanks Andrei Nigmatulin.
@@ -423,12 +505,12 @@ the --with-cpu-opt=ppc64 configuration p при некоторых условиях проксированное соединение с клиентом завершалось -преждевременно. +преждевременно.
Спасибо Владимиру Шутову.
on some condition the proxied connection with a client was terminated -prematurely. +prematurely.
Thanks to Vladimir Shutoff.
@@ -994,14 +1076,15 @@ the "config timefmt" SSI command set inc nginx не закрывал соединения с IMAP/POP3 бэкендом при использовании SSL -соединений. +соединений; +ошибка появилась в 0.3.13.
Спасибо Rob Mueller. -Ошибка появилась в 0.3.13. -
- -nginx did not close connection to IMAP/POP3 backend for the SSL connections. + + +nginx did not close connection to IMAP/POP3 backend for the SSL +connections; +bug appeared in 0.3.13.
Thanks to Rob Mueller. -Bug appeared in 0.3.13.
@@ -2132,11 +2215,11 @@ the ngx_http_autoindex_module now do not если SSL handshake завершался с ошибкой, то это могло привести также -к закрытию другого соединения. +к закрытию другого соединения.
Спасибо Rob Mueller.
-if the SSL handshake failed then another connection may be closed too. +if the SSL handshake failed then another connection may be closed too.
Thanks to Rob Mueller.
@@ -2262,11 +2345,11 @@ nginx did not try do connect to them dur -в парсинге аргументов IMAP/POP3 команд. +в парсинге аргументов IMAP/POP3 команд.
Спасибо Rob Mueller.
-in IMAP/POP3 command argument parsing. +in IMAP/POP3 command argument parsing.
Thanks to Rob Mueller.
@@ -2292,12 +2375,12 @@ errors while using SSI and gzipping. в ответах 304 не добавлялись строки заголовка ответа "Expires" и -"Cache-Control". +"Cache-Control".
Спасибо Александру Кукушкину.
the "Expires" and "Cache-Control" header lines were omitted -from the 304 responses. +from the 304 responses.
Thanks to Alexandr Kukushkin.
diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -426,7 +426,7 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, 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, 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 --- 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 --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -19,7 +19,7 @@ ngx_write_chain_to_temp_file(ngx_temp_fi 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_fi 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, n 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 --- 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 --- 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_ { 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 --- /dev/null +++ b/src/http/modules/ngx_http_dav_module.c @@ -0,0 +1,286 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#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 --- 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 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 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 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 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 --- 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 MODULE = nginx PACKAGE = nginx -int +void send_http_header(r, ...) - nginx r + CODE: - PREINIT: + ngx_http_request_t *r; + SV *sv; - 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); - - done: - - OUTPUT: - RETVAL - - -int -header_only(r) - nginx r - - CODE: - RETVAL = r->header_only; - - OUTPUT: - RETVAL + (void) ngx_http_send_header(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. - -char * -uri(r, ...) - nginx r - +void +header_only(r) CODE: - if (items != 1) { - croak("$r->uri(text) is not implemented"); - } + dXSTARG; + ngx_http_request_t *r; + + ngx_http_perl_set_request(r); - RETVAL = ngx_palloc(r->pool, r->uri.len + 1); - if (RETVAL == NULL) { - XSRETURN_UNDEF; - } + sv_upgrade(TARG, SVt_IV); + sv_setiv(TARG, r->header_only); - ngx_cpystrn((u_char *) RETVAL, r->uri.data, r->uri.len + 1); - - OUTPUT: - RETVAL + ST(0) = TARG; -char * -args(r, ...) - nginx r +void +uri(r) + CODE: + + dXSTARG; + ngx_http_request_t *r; + ngx_http_perl_set_request(r); + ngx_http_perl_set_targ(r->uri.data, r->uri.len, 0); + + ST(0) = TARG; + + +void +args(r) CODE: - if (items != 1) { - croak("$r->args(text) is not implemented"); - } + dXSTARG; + ngx_http_request_t *r; - RETVAL = ngx_palloc(r->pool, r->args.len + 1); - if (RETVAL == NULL) { - XSRETURN_UNDEF; - } + ngx_http_perl_set_request(r); + ngx_http_perl_set_targ(r->args.data, r->args.len, 0); - ngx_cpystrn((u_char *) RETVAL, r->args.data, r->args.len + 1); - - OUTPUT: - RETVAL + ST(0) = TARG; -char * +void request_method(r) - nginx r + CODE: + + dXSTARG; + ngx_http_request_t *r; + ngx_http_perl_set_request(r); + ngx_http_perl_set_targ(r->method_name.data, r->method_name.len, 0); + + ST(0) = TARG; + + +void +remote_addr(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->connection->addr_text.data, + r->connection->addr_text.len, 1); - OUTPUT: - RETVAL + ST(0) = TARG; -char * -remote_addr(r) - nginx r - +void +header_in(r, key) CODE: - RETVAL = (char *) r->connection->addr_text.data; - - OUTPUT: - RETVAL - + 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; -char * -header_in(r, key) - nginx r - SV *key - - PREINIT: + ngx_http_perl_set_request(r); - u_char *p; - STRLEN len; - ngx_uint_t i; - ngx_list_part_t *part; - ngx_table_elt_t *header; - - 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; + + ngx_http_perl_set_request(r); - for (cl = r->request_body->bufs; cl; cl = cl->next) { - if (cl->buf->in_file) { - XSRETURN_UNDEF; - } + if (r->request_body->temp_file || r->request_body->bufs == NULL) { + XSRETURN_UNDEF; + } - len += cl->buf->last - cl->buf->pos; - } + 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); - for (cl = r->request_body->bufs; cl; cl = cl->next) { - sv_catpvn(RETVAL, cl->buf->pos, cl->buf->last - cl->buf->pos); - } - - OUTPUT: - RETVAL + ST(0) = TARG; -int -header_out(r, key, value) - nginx r - SV *key - SV *value +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; + } + + ngx_http_perl_set_targ(r->request_body->temp_file->file.name.data, + r->request_body->temp_file->file.name.len, 1); - PREINIT: + ST(0) = TARG; + + +void +header_out(r, key, value) + CODE: - ngx_table_elt_t *header; + ngx_http_request_t *r; + SV *key; + SV *value; + ngx_table_elt_t *header; - CODE: + ngx_http_perl_set_request(r); + + 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 + CODE: - PREINIT: - - 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); + (void) ngx_http_perl_output(r, b); - done: - - 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 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 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 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: - - OUTPUT: - RETVAL + XSRETURN_EMPTY; -int +void rflush(r) - nginx r + CODE: - PREINIT: + ngx_http_request_t *r; + ngx_buf_t *b; - 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 + CODE: - PREINIT: - + 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 - - PREINIT: - - u_char *p, *dst, *src; - STRLEN n; - CODE: - src = (u_char *) SvPV(text, n); + dXSTARG; + ngx_http_request_t *r; + SV *text; + int type; + u_char *p, *dst, *src; + STRLEN len; - p = ngx_palloc(r->pool, n + 1); + ngx_http_perl_set_request(r); + + text = ST(1); + + src = (u_char *) SvPV(text, len); + + 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 --- 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_ #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 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_re 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_ 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_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_htt 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 --- 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_modul #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 --- 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_comm 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 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 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 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 --- 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 --- 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_ #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 --- 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[] = " 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 --- 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_req 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_req { 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 --- 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_hand 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 * } -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 --- 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 --- 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 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_ht * 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_ht 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_ht 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_handle 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 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 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 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 } } + 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 --- 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_ht } } - 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 --- 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_modu 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 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 --- 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_rea 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_im { 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_im 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_im } +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 --- 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 --- 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 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 --- 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 #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 --- a/src/os/win32/ngx_errno.h +++ b/src/os/win32/ngx_errno.h @@ -26,6 +26,7 @@ typedef DWORD ngx_e #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 --- 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 #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 #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);