diff options
Diffstat (limited to 'src')
51 files changed, 2535 insertions, 1255 deletions
diff --git a/src/go/unit/nxt_cgo_lib.c b/src/go/unit/nxt_cgo_lib.c index 98a23482..cc1228f5 100644 --- a/src/go/unit/nxt_cgo_lib.c +++ b/src/go/unit/nxt_cgo_lib.c @@ -83,6 +83,10 @@ nxt_cgo_request_handler(nxt_unit_request_info_t *req) nxt_go_request_set_remote_addr(go_req, nxt_cgo_str_init(&remote_addr, &r->remote, r->remote_length)); + if (r->tls) { + nxt_go_request_set_tls(go_req); + } + nxt_go_request_handler(go_req, (uintptr_t) req->unit->data); } diff --git a/src/go/unit/request.go b/src/go/unit/request.go index 829a2c64..ad56cabb 100644 --- a/src/go/unit/request.go +++ b/src/go/unit/request.go @@ -14,6 +14,7 @@ import ( "io" "net/http" "net/url" + "crypto/tls" "unsafe" ) @@ -125,6 +126,12 @@ func nxt_go_request_set_remote_addr(go_req uintptr, addr *C.nxt_cgo_str_t) { get_request(go_req).req.RemoteAddr = C.GoStringN(addr.start, addr.length) } +//export nxt_go_request_set_tls +func nxt_go_request_set_tls(go_req uintptr) { + + get_request(go_req).req.TLS = &tls.ConnectionState{ } +} + //export nxt_go_request_handler func nxt_go_request_handler(go_req uintptr, h uintptr) { r := get_request(go_req) diff --git a/src/java/nginx/unit/Context.java b/src/java/nginx/unit/Context.java index 643a336b..f6d5e339 100644 --- a/src/java/nginx/unit/Context.java +++ b/src/java/nginx/unit/Context.java @@ -306,7 +306,7 @@ public class Context implements ServletContext, InitParams PrintWriter writer = response.getWriter(); for (String n : ls) { - writer.println("<a href=\"" + n + "\">" + n + "</a><br>"); + writer.println("<a href=\"" + n + "\">" + n + "</a><br>"); } writer.close(); @@ -547,7 +547,7 @@ public class Context implements ServletContext, InitParams j = j.getParent(); } } - system_loader = j; + system_loader = j; } private boolean isSystemPath(String path) @@ -1733,7 +1733,7 @@ public class Context implements ServletContext, InitParams @Override public FileVisitResult visitFile( - Path file, BasicFileAttributes attrs) + Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; diff --git a/src/java/nginx/unit/Request.java b/src/java/nginx/unit/Request.java index dc73c656..3ba46f6c 100644 --- a/src/java/nginx/unit/Request.java +++ b/src/java/nginx/unit/Request.java @@ -920,7 +920,7 @@ public class Request implements HttpServletRequest, DynamicPathRequest @Override public String getScheme() { - log("getScheme"); + trace("getScheme"); return getScheme(req_ptr); } @@ -980,11 +980,13 @@ public class Request implements HttpServletRequest, DynamicPathRequest @Override public boolean isSecure() { - log("isSecure"); + trace("isSecure"); - return false; + return isSecure(req_ptr); } + private static native boolean isSecure(long req_ptr); + @Override public void removeAttribute(String name) { diff --git a/src/java/nxt_jni_Context.c b/src/java/nxt_jni_Context.c index 8f7adddf..589e1c5b 100644 --- a/src/java/nxt_jni_Context.c +++ b/src/java/nxt_jni_Context.c @@ -55,7 +55,7 @@ nxt_java_initContext(JNIEnv *env, jobject cl) } nxt_java_Context_stop = (*env)->GetMethodID(env, cls, "stop", "()V"); - if (nxt_java_Context_service == NULL) { + if (nxt_java_Context_stop == NULL) { nxt_unit_warn(NULL, "nginx.unit.Context.stop() not found"); goto failed; } diff --git a/src/java/nxt_jni_Request.c b/src/java/nxt_jni_Request.c index 6fb9cb44..733290dd 100644 --- a/src/java/nxt_jni_Request.c +++ b/src/java/nxt_jni_Request.c @@ -56,6 +56,8 @@ static jstring JNICALL nxt_java_Request_getServerName(JNIEnv *env, jclass cls, jlong req_ptr); static jint JNICALL nxt_java_Request_getServerPort(JNIEnv *env, jclass cls, jlong req_ptr); +static jboolean JNICALL nxt_java_Request_isSecure(JNIEnv *env, jclass cls, + jlong req_ptr); static void JNICALL nxt_java_Request_log(JNIEnv *env, jclass cls, jlong req_info_ptr, jstring msg, jint msg_len); static void JNICALL nxt_java_Request_trace(JNIEnv *env, jclass cls, @@ -166,6 +168,10 @@ nxt_java_initRequest(JNIEnv *env, jobject cl) (char *) "(J)I", nxt_java_Request_getServerPort }, + { (char *) "isSecure", + (char *) "(J)Z", + nxt_java_Request_isSecure }, + { (char *) "log", (char *) "(JLjava/lang/String;I)V", nxt_java_Request_log }, @@ -536,7 +542,11 @@ nxt_java_Request_getRemotePort(JNIEnv *env, jclass cls, jlong req_ptr) static jstring JNICALL nxt_java_Request_getScheme(JNIEnv *env, jclass cls, jlong req_ptr) { - return (*env)->NewStringUTF(env, "http"); + nxt_unit_request_t *r; + + r = nxt_jlong2ptr(req_ptr); + + return (*env)->NewStringUTF(env, r->tls ? "https" : "http"); } @@ -603,6 +613,17 @@ nxt_java_Request_getServerPort(JNIEnv *env, jclass cls, jlong req_ptr) } +static jboolean JNICALL +nxt_java_Request_isSecure(JNIEnv *env, jclass cls, jlong req_ptr) +{ + nxt_unit_request_t *r; + + r = nxt_jlong2ptr(req_ptr); + + return r->tls != 0; +} + + static void JNICALL nxt_java_Request_log(JNIEnv *env, jclass cls, jlong req_info_ptr, jstring msg, jint msg_len) diff --git a/src/nodejs/unit-http/binding.gyp b/src/nodejs/unit-http/binding.gyp index ee09bfed..55d965bd 100644 --- a/src/nodejs/unit-http/binding.gyp +++ b/src/nodejs/unit-http/binding.gyp @@ -1,6 +1,15 @@ { 'targets': [{ 'target_name': "unit-http", + 'cflags!': [ '-fno-exceptions' ], + 'cflags_cc!': [ '-fno-exceptions' ], + 'conditions': [ + ['OS=="mac"', { + 'xcode_settings': { + 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES' + } + }] + ], 'sources': ["unit.cpp", "addon.cpp"], 'include_dirs': [ "<!(echo $UNIT_SRC_PATH)", "<!(echo $UNIT_BUILD_PATH)" diff --git a/src/nodejs/unit-http/binding_pub.gyp b/src/nodejs/unit-http/binding_pub.gyp index 6fe3d9bc..3c39933a 100644 --- a/src/nodejs/unit-http/binding_pub.gyp +++ b/src/nodejs/unit-http/binding_pub.gyp @@ -1,6 +1,15 @@ { 'targets': [{ 'target_name': "unit-http", + 'cflags!': [ '-fno-exceptions' ], + 'cflags_cc!': [ '-fno-exceptions' ], + 'conditions': [ + ['OS=="mac"', { + 'xcode_settings': { + 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES' + } + }] + ], 'sources': ["unit.cpp", "addon.cpp"], 'libraries': ["-lunit"] }] diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 057a1f26..ae8e204a 100755 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -197,6 +197,14 @@ function writeHead(statusCode, reason, obj) { } }; +/* + * Some Node.js packages are known to be using this undocumented function, + * notably "compression" middleware. + */ +ServerResponse.prototype._implicitHeader = function _implicitHeader() { + this.writeHead(this.statusCode); +}; + ServerResponse.prototype._writeBody = function(chunk, encoding, callback) { var contentLength = 0; @@ -387,6 +395,10 @@ Server.prototype.emit_events = function (server, req, res) { }); }; +Server.prototype.emit_close = function () { + this.emit('close'); +}; + function connectionListener(socket) { } diff --git a/src/nodejs/unit-http/nxt_napi.h b/src/nodejs/unit-http/nxt_napi.h new file mode 100644 index 00000000..9bcf3a21 --- /dev/null +++ b/src/nodejs/unit-http/nxt_napi.h @@ -0,0 +1,656 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_NODEJS_NAPI_H_INCLUDED_ +#define _NXT_NODEJS_NAPI_H_INCLUDED_ + +#include <node_api.h> + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "version.h" +#include <nxt_unit.h> + +#if NXT_VERNUM != NXT_NODE_VERNUM +#error "libunit version mismatch." +#endif + +#include <nxt_unit_response.h> +#include <nxt_unit_request.h> + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +struct nxt_napi { + + struct exception { + exception(const char *s) : str(s) { } + + const char *str; + }; + + + nxt_napi(napi_env env) : env_(env) { } + + + inline napi_value + coerce_to_string(napi_value val) + { + napi_value res; + napi_status status; + + status = napi_coerce_to_string(env_, val, &res); + if (status != napi_ok) { + throw exception("Failed to coerce to string"); + } + + return res; + } + + + inline napi_value + create_buffer(size_t size, void **data) + { + napi_value res; + napi_status status; + + status = napi_create_buffer(env_, size, data, &res); + if (status != napi_ok) { + throw exception("Failed to create buffer"); + } + + return res; + } + + + inline napi_value + create_function(const char *name, size_t len, napi_callback cb, void *data) + { + napi_value res; + napi_status status; + + status = napi_create_function(env_, name, len, cb, data, &res); + if (status != napi_ok) { + throw exception("Failed to create function"); + } + + return res; + } + + + inline napi_value + create_function(napi_callback cb) + { + return create_function(NULL, 0, cb, NULL); + } + + + inline napi_value + create_object() + { + napi_value res; + napi_status status; + + status = napi_create_object(env_, &res); + if (status != napi_ok) { + throw exception("Failed to create object"); + } + + return res; + } + + + inline napi_ref + create_reference(napi_value val, int ref_count = 1) + { + napi_ref res; + napi_status status; + + status = napi_create_reference(env_, val, ref_count, &res); + if (status != napi_ok) { + throw exception("Failed to create reference"); + } + + return res; + } + + + inline napi_value + create_string_latin1(const char *str, size_t len) + { + napi_value res; + napi_status status; + + status = napi_create_string_latin1(env_, str, len, &res); + if (status != napi_ok) { + throw exception("Failed to create latin1 string"); + } + + return res; + } + + + inline napi_value + create_string_latin1(nxt_unit_sptr_t &str, size_t len) + { + const char *p; + + p = (const char *) nxt_unit_sptr_get(&str); + + return create_string_latin1(p, len); + } + + + inline napi_value + define_class(const char *name, napi_callback ctor, size_t prop_count, + const napi_property_descriptor* props) + { + napi_value res; + napi_status status; + + status = napi_define_class(env_, name, NAPI_AUTO_LENGTH, ctor, nullptr, + prop_count, props, &res); + if (status != napi_ok) { + throw exception("Failed to define class"); + } + + return res; + } + + + inline void + delete_reference(napi_ref ref) + { + napi_delete_reference(env_, ref); + } + + + inline uint32_t + get_array_length(napi_value val) + { + uint32_t res; + napi_status status; + + status = napi_get_array_length(env_, val, &res); + if (status != napi_ok) { + throw exception("Failed to get array length"); + } + + return res; + } + + + inline napi_value + get_cb_info(napi_callback_info info, size_t &argc, napi_value *argv) + { + napi_value res; + napi_status status; + + status = napi_get_cb_info(env_, info, &argc, argv, &res, nullptr); + if (status != napi_ok) { + throw exception("Failed to get arguments from js"); + } + + return res; + } + + + inline napi_value + get_cb_info(napi_callback_info info) + { + napi_value res; + napi_status status; + + status = napi_get_cb_info(env_, info, nullptr, nullptr, &res, nullptr); + if (status != napi_ok) { + throw exception("Failed to get arguments from js"); + } + + return res; + } + + + inline napi_value + get_element(napi_value obj, uint32_t i) + { + napi_value res; + napi_status status; + + status = napi_get_element(env_, obj, i, &res); + if (status != napi_ok) { + throw exception("Failed to get element"); + } + + return res; + } + + + inline napi_value + get_named_property(napi_value obj, const char *name) + { + napi_value res; + napi_status status; + + status = napi_get_named_property(env_, obj, name, &res); + if (status != napi_ok) { + throw exception("Failed to get named property"); + } + + return res; + } + + + inline napi_value + get_new_target(napi_callback_info info) + { + napi_value res; + napi_status status; + + status = napi_get_new_target(env_, info, &res); + if (status != napi_ok) { + throw exception("Failed to get new target"); + } + + return res; + } + + + inline napi_value + get_property(napi_value val, napi_value key) + { + napi_value res; + napi_status status; + + status = napi_get_property(env_, val, key, &res); + if (status != napi_ok) { + throw exception("Failed to get property"); + } + + return res; + } + + + inline napi_value + get_property_names(napi_value val) + { + napi_value res; + napi_status status; + + status = napi_get_property_names(env_, val, &res); + if (status != napi_ok) { + throw exception("Failed to get property names"); + } + + return res; + } + + + inline napi_value + get_reference_value(napi_ref ref) + { + napi_value res; + napi_status status; + + status = napi_get_reference_value(env_, ref, &res); + if (status != napi_ok) { + throw exception("Failed to get reference value"); + } + + return res; + } + + + inline nxt_unit_request_info_t * + get_request_info(napi_value obj) + { + int64_t n; + napi_status status; + + status = napi_get_value_int64(env_, obj, &n); + if (status != napi_ok) { + throw exception("Failed to get request pointer"); + } + + return (nxt_unit_request_info_t *) (intptr_t) n; + } + + + inline size_t + get_value_string_latin1(napi_value val, char *buf, size_t bufsize) + { + size_t res; + napi_status status; + + status = napi_get_value_string_latin1(env_, val, buf, bufsize, &res); + if (status != napi_ok) { + throw exception("Failed to get string latin1"); + } + + return res; + } + + + inline uint32_t + get_value_uint32(napi_value obj) + { + uint32_t res; + napi_status status; + + status = napi_get_value_uint32(env_, obj, &res); + if (status != napi_ok) { + throw exception("Failed to get uint32_t"); + } + + return res; + } + + + inline bool + is_array(napi_value val) + { + bool res; + napi_status status; + + status = napi_is_array(env_, val, &res); + if (status != napi_ok) { + throw exception("Failed to confirm value is array"); + } + + return res; + } + + + inline napi_value + make_callback(napi_async_context ctx, napi_value val, napi_value func, + int argc, const napi_value *argv) + { + napi_value res, ex; + napi_status status; + + status = napi_make_callback(env_, ctx, val, func, argc, argv, &res); + if (status != napi_ok) { + if (status != napi_pending_exception) { + throw exception("Failed to make callback"); + } + + status = napi_get_and_clear_last_exception(env_, &ex); + if (status != napi_ok) { + throw exception("Failed to get and clear last exception"); + } + + /* Logging a description of the error and call stack. */ + status = napi_fatal_exception(env_, ex); + if (status != napi_ok) { + throw exception("Failed napi_fatal_exception()"); + } + } + + return res; + } + + + inline napi_value + new_instance(napi_value ctor) + { + napi_value res; + napi_status status; + + status = napi_new_instance(env_, ctor, 0, NULL, &res); + if (status != napi_ok) { + throw exception("Failed to create instance"); + } + + return res; + } + + + inline napi_value + new_instance(napi_value ctor, napi_value param) + { + napi_value res; + napi_status status; + + status = napi_new_instance(env_, ctor, 1, ¶m, &res); + if (status != napi_ok) { + throw exception("Failed to create instance"); + } + + return res; + } + + + inline void + set_element(napi_value obj, uint32_t i, napi_value val) + { + napi_status status; + + status = napi_set_element(env_, obj, i, val); + if (status != napi_ok) { + throw exception("Failed to set element"); + } + } + + + inline void + set_named_property(napi_value obj, const char *name, napi_value val) + { + napi_status status; + + status = napi_set_named_property(env_, obj, name, val); + if (status != napi_ok) { + throw exception("Failed to set named property"); + } + } + + + inline void + set_named_property(napi_value obj, const char *name, napi_callback cb) + { + set_named_property(obj, name, create_function(cb)); + } + + + inline napi_value + set_named_property(napi_value obj, const char *name, nxt_unit_sptr_t &val, + size_t len) + { + napi_value str; + + str = create_string_latin1(val, len); + + set_named_property(obj, name, str); + + return str; + } + + + inline void + set_named_property(napi_value obj, const char *name, intptr_t val) + { + napi_value ptr; + napi_status status; + + status = napi_create_int64(env_, val, &ptr); + if (status != napi_ok) { + throw exception("Failed to create int64"); + } + + set_named_property(obj, name, ptr); + } + + + inline void + throw_error(const char *str) + { + napi_throw_error(env_, NULL, str); + } + + + inline void + throw_error(const exception &e) + { + napi_throw_error(env_, NULL, e.str); + } + + + inline napi_valuetype + type_of(napi_value val) + { + napi_status status; + napi_valuetype res; + + status = napi_typeof(env_, val, &res); + if (status != napi_ok) { + throw exception("Failed to get typeof"); + } + + return res; + } + + + inline void * + unwrap(napi_value val) + { + void *res; + napi_status status; + + status = napi_unwrap(env_, val, &res); + if (status != napi_ok) { + throw exception("Failed to unwrap"); + } + + return res; + } + + + inline napi_ref + wrap(napi_value val, void *obj, napi_finalize fin_cb, void *hint = nullptr) + { + napi_ref res; + napi_status status; + + status = napi_wrap(env_, val, obj, fin_cb, hint, &res); + if (status != napi_ok) { + throw exception("Failed to wrap"); + } + + return res; + } + + + inline + operator napi_env() + { + return env_; + } + + + napi_env env() + { + return env_; + } + +private: + napi_env env_; +}; + + +struct nxt_handle_scope : public nxt_napi { + nxt_handle_scope(napi_env env) : nxt_napi(env) + { + napi_status status; + + status = napi_open_handle_scope(env, &scope_); + if (status != napi_ok) { + throw exception("Failed to open handle scope"); + } + } + + ~nxt_handle_scope() + { + napi_status status; + + status = napi_close_handle_scope(env(), scope_); + if (status != napi_ok) { + throw_error("Failed to close handle scope"); + } + } + +private: + napi_handle_scope scope_; +}; + + +struct nxt_async_context : public nxt_napi { + nxt_async_context(napi_env env, const char *name) : + nxt_napi(env) + { + napi_value name_val; + napi_status status; + + name_val = create_string_latin1(name, NAPI_AUTO_LENGTH); + + status = napi_async_init(env, NULL, name_val, &context_); + if (status != napi_ok) { + throw exception("Failed to init async object"); + } + } + + operator napi_async_context() { + return context_; + } + + ~nxt_async_context() + { + napi_status status; + + status = napi_async_destroy(env(), context_); + if (status != napi_ok) { + throw_error("Failed to destroy async object"); + } + } + +private: + napi_async_context context_; +}; + + +struct nxt_callback_scope : public nxt_napi { + nxt_callback_scope(nxt_async_context& ctx) : + nxt_napi(ctx.env()) + { + napi_value resource; + napi_status status; + + resource = create_object(); + + status = napi_open_callback_scope(env(), resource, ctx, &scope_); + if (status != napi_ok) { + throw exception("Failed to open callback scope"); + } + } + + ~nxt_callback_scope() + { + napi_status status; + + status = napi_close_callback_scope(env(), scope_); + if (status != napi_ok) { + throw_error("Failed to close callback scope"); + } + } + +private: + napi_callback_scope scope_; +}; + + +#endif /* _NXT_NODEJS_NAPI_H_INCLUDED_ */ diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp index 60b0412a..3f66189a 100644 --- a/src/nodejs/unit-http/unit.cpp +++ b/src/nodejs/unit-http/unit.cpp @@ -20,9 +20,9 @@ struct nxt_nodejs_ctx_t { }; -Unit::Unit(napi_env env): - env_(env), - wrapper_(nullptr), +Unit::Unit(napi_env env, napi_value jsthis): + nxt_napi(env), + wrapper_(wrap(jsthis, this, destroy)), unit_ctx_(nullptr) { } @@ -30,15 +30,15 @@ Unit::Unit(napi_env env): Unit::~Unit() { - napi_delete_reference(env_, wrapper_); + delete_reference(wrapper_); } napi_value Unit::init(napi_env env, napi_value exports) { - napi_value cons, fn; - napi_status status; + nxt_napi napi(env); + napi_value cons; napi_property_descriptor properties[] = { { "createServer", 0, create_server, 0, 0, 0, napi_default, 0 }, @@ -46,61 +46,22 @@ Unit::init(napi_env env, napi_value exports) { "_read", 0, _read, 0, 0, 0, napi_default, 0 } }; - status = napi_define_class(env, "Unit", NAPI_AUTO_LENGTH, create, nullptr, - 3, properties, &cons); - if (status != napi_ok) { - goto failed; - } - - status = napi_create_reference(env, cons, 1, &constructor_); - if (status != napi_ok) { - goto failed; - } - - status = napi_set_named_property(env, exports, "Unit", cons); - if (status != napi_ok) { - goto failed; - } - - status = napi_create_function(env, NULL, 0, response_send_headers, NULL, - &fn); - if (status != napi_ok) { - goto failed; - } - - status = napi_set_named_property(env, exports, - "unit_response_headers", fn); - if (status != napi_ok) { - goto failed; - } - - status = napi_create_function(env, NULL, 0, response_write, NULL, &fn); - if (status != napi_ok) { - goto failed; - } + try { + cons = napi.define_class("Unit", create, 3, properties); + constructor_ = napi.create_reference(cons); - status = napi_set_named_property(env, exports, "unit_response_write", fn); - if (status != napi_ok) { - goto failed; - } + napi.set_named_property(exports, "Unit", cons); + napi.set_named_property(exports, "unit_response_headers", + response_send_headers); + napi.set_named_property(exports, "unit_response_write", response_write); + napi.set_named_property(exports, "unit_response_end", response_end); - status = napi_create_function(env, NULL, 0, response_end, NULL, &fn); - if (status != napi_ok) { - goto failed; - } - - status = napi_set_named_property(env, exports, "unit_response_end", fn); - if (status != napi_ok) { - goto failed; + } catch (exception &e) { + napi.throw_error(e); + return nullptr; } return exports; - -failed: - - napi_throw_error(env, NULL, "Failed to define Unit class"); - - return nullptr; } @@ -116,63 +77,33 @@ Unit::destroy(napi_env env, void *nativeObject, void *finalize_hint) napi_value Unit::create(napi_env env, napi_callback_info info) { - Unit *obj; - napi_ref ref; - napi_value target, cons, instance, jsthis; - napi_status status; - - status = napi_get_new_target(env, info, &target); - if (status != napi_ok) { - goto failed; - } + nxt_napi napi(env); + napi_value target, cons, instance, jsthis; - if (target != nullptr) { - /* Invoked as constructor: `new Unit(...)` */ - status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, - nullptr); - if (status != napi_ok) { - goto failed; - } + try { + target = napi.get_new_target(info); - obj = new Unit(env); + if (target != nullptr) { + /* Invoked as constructor: `new Unit(...)`. */ + jsthis = napi.get_cb_info(info); - status = napi_wrap(env, jsthis, reinterpret_cast<void *>(obj), - destroy, nullptr, &obj->wrapper_); - if (status != napi_ok) { - goto failed; - } + new Unit(env, jsthis); + napi.create_reference(jsthis); - status = napi_create_reference(env, jsthis, 1, &ref); - if (status != napi_ok) { - goto failed; + return jsthis; } - return jsthis; - } - - /* Invoked as plain function `Unit(...)`, turn into construct call. */ - status = napi_get_reference_value(env, constructor_, &cons); - if (status != napi_ok) { - goto failed; - } + /* Invoked as plain function `Unit(...)`, turn into construct call. */ + cons = napi.get_reference_value(constructor_); + instance = napi.new_instance(cons); + napi.create_reference(instance); - status = napi_new_instance(env, cons, 0, nullptr, &instance); - if (status != napi_ok) { - goto failed; - } - - status = napi_create_reference(env, instance, 1, &ref); - if (status != napi_ok) { - goto failed; + } catch (exception &e) { + napi.throw_error(e); + return nullptr; } return instance; - -failed: - - napi_throw_error(env, NULL, "Failed to create Unit object"); - - return nullptr; } @@ -181,20 +112,19 @@ Unit::create_server(napi_env env, napi_callback_info info) { Unit *obj; size_t argc; + nxt_napi napi(env); napi_value jsthis, argv; - napi_status status; nxt_unit_init_t unit_init; argc = 1; - status = napi_get_cb_info(env, info, &argc, &argv, &jsthis, nullptr); - if (status != napi_ok) { - goto failed; - } + try { + jsthis = napi.get_cb_info(info, argc, &argv); + obj = (Unit *) napi.unwrap(jsthis); - status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj)); - if (status != napi_ok) { - goto failed; + } catch (exception &e) { + napi.throw_error(e); + return nullptr; } memset(&unit_init, 0, sizeof(nxt_unit_init_t)); @@ -230,40 +160,22 @@ Unit::listen(napi_env env, napi_callback_info info) napi_value Unit::_read(napi_env env, napi_callback_info info) { - Unit *obj; void *data; size_t argc; - int64_t req_pointer; - napi_value jsthis, buffer, argv; - napi_status status; + nxt_napi napi(env); + napi_value buffer, argv; nxt_unit_request_info_t *req; argc = 1; - status = napi_get_cb_info(env, info, &argc, &argv, &jsthis, nullptr); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to get arguments from js"); - return nullptr; - } + try { + napi.get_cb_info(info, argc, &argv); - status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj)); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to get Unit object form js"); - return nullptr; - } + req = napi.get_request_info(argv); + buffer = napi.create_buffer((size_t) req->content_length, &data); - status = napi_get_value_int64(env, argv, &req_pointer); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to get request pointer"); - return nullptr; - } - - req = (nxt_unit_request_info_t *) (uintptr_t) req_pointer; - - status = napi_create_buffer(env, (size_t) req->content_length, - &data, &buffer); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to create request buffer"); + } catch (exception &e) { + napi.throw_error(e); return nullptr; } @@ -276,138 +188,38 @@ Unit::_read(napi_env env, napi_callback_info info) void Unit::request_handler(nxt_unit_request_info_t *req) { - Unit *obj; - napi_value socket, request, response, global, server_obj, except; - napi_value emit_events, events_res, async_name, resource_object; - napi_status status; - napi_async_context async_context; - napi_callback_scope async_scope; - napi_value events_args[3]; + Unit *obj; + napi_value socket, request, response, server_obj; + napi_value emit_events; + napi_value events_args[3]; obj = reinterpret_cast<Unit *>(req->unit->data); - napi_handle_scope scope; - status = napi_open_handle_scope(obj->env_, &scope); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to create handle scope"); - return; - } - - server_obj = obj->get_server_object(); - if (server_obj == nullptr) { - napi_throw_error(obj->env_, NULL, "Failed to get server object"); - return; - } - - status = napi_get_global(obj->env_, &global); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to get global variable"); - return; - } - - socket = obj->create_socket(server_obj, req); - if (socket == nullptr) { - napi_throw_error(obj->env_, NULL, "Failed to create socket object"); - return; - } - - request = obj->create_request(server_obj, socket); - if (request == nullptr) { - napi_throw_error(obj->env_, NULL, "Failed to create request object"); - return; - } - - response = obj->create_response(server_obj, socket, request, req, obj); - if (response == nullptr) { - napi_throw_error(obj->env_, NULL, "Failed to create response object"); - return; - } - - status = obj->create_headers(req, request); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to create headers"); - return; - } - - status = napi_get_named_property(obj->env_, server_obj, "emit_events", - &emit_events); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to get " - "'emit_events' function"); - return; - } - - events_args[0] = server_obj; - events_args[1] = request; - events_args[2] = response; + try { + nxt_handle_scope scope(obj->env()); - status = napi_create_string_utf8(obj->env_, "unit_request_handler", - sizeof("unit_request_handler") - 1, - &async_name); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to create utf-8 string"); - return; - } + server_obj = obj->get_server_object(); - status = napi_async_init(obj->env_, NULL, async_name, &async_context); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to init async object"); - return; - } + socket = obj->create_socket(server_obj, req); + request = obj->create_request(server_obj, socket); + response = obj->create_response(server_obj, socket, request, req); - status = napi_create_object(obj->env_, &resource_object); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to create object for " - "callback scope"); - return; - } + obj->create_headers(req, request); - status = napi_open_callback_scope(obj->env_, resource_object, async_context, - &async_scope); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to open callback scope"); - return; - } + emit_events = obj->get_named_property(server_obj, "emit_events"); - status = napi_make_callback(obj->env_, async_context, server_obj, - emit_events, 3, events_args, &events_res); - if (status != napi_ok) { - if (status != napi_pending_exception) { - napi_throw_error(obj->env_, NULL, "Failed to make callback"); - return; - } + events_args[0] = server_obj; + events_args[1] = request; + events_args[2] = response; - status = napi_get_and_clear_last_exception(obj->env_, &except); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, - "Failed to get and clear last exception"); - return; - } - - /* Logging a description of the error and call stack. */ - status = napi_fatal_exception(obj->env_, except); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to call " - "napi_fatal_exception() function"); - return; - } - } + nxt_async_context async_context(obj->env(), "unit_request_handler"); + nxt_callback_scope async_scope(async_context); - status = napi_close_callback_scope(obj->env_, async_scope); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to close callback scope"); - return; - } + obj->make_callback(async_context, server_obj, emit_events, + 3, events_args); - status = napi_async_destroy(obj->env_, async_context); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to destroy async object"); - return; - } - - status = napi_close_handle_scope(obj->env_, scope); - if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to close handle scope"); + } catch (exception &e) { + obj->throw_error(e); } } @@ -432,14 +244,14 @@ Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) obj = reinterpret_cast<Unit *>(ctx->unit->data); if (fcntl(port->in_fd, F_SETFL, O_NONBLOCK) == -1) { - napi_throw_error(obj->env_, NULL, "Failed to upgrade read" + obj->throw_error("Failed to upgrade read" " file descriptor to O_NONBLOCK"); return -1; } - status = napi_get_uv_event_loop(obj->env_, &loop); + status = napi_get_uv_event_loop(obj->env(), &loop); if (status != napi_ok) { - napi_throw_error(obj->env_, NULL, "Failed to get uv.loop"); + obj->throw_error("Failed to get uv.loop"); return NXT_UNIT_ERROR; } @@ -447,13 +259,13 @@ Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) err = uv_poll_init(loop, &node_ctx->poll, port->in_fd); if (err < 0) { - napi_throw_error(obj->env_, NULL, "Failed to init uv.poll"); + obj->throw_error("Failed to init uv.poll"); return NXT_UNIT_ERROR; } err = uv_poll_start(&node_ctx->poll, UV_READABLE, nxt_uv_read_callback); if (err < 0) { - napi_throw_error(obj->env_, NULL, "Failed to start uv.poll"); + obj->throw_error("Failed to start uv.poll"); return NXT_UNIT_ERROR; } @@ -467,7 +279,7 @@ Unit::add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) } -inline bool +inline bool operator == (const nxt_unit_port_id_t &p1, const nxt_unit_port_id_t &p2) { return p1.pid == p2.pid && p1.id == p2.id; @@ -498,6 +310,27 @@ Unit::remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id) void Unit::quit(nxt_unit_ctx_t *ctx) { + Unit *obj; + napi_value server_obj, emit_close; + + obj = reinterpret_cast<Unit *>(ctx->unit->data); + + try { + nxt_handle_scope scope(obj->env()); + + server_obj = obj->get_server_object(); + + emit_close = obj->get_named_property(server_obj, "emit_close"); + + nxt_async_context async_context(obj->env(), "unit_quit"); + nxt_callback_scope async_scope(async_context); + + obj->make_callback(async_context, server_obj, emit_close, 0, NULL); + + } catch (exception &e) { + obj->throw_error(e); + } + nxt_unit_done(ctx); } @@ -505,200 +338,105 @@ Unit::quit(nxt_unit_ctx_t *ctx) napi_value Unit::get_server_object() { - napi_value unit_obj, server_obj; - napi_status status; + napi_value unit_obj; - status = napi_get_reference_value(env_, wrapper_, &unit_obj); - if (status != napi_ok) { - return nullptr; - } - - status = napi_get_named_property(env_, unit_obj, "server", &server_obj); - if (status != napi_ok) { - return nullptr; - } + unit_obj = get_reference_value(wrapper_); - return server_obj; + return get_named_property(unit_obj, "server"); } -napi_status +void Unit::create_headers(nxt_unit_request_info_t *req, napi_value request) { uint32_t i; - const char *p; - napi_value headers, raw_headers, str; + napi_value headers, raw_headers; napi_status status; - nxt_unit_field_t *f; nxt_unit_request_t *r; r = req->request; - status = napi_create_object(env_, &headers); - if (status != napi_ok) { - return status; - } + headers = create_object(); - status = napi_create_array_with_length(env_, r->fields_count * 2, + status = napi_create_array_with_length(env(), r->fields_count * 2, &raw_headers); if (status != napi_ok) { - return status; + throw exception("Failed to create array"); } for (i = 0; i < r->fields_count; i++) { - f = r->fields + i; - - status = this->append_header(f, headers, raw_headers, i); - if (status != napi_ok) { - return status; - } - } - - status = napi_set_named_property(env_, request, "headers", headers); - if (status != napi_ok) { - return status; - } - - status = napi_set_named_property(env_, request, "rawHeaders", raw_headers); - if (status != napi_ok) { - return status; - } - - p = (const char *) nxt_unit_sptr_get(&r->version); - - status = napi_create_string_latin1(env_, p, r->version_length, &str); - if (status != napi_ok) { - return status; - } - - status = napi_set_named_property(env_, request, "httpVersion", str); - if (status != napi_ok) { - return status; - } - - p = (const char *) nxt_unit_sptr_get(&r->method); - - status = napi_create_string_latin1(env_, p, r->method_length, &str); - if (status != napi_ok) { - return status; + append_header(r->fields + i, headers, raw_headers, i); } - status = napi_set_named_property(env_, request, "method", str); - if (status != napi_ok) { - return status; - } - - p = (const char *) nxt_unit_sptr_get(&r->target); - - status = napi_create_string_latin1(env_, p, r->target_length, &str); - if (status != napi_ok) { - return status; - } + set_named_property(request, "headers", headers); + set_named_property(request, "rawHeaders", raw_headers); + set_named_property(request, "httpVersion", r->version, r->version_length); + set_named_property(request, "method", r->method, r->method_length); + set_named_property(request, "url", r->target, r->target_length); +} - status = napi_set_named_property(env_, request, "url", str); - if (status != napi_ok) { - return status; - } - return napi_ok; +inline char +lowcase(char c) +{ + return (c >= 'A' && c <= 'Z') ? (c | 0x20) : c; } -inline napi_status +inline void Unit::append_header(nxt_unit_field_t *f, napi_value headers, - napi_value raw_headers, uint32_t idx) + napi_value raw_headers, uint32_t idx) { - const char *name, *value; - napi_value str, vstr; - napi_status status; + char *name; + uint8_t i; + napi_value str, vstr; - value = (const char *) nxt_unit_sptr_get(&f->value); + name = (char *) nxt_unit_sptr_get(&f->name); - status = napi_create_string_latin1(env_, value, f->value_length, &vstr); - if (status != napi_ok) { - return status; - } - - name = (const char *) nxt_unit_sptr_get(&f->name); - - status = napi_set_named_property(env_, headers, name, vstr); - if (status != napi_ok) { - return status; - } + str = create_string_latin1(name, f->name_length); - status = napi_create_string_latin1(env_, name, f->name_length, &str); - if (status != napi_ok) { - return status; + for (i = 0; i < f->name_length; i++) { + name[i] = lowcase(name[i]); } - status = napi_set_element(env_, raw_headers, idx * 2, str); - if (status != napi_ok) { - return status; - } - - status = napi_set_element(env_, raw_headers, idx * 2 + 1, vstr); - if (status != napi_ok) { - return status; - } + vstr = set_named_property(headers, name, f->value, f->value_length); - return napi_ok; + set_element(raw_headers, idx * 2, str); + set_element(raw_headers, idx * 2 + 1, vstr); } napi_value Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req) { - napi_value constructor, return_val, req_pointer; - napi_status status; + napi_value constructor, res; + nxt_unit_request_t *r; - status = napi_get_named_property(env_, server_obj, "socket", - &constructor); - if (status != napi_ok) { - return nullptr; - } + r = req->request; - status = napi_new_instance(env_, constructor, 0, NULL, &return_val); - if (status != napi_ok) { - return nullptr; - } + constructor = get_named_property(server_obj, "socket"); - status = napi_create_int64(env_, (uintptr_t) req, &req_pointer); - if (status != napi_ok) { - return nullptr; - } + res = new_instance(constructor); - status = napi_set_named_property(env_, return_val, "req_pointer", - req_pointer); - if (status != napi_ok) { - return nullptr; - } + set_named_property(res, "req_pointer", (intptr_t) req); + set_named_property(res, "remoteAddress", r->remote, r->remote_length); + set_named_property(res, "localAddress", r->local, r->local_length); - return return_val; + return res; } napi_value Unit::create_request(napi_value server_obj, napi_value socket) { - napi_value constructor, return_val; - napi_status status; + napi_value constructor, return_val; - status = napi_get_named_property(env_, server_obj, "request", - &constructor); - if (status != napi_ok) { - return nullptr; - } + constructor = get_named_property(server_obj, "request"); - status = napi_new_instance(env_, constructor, 1, &server_obj, - &return_val); - if (status != napi_ok) { - return nullptr; - } + return_val = new_instance(constructor, server_obj); - status = napi_set_named_property(env_, return_val, "socket", socket); - if (status != napi_ok) { - return nullptr; - } + set_named_property(return_val, "socket", socket); + set_named_property(return_val, "connection", socket); return return_val; } @@ -706,37 +444,17 @@ Unit::create_request(napi_value server_obj, napi_value socket) napi_value Unit::create_response(napi_value server_obj, napi_value socket, - napi_value request, nxt_unit_request_info_t *req, - Unit *obj) + napi_value request, nxt_unit_request_info_t *req) { - napi_value constructor, return_val, req_num; - napi_status status; + napi_value constructor, return_val; - status = napi_get_named_property(env_, server_obj, "response", - &constructor); - if (status != napi_ok) { - return nullptr; - } + constructor = get_named_property(server_obj, "response"); - status = napi_new_instance(env_, constructor, 1, &request, &return_val); - if (status != napi_ok) { - return nullptr; - } + return_val = new_instance(constructor, request); - status = napi_set_named_property(env_, return_val, "socket", socket); - if (status != napi_ok) { - return nullptr; - } - - status = napi_create_int64(env_, (int64_t) (uintptr_t) req, &req_num); - if (status != napi_ok) { - return nullptr; - } - - status = napi_set_named_property(env_, return_val, "_req_point", req_num); - if (status != napi_ok) { - return nullptr; - } + set_named_property(return_val, "socket", socket); + set_named_property(return_val, "connection", socket); + set_named_property(return_val, "_req_point", (intptr_t) req); return return_val; } @@ -749,13 +467,12 @@ Unit::response_send_headers(napi_env env, napi_callback_info info) char *ptr, *name_ptr; bool is_array; size_t argc, name_len, value_len; - int64_t req_p; uint32_t status_code, header_len, keys_len, array_len; uint32_t keys_count, i, j; uint16_t hash; + nxt_napi napi(env); napi_value this_arg, headers, keys, name, value, array_val; napi_value req_num, array_entry; - napi_status status; napi_valuetype val_type; nxt_unit_field_t *f; nxt_unit_request_info_t *req; @@ -763,137 +480,97 @@ Unit::response_send_headers(napi_env env, napi_callback_info info) argc = 5; - status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL); - if (status != napi_ok) { - return nullptr; - } + try { + this_arg = napi.get_cb_info(info, argc, argv); + if (argc != 5) { + napi.throw_error("Wrong args count. Expected: " + "statusCode, headers, headers count, " + "headers length"); + return nullptr; + } - if (argc != 5) { - napi_throw_error(env, NULL, "Wrong args count. Need three: " - "statusCode, headers, headers count, headers length"); - return nullptr; - } + req_num = napi.get_named_property(argv[0], "_req_point"); - status = napi_get_named_property(env, argv[0], "_req_point", &req_num); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to get request pointer"); - return nullptr; - } + req = napi.get_request_info(req_num); - status = napi_get_value_int64(env, req_num, &req_p); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to get request pointer"); - return nullptr; - } + status_code = napi.get_value_uint32(argv[1]); + keys_count = napi.get_value_uint32(argv[3]); + header_len = napi.get_value_uint32(argv[4]); - req = (nxt_unit_request_info_t *) (uintptr_t) req_p; + /* Need to reserve extra byte for C-string 0-termination. */ + header_len++; - status = napi_get_value_uint32(env, argv[1], &status_code); - if (status != napi_ok) { - goto failed; - } + headers = argv[2]; - status = napi_get_value_uint32(env, argv[3], &keys_count); - if (status != napi_ok) { - goto failed; - } + ret = nxt_unit_response_init(req, status_code, keys_count, header_len); + if (ret != NXT_UNIT_OK) { + napi.throw_error("Failed to create response"); + return nullptr; + } - status = napi_get_value_uint32(env, argv[4], &header_len); - if (status != napi_ok) { - goto failed; - } + keys = napi.get_property_names(headers); + keys_len = napi.get_array_length(keys); - /* Need to reserve extra byte for C-string 0-termination. */ - header_len++; + ptr = req->response_buf->free; - headers = argv[2]; + for (i = 0; i < keys_len; i++) { + name = napi.get_element(keys, i); - ret = nxt_unit_response_init(req, status_code, keys_count, header_len); - if (ret != NXT_UNIT_OK) { - goto failed; - } + array_entry = napi.get_property(headers, name); - status = napi_get_property_names(env, headers, &keys); - if (status != napi_ok) { - goto failed; - } + name = napi.get_element(array_entry, 0); + value = napi.get_element(array_entry, 1); - status = napi_get_array_length(env, keys, &keys_len); - if (status != napi_ok) { - goto failed; - } + name_len = napi.get_value_string_latin1(name, ptr, header_len); + name_ptr = ptr; - ptr = req->response_buf->free; + ptr += name_len; + header_len -= name_len; - for (i = 0; i < keys_len; i++) { - status = napi_get_element(env, keys, i, &name); - if (status != napi_ok) { - goto failed; - } + hash = nxt_unit_field_hash(name_ptr, name_len); - status = napi_get_property(env, headers, name, &array_entry); - if (status != napi_ok) { - goto failed; - } + is_array = napi.is_array(value); - status = napi_get_element(env, array_entry, 0, &name); - if (status != napi_ok) { - goto failed; - } + if (is_array) { + array_len = napi.get_array_length(value); - status = napi_get_element(env, array_entry, 1, &value); - if (status != napi_ok) { - goto failed; - } + for (j = 0; j < array_len; j++) { + array_val = napi.get_element(value, j); - status = napi_get_value_string_latin1(env, name, ptr, header_len, - &name_len); - if (status != napi_ok) { - goto failed; - } + val_type = napi.type_of(array_val); - name_ptr = ptr; + if (val_type != napi_string) { + array_val = napi.coerce_to_string(array_val); + } - ptr += name_len; - header_len -= name_len; + value_len = napi.get_value_string_latin1(array_val, ptr, + header_len); - hash = nxt_unit_field_hash(name_ptr, name_len); + f = req->response->fields + req->response->fields_count; + f->skip = 0; - status = napi_is_array(env, value, &is_array); - if (status != napi_ok) { - goto failed; - } + nxt_unit_sptr_set(&f->name, name_ptr); - if (is_array) { - status = napi_get_array_length(env, value, &array_len); - if (status != napi_ok) { - goto failed; - } + f->name_length = name_len; + f->hash = hash; - for (j = 0; j < array_len; j++) { - status = napi_get_element(env, value, j, &array_val); - if (status != napi_ok) { - goto failed; - } + nxt_unit_sptr_set(&f->value, ptr); + f->value_length = (uint32_t) value_len; - napi_typeof(env, array_val, &val_type); - if (status != napi_ok) { - goto failed; + ptr += value_len; + header_len -= value_len; + + req->response->fields_count++; } + } else { + val_type = napi.type_of(value); + if (val_type != napi_string) { - status = napi_coerce_to_string(env, array_val, &array_val); - if (status != napi_ok) { - goto failed; - } + value = napi.coerce_to_string(value); } - status = napi_get_value_string_latin1(env, array_val, ptr, - header_len, - &value_len); - if (status != napi_ok) { - goto failed; - } + value_len = napi.get_value_string_latin1(value, ptr, header_len); f = req->response->fields + req->response->fields_count; f->skip = 0; @@ -911,60 +588,22 @@ Unit::response_send_headers(napi_env env, napi_callback_info info) req->response->fields_count++; } - - } else { - napi_typeof(env, value, &val_type); - if (status != napi_ok) { - goto failed; - } - - if (val_type != napi_string) { - status = napi_coerce_to_string(env, value, &value); - if (status != napi_ok) { - goto failed; - } - } - - status = napi_get_value_string_latin1(env, value, ptr, header_len, - &value_len); - if (status != napi_ok) { - goto failed; - } - - f = req->response->fields + req->response->fields_count; - f->skip = 0; - - nxt_unit_sptr_set(&f->name, name_ptr); - - f->name_length = name_len; - f->hash = hash; - - nxt_unit_sptr_set(&f->value, ptr); - f->value_length = (uint32_t) value_len; - - ptr += value_len; - header_len -= value_len; - - req->response->fields_count++; } + + } catch (exception &e) { + napi.throw_error(e); + return nullptr; } req->response_buf->free = ptr; ret = nxt_unit_response_send(req); if (ret != NXT_UNIT_OK) { - goto failed; + napi.throw_error("Failed to send response"); + return nullptr; } return this_arg; - -failed: - - req->response->fields_count = 0; - - napi_throw_error(env, NULL, "Failed to write headers"); - - return nullptr; } @@ -974,8 +613,8 @@ Unit::response_write(napi_env env, napi_callback_info info) int ret; char *ptr; size_t argc, have_buf_len; - int64_t req_p; uint32_t buf_len; + nxt_napi napi(env); napi_value this_arg, req_num; napi_status status; nxt_unit_buf_t *buf; @@ -985,39 +624,23 @@ Unit::response_write(napi_env env, napi_callback_info info) argc = 3; - status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL); - if (status != napi_ok) { - goto failed; - } - - if (argc != 3) { - napi_throw_error(env, NULL, "Wrong args count. Need two: " - "chunk, chunk length"); - return nullptr; - } - - status = napi_get_named_property(env, argv[0], "_req_point", &req_num); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to get request pointer"); - return nullptr; - } + try { + this_arg = napi.get_cb_info(info, argc, argv); + if (argc != 3) { + throw exception("Wrong args count. Expected: " + "chunk, chunk length"); + } - status = napi_get_value_int64(env, req_num, &req_p); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to get request pointer"); - return nullptr; - } + req_num = napi.get_named_property(argv[0], "_req_point"); + req = napi.get_request_info(req_num); - req = (nxt_unit_request_info_t *) (uintptr_t) req_p; + buf_len = napi.get_value_uint32(argv[2]); - status = napi_get_value_uint32(env, argv[2], &buf_len); - if (status != napi_ok) { - goto failed; - } + buf_type = napi.type_of(argv[1]); - status = napi_typeof(env, argv[1], &buf_type); - if (status != napi_ok) { - goto failed; + } catch (exception &e) { + napi.throw_error(e); + return nullptr; } buf_len++; @@ -1055,7 +678,7 @@ Unit::response_write(napi_env env, napi_callback_info info) failed: - napi_throw_error(env, NULL, "Failed to write body"); + napi.throw_error("Failed to write body"); return nullptr; } @@ -1065,33 +688,23 @@ napi_value Unit::response_end(napi_env env, napi_callback_info info) { size_t argc; - int64_t req_p; + nxt_napi napi(env); napi_value resp, this_arg, req_num; - napi_status status; nxt_unit_request_info_t *req; argc = 1; - status = napi_get_cb_info(env, info, &argc, &resp, &this_arg, NULL); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to finalize sending body"); - return nullptr; - } + try { + this_arg = napi.get_cb_info(info, argc, &resp); - status = napi_get_named_property(env, resp, "_req_point", &req_num); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to get request pointer"); - return nullptr; - } + req_num = napi.get_named_property(resp, "_req_point"); + req = napi.get_request_info(req_num); - status = napi_get_value_int64(env, req_num, &req_p); - if (status != napi_ok) { - napi_throw_error(env, NULL, "Failed to get request pointer"); + } catch (exception &e) { + napi.throw_error(e); return nullptr; } - req = (nxt_unit_request_info_t *) (uintptr_t) req_p; - nxt_unit_request_done(req, NXT_UNIT_OK); return this_arg; diff --git a/src/nodejs/unit-http/unit.h b/src/nodejs/unit-http/unit.h index db85e85c..e76d805a 100644 --- a/src/nodejs/unit-http/unit.h +++ b/src/nodejs/unit-http/unit.h @@ -6,34 +6,15 @@ #ifndef _NXT_NODEJS_UNIT_H_INCLUDED_ #define _NXT_NODEJS_UNIT_H_INCLUDED_ -#include <node_api.h> +#include "nxt_napi.h" -#ifdef __cplusplus -extern "C" { -#endif -#include "version.h" -#include <nxt_unit.h> - -#if NXT_VERNUM != NXT_NODE_VERNUM -#error "libunit version mismatch." -#endif - -#include <nxt_unit_response.h> -#include <nxt_unit_request.h> - - -#ifdef __cplusplus -} /* extern "C" */ -#endif - - -class Unit { +class Unit : public nxt_napi { public: static napi_value init(napi_env env, napi_value exports); private: - Unit(napi_env env); + Unit(napi_env env, napi_value jsthis); ~Unit(); static napi_value create(napi_env env, napi_callback_info info); @@ -56,7 +37,7 @@ private: napi_value create_response(napi_value server_obj, napi_value socket, napi_value request, - nxt_unit_request_info_t *req, Unit *obj); + nxt_unit_request_info_t *req); static napi_value response_send_headers(napi_env env, napi_callback_info info); @@ -64,18 +45,16 @@ private: static napi_value response_write(napi_env env, napi_callback_info info); static napi_value response_end(napi_env env, napi_callback_info info); - napi_status create_headers(nxt_unit_request_info_t *req, - napi_value request); + void create_headers(nxt_unit_request_info_t *req, napi_value request); - inline napi_status append_header(nxt_unit_field_t *f, napi_value headers, + void append_header(nxt_unit_field_t *f, napi_value headers, napi_value raw_headers, uint32_t idx); static napi_ref constructor_; - napi_env env_; napi_ref wrapper_; nxt_unit_ctx_t *unit_ctx_; }; -#endif /* _NXT_NODEJS_H_INCLUDED_ */ +#endif /* _NXT_NODEJS_UNIT_H_INCLUDED_ */ diff --git a/src/nxt_application.c b/src/nxt_application.c index a2827b75..f63b90fb 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -36,8 +36,6 @@ static nxt_app_module_t *nxt_app_module_load(nxt_task_t *task, const char *name); static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment); -static void nxt_app_http_release(nxt_task_t *task, void *obj, void *data); - static uint32_t compat[] = { NXT_VERNUM, NXT_DEBUG, @@ -431,32 +429,6 @@ nxt_app_set_environment(nxt_conf_value_t *environment) } -nxt_int_t -nxt_app_http_req_done(nxt_task_t *task, nxt_app_parse_ctx_t *ar) -{ - ar->timer.handler = nxt_app_http_release; - nxt_timer_add(task->thread->engine, &ar->timer, 0); - - return NXT_OK; -} - - -static void -nxt_app_http_release(nxt_task_t *task, void *obj, void *data) -{ - nxt_timer_t *timer; - nxt_app_parse_ctx_t *ar; - - timer = obj; - - nxt_debug(task, "http app release"); - - ar = nxt_timer_data(timer, nxt_app_parse_ctx_t, timer); - - nxt_mp_release(ar->request->mem_pool); -} - - nxt_app_lang_module_t * nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name) { diff --git a/src/nxt_application.h b/src/nxt_application.h index 781f05e0..7ff4bb11 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -99,62 +99,6 @@ struct nxt_common_app_conf_s { }; -typedef struct { - nxt_str_t method; - nxt_str_t target; - nxt_str_t version; - nxt_str_t path; - nxt_str_t query; - nxt_str_t server_name; - - nxt_list_t *fields; - - nxt_str_t cookie; - nxt_str_t content_length; - nxt_str_t content_type; - - off_t parsed_content_length; - nxt_bool_t done; - - size_t bufs; - nxt_buf_t *buf; -} nxt_app_request_header_t; - - -typedef struct { - size_t preread_size; - nxt_bool_t done; - - nxt_buf_t *buf; -} nxt_app_request_body_t; - - -typedef struct { - nxt_app_request_header_t header; - nxt_app_request_body_t body; - - nxt_str_t remote; - nxt_str_t local; -} nxt_app_request_t; - - -typedef struct nxt_app_parse_ctx_s nxt_app_parse_ctx_t; - - -struct nxt_app_parse_ctx_s { - nxt_app_request_t r; - nxt_http_request_t *request; - nxt_timer_t timer; - void *timer_data; - nxt_http_request_parse_t parser; - nxt_http_request_parse_t resp_parser; - nxt_mp_t *mem_pool; -}; - - -nxt_int_t nxt_app_http_req_done(nxt_task_t *task, nxt_app_parse_ctx_t *ctx); - - struct nxt_app_module_s { size_t compat_length; uint32_t *compat; diff --git a/src/nxt_buf.h b/src/nxt_buf.h index d9d4ee1b..9c22d650 100644 --- a/src/nxt_buf.h +++ b/src/nxt_buf.h @@ -206,7 +206,7 @@ nxt_buf_set_last(b) \ #define \ nxt_buf_clear_last(b) \ - (b)->is_last = 0 + (b)->is_last = 0 #define \ diff --git a/src/nxt_conf.c b/src/nxt_conf.c index 4c6d8839..57870838 100644 --- a/src/nxt_conf.c +++ b/src/nxt_conf.c @@ -87,7 +87,6 @@ struct nxt_conf_op_s { uint32_t index; uint32_t action; /* nxt_conf_op_action_t */ void *ctx; - nxt_conf_op_t *next; }; @@ -113,6 +112,8 @@ static void nxt_conf_json_parse_error(nxt_conf_json_error_t *error, u_char *pos, static nxt_int_t nxt_conf_copy_value(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst, nxt_conf_value_t *src); +static nxt_int_t nxt_conf_copy_array(nxt_mp_t *mp, nxt_conf_op_t *op, + nxt_conf_value_t *dst, nxt_conf_value_t *src); static nxt_int_t nxt_conf_copy_object(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst, nxt_conf_value_t *src); @@ -736,12 +737,14 @@ nxt_conf_array_qsort(nxt_conf_value_t *value, } -nxt_int_t +nxt_conf_op_ret_t nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops, nxt_conf_value_t *root, - nxt_str_t *path, nxt_conf_value_t *value) + nxt_str_t *path, nxt_conf_value_t *value, nxt_bool_t add) { nxt_str_t token; + nxt_int_t index; nxt_conf_op_t *op, **parent; + nxt_conf_value_t *node; nxt_conf_path_parse_t parse; nxt_conf_object_member_t *member; @@ -754,7 +757,7 @@ nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops, nxt_conf_value_t *root, for ( ;; ) { op = nxt_mp_zget(mp, sizeof(nxt_conf_op_t)); if (nxt_slow_path(op == NULL)) { - return NXT_ERROR; + return NXT_CONF_OP_ERROR; } *parent = op; @@ -762,50 +765,107 @@ nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops, nxt_conf_value_t *root, nxt_conf_path_next_token(&parse, &token); - root = nxt_conf_get_object_member(root, &token, &op->index); + switch (root->type) { + + case NXT_CONF_VALUE_OBJECT: + node = nxt_conf_get_object_member(root, &token, &op->index); + break; + + case NXT_CONF_VALUE_ARRAY: + index = nxt_int_parse(token.start, token.length); + + if (index < 0 || index > NXT_INT32_T_MAX) { + return NXT_CONF_OP_NOT_FOUND; + } + + op->index = index; + + node = nxt_conf_get_array_element(root, index); + break; + + default: + node = NULL; + } if (parse.last) { break; } - if (root == NULL) { - return NXT_DECLINED; + if (node == NULL) { + return NXT_CONF_OP_NOT_FOUND; } op->action = NXT_CONF_OP_PASS; + root = node; } if (value == NULL) { - if (root == NULL) { - return NXT_DECLINED; + if (node == NULL) { + return NXT_CONF_OP_NOT_FOUND; } op->action = NXT_CONF_OP_DELETE; - return NXT_OK; + return NXT_CONF_OP_OK; + } + + if (add) { + if (node == NULL) { + return NXT_CONF_OP_NOT_FOUND; + } + + if (node->type != NXT_CONF_VALUE_ARRAY) { + return NXT_CONF_OP_NOT_ALLOWED; + } + + op->action = NXT_CONF_OP_PASS; + + op = nxt_mp_zget(mp, sizeof(nxt_conf_op_t)); + if (nxt_slow_path(op == NULL)) { + return NXT_CONF_OP_ERROR; + } + + *parent = op; + + op->index = node->u.array->count; + op->action = NXT_CONF_OP_CREATE; + op->ctx = value; + + return NXT_CONF_OP_OK; + } + + if (node != NULL) { + op->action = NXT_CONF_OP_REPLACE; + op->ctx = value; + + return NXT_CONF_OP_OK; } - if (root == NULL) { + op->action = NXT_CONF_OP_CREATE; + if (root->type == NXT_CONF_VALUE_ARRAY) { + if (op->index > root->u.array->count) { + return NXT_CONF_OP_NOT_FOUND; + } + + op->ctx = value; + + } else { member = nxt_mp_zget(mp, sizeof(nxt_conf_object_member_t)); if (nxt_slow_path(member == NULL)) { - return NXT_ERROR; + return NXT_CONF_OP_ERROR; } nxt_conf_set_string(&member->name, &token); member->value = *value; - op->action = NXT_CONF_OP_CREATE; + op->index = root->u.object->count; op->ctx = member; - - } else { - op->action = NXT_CONF_OP_REPLACE; - op->ctx = value; } - return NXT_OK; + return NXT_CONF_OP_OK; } @@ -834,16 +894,13 @@ static nxt_int_t nxt_conf_copy_value(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst, nxt_conf_value_t *src) { - size_t size; - nxt_int_t rc; - nxt_uint_t n; - - if (op != NULL && src->type != NXT_CONF_VALUE_OBJECT) { + if (op != NULL + && src->type != NXT_CONF_VALUE_ARRAY + && src->type != NXT_CONF_VALUE_OBJECT) + { return NXT_ERROR; } - dst->type = src->type; - switch (src->type) { case NXT_CONF_VALUE_STRING: @@ -861,34 +918,116 @@ nxt_conf_copy_value(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst, break; case NXT_CONF_VALUE_ARRAY: + return nxt_conf_copy_array(mp, op, dst, src); - size = sizeof(nxt_conf_array_t) - + src->u.array->count * sizeof(nxt_conf_value_t); + case NXT_CONF_VALUE_OBJECT: + return nxt_conf_copy_object(mp, op, dst, src); - dst->u.array = nxt_mp_get(mp, size); - if (nxt_slow_path(dst->u.array == NULL)) { - return NXT_ERROR; + default: + dst->u = src->u; + } + + dst->type = src->type; + + return NXT_OK; +} + + +static nxt_int_t +nxt_conf_copy_array(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst, + nxt_conf_value_t *src) +{ + size_t size; + nxt_int_t rc; + nxt_uint_t s, d, count, index; + nxt_conf_op_t *pass_op; + nxt_conf_value_t *value; + + count = src->u.array->count; + + if (op != NULL) { + if (op->action == NXT_CONF_OP_CREATE) { + count++; + + } else if (op->action == NXT_CONF_OP_DELETE) { + count--; } + } + + size = sizeof(nxt_conf_array_t) + count * sizeof(nxt_conf_value_t); + + dst->u.array = nxt_mp_get(mp, size); + if (nxt_slow_path(dst->u.array == NULL)) { + return NXT_ERROR; + } + + dst->u.array->count = count; + + s = 0; + d = 0; - dst->u.array->count = src->u.array->count; + pass_op = NULL; - for (n = 0; n < src->u.array->count; n++) { - rc = nxt_conf_copy_value(mp, NULL, &dst->u.array->elements[n], - &src->u.array->elements[n]); + /* + * This initialization is needed only to + * suppress a warning on GCC 4.8 and older. + */ + index = 0; + + do { + if (pass_op == NULL) { + index = (op == NULL) ? src->u.array->count : op->index; + } + while (s != index) { + rc = nxt_conf_copy_value(mp, pass_op, &dst->u.array->elements[d], + &src->u.array->elements[s]); if (nxt_slow_path(rc != NXT_OK)) { return NXT_ERROR; } + + s++; + d++; } - break; + if (pass_op != NULL) { + pass_op = NULL; + continue; + } - case NXT_CONF_VALUE_OBJECT: - return nxt_conf_copy_object(mp, op, dst, src); + if (op != NULL) { + switch (op->action) { + case NXT_CONF_OP_PASS: + pass_op = op->ctx; + index++; + break; - default: - dst->u = src->u; - } + case NXT_CONF_OP_CREATE: + value = op->ctx; + dst->u.array->elements[d] = *value; + + d++; + break; + + case NXT_CONF_OP_REPLACE: + value = op->ctx; + dst->u.array->elements[d] = *value; + + s++; + d++; + break; + + case NXT_CONF_OP_DELETE: + s++; + break; + } + + op = NULL; + } + + } while (d != count); + + dst->type = src->type; return NXT_OK; } @@ -939,9 +1078,7 @@ nxt_conf_copy_object(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst, do { if (pass_op == NULL) { - index = (op == NULL || op->action == NXT_CONF_OP_CREATE) - ? src->u.object->count - : op->index; + index = (op == NULL) ? src->u.object->count : op->index; } while (s != index) { @@ -1015,7 +1152,7 @@ nxt_conf_copy_object(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst, break; } - op = op->next; + op = NULL; } } while (d != count); diff --git a/src/nxt_conf.h b/src/nxt_conf.h index 20ff3b1e..2435b0e2 100644 --- a/src/nxt_conf.h +++ b/src/nxt_conf.h @@ -20,6 +20,14 @@ typedef enum { } nxt_conf_type_t; +typedef enum { + NXT_CONF_OP_OK = 0, + NXT_CONF_OP_NOT_FOUND, + NXT_CONF_OP_NOT_ALLOWED, + NXT_CONF_OP_ERROR, +} nxt_conf_op_ret_t; + + typedef struct nxt_conf_value_s nxt_conf_value_t; typedef struct nxt_conf_op_s nxt_conf_op_t; @@ -80,8 +88,9 @@ NXT_EXPORT nxt_conf_value_t *nxt_conf_get_array_element(nxt_conf_value_t *value, NXT_EXPORT nxt_int_t nxt_conf_map_object(nxt_mp_t *mp, nxt_conf_value_t *value, nxt_conf_map_t *map, nxt_uint_t n, void *data); -nxt_int_t nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops, - nxt_conf_value_t *root, nxt_str_t *path, nxt_conf_value_t *value); +nxt_conf_op_ret_t nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops, + nxt_conf_value_t *root, nxt_str_t *path, nxt_conf_value_t *value, + nxt_bool_t add); nxt_conf_value_t *nxt_conf_clone(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *value); diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 5653b9eb..bee82dd4 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -66,6 +66,12 @@ static nxt_int_t nxt_conf_vldt_match_patterns(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_match_patterns_set(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_match_patterns_set_member( + nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt, @@ -218,6 +224,21 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { &nxt_conf_vldt_match_patterns, NULL }, + { nxt_string("arguments"), + NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, + &nxt_conf_vldt_match_patterns_sets, + NULL }, + + { nxt_string("headers"), + NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, + &nxt_conf_vldt_match_patterns_sets, + NULL }, + + { nxt_string("cookies"), + NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, + &nxt_conf_vldt_match_patterns_sets, + NULL }, + NXT_CONF_VLDT_END }; @@ -741,9 +762,15 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, nxt_str_t pattern; nxt_uint_t i, first, last; + enum { + sw_none, + sw_side, + sw_middle + } state; + if (nxt_conf_type(value) != NXT_CONF_STRING) { - return nxt_conf_vldt_error(vldt, - "The \"match\" patterns must be strings."); + return nxt_conf_vldt_error(vldt, "The \"match\" patterns for \"host\", " + "\"uri\", and \"method\" must be strings."); } nxt_conf_get_string(value, &pattern); @@ -754,17 +781,37 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, first = (pattern.start[0] == '!'); last = pattern.length - 1; + state = sw_none; for (i = first; i != pattern.length; i++) { + ch = pattern.start[i]; if (ch != '*') { continue; } - if (i != first && i != last) { - return nxt_conf_vldt_error(vldt, "The \"match\" patterns can only " - "contain \"*\" markers at the sides."); + switch (state) { + case sw_none: + state = (i == first) ? sw_side : sw_middle; + break; + + case sw_side: + if (i == last) { + if (last - first != 1) { + break; + } + + return nxt_conf_vldt_error(vldt, "The \"match\" pattern must " + "not contain double \"*\" markers."); + } + + /* Fall through. */ + + case sw_middle: + return nxt_conf_vldt_error(vldt, "The \"match\" patterns can " + "either contain \"*\" markers at " + "the sides or only one in the middle."); } } @@ -772,6 +819,49 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, } +static nxt_int_t +nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + if (nxt_conf_type(value) == NXT_CONF_ARRAY) { + return nxt_conf_vldt_array_iterator(vldt, value, + &nxt_conf_vldt_match_patterns_set); + } + + /* NXT_CONF_OBJECT */ + + return nxt_conf_vldt_match_patterns_set(vldt, value); +} + + +static nxt_int_t +nxt_conf_vldt_match_patterns_set(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value) +{ + if (nxt_conf_type(value) != NXT_CONF_OBJECT) { + return nxt_conf_vldt_error(vldt, "The \"match\" patterns for " + "\"arguments\", \"cookies\", and " + "\"headers\" must be objects."); + } + + return nxt_conf_vldt_object_iterator(vldt, value, + &nxt_conf_vldt_match_patterns_set_member); +} + + +static nxt_int_t +nxt_conf_vldt_match_patterns_set_member(nxt_conf_validation_t *vldt, + nxt_str_t *name, nxt_conf_value_t *value) +{ + if (name->length == 0) { + return nxt_conf_vldt_error(vldt, "The \"match\" pattern objects must " + "not contain empty member names."); + } + + return nxt_conf_vldt_match_patterns(vldt, value, NULL); +} + + #if (NXT_TLS) static nxt_int_t @@ -1268,6 +1358,7 @@ nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt, nxt_conf_value_t *valu return NXT_OK; } + static nxt_int_t nxt_conf_vldt_java_option(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) { diff --git a/src/nxt_controller.c b/src/nxt_controller.c index 29838bd9..49afbe46 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -184,6 +184,7 @@ nxt_controller_start(nxt_task_t *task, void *data) vldt.pool = nxt_mp_create(1024, 128, 256, 32); if (nxt_slow_path(vldt.pool == NULL)) { + nxt_mp_destroy(mp); return NXT_ERROR; } @@ -929,6 +930,7 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, nxt_mp_t *mp; nxt_int_t rc; nxt_conn_t *c; + nxt_bool_t post; nxt_buf_mem_t *mbuf; nxt_conf_op_t *ops; nxt_conf_value_t *value; @@ -957,7 +959,18 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, return; } - if (nxt_str_eq(&req->parser.method, "PUT", 3)) { + if (nxt_str_eq(&req->parser.method, "POST", 4)) { + if (path->length == 1) { + goto not_allowed; + } + + post = 1; + + } else { + post = 0; + } + + if (post || nxt_str_eq(&req->parser.method, "PUT", 3)) { if (!nxt_queue_is_empty(&nxt_controller_waiting_requests)) { nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link); @@ -999,15 +1012,20 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, if (path->length != 1) { rc = nxt_conf_op_compile(c->mem_pool, &ops, nxt_controller_conf.root, - path, value); + path, value, post); - if (rc != NXT_OK) { + if (rc != NXT_CONF_OP_OK) { nxt_mp_destroy(mp); - if (rc == NXT_DECLINED) { + switch (rc) { + case NXT_CONF_OP_NOT_FOUND: goto not_found; + + case NXT_CONF_OP_NOT_ALLOWED: + goto not_allowed; } + /* rc == NXT_CONF_OP_ERROR */ goto alloc_fail; } @@ -1079,13 +1097,14 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, } else { rc = nxt_conf_op_compile(c->mem_pool, &ops, nxt_controller_conf.root, - path, NULL); + path, NULL, 0); if (rc != NXT_OK) { - if (rc == NXT_DECLINED) { + if (rc == NXT_CONF_OP_NOT_FOUND) { goto not_found; } + /* rc == NXT_CONF_OP_ERROR */ goto alloc_fail; } @@ -1144,8 +1163,10 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, return; } +not_allowed: + resp.status = 405; - resp.title = (u_char *) "Invalid method."; + resp.title = (u_char *) "Method isn't allowed."; resp.offset = -1; nxt_controller_response(task, req, &resp); diff --git a/src/nxt_epoll_engine.c b/src/nxt_epoll_engine.c index 9f9c8f62..9cdaab9b 100644 --- a/src/nxt_epoll_engine.c +++ b/src/nxt_epoll_engine.c @@ -1059,7 +1059,7 @@ nxt_epoll_edge_conn_io_connect(nxt_task_t *task, void *obj, void *data) state = c->write_state; - switch (nxt_socket_connect(task, c->socket.fd, c->remote) ){ + switch (nxt_socket_connect(task, c->socket.fd, c->remote)) { case NXT_OK: c->socket.write_ready = 1; diff --git a/src/nxt_errno.h b/src/nxt_errno.h index b3d7105a..e3ce8349 100644 --- a/src/nxt_errno.h +++ b/src/nxt_errno.h @@ -45,6 +45,7 @@ typedef int nxt_err_t; #define NXT_EILSEQ EILSEQ #define NXT_ETIME ETIME #define NXT_ENOMOREFILES 0 +#define NXT_ENOBUFS ENOBUFS #if (NXT_HPUX) /* HP-UX uses EWOULDBLOCK instead of EAGAIN. */ diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c index 07e3c7bc..3a822042 100644 --- a/src/nxt_h1proto.c +++ b/src/nxt_h1proto.c @@ -35,6 +35,7 @@ static void nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r); static void nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj, void *data); static void nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r); +static void nxt_h1p_request_tls(nxt_task_t *task, nxt_http_request_t *r); static void nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r); static void nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r, @@ -103,6 +104,13 @@ const nxt_http_proto_local_addr_t nxt_http_proto_local_addr[3] = { }; +const nxt_http_proto_tls_t nxt_http_proto_tls[3] = { + nxt_h1p_request_tls, + NULL, + NULL, +}; + + const nxt_http_proto_header_send_t nxt_http_proto_header_send[3] = { nxt_h1p_request_header_send, NULL, @@ -813,6 +821,15 @@ nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r) } +static void +nxt_h1p_request_tls(nxt_task_t *task, nxt_http_request_t *r) +{ +#if (NXT_TLS) + r->tls = r->proto.h1->conn->u.tls; +#endif +} + + #define NXT_HTTP_LAST_SUCCESS \ (NXT_HTTP_OK + nxt_nitems(nxt_http_success) - 1) diff --git a/src/nxt_hpux_sendfile.c b/src/nxt_hpux_sendfile.c index 3c42c559..df200b64 100644 --- a/src/nxt_hpux_sendfile.c +++ b/src/nxt_hpux_sendfile.c @@ -13,7 +13,7 @@ ssize_t nxt_hpux_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b, size_t limit); static ssize_t nxt_sys_sendfile(int s, int fd, off_t offset, size_t nbytes, - const struct iovec *hdtrl, int flags) + const struct iovec *hdtrl, int flags) { return -1; } @@ -23,7 +23,7 @@ static ssize_t nxt_sys_sendfile(int s, int fd, off_t offset, size_t nbytes, /* sendfile() is not declared if _XOPEN_SOURCE_EXTENDED is defined. */ sbsize_t sendfile(int s, int fd, off_t offset, bsize_t nbytes, - const struct iovec *hdtrl, int flags); + const struct iovec *hdtrl, int flags); #define nxt_sys_sendfile sendfile diff --git a/src/nxt_http.h b/src/nxt_http.h index 23c406d3..835cf66d 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -114,12 +114,15 @@ struct nxt_http_request_s { const nxt_http_request_state_t *state; nxt_str_t host; + nxt_str_t server_name; nxt_str_t target; nxt_str_t version; nxt_str_t *method; nxt_str_t *path; nxt_str_t *args; + nxt_array_t *arguments; /* of nxt_http_name_value_t */ + nxt_array_t *cookies; /* of nxt_http_name_value_t */ nxt_list_t *fields; nxt_http_field_t *content_type; nxt_http_field_t *content_length; @@ -130,6 +133,10 @@ struct nxt_http_request_s { nxt_sockaddr_t *remote; nxt_sockaddr_t *local; + void *tls; + + nxt_timer_t timer; + void *timer_data; nxt_buf_t *last; @@ -165,6 +172,7 @@ typedef void (*nxt_http_proto_body_read_t)(nxt_task_t *task, nxt_http_request_t *r); typedef void (*nxt_http_proto_local_addr_t)(nxt_task_t *task, nxt_http_request_t *r); +typedef void (*nxt_http_proto_tls_t)(nxt_task_t *task, nxt_http_request_t *r); typedef void (*nxt_http_proto_header_send_t)(nxt_task_t *task, nxt_http_request_t *r); typedef void (*nxt_http_proto_send_t)(nxt_task_t *task, nxt_http_request_t *r, @@ -186,7 +194,6 @@ nxt_http_request_t *nxt_http_request_create(nxt_task_t *task); void nxt_http_request_error(nxt_task_t *task, nxt_http_request_t *r, nxt_http_status_t status); void nxt_http_request_read_body(nxt_task_t *task, nxt_http_request_t *r); -void nxt_http_request_local_addr(nxt_task_t *task, nxt_http_request_t *r); void nxt_http_request_header_send(nxt_task_t *task, nxt_http_request_t *r); void nxt_http_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out); @@ -221,6 +228,7 @@ extern nxt_lvlhsh_t nxt_response_fields_hash; extern const nxt_http_proto_body_read_t nxt_http_proto_body_read[]; extern const nxt_http_proto_local_addr_t nxt_http_proto_local_addr[]; +extern const nxt_http_proto_tls_t nxt_http_proto_tls[]; extern const nxt_http_proto_header_send_t nxt_http_proto_header_send[]; extern const nxt_http_proto_send_t nxt_http_proto_send[]; extern const nxt_http_proto_body_bytes_sent_t nxt_http_proto_body_bytes_sent[]; diff --git a/src/nxt_http_parse.c b/src/nxt_http_parse.c index 34eaaaf9..05df245e 100644 --- a/src/nxt_http_parse.c +++ b/src/nxt_http_parse.c @@ -34,10 +34,6 @@ static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, #define NXT_HTTP_FIELD_LVLHSH_SHIFT 5 -#define NXT_HTTP_FIELD_HASH_INIT 159406 -#define nxt_http_field_hash_char(h, c) (((h) << 4) + (h) + (c)) -#define nxt_http_field_hash_end(h) (((h) >> 16) ^ (h)) - typedef enum { NXT_HTTP_TARGET_SPACE = 1, /* \s */ @@ -119,7 +115,7 @@ nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp) rp->mem_pool = mp; rp->fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t)); - if (nxt_slow_path(rp->fields == NULL)){ + if (nxt_slow_path(rp->fields == NULL)) { return NXT_ERROR; } diff --git a/src/nxt_http_parse.h b/src/nxt_http_parse.h index 0326d45c..6c629936 100644 --- a/src/nxt_http_parse.h +++ b/src/nxt_http_parse.h @@ -93,6 +93,11 @@ struct nxt_http_field_s { }; +#define NXT_HTTP_FIELD_HASH_INIT 159406U +#define nxt_http_field_hash_char(h, c) (((h) << 4) + (h) + (c)) +#define nxt_http_field_hash_end(h) (((h) >> 16) ^ (h)) + + nxt_int_t nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp); nxt_int_t nxt_http_parse_request(nxt_http_request_parse_t *rp, diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 724b0808..1265c186 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -11,6 +11,8 @@ static nxt_int_t nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp); static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data); static void nxt_http_request_pass(nxt_task_t *task, void *obj, void *data); +static void nxt_http_request_proto_info(nxt_task_t *task, + nxt_http_request_t *r); static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj, void *data); static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data); @@ -293,26 +295,23 @@ nxt_http_request_pass(nxt_task_t *task, void *obj, void *data) pass = r->conf->socket_conf->pass; - if (nxt_slow_path(pass == NULL)) { - goto fail; - } + if (nxt_fast_path(pass != NULL)) { - for ( ;; ) { - nxt_debug(task, "http request route: %V", &pass->name); + do { + nxt_debug(task, "http request route: %V", &pass->name); - pass = pass->handler(task, r, pass); - if (pass == NULL) { - break; - } + pass = pass->handler(task, r, pass); - if (nxt_slow_path(r->pass_count++ == 255)) { - goto fail; - } - } + if (pass == NULL) { + return; + } - return; + if (pass == NXT_HTTP_PASS_ERROR) { + break; + } -fail: + } while (r->pass_count++ < 255); + } nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); } @@ -322,117 +321,52 @@ nxt_http_pass_t * nxt_http_request_application(nxt_task_t *task, nxt_http_request_t *r, nxt_http_pass_t *pass) { - nxt_int_t ret; - nxt_event_engine_t *engine; - nxt_app_parse_ctx_t *ar; + nxt_event_engine_t *engine; nxt_debug(task, "http request application"); - ar = nxt_mp_zget(r->mem_pool, sizeof(nxt_app_parse_ctx_t)); - if (nxt_slow_path(ar == NULL)) { - nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); - return NULL; - } - - ar->request = r; - ar->mem_pool = r->mem_pool; nxt_mp_retain(r->mem_pool); - // STUB engine = task->thread->engine; - ar->timer.task = &engine->task; - ar->timer.work_queue = &engine->fast_work_queue; - ar->timer.log = engine->task.log; - ar->timer.bias = NXT_TIMER_DEFAULT_BIAS; - - ar->r.remote.start = nxt_sockaddr_address(r->remote); - ar->r.remote.length = r->remote->address_length; + r->timer.task = &engine->task; + r->timer.work_queue = &engine->fast_work_queue; + r->timer.log = engine->task.log; + r->timer.bias = NXT_TIMER_DEFAULT_BIAS; /* * TODO: need an application flag to get local address * required by "SERVER_ADDR" in Pyhton and PHP. Not used in Go. */ - nxt_http_request_local_addr(task, r); - - if (nxt_fast_path(r->local != NULL)) { - ar->r.local.start = nxt_sockaddr_address(r->local); - ar->r.local.length = r->local->address_length; - } - - ar->r.header.fields = r->fields; - ar->r.header.done = 1; - ar->r.header.version = r->version; - - if (r->method != NULL) { - ar->r.header.method = *r->method; - } + nxt_http_request_proto_info(task, r); if (r->host.length != 0) { - ar->r.header.server_name = r->host; + r->server_name = r->host; } else { - nxt_str_set(&ar->r.header.server_name, "localhost"); + nxt_str_set(&r->server_name, "localhost"); } - ar->r.header.target = r->target; - - if (r->path != NULL) { - ar->r.header.path = *r->path; - } - - if (r->args != NULL) { - ar->r.header.query = *r->args; - } - - if (r->content_type != NULL) { - ar->r.header.content_type.length = r->content_type->value_length; - ar->r.header.content_type.start = r->content_type->value; - } - - if (r->content_length != NULL) { - ar->r.header.content_length.length = r->content_length->value_length; - ar->r.header.content_length.start = r->content_length->value; - } - - if (r->cookie != NULL) { - ar->r.header.cookie.length = r->cookie->value_length; - ar->r.header.cookie.start = r->cookie->value; - } - - if (r->body != NULL) { - ar->r.body.buf = r->body; - ar->r.body.preread_size = r->content_length_n; - ar->r.header.parsed_content_length = r->content_length_n; - } - - ar->r.body.done = 1; - - ret = nxt_http_parse_request_init(&ar->resp_parser, r->mem_pool); - if (nxt_slow_path(ret != NXT_OK)) { - nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); - return NULL; - } - - nxt_router_process_http_request(task, ar, pass->u.application); + nxt_router_process_http_request(task, r, pass->u.application); return NULL; } -void -nxt_http_request_read_body(nxt_task_t *task, nxt_http_request_t *r) +static void +nxt_http_request_proto_info(nxt_task_t *task, nxt_http_request_t *r) { if (r->proto.any != NULL) { - nxt_http_proto_body_read[r->protocol](task, r); + nxt_http_proto_local_addr[r->protocol](task, r); + nxt_http_proto_tls[r->protocol](task, r); } } void -nxt_http_request_local_addr(nxt_task_t *task, nxt_http_request_t *r) +nxt_http_request_read_body(nxt_task_t *task, nxt_http_request_t *r) { if (r->proto.any != NULL) { - nxt_http_proto_local_addr[r->protocol](task, r); + nxt_http_proto_body_read[r->protocol](task, r); } } @@ -589,6 +523,8 @@ nxt_http_request_error_handler(nxt_task_t *task, void *obj, void *data) nxt_debug(task, "http request error handler"); + r->error = 1; + if (proto.any != NULL) { nxt_http_proto_discard[r->protocol](task, r, nxt_http_buf_last(r)); } diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 133c39ab..d6749acb 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -9,9 +9,9 @@ typedef enum { - NXT_HTTP_ROUTE_STRING = 0, + NXT_HTTP_ROUTE_TABLE = 0, + NXT_HTTP_ROUTE_STRING, NXT_HTTP_ROUTE_STRING_PTR, - NXT_HTTP_ROUTE_FIELD, NXT_HTTP_ROUTE_HEADER, NXT_HTTP_ROUTE_ARGUMENT, NXT_HTTP_ROUTE_COOKIE, @@ -21,6 +21,7 @@ typedef enum { typedef enum { NXT_HTTP_ROUTE_PATTERN_EXACT = 0, NXT_HTTP_ROUTE_PATTERN_BEGIN, + NXT_HTTP_ROUTE_PATTERN_MIDDLE, NXT_HTTP_ROUTE_PATTERN_END, NXT_HTTP_ROUTE_PATTERN_SUBSTRING, } nxt_http_route_pattern_type_t; @@ -37,11 +38,17 @@ typedef struct { nxt_conf_value_t *host; nxt_conf_value_t *uri; nxt_conf_value_t *method; + nxt_conf_value_t *headers; + nxt_conf_value_t *arguments; + nxt_conf_value_t *cookies; } nxt_http_route_match_conf_t; typedef struct { - nxt_str_t test; + u_char *start1; + u_char *start2; + uint32_t length1; + uint32_t length2; uint32_t min_length; nxt_http_route_pattern_type_t type:8; @@ -52,17 +59,66 @@ typedef struct { typedef struct { - uintptr_t offset; - uint32_t items; + uint16_t hash; + uint16_t name_length; + uint32_t value_length; + u_char *name; + u_char *value; +} nxt_http_name_value_t; + + +typedef struct { + uint16_t hash; + uint16_t name_length; + uint32_t value_length; + u_char *name; + u_char *value; +} nxt_http_cookie_t; + + +typedef struct { + /* The object must be the first field. */ nxt_http_route_object_t object:8; + uint32_t items; + + union { + uintptr_t offset; + + struct { + u_char *start; + uint16_t hash; + uint16_t length; + } name; + } u; + nxt_http_route_pattern_t pattern[0]; } nxt_http_route_rule_t; typedef struct { uint32_t items; - nxt_http_pass_t pass; nxt_http_route_rule_t *rule[0]; +} nxt_http_route_ruleset_t; + + +typedef struct { + /* The object must be the first field. */ + nxt_http_route_object_t object:8; + uint32_t items; + nxt_http_route_ruleset_t *ruleset[0]; +} nxt_http_route_table_t; + + +typedef union { + nxt_http_route_rule_t *rule; + nxt_http_route_table_t *table; +} nxt_http_route_test_t; + + +typedef struct { + uint32_t items; + nxt_http_pass_t pass; + nxt_http_route_test_t test[0]; } nxt_http_route_match_t; @@ -79,17 +135,39 @@ struct nxt_http_routes_s { }; +#define NJS_COOKIE_HASH \ + (nxt_http_field_hash_end( \ + nxt_http_field_hash_char( \ + nxt_http_field_hash_char( \ + nxt_http_field_hash_char( \ + nxt_http_field_hash_char( \ + nxt_http_field_hash_char( \ + nxt_http_field_hash_char(NXT_HTTP_FIELD_HASH_INIT, \ + 'c'), 'o'), 'o'), 'k'), 'i'), 'e')) & 0xFFFF) + + static nxt_http_route_t *nxt_http_route_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv); static nxt_http_route_match_t *nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv); +static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task, + nxt_mp_t *mp, nxt_conf_value_t *table_cv, nxt_http_route_object_t object, + nxt_bool_t case_sensitive); +static nxt_http_route_ruleset_t *nxt_http_route_ruleset_create(nxt_task_t *task, + nxt_mp_t *mp, nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object, + nxt_bool_t case_sensitive); +static nxt_http_route_rule_t *nxt_http_route_rule_name_create(nxt_task_t *task, + nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name, + nxt_bool_t case_sensitive); static nxt_http_route_rule_t *nxt_http_route_rule_create(nxt_task_t *task, - nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, - nxt_bool_t case_sensitive, nxt_http_route_pattern_case_t pattern_case); + nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive, + nxt_http_route_pattern_case_t pattern_case); static int nxt_http_pattern_compare(const void *one, const void *two); static nxt_int_t nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern, nxt_http_route_pattern_case_t pattern_case); +static u_char *nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test, + nxt_http_route_pattern_case_t pattern_case); static void nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route); @@ -103,10 +181,37 @@ static nxt_http_pass_t *nxt_http_route_pass(nxt_task_t *task, nxt_http_request_t *r, nxt_http_pass_t *start); static nxt_http_pass_t *nxt_http_route_match(nxt_http_request_t *r, nxt_http_route_match_t *match); -static nxt_bool_t nxt_http_route_rule(nxt_http_request_t *r, +static nxt_int_t nxt_http_route_table(nxt_http_request_t *r, + nxt_http_route_table_t *table); +static nxt_int_t nxt_http_route_ruleset(nxt_http_request_t *r, + nxt_http_route_ruleset_t *ruleset); +static nxt_int_t nxt_http_route_rule(nxt_http_request_t *r, + nxt_http_route_rule_t *rule); +static nxt_int_t nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule); -static nxt_bool_t nxt_http_route_pattern(nxt_http_request_t *r, +static nxt_int_t nxt_http_route_arguments(nxt_http_request_t *r, + nxt_http_route_rule_t *rule); +static nxt_array_t *nxt_http_route_arguments_parse(nxt_http_request_t *r); +static nxt_http_name_value_t *nxt_http_route_argument(nxt_array_t *array, + u_char *name, size_t name_length, uint32_t hash, u_char *start, + u_char *end); +static nxt_int_t nxt_http_route_test_argument(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, nxt_array_t *array); +static nxt_int_t nxt_http_route_cookies(nxt_http_request_t *r, + nxt_http_route_rule_t *rule); +static nxt_array_t *nxt_http_route_cookies_parse(nxt_http_request_t *r); +static nxt_int_t nxt_http_route_cookie_parse(nxt_array_t *cookies, + u_char *start, u_char *end); +static nxt_http_name_value_t *nxt_http_route_cookie(nxt_array_t *array, + u_char *name, size_t name_length, u_char *start, u_char *end); +static nxt_int_t nxt_http_route_test_cookie(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, nxt_array_t *array); +static nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, u_char *start, size_t length); +static nxt_int_t nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, u_char *start, size_t length); +static nxt_int_t nxt_http_route_memcmp(u_char *start, u_char *test, + size_t length, nxt_bool_t case_sensitive); nxt_http_routes_t * @@ -188,6 +293,24 @@ static nxt_conf_map_t nxt_http_route_match_conf[] = { NXT_CONF_MAP_PTR, offsetof(nxt_http_route_match_conf_t, method), }, + + { + nxt_string("headers"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, headers), + }, + + { + nxt_string("arguments"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, arguments), + }, + + { + nxt_string("cookies"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, cookies), + }, }; @@ -233,10 +356,13 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, { size_t size; uint32_t n; + nxt_mp_t *mp; nxt_int_t ret; nxt_str_t pass, *string; nxt_conf_value_t *match_conf, *pass_conf; - nxt_http_route_rule_t *rule, **p; + nxt_http_route_test_t *test; + nxt_http_route_rule_t *rule; + nxt_http_route_table_t *table; nxt_http_route_match_t *match; nxt_http_route_match_conf_t mtcf; @@ -255,7 +381,9 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, n = (match_conf != NULL) ? nxt_conf_object_members_count(match_conf) : 0; size = sizeof(nxt_http_route_match_t) + n * sizeof(nxt_http_route_rule_t *); - match = nxt_mp_alloc(tmcf->router_conf->mem_pool, size); + mp = tmcf->router_conf->mem_pool; + + match = nxt_mp_alloc(mp, size); if (nxt_slow_path(match == NULL)) { return NULL; } @@ -264,7 +392,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, match->pass.handler = NULL; match->items = n; - string = nxt_str_dup(tmcf->router_conf->mem_pool, &match->pass.name, &pass); + string = nxt_str_dup(mp, &match->pass.name, &pass); if (nxt_slow_path(string == NULL)) { return NULL; } @@ -282,64 +410,232 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NULL; } - p = &match->rule[0]; + test = &match->test[0]; if (mtcf.host != NULL) { - rule = nxt_http_route_rule_create(task, tmcf, mtcf.host, 1, + rule = nxt_http_route_rule_create(task, mp, mtcf.host, 1, NXT_HTTP_ROUTE_PATTERN_LOWCASE); if (rule == NULL) { return NULL; } - rule->offset = offsetof(nxt_http_request_t, host); + rule->u.offset = offsetof(nxt_http_request_t, host); rule->object = NXT_HTTP_ROUTE_STRING; - *p++ = rule; + test->rule = rule; + test++; } if (mtcf.uri != NULL) { - rule = nxt_http_route_rule_create(task, tmcf, mtcf.uri, 1, + rule = nxt_http_route_rule_create(task, mp, mtcf.uri, 1, NXT_HTTP_ROUTE_PATTERN_NOCASE); if (rule == NULL) { return NULL; } - rule->offset = offsetof(nxt_http_request_t, path); + rule->u.offset = offsetof(nxt_http_request_t, path); rule->object = NXT_HTTP_ROUTE_STRING_PTR; - *p++ = rule; + test->rule = rule; + test++; } if (mtcf.method != NULL) { - rule = nxt_http_route_rule_create(task, tmcf, mtcf.method, 1, + rule = nxt_http_route_rule_create(task, mp, mtcf.method, 1, NXT_HTTP_ROUTE_PATTERN_UPCASE); if (rule == NULL) { return NULL; } - rule->offset = offsetof(nxt_http_request_t, method); + rule->u.offset = offsetof(nxt_http_request_t, method); rule->object = NXT_HTTP_ROUTE_STRING_PTR; - *p++ = rule; + test->rule = rule; + test++; + } + + if (mtcf.headers != NULL) { + table = nxt_http_route_table_create(task, mp, mtcf.headers, + NXT_HTTP_ROUTE_HEADER, 0); + if (table == NULL) { + return NULL; + } + + test->table = table; + test++; + } + + if (mtcf.arguments != NULL) { + table = nxt_http_route_table_create(task, mp, mtcf.arguments, + NXT_HTTP_ROUTE_ARGUMENT, 1); + if (table == NULL) { + return NULL; + } + + test->table = table; + test++; + } + + if (mtcf.cookies != NULL) { + table = nxt_http_route_table_create(task, mp, mtcf.cookies, + NXT_HTTP_ROUTE_COOKIE, 0); + if (table == NULL) { + return NULL; + } + + test->table = table; + test++; } return match; } +static nxt_http_route_table_t * +nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp, + nxt_conf_value_t *table_cv, nxt_http_route_object_t object, + nxt_bool_t case_sensitive) +{ + size_t size; + uint32_t i, n; + nxt_bool_t array; + nxt_conf_value_t *ruleset_cv; + nxt_http_route_table_t *table; + nxt_http_route_ruleset_t *ruleset; + + array = (nxt_conf_type(table_cv) == NXT_CONF_ARRAY); + n = array ? nxt_conf_array_elements_count(table_cv) : 1; + size = sizeof(nxt_http_route_table_t) + + n * sizeof(nxt_http_route_ruleset_t *); + + table = nxt_mp_alloc(mp, size); + if (nxt_slow_path(table == NULL)) { + return NULL; + } + + table->items = n; + table->object = NXT_HTTP_ROUTE_TABLE; + + if (!array) { + ruleset = nxt_http_route_ruleset_create(task, mp, table_cv, + object, case_sensitive); + if (nxt_slow_path(ruleset == NULL)) { + return NULL; + } + + table->ruleset[0] = ruleset; + + return table; + } + + for (i = 0; i < n; i++) { + ruleset_cv = nxt_conf_get_array_element(table_cv, i); + + ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv, + object, case_sensitive); + if (nxt_slow_path(ruleset == NULL)) { + return NULL; + } + + table->ruleset[i] = ruleset; + } + + return table; +} + + +static nxt_http_route_ruleset_t * +nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp, + nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object, + nxt_bool_t case_sensitive) +{ + size_t size; + uint32_t i, n, next; + nxt_str_t name; + nxt_conf_value_t *rule_cv; + nxt_http_route_rule_t *rule; + nxt_http_route_ruleset_t *ruleset; + + n = nxt_conf_object_members_count(ruleset_cv); + size = sizeof(nxt_http_route_ruleset_t) + + n * sizeof(nxt_http_route_rule_t *); + + ruleset = nxt_mp_alloc(mp, size); + if (nxt_slow_path(ruleset == NULL)) { + return NULL; + } + + ruleset->items = n; + + next = 0; + + for (i = 0; i < n; i++) { + rule_cv = nxt_conf_next_object_member(ruleset_cv, &name, &next); + + rule = nxt_http_route_rule_name_create(task, mp, rule_cv, &name, + case_sensitive); + if (nxt_slow_path(rule == NULL)) { + return NULL; + } + + rule->object = object; + ruleset->rule[i] = rule; + } + + return ruleset; +} + + static nxt_http_route_rule_t * -nxt_http_route_rule_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, +nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp, + nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive) +{ + u_char c, *p; + uint32_t hash; + nxt_uint_t i; + nxt_http_route_rule_t *rule; + + rule = nxt_http_route_rule_create(task, mp, rule_cv, case_sensitive, + NXT_HTTP_ROUTE_PATTERN_NOCASE); + if (nxt_slow_path(rule == NULL)) { + return NULL; + } + + rule->u.name.length = name->length; + + p = nxt_mp_nget(mp, name->length); + if (nxt_slow_path(p == NULL)) { + return NULL; + } + + rule->u.name.start = p; + + hash = NXT_HTTP_FIELD_HASH_INIT; + + for (i = 0; i < name->length; i++) { + c = name->start[i]; + *p++ = c; + + c = nxt_lowcase(c); + hash = nxt_http_field_hash_char(hash, c); + } + + rule->u.name.hash = nxt_http_field_hash_end(hash) & 0xFFFF; + + return rule; +} + + +static nxt_http_route_rule_t * +nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive, nxt_http_route_pattern_case_t pattern_case) { size_t size; uint32_t i, n; - nxt_mp_t *mp; nxt_int_t ret; nxt_bool_t string; nxt_conf_value_t *value; nxt_http_route_rule_t *rule; nxt_http_route_pattern_t *pattern; - mp = tmcf->router_conf->mem_pool; - string = (nxt_conf_type(cv) != NXT_CONF_ARRAY); n = string ? 1 : nxt_conf_array_elements_count(cv); size = sizeof(nxt_http_route_rule_t) + n * sizeof(nxt_http_route_pattern_t); @@ -407,8 +703,12 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, { u_char *start; nxt_str_t test; + nxt_uint_t n, length; nxt_http_route_pattern_type_t type; + /* Suppress warning about uninitialized variable. */ + length = 0; + type = NXT_HTTP_ROUTE_PATTERN_EXACT; nxt_conf_get_string(cv, &test); @@ -448,57 +748,98 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, } else if (test.start[test.length - 1] == '*') { test.length--; type = NXT_HTTP_ROUTE_PATTERN_BEGIN; + + } else { + length = test.length - 1; + + for (n = 1; n < length; n++) { + if (test.start[n] == '*') { + test.length = n; + type = NXT_HTTP_ROUTE_PATTERN_MIDDLE; + break; + } + } } } } pattern->type = type; pattern->min_length = test.length; - pattern->test.length = test.length; + pattern->length1 = test.length; - start = nxt_mp_nget(mp, test.length); + start = nxt_http_route_pattern_copy(mp, &test, pattern_case); if (nxt_slow_path(start == NULL)) { return NXT_ERROR; } - pattern->test.start = start; + pattern->start1 = start; + + if (type == NXT_HTTP_ROUTE_PATTERN_MIDDLE) { + length -= test.length; + pattern->length2 = length; + pattern->min_length += length; + + test.start = &test.start[test.length + 1]; + test.length = length; + + start = nxt_http_route_pattern_copy(mp, &test, pattern_case); + if (nxt_slow_path(start == NULL)) { + return NXT_ERROR; + } + + pattern->start2 = start; + } + + return NXT_OK; +} + + +static u_char * +nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test, + nxt_http_route_pattern_case_t pattern_case) +{ + u_char *start; + + start = nxt_mp_nget(mp, test->length); + if (nxt_slow_path(start == NULL)) { + return start; + } switch (pattern_case) { case NXT_HTTP_ROUTE_PATTERN_UPCASE: - nxt_memcpy_upcase(start, test.start, test.length); + nxt_memcpy_upcase(start, test->start, test->length); break; case NXT_HTTP_ROUTE_PATTERN_LOWCASE: - nxt_memcpy_lowcase(start, test.start, test.length); + nxt_memcpy_lowcase(start, test->start, test->length); break; case NXT_HTTP_ROUTE_PATTERN_NOCASE: - nxt_memcpy(start, test.start, test.length); + nxt_memcpy(start, test->start, test->length); break; } - return NXT_OK; + return start; } void nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) { - nxt_uint_t items; - nxt_http_route_t **route; + nxt_http_route_t **route, **end; nxt_http_routes_t *routes; routes = tmcf->router_conf->routes; + if (routes != NULL) { - items = routes->items; route = &routes->route[0]; + end = route + routes->items; - while (items != 0) { + while (route < end) { nxt_http_route_resolve(task, tmcf, *route); route++; - items--; } } } @@ -508,17 +849,15 @@ static void nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route) { - nxt_uint_t items; - nxt_http_route_match_t **match; + nxt_http_route_match_t **match, **end; - items = route->items; match = &route->match[0]; + end = match + route->items; - while (items != 0) { + while (match < end) { nxt_http_pass_resolve(task, tmcf, &(*match)->pass); match++; - items--; } } @@ -561,21 +900,18 @@ nxt_http_pass_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, static nxt_http_route_t * nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name) { - nxt_uint_t items; - nxt_http_route_t **route; + nxt_http_route_t **route, **end; - items = routes->items; route = &routes->route[0]; + end = route + routes->items; - do { + while (route < end) { if (nxt_strstr_eq(&(*route)->name, name)) { return *route; } route++; - items--; - - } while (items != 0); + } return NULL; } @@ -627,20 +963,17 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, void nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes) { - nxt_uint_t items; - nxt_http_route_t **route; + nxt_http_route_t **route, **end; if (routes != NULL) { - items = routes->items; route = &routes->route[0]; + end = route + routes->items; - do { + while (route < end) { nxt_http_route_cleanup(task, *route); route++; - items--; - - } while (items != 0); + } } } @@ -648,19 +981,16 @@ nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes) static void nxt_http_route_cleanup(nxt_task_t *task, nxt_http_route_t *route) { - nxt_uint_t items; - nxt_http_route_match_t **match; + nxt_http_route_match_t **match, **end; - items = route->items; match = &route->match[0]; + end = match + route->items; - do { + while (match < end) { nxt_http_pass_cleanup(task, &(*match)->pass); match++; - items--; - - } while (items != 0); + } } @@ -677,23 +1007,21 @@ static nxt_http_pass_t * nxt_http_route_pass(nxt_task_t *task, nxt_http_request_t *r, nxt_http_pass_t *start) { - nxt_uint_t items; nxt_http_pass_t *pass; nxt_http_route_t *route; - nxt_http_route_match_t **match; + nxt_http_route_match_t **match, **end; route = start->u.route; - items = route->items; match = &route->match[0]; + end = match + route->items; - while (items != 0) { + while (match < end) { pass = nxt_http_route_match(r, *match); if (pass != NULL) { return pass; } match++; - items--; } nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND); @@ -705,87 +1033,488 @@ nxt_http_route_pass(nxt_task_t *task, nxt_http_request_t *r, static nxt_http_pass_t * nxt_http_route_match(nxt_http_request_t *r, nxt_http_route_match_t *match) { - nxt_uint_t items; - nxt_http_route_rule_t **rule; + nxt_int_t ret; + nxt_http_route_test_t *test, *end; - rule = &match->rule[0]; - items = match->items; + test = &match->test[0]; + end = test + match->items; - while (items != 0) { - if (!nxt_http_route_rule(r, *rule)) { - return NULL; + while (test < end) { + if (test->rule->object != NXT_HTTP_ROUTE_TABLE) { + ret = nxt_http_route_rule(r, test->rule); + + } else { + ret = nxt_http_route_table(r, test->table); } - rule++; - items--; + if (ret <= 0) { + /* 0 => NULL, -1 => NXT_HTTP_PASS_ERROR. */ + return (nxt_http_pass_t *) (intptr_t) ret; + } + + test++; } return &match->pass; } -static nxt_bool_t +static nxt_int_t +nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table) +{ + nxt_int_t ret; + nxt_http_route_ruleset_t **ruleset, **end; + + ret = 1; + ruleset = &table->ruleset[0]; + end = ruleset + table->items; + + while (ruleset < end) { + ret = nxt_http_route_ruleset(r, *ruleset); + + if (ret != 0) { + return ret; + } + + ruleset++; + } + + return ret; +} + + +static nxt_int_t +nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset) +{ + nxt_int_t ret; + nxt_http_route_rule_t **rule, **end; + + rule = &ruleset->rule[0]; + end = rule + ruleset->items; + + while (rule < end) { + ret = nxt_http_route_rule(r, *rule); + + if (ret <= 0) { + return ret; + } + + rule++; + } + + return 1; +} + + +static nxt_int_t nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule) { - void *p, **pp; - u_char *start; - size_t length; - nxt_str_t *s; - nxt_uint_t items; - nxt_bool_t ret; - nxt_http_field_t *f; - nxt_http_route_pattern_t *pattern; + void *p, **pp; + u_char *start; + size_t length; + nxt_str_t *s; + + switch (rule->object) { + + case NXT_HTTP_ROUTE_HEADER: + return nxt_http_route_header(r, rule); - p = nxt_pointer_to(r, rule->offset); + case NXT_HTTP_ROUTE_ARGUMENT: + return nxt_http_route_arguments(r, rule); + + case NXT_HTTP_ROUTE_COOKIE: + return nxt_http_route_cookies(r, rule); + + default: + break; + } + + p = nxt_pointer_to(r, rule->u.offset); if (rule->object == NXT_HTTP_ROUTE_STRING) { s = p; - length = s->length; - start = s->start; } else { + /* NXT_HTTP_ROUTE_STRING_PTR */ pp = p; - p = *pp; + s = *pp; - if (p == NULL) { + if (s == NULL) { return 0; } + } - switch (rule->object) { + length = s->length; + start = s->start; - case NXT_HTTP_ROUTE_STRING_PTR: - s = p; - length = s->length; - start = s->start; - break; + return nxt_http_route_test_rule(r, rule, start, length); +} - case NXT_HTTP_ROUTE_FIELD: - f = p; - length = f->value_length; - start = f->value; - break; - case NXT_HTTP_ROUTE_HEADER: - return 0; +static nxt_int_t +nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule) +{ + nxt_int_t ret; + nxt_http_field_t *f; - case NXT_HTTP_ROUTE_ARGUMENT: - return 0; + ret = 0; - case NXT_HTTP_ROUTE_COOKIE: - return 0; + nxt_list_each(f, r->fields) { - default: - nxt_unreachable(); - return 0; + if (rule->u.name.hash != f->hash + || rule->u.name.length != f->name_length + || nxt_strncasecmp(rule->u.name.start, f->name, f->name_length) + != 0) + { + continue; + } + + ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length); + + if (ret == 0) { + return ret; + } + + } nxt_list_loop; + + return ret; +} + + +static nxt_int_t +nxt_http_route_arguments(nxt_http_request_t *r, nxt_http_route_rule_t *rule) +{ + nxt_array_t *arguments; + + if (r->args == NULL) { + return 0; + } + + arguments = nxt_http_route_arguments_parse(r); + if (nxt_slow_path(arguments == NULL)) { + return -1; + } + + return nxt_http_route_test_argument(r, rule, arguments); +} + + +static nxt_array_t * +nxt_http_route_arguments_parse(nxt_http_request_t *r) +{ + size_t name_length; + u_char c, *p, *start, *end, *name; + uint32_t hash; + nxt_bool_t valid; + nxt_array_t *args; + nxt_http_name_value_t *nv; + + if (r->arguments != NULL) { + return r->arguments; + } + + args = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t)); + if (nxt_slow_path(args == NULL)) { + return NULL; + } + + hash = NXT_HTTP_FIELD_HASH_INIT; + valid = 1; + name = NULL; + name_length = 0; + + start = r->args->start; + end = start + r->args->length; + + for (p = start; p < end; p++) { + c = *p; + + if (c == '=') { + name_length = p - start; + name = start; + start = p + 1; + valid = (name_length != 0); + + } else if (c == '&') { + if (valid) { + nv = nxt_http_route_argument(args, name, name_length, hash, + start, p); + if (nxt_slow_path(nv == NULL)) { + return NULL; + } + } + + hash = NXT_HTTP_FIELD_HASH_INIT; + valid = 1; + name = NULL; + start = p + 1; + + } else if (name == NULL) { + hash = nxt_http_field_hash_char(hash, c); + } + } + + if (valid) { + nv = nxt_http_route_argument(args, name, name_length, hash, start, p); + if (nxt_slow_path(nv == NULL)) { + return NULL; + } + } + + r->arguments = args; + + return args; +} + + +static nxt_http_name_value_t * +nxt_http_route_argument(nxt_array_t *array, u_char *name, size_t name_length, + uint32_t hash, u_char *start, u_char *end) +{ + size_t length; + nxt_http_name_value_t *nv; + + nv = nxt_array_add(array); + if (nxt_slow_path(nv == NULL)) { + return NULL; + } + + nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF; + + length = end - start; + + if (name == NULL) { + name_length = length; + name = start; + length = 0; + } + + nv->name_length = name_length; + nv->value_length = length; + nv->name = name; + nv->value = start; + + return nv; +} + + +static nxt_int_t +nxt_http_route_test_argument(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, nxt_array_t *array) +{ + nxt_bool_t ret; + nxt_http_name_value_t *nv, *end; + + ret = 0; + + nv = array->elts; + end = nv + array->nelts; + + while (nv < end) { + + if (rule->u.name.hash == nv->hash + && rule->u.name.length == nv->name_length + && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0) + { + ret = nxt_http_route_test_rule(r, rule, nv->value, + nv->value_length); + if (ret == 0) { + break; + } + } + + nv++; + } + + return ret; +} + + +static nxt_int_t +nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule) +{ + nxt_array_t *cookies; + + cookies = nxt_http_route_cookies_parse(r); + if (nxt_slow_path(cookies == NULL)) { + return -1; + } + + return nxt_http_route_test_cookie(r, rule, cookies); +} + + +static nxt_array_t * +nxt_http_route_cookies_parse(nxt_http_request_t *r) +{ + nxt_int_t ret; + nxt_array_t *cookies; + nxt_http_field_t *f; + + if (r->cookies != NULL) { + return r->cookies; + } + + cookies = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t)); + if (nxt_slow_path(cookies == NULL)) { + return NULL; + } + + nxt_list_each(f, r->fields) { + + if (f->hash != NJS_COOKIE_HASH + || f->name_length != 6 + || nxt_strncasecmp(f->name, (u_char *) "Cookie", 6) != 0) + { + continue; + } + + ret = nxt_http_route_cookie_parse(cookies, f->value, + f->value + f->value_length); + if (ret != NXT_OK) { + return NULL; + } + + } nxt_list_loop; + + r->cookies = cookies; + + return cookies; +} + + +static nxt_int_t +nxt_http_route_cookie_parse(nxt_array_t *cookies, u_char *start, u_char *end) +{ + size_t name_length; + u_char c, *p, *name; + nxt_http_name_value_t *nv; + + name = NULL; + name_length = 0; + + for (p = start; p < end; p++) { + c = *p; + + if (c == '=') { + while (start[0] == ' ') { start++; } + + name_length = p - start; + + if (name_length != 0) { + name = start; + } + + start = p + 1; + + } else if (c == ';') { + if (name != NULL) { + nv = nxt_http_route_cookie(cookies, name, name_length, + start, p); + if (nxt_slow_path(nv == NULL)) { + return NXT_ERROR; + } + } + + name = NULL; + start = p + 1; + } + } + + if (name != NULL) { + nv = nxt_http_route_cookie(cookies, name, name_length, start, p); + if (nxt_slow_path(nv == NULL)) { + return NXT_ERROR; + } + } + + return NXT_OK; +} + + +static nxt_http_name_value_t * +nxt_http_route_cookie(nxt_array_t *array, u_char *name, size_t name_length, + u_char *start, u_char *end) +{ + u_char c, *p; + uint32_t hash; + nxt_http_name_value_t *nv; + + nv = nxt_array_add(array); + if (nxt_slow_path(nv == NULL)) { + return NULL; + } + + nv->name_length = name_length; + nv->name = name; + + hash = NXT_HTTP_FIELD_HASH_INIT; + + for (p = name; p < name + name_length; p++) { + c = *p; + c = nxt_lowcase(c); + hash = nxt_http_field_hash_char(hash, c); + } + + nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF; + + while (start < end && end[-1] == ' ') { end--; } + + nv->value_length = end - start; + nv->value = start; + + return nv; +} + + +static nxt_int_t +nxt_http_route_test_cookie(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, nxt_array_t *array) +{ + nxt_bool_t ret; + nxt_http_name_value_t *nv, *end; + + ret = 0; + + nv = array->elts; + end = nv + array->nelts; + + while (nv < end) { + + if (rule->u.name.hash == nv->hash + && rule->u.name.length == nv->name_length + && nxt_strncasecmp(rule->u.name.start, nv->name, nv->name_length) + == 0) + { + ret = nxt_http_route_test_rule(r, rule, nv->value, + nv->value_length); + if (ret == 0) { + break; + } } + + nv++; } - items = rule->items; + return ret; +} + + +static nxt_int_t +nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule, + u_char *start, size_t length) +{ + nxt_int_t ret; + nxt_http_route_pattern_t *pattern, *end; + + ret = 1; pattern = &rule->pattern[0]; + end = pattern + rule->items; - do { + while (pattern < end) { ret = nxt_http_route_pattern(r, pattern, start, length); + /* nxt_http_route_pattern() returns either 1 or 0. */ ret ^= pattern->negative; if (pattern->any == ret) { @@ -793,30 +1522,31 @@ nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule) } pattern++; - items--; - - } while (items != 0); + } return ret; } -static nxt_bool_t +static nxt_int_t nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, u_char *start, size_t length) { - nxt_str_t *test; + u_char *p, *end, *test; + size_t test_length; + nxt_int_t ret; if (length < pattern->min_length) { return 0; } - test = &pattern->test; + test = pattern->start1; + test_length = pattern->length1; switch (pattern->type) { case NXT_HTTP_ROUTE_PATTERN_EXACT: - if (length != test->length) { + if (length != test_length) { return 0; } @@ -825,25 +1555,52 @@ nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, case NXT_HTTP_ROUTE_PATTERN_BEGIN: break; + case NXT_HTTP_ROUTE_PATTERN_MIDDLE: + ret = nxt_http_route_memcmp(start, test, test_length, + pattern->case_sensitive); + if (!ret) { + return ret; + } + + test = pattern->start2; + test_length = pattern->length2; + + /* Fall through. */ + case NXT_HTTP_ROUTE_PATTERN_END: - start += length - test->length; + start += length - test_length; break; case NXT_HTTP_ROUTE_PATTERN_SUBSTRING: + end = start + length; + if (pattern->case_sensitive) { - return (nxt_memstrn(start, start + length, - (char *) test->start, test->length) - != NULL); + p = nxt_memstrn(start, end, (char *) test, test_length); + + } else { + p = nxt_memcasestrn(start, end, (char *) test, test_length); } - return (nxt_memcasestrn(start, start + length, - (char *) test->start, test->length) - != NULL); + return (p != NULL); } - if (pattern->case_sensitive) { - return (nxt_memcmp(start, test->start, test->length) == 0); + return nxt_http_route_memcmp(start, test, test_length, + pattern->case_sensitive); +} + + +static nxt_int_t +nxt_http_route_memcmp(u_char *start, u_char *test, size_t test_length, + nxt_bool_t case_sensitive) +{ + nxt_int_t n; + + if (case_sensitive) { + n = nxt_memcmp(start, test, test_length); + + } else { + n = nxt_memcasecmp(start, test, test_length); } - return (nxt_memcasecmp(start, test->start, test->length) == 0); + return (n == 0); } diff --git a/src/nxt_job_resolve.c b/src/nxt_job_resolve.c index a1317756..0f1fb9aa 100644 --- a/src/nxt_job_resolve.c +++ b/src/nxt_job_resolve.c @@ -59,11 +59,11 @@ nxt_job_resolve(nxt_job_resolve_t *jbr) case AF_INET6: #endif case AF_INET: - n++; - break; + n++; + break; default: - break; + break; } } @@ -81,15 +81,15 @@ nxt_job_resolve(nxt_job_resolve_t *jbr) switch (r->ai_addr->sa_family) { #if (NXT_INET6) case AF_INET6: - length = NXT_INET6_ADDR_STR_LEN; - break; + length = NXT_INET6_ADDR_STR_LEN; + break; #endif case AF_INET: - length = NXT_INET_ADDR_STR_LEN; - break; + length = NXT_INET_ADDR_STR_LEN; + break; default: - continue; + continue; } sa = nxt_sockaddr_create(mp, r->ai_addr, r->ai_addrlen, length); diff --git a/src/nxt_kqueue_engine.c b/src/nxt_kqueue_engine.c index 0e68fbdc..0212b331 100644 --- a/src/nxt_kqueue_engine.c +++ b/src/nxt_kqueue_engine.c @@ -856,7 +856,7 @@ nxt_kqueue_conn_io_connect(nxt_task_t *task, void *obj, void *data) state = c->write_state; - switch (nxt_socket_connect(task, c->socket.fd, c->remote) ){ + switch (nxt_socket_connect(task, c->socket.fd, c->remote)) { case NXT_OK: c->socket.write_ready = 1; diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index f756bff7..40682eb9 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -842,7 +842,7 @@ nxt_main_process_sigusr1_handler(nxt_task_t *task, void *obj, void *data) nxt_mp_destroy(mp); return; - } + } fail: diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 80321a85..a6ec6c60 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -699,7 +699,7 @@ fail: static int nxt_php_startup(sapi_module_struct *sapi_module) { - return php_module_startup(sapi_module, NULL, 0); + return php_module_startup(sapi_module, NULL, 0); } @@ -929,6 +929,10 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) track_vars_array TSRMLS_CC); nxt_php_set_cstr(req, "SERVER_PORT", "80", 2, track_vars_array TSRMLS_CC); + if (r->tls) { + nxt_php_set_cstr(req, "HTTPS", "on", 2, track_vars_array TSRMLS_CC); + } + f_end = r->fields + r->fields_count; for (f = r->fields; f < f_end; f++) { name = nxt_unit_sptr_get(&f->name); diff --git a/src/nxt_poll_engine.c b/src/nxt_poll_engine.c index acb44a22..f514e0a9 100644 --- a/src/nxt_poll_engine.c +++ b/src/nxt_poll_engine.c @@ -370,7 +370,7 @@ nxt_poll_commit_changes(nxt_event_engine_t *engine) retval = NXT_ERROR; - next: + next: change++; diff --git a/src/nxt_port.c b/src/nxt_port.c index 30719ad3..aff46666 100644 --- a/src/nxt_port.c +++ b/src/nxt_port.c @@ -510,7 +510,7 @@ nxt_port_post(nxt_task_t *task, nxt_port_t *port, pw = nxt_zalloc(sizeof(nxt_port_work_t)); if (nxt_slow_path(pw == NULL)) { - return NXT_ERROR; + return NXT_ERROR; } nxt_atomic_fetch_add(&port->use_count, 1); diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c index 774f1f33..b908041c 100644 --- a/src/nxt_port_memory.c +++ b/src/nxt_port_memory.c @@ -49,10 +49,10 @@ nxt_port_mmap_at(nxt_port_mmaps_t *port_mmaps, uint32_t i) while (i + 1 > cap) { if (cap < 16) { - cap = cap * 2; + cap = cap * 2; } else { - cap = cap + cap / 2; + cap = cap + cap / 2; } } diff --git a/src/nxt_port_socket.c b/src/nxt_port_socket.c index 01fe2dab..c9b5105b 100644 --- a/src/nxt_port_socket.c +++ b/src/nxt_port_socket.c @@ -195,7 +195,24 @@ nxt_port_msg_create(nxt_task_t *task, nxt_port_send_msg_t *m) static nxt_port_send_msg_t * -nxt_port_msg_push(nxt_task_t *task, nxt_port_t *port, nxt_port_send_msg_t *msg) +nxt_port_msg_insert_head(nxt_task_t *task, nxt_port_t *port, + nxt_port_send_msg_t *msg) +{ + if (msg->work.data == NULL) { + msg = nxt_port_msg_create(task, msg); + } + + if (msg != NULL) { + nxt_queue_insert_head(&port->messages, &msg->link); + } + + return msg; +} + + +static nxt_port_send_msg_t * +nxt_port_msg_insert_tail(nxt_task_t *task, nxt_port_t *port, + nxt_port_send_msg_t *msg) { if (msg->work.data == NULL) { msg = nxt_port_msg_create(task, msg); @@ -260,7 +277,7 @@ nxt_port_socket_twrite(nxt_task_t *task, nxt_port_t *port, nxt_uint_t type, } else { nxt_thread_mutex_lock(&port->write_mutex); - res = nxt_port_msg_push(task, port, &msg); + res = nxt_port_msg_insert_tail(task, port, &msg); nxt_thread_mutex_unlock(&port->write_mutex); @@ -349,6 +366,8 @@ nxt_port_write_handler(nxt_task_t *task, void *obj, void *data) iov[0].iov_len += sizeof(msg->tracking_msg); } + sb.limit -= iov[0].iov_len; + nxt_sendbuf_mem_coalesce(task, &sb); plain_size = sb.size; @@ -407,7 +426,7 @@ nxt_port_write_handler(nxt_task_t *task, void *obj, void *data) } data = NULL; - if (nxt_port_msg_push(task, port, msg) != NULL) { + if (nxt_port_msg_insert_tail(task, port, msg) != NULL) { use_delta++; } } @@ -422,16 +441,17 @@ nxt_port_write_handler(nxt_task_t *task, void *obj, void *data) data = NULL; } - } else if (nxt_slow_path(n == NXT_ERROR)) { + } else { if (msg->link.next == NULL) { - if (nxt_port_msg_push(task, port, msg) != NULL) { + if (nxt_port_msg_insert_head(task, port, msg) != NULL) { use_delta++; } } - goto fail; - } - /* n == NXT_AGAIN */ + if (nxt_slow_path(n == NXT_ERROR)) { + goto fail; + } + } } while (port->socket.write_ready); @@ -546,6 +566,7 @@ void nxt_port_read_close(nxt_port_t *port) { port->socket.read_ready = 0; + port->socket.read = NXT_EVENT_INACTIVE; nxt_socket_close(port->socket.task, port->pair[0]); port->pair[0] = -1; } @@ -618,15 +639,24 @@ nxt_port_read_handler(nxt_task_t *task, void *obj, void *data) } +typedef struct { + uint32_t stream; + uint32_t pid; +} nxt_port_frag_key_t; + + static nxt_int_t nxt_port_lvlhsh_frag_test(nxt_lvlhsh_query_t *lhq, void *data) { nxt_port_recv_msg_t *fmsg; + nxt_port_frag_key_t *frag_key; fmsg = data; + frag_key = (nxt_port_frag_key_t *) lhq->key.start; - if (lhq->key.length == sizeof(uint32_t) - && *(uint32_t *) lhq->key.start == fmsg->port_msg.stream) + if (lhq->key.length == sizeof(nxt_port_frag_key_t) + && frag_key->stream == fmsg->port_msg.stream + && frag_key->pid == (uint32_t) fmsg->port_msg.pid) { return NXT_OK; } @@ -664,6 +694,7 @@ nxt_port_frag_start(nxt_task_t *task, nxt_port_t *port, nxt_int_t res; nxt_lvlhsh_query_t lhq; nxt_port_recv_msg_t *fmsg; + nxt_port_frag_key_t frag_key; nxt_debug(task, "start frag stream #%uD", msg->port_msg.stream); @@ -675,9 +706,12 @@ nxt_port_frag_start(nxt_task_t *task, nxt_port_t *port, *fmsg = *msg; - lhq.key_hash = nxt_murmur_hash2(&fmsg->port_msg.stream, sizeof(uint32_t)); - lhq.key.length = sizeof(uint32_t); - lhq.key.start = (u_char *) &fmsg->port_msg.stream; + frag_key.stream = fmsg->port_msg.stream; + frag_key.pid = fmsg->port_msg.pid; + + lhq.key_hash = nxt_murmur_hash2(&frag_key, sizeof(nxt_port_frag_key_t)); + lhq.key.length = sizeof(nxt_port_frag_key_t); + lhq.key.start = (u_char *) &frag_key; lhq.proto = &lvlhsh_frag_proto; lhq.replace = 0; lhq.value = fmsg; @@ -710,17 +744,24 @@ nxt_port_frag_start(nxt_task_t *task, nxt_port_t *port, static nxt_port_recv_msg_t * -nxt_port_frag_find(nxt_task_t *task, nxt_port_t *port, uint32_t stream, - nxt_bool_t last) +nxt_port_frag_find(nxt_task_t *task, nxt_port_t *port, nxt_port_recv_msg_t *msg) { - nxt_int_t res; - nxt_lvlhsh_query_t lhq; + nxt_int_t res; + nxt_bool_t last; + nxt_lvlhsh_query_t lhq; + nxt_port_frag_key_t frag_key; + + last = msg->port_msg.mf == 0; + + nxt_debug(task, "%s frag stream #%uD", last ? "last" : "next", + msg->port_msg.stream); - nxt_debug(task, "%s frag stream #%uD", last ? "last" : "next", stream); + frag_key.stream = msg->port_msg.stream; + frag_key.pid = msg->port_msg.pid; - lhq.key_hash = nxt_murmur_hash2(&stream, sizeof(uint32_t)); - lhq.key.length = sizeof(uint32_t); - lhq.key.start = (u_char *) &stream; + lhq.key_hash = nxt_murmur_hash2(&frag_key, sizeof(nxt_port_frag_key_t)); + lhq.key.length = sizeof(nxt_port_frag_key_t); + lhq.key.start = (u_char *) &frag_key; lhq.proto = &lvlhsh_frag_proto; lhq.pool = port->mem_pool; @@ -733,7 +774,8 @@ nxt_port_frag_find(nxt_task_t *task, nxt_port_t *port, uint32_t stream, return lhq.value; default: - nxt_log(task, NXT_LOG_INFO, "frag stream #%uD not found", stream); + nxt_log(task, NXT_LOG_INFO, "frag stream #%uD not found", + frag_key.stream); return NULL; } @@ -773,8 +815,7 @@ nxt_port_read_msg_process(nxt_task_t *task, nxt_port_t *port, if (nxt_slow_path(msg->port_msg.nf != 0)) { - fmsg = nxt_port_frag_find(task, port, msg->port_msg.stream, - msg->port_msg.mf == 0); + fmsg = nxt_port_frag_find(task, port, msg); if (nxt_slow_path(fmsg == NULL)) { goto fmsg_failed; diff --git a/src/nxt_process.c b/src/nxt_process.c index 59520297..c4aef21c 100644 --- a/src/nxt_process.c +++ b/src/nxt_process.c @@ -136,9 +136,11 @@ nxt_process_start(nxt_task_t *task, nxt_process_t *process) nxt_random_init(&thread->random); - if (init->user_cred != NULL && getuid() == 0) { - /* Super-user. */ - + if (init->user_cred != NULL) { + /* + * Changing user credentials requires either root privileges + * or CAP_SETUID and CAP_SETGID capabilities on Linux. + */ ret = nxt_user_cred_set(task, init->user_cred); if (ret != NXT_OK) { goto fail; @@ -434,11 +436,7 @@ nxt_user_cred_get(nxt_task_t *task, nxt_user_cred_t *uc, const char *group) uc->base_gid = grp->gr_gid; } - if (getuid() == 0) { - return nxt_user_groups_get(task, uc); - } - - return NXT_OK; + return nxt_user_groups_get(task, uc); } @@ -505,14 +503,26 @@ nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc) if (nsaved == -1) { nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno); - goto fail; + goto free; } nxt_debug(task, "getgroups(): %d", nsaved); if (initgroups(uc->user, uc->base_gid) != 0) { - nxt_alert(task, "initgroups(%s, %d) failed", uc->user, uc->base_gid); - goto restore; + if (nxt_errno == NXT_EPERM) { + nxt_log(task, NXT_LOG_NOTICE, + "initgroups(%s, %d) failed %E, ignored", + uc->user, uc->base_gid, nxt_errno); + + ret = NXT_OK; + + goto free; + + } else { + nxt_alert(task, "initgroups(%s, %d) failed %E", + uc->user, uc->base_gid, nxt_errno); + goto restore; + } } ngroups = getgroups(0, NULL); @@ -567,7 +577,7 @@ restore: ret = NXT_ERROR; } -fail: +free: nxt_free(saved); @@ -582,8 +592,15 @@ nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc) uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid); if (setgid(uc->base_gid) != 0) { - nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno); - return NXT_ERROR; + if (nxt_errno == NXT_EPERM) { + nxt_log(task, NXT_LOG_NOTICE, "setgid(%d) failed %E, ignored", + uc->base_gid, nxt_errno); + return NXT_OK; + + } else { + nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno); + return NXT_ERROR; + } } if (uc->gids != NULL) { @@ -595,8 +612,8 @@ nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc) } else { /* MacOSX fallback. */ if (initgroups(uc->user, uc->base_gid) != 0) { - nxt_alert(task, "initgroups(%s, %d) failed", - uc->user, uc->base_gid); + nxt_alert(task, "initgroups(%s, %d) failed %E", + uc->user, uc->base_gid, nxt_errno); return NXT_ERROR; } } diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c index 6478f38c..a6d5f217 100644 --- a/src/nxt_python_wsgi.c +++ b/src/nxt_python_wsgi.c @@ -619,26 +619,6 @@ nxt_python_create_environ(nxt_task_t *task) } - obj = PyString_FromStringAndSize("http", nxt_length("http")); - - if (nxt_slow_path(obj == NULL)) { - nxt_alert(task, - "Python failed to create the \"wsgi.url_scheme\" environ value"); - goto fail; - } - - if (nxt_slow_path(PyDict_SetItemString(environ, "wsgi.url_scheme", obj) - != 0)) - { - nxt_alert(task, - "Python failed to set the \"wsgi.url_scheme\" environ value"); - goto fail; - } - - Py_DECREF(obj); - obj = NULL; - - if (nxt_slow_path(PyType_Ready(&nxt_py_input_type) != 0)) { nxt_alert(task, "Python failed to initialize the \"wsgi.input\" type object"); @@ -726,6 +706,13 @@ nxt_python_get_environ(nxt_python_run_ctx_t *ctx) RC(nxt_python_add_sptr(ctx, "REMOTE_ADDR", &r->remote, r->remote_length)); RC(nxt_python_add_sptr(ctx, "SERVER_ADDR", &r->local, r->local_length)); + if (r->tls) { + RC(nxt_python_add_str(ctx, "wsgi.url_scheme", "https", 5)); + + } else { + RC(nxt_python_add_str(ctx, "wsgi.url_scheme", "http", 4)); + } + RC(nxt_python_add_sptr(ctx, "SERVER_PROTOCOL", &r->version, r->version_length)); diff --git a/src/nxt_router.c b/src/nxt_router.c index e46e8f82..149a0ff3 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -62,7 +62,7 @@ typedef struct { uint32_t stream; nxt_app_t *app; nxt_port_t *app_port; - nxt_app_parse_ctx_t *ap; + nxt_http_request_t *request; nxt_msg_info_t msg_info; nxt_req_app_link_t *ra; @@ -75,7 +75,7 @@ struct nxt_req_app_link_s { nxt_atomic_t use_count; nxt_port_t *app_port; nxt_port_t *reply_port; - nxt_app_parse_ctx_t *ap; + nxt_http_request_t *request; nxt_msg_info_t msg_info; nxt_req_conn_link_t *rc; @@ -264,8 +264,8 @@ static nxt_int_t nxt_router_app_port(nxt_task_t *task, nxt_app_t *app, static void nxt_router_app_prepare_request(nxt_task_t *task, nxt_req_app_link_t *ra); -static nxt_buf_t *nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, - nxt_port_t *port, const nxt_str_t *prefix); +static nxt_buf_t *nxt_router_prepare_msg(nxt_task_t *task, + nxt_http_request_t *r, nxt_port_t *port, const nxt_str_t *prefix); static void nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data); static void nxt_router_adjust_idle_timer(nxt_task_t *task, void *obj, @@ -282,6 +282,11 @@ static void nxt_http_request_send_body(nxt_task_t *task, void *obj, void *data); static void nxt_router_app_joint_use(nxt_task_t *task, nxt_app_joint_t *app_joint, int i); +static nxt_int_t nxt_router_http_request_done(nxt_task_t *task, + nxt_http_request_t *r); +static void nxt_router_http_request_release(nxt_task_t *task, void *obj, + void *data); + static nxt_router_t *nxt_router; static const nxt_str_t http_prefix = nxt_string("HTTP_"); @@ -502,7 +507,7 @@ nxt_router_ra_init(nxt_task_t *task, nxt_req_app_link_t *ra, ra->rc = rc; rc->ra = ra; ra->reply_port = engine->port; - ra->ap = rc->ap; + ra->request = rc->request; ra->work.handler = NULL; ra->work.task = &engine->task; @@ -521,7 +526,7 @@ nxt_router_ra_create(nxt_task_t *task, nxt_req_app_link_t *ra_src) return ra_src; } - mp = ra_src->ap->mem_pool; + mp = ra_src->request->mem_pool; ra = nxt_mp_alloc(mp, sizeof(nxt_req_app_link_t)); @@ -645,16 +650,16 @@ nxt_router_ra_release(nxt_task_t *task, nxt_req_app_link_t *ra) if (rc != NULL) { if (nxt_slow_path(ra->err_code != 0)) { - nxt_http_request_error(task, rc->ap->request, ra->err_code); + nxt_http_request_error(task, rc->request, ra->err_code); } else { rc->app_port = ra->app_port; rc->msg_info = ra->msg_info; if (rc->app->timeout != 0) { - rc->ap->timer.handler = nxt_router_app_timeout; - rc->ap->timer_data = rc; - nxt_timer_add(task->thread->engine, &rc->ap->timer, + rc->request->timer.handler = nxt_router_app_timeout; + rc->request->timer_data = rc; + nxt_timer_add(task->thread->engine, &rc->request->timer, rc->app->timeout); } @@ -817,12 +822,12 @@ nxt_router_rc_unlink(nxt_task_t *task, nxt_req_conn_link_t *rc) rc->app = NULL; } - if (rc->ap != NULL) { - rc->ap->timer_data = NULL; + if (rc->request != NULL) { + rc->request->timer_data = NULL; - nxt_app_http_req_done(task, rc->ap); + nxt_router_http_request_done(task, rc->request); - rc->ap = NULL; + rc->request = NULL; } } @@ -3377,33 +3382,27 @@ static void nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) { - size_t dump_size; nxt_int_t ret; nxt_buf_t *b; + nxt_unit_field_t *f; + nxt_http_field_t *field; nxt_http_request_t *r; nxt_req_conn_link_t *rc; - nxt_app_parse_ctx_t *ar; nxt_unit_response_t *resp; b = msg->buf; rc = data; - dump_size = nxt_buf_used_size(b); - - if (dump_size > 300) { - dump_size = 300; - } - if (msg->size == 0) { b = NULL; } - ar = rc->ap; - if (nxt_slow_path(ar == NULL)) { + r = rc->request; + if (nxt_slow_path(r == NULL)) { return; } - if (ar->request->error) { + if (r->error) { nxt_router_rc_unlink(task, rc); return; } @@ -3411,15 +3410,15 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, if (msg->port_msg.last != 0) { nxt_debug(task, "router data create last buf"); - nxt_buf_chain_add(&b, nxt_http_buf_last(ar->request)); + nxt_buf_chain_add(&b, nxt_http_buf_last(r)); nxt_router_rc_unlink(task, rc); } else { if (rc->app != NULL && rc->app->timeout != 0) { - ar->timer.handler = nxt_router_app_timeout; - ar->timer_data = rc; - nxt_timer_add(task->thread->engine, &ar->timer, rc->app->timeout); + r->timer.handler = nxt_router_app_timeout; + r->timer_data = rc; + nxt_timer_add(task->thread->engine, &r->timer, rc->app->timeout); } } @@ -3432,8 +3431,6 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, msg->buf = NULL; } - r = ar->request; - if (r->header_sent) { nxt_buf_chain_add(&r->out, b); nxt_http_request_send_body(task, r, NULL); @@ -3451,11 +3448,8 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, goto fail; } - nxt_unit_field_t *f; - nxt_http_field_t *field; - for (f = resp->fields; f < resp->fields + resp->fields_count; f++) { - field = nxt_list_add(ar->resp_parser.fields); + field = nxt_list_add(r->resp.fields); if (nxt_slow_path(field == NULL)) { goto fail; @@ -3473,15 +3467,8 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, (size_t) field->name_length, field->name, (size_t) field->value_length, field->value); } - r->status = resp->status; -/* - ret = nxt_http_parse_fields(&ar->resp_parser, &b->mem); - if (nxt_slow_path(ret != NXT_DONE)) { - goto fail; - } -*/ - r->resp.fields = ar->resp_parser.fields; + r->status = resp->status; ret = nxt_http_fields_process(r->resp.fields, &nxt_response_fields_hash, r); @@ -3590,8 +3577,8 @@ nxt_router_response_error_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, } } - if (rc->ap != NULL) { - nxt_http_request_error(task, rc->ap->request, + if (rc->request != NULL) { + nxt_http_request_error(task, rc->request, NXT_HTTP_SERVICE_UNAVAILABLE); } @@ -4452,17 +4439,15 @@ nxt_router_app_port(nxt_task_t *task, nxt_app_t *app, nxt_req_app_link_t *ra) void -nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar, +nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r, nxt_app_t *app) { nxt_int_t res; nxt_port_t *port; nxt_event_engine_t *engine; - nxt_http_request_t *r; nxt_req_app_link_t ra_local, *ra; nxt_req_conn_link_t *rc; - r = ar->request; engine = task->thread->engine; rc = nxt_port_rpc_register_handler_ex(task, engine->port, @@ -4480,7 +4465,7 @@ nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar, nxt_router_app_use(task, app, 1); - rc->ap = ar; + rc->request = r; ra = &ra_local; nxt_router_ra_init(task, ra, rc); @@ -4511,17 +4496,15 @@ nxt_router_dummy_buf_completion(nxt_task_t *task, void *obj, void *data) static void nxt_router_app_prepare_request(nxt_task_t *task, nxt_req_app_link_t *ra) { - uint32_t request_failed; - nxt_buf_t *buf; - nxt_int_t res; - nxt_port_t *port, *c_port, *reply_port; - nxt_app_parse_ctx_t *ap; + uint32_t request_failed; + nxt_buf_t *buf; + nxt_int_t res; + nxt_port_t *port, *c_port, *reply_port; nxt_assert(ra->app_port != NULL); port = ra->app_port; reply_port = ra->reply_port; - ap = ra->ap; request_failed = 1; @@ -4539,7 +4522,7 @@ nxt_router_app_prepare_request(nxt_task_t *task, nxt_req_app_link_t *ra) nxt_process_connected_port_add(port->process, reply_port); } - buf = nxt_router_prepare_msg(task, &ap->r, port, + buf = nxt_router_prepare_msg(task, ra->request, port, nxt_app_msg_prefix[port->app->type]); if (nxt_slow_path(buf == NULL)) { @@ -4642,34 +4625,33 @@ nxt_fields_next(nxt_fields_iter_t *i) static nxt_buf_t * -nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, +nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r, nxt_port_t *port, const nxt_str_t *prefix) { - void *target_pos, *query_pos; - u_char *pos, *end, *p, c; - size_t fields_count, req_size, size, free_size; - size_t copy_size; - nxt_buf_t *b, *buf, *out, **tail; - nxt_http_field_t *field, *dup; - nxt_unit_field_t *dst_field; - nxt_fields_iter_t iter, dup_iter; - nxt_unit_request_t *req; - nxt_app_request_header_t *h; - - h = &r->header; + void *target_pos, *query_pos; + u_char *pos, *end, *p, c; + size_t fields_count, req_size, size, free_size; + size_t copy_size; + nxt_off_t content_length; + nxt_buf_t *b, *buf, *out, **tail; + nxt_http_field_t *field, *dup; + nxt_unit_field_t *dst_field; + nxt_fields_iter_t iter, dup_iter; + nxt_unit_request_t *req; req_size = sizeof(nxt_unit_request_t) - + h->method.length + 1 - + h->version.length + 1 - + r->remote.length + 1 - + r->local.length + 1 - + h->server_name.length + 1 - + h->target.length + 1 - + (h->path.start != h->target.start ? h->path.length + 1 : 0); - + + r->method->length + 1 + + r->version.length + 1 + + r->remote->length + 1 + + r->local->length + 1 + + r->server_name.length + 1 + + r->target.length + 1 + + (r->path->start != r->target.start ? r->path->length + 1 : 0); + + content_length = r->content_length_n < 0 ? 0 : r->content_length_n; fields_count = 0; - nxt_list_each(field, h->fields) { + nxt_list_each(field, r->fields) { fields_count++; req_size += field->name_length + prefix->length + 1 @@ -4686,7 +4668,7 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, } out = nxt_port_mmap_get_buf(task, port, - nxt_min(req_size + r->body.preread_size, PORT_MMAP_DATA_SIZE)); + nxt_min(req_size + content_length, PORT_MMAP_DATA_SIZE)); if (nxt_slow_path(out == NULL)) { return NULL; } @@ -4694,57 +4676,60 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, req = (nxt_unit_request_t *) out->mem.free; out->mem.free += req_size; - req->content_length = h->parsed_content_length; + req->content_length = content_length; p = (u_char *) (req->fields + fields_count); nxt_debug(task, "fields_count=%d", (int) fields_count); - req->method_length = h->method.length; + req->method_length = r->method->length; nxt_unit_sptr_set(&req->method, p); - p = nxt_cpymem(p, h->method.start, h->method.length); + p = nxt_cpymem(p, r->method->start, r->method->length); *p++ = '\0'; - req->version_length = h->version.length; + req->version_length = r->version.length; nxt_unit_sptr_set(&req->version, p); - p = nxt_cpymem(p, h->version.start, h->version.length); + p = nxt_cpymem(p, r->version.start, r->version.length); *p++ = '\0'; - req->remote_length = r->remote.length; + req->remote_length = r->remote->address_length; nxt_unit_sptr_set(&req->remote, p); - p = nxt_cpymem(p, r->remote.start, r->remote.length); + p = nxt_cpymem(p, nxt_sockaddr_address(r->remote), + r->remote->address_length); *p++ = '\0'; - req->local_length = r->local.length; + req->local_length = r->local->address_length; nxt_unit_sptr_set(&req->local, p); - p = nxt_cpymem(p, r->local.start, r->local.length); + p = nxt_cpymem(p, nxt_sockaddr_address(r->local), r->local->address_length); *p++ = '\0'; - req->server_name_length = h->server_name.length; + req->tls = (r->tls != NULL); + + req->server_name_length = r->server_name.length; nxt_unit_sptr_set(&req->server_name, p); - p = nxt_cpymem(p, h->server_name.start, h->server_name.length); + p = nxt_cpymem(p, r->server_name.start, r->server_name.length); *p++ = '\0'; target_pos = p; - req->target_length = h->target.length; + req->target_length = (uint32_t) r->target.length; nxt_unit_sptr_set(&req->target, p); - p = nxt_cpymem(p, h->target.start, h->target.length); + p = nxt_cpymem(p, r->target.start, r->target.length); *p++ = '\0'; - req->path_length = h->path.length; - if (h->path.start == h->target.start) { + req->path_length = (uint32_t) r->path->length; + if (r->path->start == r->target.start) { nxt_unit_sptr_set(&req->path, target_pos); } else { nxt_unit_sptr_set(&req->path, p); - p = nxt_cpymem(p, h->path.start, h->path.length); + p = nxt_cpymem(p, r->path->start, r->path->length); *p++ = '\0'; } - req->query_length = h->query.length; - if (h->query.start != NULL) { + req->query_length = r->args != NULL ? (uint32_t) r->args->length : 0; + if (r->args != NULL && r->args->start != NULL) { query_pos = nxt_pointer_to(target_pos, - h->query.start - h->target.start); + r->args->start - r->target.start); nxt_unit_sptr_set(&req->query, query_pos); @@ -4758,7 +4743,7 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, dst_field = req->fields; - for (field = nxt_fields_first(h->fields, &iter); + for (field = nxt_fields_first(r->fields, &iter); field != NULL; field = nxt_fields_next(&iter)) { @@ -4771,13 +4756,13 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, dst_field->name_length = field->name_length + prefix->length; dst_field->value_length = field->value_length; - if (field->value == h->content_length.start) { + if (field == r->content_length) { req->content_length_field = dst_field - req->fields; - } else if (field->value == h->content_type.start) { + } else if (field == r->content_type) { req->content_type_field = dst_field - req->fields; - } else if (field->value == h->cookie.start) { + } else if (field == r->cookie) { req->cookie_field = dst_field - req->fields; } @@ -4846,14 +4831,14 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, dst_field++; } - req->fields_count = dst_field - req->fields; + req->fields_count = (uint32_t) (dst_field - req->fields); nxt_unit_sptr_set(&req->preread_content, out->mem.free); buf = out; tail = &buf->next; - for (b = r->body.buf; b != NULL; b = b->next) { + for (b = r->body; b != NULL; b = b->next) { size = nxt_buf_mem_used_size(&b->mem); pos = b->mem.pos; @@ -4913,8 +4898,8 @@ nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data) nxt_port_t *port; nxt_timer_t *timer; nxt_queue_link_t *lnk; + nxt_http_request_t *r; nxt_req_app_link_t *pending_ra; - nxt_app_parse_ctx_t *ar; nxt_req_conn_link_t *rc; nxt_port_select_state_t state; @@ -4922,8 +4907,8 @@ nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data) nxt_debug(task, "router app timeout"); - ar = nxt_timer_data(timer, nxt_app_parse_ctx_t, timer); - rc = ar->timer_data; + r = nxt_timer_data(timer, nxt_http_request_t, timer); + rc = r->timer_data; app = rc->app; if (app == NULL) { @@ -4994,7 +4979,30 @@ nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data) generate_error: - nxt_http_request_error(task, ar->request, NXT_HTTP_SERVICE_UNAVAILABLE); + nxt_http_request_error(task, r, NXT_HTTP_SERVICE_UNAVAILABLE); nxt_router_rc_unlink(task, rc); } + + +static nxt_int_t +nxt_router_http_request_done(nxt_task_t *task, nxt_http_request_t *r) +{ + r->timer.handler = nxt_router_http_request_release; + nxt_timer_add(task->thread->engine, &r->timer, 0); + + return NXT_OK; +} + + +static void +nxt_router_http_request_release(nxt_task_t *task, void *obj, void *data) +{ + nxt_http_request_t *r; + + nxt_debug(task, "http app release"); + + r = nxt_timer_data(obj, nxt_http_request_t, timer); + + nxt_mp_release(r->mem_pool); +} diff --git a/src/nxt_router.h b/src/nxt_router.h index dec56bd5..d9fbfe05 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -21,6 +21,9 @@ typedef struct nxt_http_routes_s nxt_http_routes_t; typedef struct nxt_router_access_log_s nxt_router_access_log_t; +#define NXT_HTTP_PASS_ERROR ((nxt_http_pass_t *) -1) + + typedef struct { nxt_thread_spinlock_t lock; nxt_queue_t engines; @@ -192,7 +195,7 @@ void nxt_router_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); void nxt_router_access_log_reopen_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); -void nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar, +void nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r, nxt_app_t *app); void nxt_router_app_port_close(nxt_task_t *task, nxt_port_t *port); nxt_app_t *nxt_router_listener_application(nxt_router_temp_conf_t *tmcf, diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index 547c7494..06478f72 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -53,14 +53,13 @@ nxt_runtime_create(nxt_task_t *task) nxt_app_lang_module_t *lang; mp = nxt_mp_create(1024, 128, 256, 32); - if (nxt_slow_path(mp == NULL)) { return NXT_ERROR; } rt = nxt_mp_zget(mp, sizeof(nxt_runtime_t)); if (nxt_slow_path(rt == NULL)) { - return NXT_ERROR; + goto fail; } task->thread->runtime = rt; diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c index a001c730..99cf54b4 100644 --- a/src/nxt_sockaddr.c +++ b/src/nxt_sockaddr.c @@ -197,23 +197,23 @@ nxt_getsockname(nxt_task_t *task, nxt_mp_t *mp, nxt_socket_t s) switch (sockaddr.buf.sa_family) { #if (NXT_INET6) case AF_INET6: - length = NXT_INET6_ADDR_STR_LEN; - break; + length = NXT_INET6_ADDR_STR_LEN; + break; #endif #if (NXT_HAVE_UNIX_DOMAIN) case AF_UNIX: - length = nxt_length("unix:") + socklen; + length = nxt_length("unix:") + socklen; #endif - break; + break; case AF_INET: - length = NXT_INET_ADDR_STR_LEN; - break; + length = NXT_INET_ADDR_STR_LEN; + break; default: - length = 0; - break; + length = 0; + break; } return nxt_sockaddr_create(mp, &sockaddr.buf, socklen, length); diff --git a/src/nxt_socket.h b/src/nxt_socket.h index 42ef6c53..3f00648d 100644 --- a/src/nxt_socket.h +++ b/src/nxt_socket.h @@ -112,9 +112,9 @@ NXT_EXPORT nxt_int_t nxt_socketpair_create(nxt_task_t *task, nxt_socket_t *pair); NXT_EXPORT void nxt_socketpair_close(nxt_task_t *task, nxt_socket_t *pair); NXT_EXPORT ssize_t nxt_socketpair_send(nxt_fd_event_t *ev, nxt_fd_t fd, - nxt_iobuf_t *iob, nxt_uint_t niob); + nxt_iobuf_t *iob, nxt_uint_t niob); NXT_EXPORT ssize_t nxt_socketpair_recv(nxt_fd_event_t *ev, nxt_fd_t *fd, - nxt_iobuf_t *iob, nxt_uint_t niob); + nxt_iobuf_t *iob, nxt_uint_t niob); #define \ diff --git a/src/nxt_socketpair.c b/src/nxt_socketpair.c index a7396b31..10ea562e 100644 --- a/src/nxt_socketpair.c +++ b/src/nxt_socketpair.c @@ -21,9 +21,9 @@ static ssize_t nxt_sendmsg(nxt_socket_t s, nxt_fd_t fd, nxt_iobuf_t *iob, - nxt_uint_t niob); + nxt_uint_t niob); static ssize_t nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, - nxt_uint_t niob); + nxt_uint_t niob); nxt_int_t @@ -94,9 +94,14 @@ nxt_socketpair_send(nxt_fd_event_t *ev, nxt_fd_t fd, nxt_iobuf_t *iob, case NXT_EAGAIN: nxt_debug(ev->task, "sendmsg(%d) not ready", ev->fd); - ev->write_ready = 0; + break; - return NXT_AGAIN; + /* + * Returned (at least on OSX) when trying to send many small messages. + */ + case NXT_ENOBUFS: + nxt_debug(ev->task, "sendmsg(%d) no buffers", ev->fd); + break; case NXT_EINTR: nxt_debug(ev->task, "sendmsg(%d) interrupted", ev->fd); @@ -108,6 +113,10 @@ nxt_socketpair_send(nxt_fd_event_t *ev, nxt_fd_t fd, nxt_iobuf_t *iob, return NXT_ERROR; } + + ev->write_ready = 0; + + return NXT_AGAIN; } } diff --git a/src/nxt_sprintf.c b/src/nxt_sprintf.c index 0b387883..240f47ef 100644 --- a/src/nxt_sprintf.c +++ b/src/nxt_sprintf.c @@ -76,12 +76,12 @@ nxt_sprintf(u_char *buf, u_char *end, const char *fmt, ...) */ typedef struct { - u_char *end; - const u_char *hex; - uint32_t width; - int32_t frac_width; - uint8_t max_width; - u_char padding; + u_char *end; + const u_char *hex; + uint32_t width; + int32_t frac_width; + uint8_t max_width; + u_char padding; } nxt_sprintf_t; diff --git a/src/nxt_timer.h b/src/nxt_timer.h index 4199f0dd..3ccff848 100644 --- a/src/nxt_timer.h +++ b/src/nxt_timer.h @@ -69,7 +69,7 @@ typedef struct { nxt_uint_t mchanges; nxt_uint_t nchanges; - nxt_timer_change_t *changes; + nxt_timer_change_t *changes; } nxt_timers_t; diff --git a/src/nxt_unicode_lowcase.pl b/src/nxt_unicode_lowcase.pl index 974ae23a..abf64965 100644 --- a/src/nxt_unicode_lowcase.pl +++ b/src/nxt_unicode_lowcase.pl @@ -29,9 +29,9 @@ my $last_block_size = $max_lowcase % BLOCK_SIZE + 1; for my $block (sort { $a <=> $b } keys %blocks) { - if ($max_block < $block) { - $max_block = $block; - } + if ($max_block < $block) { + $max_block = $block; + } } diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 6339aec5..88c7fa6a 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -1946,10 +1946,10 @@ nxt_unit_mmap_at(nxt_unit_mmaps_t *mmaps, uint32_t i) while (i + 1 > cap) { if (cap < 16) { - cap = cap * 2; + cap = cap * 2; } else { - cap = cap + cap / 2; + cap = cap + cap / 2; } } diff --git a/src/nxt_unit_request.h b/src/nxt_unit_request.h index 88d569a6..2207cefa 100644 --- a/src/nxt_unit_request.h +++ b/src/nxt_unit_request.h @@ -19,6 +19,7 @@ struct nxt_unit_request_s { uint8_t version_length; uint8_t remote_length; uint8_t local_length; + uint8_t tls; uint32_t server_name_length; uint32_t target_length; uint32_t path_length; diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c index 0b4b31d7..b99d3269 100644 --- a/src/perl/nxt_perl_psgi.c +++ b/src/perl/nxt_perl_psgi.c @@ -656,8 +656,11 @@ nxt_perl_psgi_env_create(PerlInterpreter *my_perl, RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.version"), newRV_noinc((SV *) array_version))); + RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.url_scheme"), - newSVpv("http", 4))); + r->tls ? newSVpv("https", 5) + : newSVpv("http", 4))); + RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.input"), SvREFCNT_inc(nxt_perl_psgi_arg_input.io))); RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.errors"), diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index b2398abe..ab9f7020 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -327,7 +327,6 @@ nxt_ruby_rack_env_create(VALUE arg) rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR)); rb_hash_aset(hash_env, rb_str_new2("rack.version"), version); - rb_hash_aset(hash_env, rb_str_new2("rack.url_scheme"), rb_str_new2("http")); rb_hash_aset(hash_env, rb_str_new2("rack.input"), nxt_ruby_io_input); rb_hash_aset(hash_env, rb_str_new2("rack.errors"), nxt_ruby_io_error); rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), Qfalse); @@ -454,6 +453,9 @@ nxt_ruby_read_request(VALUE hash_env) r->server_name_length); nxt_ruby_add_str(hash_env, NL("SERVER_PORT"), "80", 2); + rb_hash_aset(hash_env, rb_str_new2("rack.url_scheme"), + r->tls ? rb_str_new2("https") : rb_str_new2("http")); + for (i = 0; i < r->fields_count; i++) { f = r->fields + i; |