diff options
Diffstat (limited to '')
42 files changed, 6493 insertions, 4336 deletions
diff --git a/src/go/unit/nxt_cgo_lib.c b/src/go/unit/nxt_cgo_lib.c new file mode 100644 index 00000000..9c080730 --- /dev/null +++ b/src/go/unit/nxt_cgo_lib.c @@ -0,0 +1,207 @@ + +/* + * Copyright (C) Max Romanov + * Copyright (C) NGINX, Inc. + */ + +#include "_cgo_export.h" + +#include <nxt_main.h> +#include <nxt_unit.h> +#include <nxt_unit_request.h> + + +static void nxt_cgo_request_handler(nxt_unit_request_info_t *req); +static nxt_cgo_str_t *nxt_cgo_str_init(nxt_cgo_str_t *dst, + nxt_unit_sptr_t *sptr, uint32_t length); +static int nxt_cgo_add_port(nxt_unit_ctx_t *, nxt_unit_port_t *port); +static void nxt_cgo_remove_port(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id); +static ssize_t nxt_cgo_port_send(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id, + const void *buf, size_t buf_size, const void *oob, size_t oob_size); +static ssize_t nxt_cgo_port_recv(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id, + void *buf, size_t buf_size, void *oob, size_t oob_size); + +int +nxt_cgo_run(uintptr_t handler) +{ + int rc; + nxt_unit_ctx_t *ctx; + nxt_unit_init_t init; + + memset(&init, 0, sizeof(init)); + + init.callbacks.request_handler = nxt_cgo_request_handler; + init.callbacks.add_port = nxt_cgo_add_port; + init.callbacks.remove_port = nxt_cgo_remove_port; + init.callbacks.port_send = nxt_cgo_port_send; + init.callbacks.port_recv = nxt_cgo_port_recv; + + init.data = (void *) handler; + + ctx = nxt_unit_init(&init); + if (nxt_slow_path(ctx == NULL)) { + return NXT_UNIT_ERROR; + } + + rc = nxt_unit_run(ctx); + + nxt_unit_done(ctx); + + return rc; +} + + +static void +nxt_cgo_request_handler(nxt_unit_request_info_t *req) +{ + uint32_t i; + uintptr_t go_req; + nxt_cgo_str_t method, uri, name, value, proto, host, remote_addr; + nxt_unit_field_t *f; + nxt_unit_request_t *r; + + r = req->request; + + go_req = nxt_go_request_create((uintptr_t) req, + nxt_cgo_str_init(&method, &r->method, r->method_length), + nxt_cgo_str_init(&uri, &r->target, r->target_length)); + + nxt_go_request_set_proto(go_req, + nxt_cgo_str_init(&proto, &r->version, r->version_length), 1, 1); + + for (i = 0; i < r->fields_count; i++) { + f = &r->fields[i]; + + nxt_go_request_add_header(go_req, + nxt_cgo_str_init(&name, &f->name, f->name_length), + nxt_cgo_str_init(&value, &f->value, f->value_length)); + + if (f->hash == NXT_UNIT_HASH_HOST) { + host = value; + } + } + + nxt_go_request_set_content_length(go_req, r->content_length); + nxt_go_request_set_host(go_req, &host); + nxt_go_request_set_remote_addr(go_req, + nxt_cgo_str_init(&remote_addr, &r->remote, r->remote_length)); + + nxt_go_request_handler(go_req, (uintptr_t) req->unit->data); +} + + +static nxt_cgo_str_t * +nxt_cgo_str_init(nxt_cgo_str_t *dst, nxt_unit_sptr_t *sptr, uint32_t length) +{ + dst->length = length; + dst->start = nxt_unit_sptr_get(sptr); + + return dst; +} + + +static int +nxt_cgo_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) +{ + nxt_go_add_port(port->id.pid, port->id.id, + port->in_fd, port->out_fd); + + return nxt_unit_add_port(ctx, port); +} + + +static void +nxt_cgo_remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id) +{ + nxt_go_remove_port(port_id->pid, port_id->id); + + nxt_unit_remove_port(ctx, port_id); +} + + +static ssize_t +nxt_cgo_port_send(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, + const void *buf, size_t buf_size, const void *oob, size_t oob_size) +{ + return nxt_go_port_send(port_id->pid, port_id->id, + (void *) buf, buf_size, (void *) oob, oob_size); +} + + +static ssize_t +nxt_cgo_port_recv(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, + void *buf, size_t buf_size, void *oob, size_t oob_size) +{ + return nxt_go_port_recv(port_id->pid, port_id->id, + buf, buf_size, oob, oob_size); +} + + +int +nxt_cgo_response_create(uintptr_t req, int status, int fields, + uint32_t fields_size) +{ + return nxt_unit_response_init((nxt_unit_request_info_t *) req, + status, fields, fields_size); +} + + +int +nxt_cgo_response_add_field(uintptr_t req, uintptr_t name, uint8_t name_len, + uintptr_t value, uint32_t value_len) +{ + return nxt_unit_response_add_field((nxt_unit_request_info_t *) req, + (char *) name, name_len, + (char *) value, value_len); +} + + +int +nxt_cgo_response_send(uintptr_t req) +{ + return nxt_unit_response_send((nxt_unit_request_info_t *) req); +} + + +ssize_t +nxt_cgo_response_write(uintptr_t req, uintptr_t start, uint32_t len) +{ + int rc; + + rc = nxt_unit_response_write((nxt_unit_request_info_t *) req, + (void *) start, len); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return -1; + } + + return len; +} + + +ssize_t +nxt_cgo_request_read(uintptr_t req, uintptr_t dst, uint32_t dst_len) +{ + return nxt_unit_request_read((nxt_unit_request_info_t *) req, + (void *) dst, dst_len); +} + + +int +nxt_cgo_request_close(uintptr_t req) +{ + return 0; +} + + +void +nxt_cgo_request_done(uintptr_t req, int res) +{ + nxt_unit_request_done((nxt_unit_request_info_t *) req, res); +} + + +void +nxt_cgo_warn(uintptr_t msg, uint32_t msg_len) +{ + nxt_unit_warn(NULL, ".*s", (int) msg_len, (char *) msg); +} diff --git a/src/go/unit/nxt_cgo_lib.h b/src/go/unit/nxt_cgo_lib.h new file mode 100644 index 00000000..5317380b --- /dev/null +++ b/src/go/unit/nxt_cgo_lib.h @@ -0,0 +1,40 @@ + +/* + * Copyright (C) Max Romanov + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_CGO_LIB_H_INCLUDED_ +#define _NXT_CGO_LIB_H_INCLUDED_ + + +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> + +typedef struct { + int length; + char *start; +} nxt_cgo_str_t; + +int nxt_cgo_run(uintptr_t handler); + +int nxt_cgo_response_create(uintptr_t req, int code, int fields, + uint32_t fields_size); + +int nxt_cgo_response_add_field(uintptr_t req, uintptr_t name, uint8_t name_len, + uintptr_t value, uint32_t value_len); + +int nxt_cgo_response_send(uintptr_t req); + +ssize_t nxt_cgo_response_write(uintptr_t req, uintptr_t src, uint32_t len); + +ssize_t nxt_cgo_request_read(uintptr_t req, uintptr_t dst, uint32_t dst_len); + +int nxt_cgo_request_close(uintptr_t req); + +void nxt_cgo_request_done(uintptr_t req, int res); + +void nxt_cgo_warn(uintptr_t msg, uint32_t msg_len); + +#endif /* _NXT_CGO_LIB_H_INCLUDED_ */ diff --git a/src/go/unit/nxt_go_array.c b/src/go/unit/nxt_go_array.c deleted file mode 100644 index fea97faf..00000000 --- a/src/go/unit/nxt_go_array.c +++ /dev/null @@ -1,62 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#include <stdint.h> -#include <sys/types.h> - -#include <nxt_main.h> - -#include "nxt_go_array.h" - -void -nxt_go_array_init(nxt_array_t *array, nxt_uint_t n, size_t size) -{ - array->elts = malloc(n * size); - - if (nxt_slow_path(n != 0 && array->elts == NULL)) { - return; - } - - array->nelts = 0; - array->size = size; - array->nalloc = n; - array->mem_pool = NULL; -} - -void * -nxt_go_array_add(nxt_array_t *array) -{ - void *p; - uint32_t nalloc, new_alloc; - - nalloc = array->nalloc; - - if (array->nelts == nalloc) { - - if (nalloc < 16) { - /* Allocate new array twice larger than current. */ - new_alloc = nalloc * 2; - - } else { - /* Allocate new array 1.5 times larger than current. */ - new_alloc = nalloc + nalloc / 2; - } - - p = realloc(array->elts, array->size * new_alloc); - - if (nxt_slow_path(p == NULL)) { - return NULL; - } - - array->elts = p; - array->nalloc = new_alloc; - } - - p = nxt_pointer_to(array->elts, array->size * array->nelts); - array->nelts++; - - return p; -} diff --git a/src/go/unit/nxt_go_array.h b/src/go/unit/nxt_go_array.h deleted file mode 100644 index d96db663..00000000 --- a/src/go/unit/nxt_go_array.h +++ /dev/null @@ -1,36 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#ifndef _NXT_GO_ARRAY_H_INCLUDED_ -#define _NXT_GO_ARRAY_H_INCLUDED_ - - -#include <nxt_array.h> - -void nxt_go_array_init(nxt_array_t *array, nxt_uint_t n, size_t size); - -void *nxt_go_array_add(nxt_array_t *array); - -nxt_inline void * -nxt_go_array_zero_add(nxt_array_t *array) -{ - void *p; - - p = nxt_go_array_add(array); - - if (nxt_fast_path(p != NULL)) { - nxt_memzero(p, array->size); - } - - return p; -} - -#define \ -nxt_go_array_at(array, n) \ - nxt_pointer_to((array)->elts, (array)->size * (n)) - - -#endif /* _NXT_GO_ARRAY_H_INCLUDED_ */ diff --git a/src/go/unit/nxt_go_lib.c b/src/go/unit/nxt_go_lib.c deleted file mode 100644 index eeb7aa50..00000000 --- a/src/go/unit/nxt_go_lib.c +++ /dev/null @@ -1,143 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#include "nxt_go_run_ctx.h" -#include "nxt_go_log.h" -#include "nxt_go_port.h" - -#include "_cgo_export.h" - -#include <nxt_main.h> - -int -nxt_go_response_write(nxt_go_request_t r, uintptr_t buf, size_t len) -{ - nxt_int_t rc; - nxt_go_run_ctx_t *ctx; - - if (nxt_slow_path(r == 0)) { - return 0; - } - - nxt_go_debug("write: %d", (int) len); - - ctx = (nxt_go_run_ctx_t *) r; - rc = nxt_go_ctx_write(ctx, (void *) buf, len); - - return rc == NXT_OK ? len : -1; -} - - -void -nxt_go_response_flush(nxt_go_request_t r) -{ - nxt_go_run_ctx_t *ctx; - - if (nxt_slow_path(r == 0)) { - return; - } - - ctx = (nxt_go_run_ctx_t *) r; - - if (ctx->nwbuf > 0) { - nxt_go_ctx_flush(ctx, 0); - } -} - - -int -nxt_go_request_read(nxt_go_request_t r, uintptr_t dst, size_t dst_len) -{ - size_t res; - nxt_go_run_ctx_t *ctx; - - if (nxt_slow_path(r == 0)) { - return 0; - } - - ctx = (nxt_go_run_ctx_t *) r; - - dst_len = nxt_min(dst_len, ctx->request.body.preread_size); - - res = nxt_go_ctx_read_raw(ctx, (void *) dst, dst_len); - - ctx->request.body.preread_size -= res; - - return res; -} - - -int -nxt_go_request_close(nxt_go_request_t r) -{ - return 0; -} - - -int -nxt_go_request_done(nxt_go_request_t r) -{ - nxt_int_t res; - nxt_go_run_ctx_t *ctx; - nxt_go_msg_t *msg, *b; - - if (nxt_slow_path(r == 0)) { - return 0; - } - - ctx = (nxt_go_run_ctx_t *) r; - - res = nxt_go_ctx_flush(ctx, 1); - - nxt_go_ctx_release_msg(ctx, &ctx->msg); - - msg = ctx->msg.next; - while (msg != NULL) { - nxt_go_ctx_release_msg(ctx, msg); - - b = msg; - msg = b->next; - - free(b); - } - - free(ctx); - - return res; -} - - -void -nxt_go_ready(uint32_t stream) -{ - nxt_port_msg_t port_msg; - - port_msg.stream = stream; - port_msg.pid = getpid(); - port_msg.reply_port = 0; - port_msg.type = _NXT_PORT_MSG_PROCESS_READY; - port_msg.last = 1; - port_msg.mmap = 0; - port_msg.nf = 0; - port_msg.mf = 0; - port_msg.tracking = 0; - - nxt_go_main_send(&port_msg, sizeof(port_msg), NULL, 0); -} - - -nxt_go_request_t -nxt_go_process_port_msg(uintptr_t buf, size_t buf_len, uintptr_t oob, size_t oob_len) -{ - return nxt_go_port_on_read((void *) buf, buf_len, (void *) oob, oob_len); -} - - -const char * -nxt_go_version() -{ - return NXT_VERSION; -} diff --git a/src/go/unit/nxt_go_lib.h b/src/go/unit/nxt_go_lib.h deleted file mode 100644 index 0621a4dc..00000000 --- a/src/go/unit/nxt_go_lib.h +++ /dev/null @@ -1,40 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#ifndef _NXT_GO_LIB_H_INCLUDED_ -#define _NXT_GO_LIB_H_INCLUDED_ - - -#include <stdint.h> -#include <stdlib.h> -#include <sys/types.h> - -typedef struct { - int length; - char *start; -} nxt_go_str_t; - -typedef uintptr_t nxt_go_request_t; - -int nxt_go_response_write(nxt_go_request_t r, uintptr_t buf, size_t len); - -void nxt_go_response_flush(nxt_go_request_t r); - -int nxt_go_request_read(nxt_go_request_t r, uintptr_t dst, size_t dst_len); - -int nxt_go_request_close(nxt_go_request_t r); - -int nxt_go_request_done(nxt_go_request_t r); - -void nxt_go_ready(uint32_t stream); - -nxt_go_request_t nxt_go_process_port_msg(uintptr_t buf, size_t buf_len, - uintptr_t oob, size_t oob_len); - -const char *nxt_go_version(); - - -#endif /* _NXT_GO_LIB_H_INCLUDED_ */ diff --git a/src/go/unit/nxt_go_log.h b/src/go/unit/nxt_go_log.h deleted file mode 100644 index d596cfb3..00000000 --- a/src/go/unit/nxt_go_log.h +++ /dev/null @@ -1,34 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#ifndef _NXT_GO_LOG_H_INCLUDED_ -#define _NXT_GO_LOG_H_INCLUDED_ - - -#include <stdio.h> -#include <pthread.h> - -#include <nxt_auto_config.h> - -#if (NXT_DEBUG) - -#define nxt_go_debug(fmt, ARGS...) \ - fprintf(stderr, "[go debug] " fmt "\n", ##ARGS) - -#else - -#define nxt_go_debug(fmt, ARGS...) - -#endif - -#define nxt_go_warn(fmt, ARGS...) \ - fprintf(stderr, "[go warn] " fmt "\n", ##ARGS) - -#define nxt_go_error(fmt, ARGS...) \ - fprintf(stderr, "[go error] " fmt "\n", ##ARGS) - - -#endif /* _NXT_GO_LOG_H_INCLUDED_ */ diff --git a/src/go/unit/nxt_go_mutex.h b/src/go/unit/nxt_go_mutex.h deleted file mode 100644 index 98bd27f0..00000000 --- a/src/go/unit/nxt_go_mutex.h +++ /dev/null @@ -1,21 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#ifndef _NXT_GO_MUTEX_H_INCLUDED_ -#define _NXT_GO_MUTEX_H_INCLUDED_ - - -#include <pthread.h> - -typedef pthread_mutex_t nxt_go_mutex_t; - -#define nxt_go_mutex_create(mutex) pthread_mutex_init(mutex, NULL) -#define nxt_go_mutex_destroy(mutex) pthread_mutex_destroy(mutex) -#define nxt_go_mutex_lock(mutex) pthread_mutex_lock(mutex) -#define nxt_go_mutex_unlock(mutex) pthread_mutex_unlock(mutex) - - -#endif /* _NXT_GO_MUTEX_H_INCLUDED_ */ diff --git a/src/go/unit/nxt_go_port.c b/src/go/unit/nxt_go_port.c deleted file mode 100644 index f2f1fc5a..00000000 --- a/src/go/unit/nxt_go_port.c +++ /dev/null @@ -1,216 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#include "nxt_go_port.h" -#include "nxt_go_log.h" -#include "nxt_go_process.h" -#include "nxt_go_run_ctx.h" - -#include "_cgo_export.h" - -#include <nxt_main.h> - - -#define nxt_go_str(p) ((nxt_go_str_t *)(p)) - -static nxt_go_request_t -nxt_go_data_handler(nxt_port_msg_t *port_msg, size_t size) -{ - size_t s; - nxt_str_t n, v; - nxt_int_t rc; - nxt_uint_t i; - nxt_go_run_ctx_t *ctx; - nxt_go_request_t r; - nxt_app_request_header_t *h; - - ctx = malloc(sizeof(nxt_go_run_ctx_t) + size); - - memcpy(ctx->port_msg, port_msg, size); - port_msg = ctx->port_msg; - - size -= sizeof(nxt_port_msg_t); - - nxt_go_ctx_init(ctx, port_msg, size); - - if (nxt_slow_path(ctx->cancelled)) { - nxt_go_debug("request already cancelled by router"); - free(ctx); - return 0; - } - - r = (nxt_go_request_t)(ctx); - h = &ctx->request.header; - - nxt_go_ctx_read_str(ctx, &h->method); - nxt_go_ctx_read_str(ctx, &h->target); - nxt_go_ctx_read_str(ctx, &h->path); - - nxt_go_ctx_read_size(ctx, &s); - if (s > 0) { - s--; - h->query.start = h->target.start + s; - h->query.length = h->target.length - s; - - if (h->path.start == NULL) { - h->path.start = h->target.start; - h->path.length = s - 1; - } - } - - if (h->path.start == NULL) { - h->path = h->target; - } - - ctx->go_request = nxt_go_new_request(r, port_msg->stream, - nxt_go_str(&h->method), - nxt_go_str(&h->target)); - - nxt_go_ctx_read_str(ctx, &h->version); - - nxt_go_request_set_proto(ctx->go_request, nxt_go_str(&h->version), - h->version.start[5] - '0', - h->version.start[7] - '0'); - - nxt_go_ctx_read_str(ctx, &ctx->request.remote); - if (ctx->request.remote.start != NULL) { - nxt_go_request_set_remote_addr(ctx->go_request, - nxt_go_str(&ctx->request.remote)); - } - - nxt_go_ctx_read_str(ctx, &h->host); - nxt_go_ctx_read_str(ctx, &h->cookie); - nxt_go_ctx_read_str(ctx, &h->content_type); - nxt_go_ctx_read_str(ctx, &h->content_length); - - if (h->host.start != NULL) { - nxt_go_request_set_host(ctx->go_request, nxt_go_str(&h->host)); - } - - nxt_go_ctx_read_size(ctx, &s); - h->parsed_content_length = s; - - do { - rc = nxt_go_ctx_read_str(ctx, &n); - - if (n.length == 0) { - break; - } - - rc = nxt_go_ctx_read_str(ctx, &v); - nxt_go_request_add_header(ctx->go_request, nxt_go_str(&n), - nxt_go_str(&v)); - } while(1); - - nxt_go_ctx_read_size(ctx, &s); - ctx->request.body.preread_size = s; - - if (h->parsed_content_length > 0) { - nxt_go_request_set_content_length(ctx->go_request, - h->parsed_content_length); - } - - if (ctx->request.body.preread_size < h->parsed_content_length) { - nxt_go_warn("preread_size < content_length"); - } - - return ctx->go_request; -} - -nxt_go_request_t -nxt_go_port_on_read(void *buf, size_t buf_size, void *oob, size_t oob_size) -{ - void *buf_end; - void *payload; - size_t payload_size; - nxt_fd_t fd; - struct cmsghdr *cm; - nxt_port_msg_t *port_msg; - nxt_port_msg_new_port_t *new_port_msg; - - fd = -1; - nxt_go_debug("on read: %d (%d)", (int) buf_size, (int) oob_size); - - cm = oob; - if (oob_size >= CMSG_SPACE(sizeof(int)) - && cm->cmsg_len == CMSG_LEN(sizeof(int)) - && cm->cmsg_level == SOL_SOCKET - && cm->cmsg_type == SCM_RIGHTS) { - - nxt_memcpy(&fd, CMSG_DATA(cm), sizeof(int)); - nxt_go_debug("fd = %d", fd); - } - - port_msg = buf; - if (buf_size < sizeof(nxt_port_msg_t)) { - nxt_go_warn("message too small (%d bytes)", (int) buf_size); - goto fail; - } - - buf_end = ((char *) buf) + buf_size; - - payload = port_msg + 1; - payload_size = buf_size - sizeof(nxt_port_msg_t); - - if (port_msg->mmap) { - nxt_go_debug("using data in shared memory"); - } - - if (port_msg->type >= NXT_PORT_MSG_MAX) { - nxt_go_warn("unknown message type (%d)", (int) port_msg->type); - goto fail; - } - - switch (port_msg->type) { - case _NXT_PORT_MSG_QUIT: - nxt_go_debug("quit"); - - nxt_go_set_quit(); - break; - - case _NXT_PORT_MSG_NEW_PORT: - nxt_go_debug("new port"); - new_port_msg = payload; - - nxt_go_new_port(new_port_msg->pid, new_port_msg->id, new_port_msg->type, - -1, fd); - break; - - case _NXT_PORT_MSG_CHANGE_FILE: - nxt_go_debug("change file"); - break; - - case _NXT_PORT_MSG_MMAP: - nxt_go_debug("mmap"); - - nxt_go_new_incoming_mmap(port_msg->pid, fd); - break; - - case _NXT_PORT_MSG_DATA: - nxt_go_debug("data"); - - return nxt_go_data_handler(port_msg, buf_size); - - case _NXT_PORT_MSG_REMOVE_PID: - nxt_go_debug("remove pid"); - - /* TODO remove all ports for this pid in Go */ - /* TODO remove incoming & outgoing mmaps for this pid */ - break; - - default: - goto fail; - } - - -fail: - - if (fd != -1) { - close(fd); - } - - return 0; -} diff --git a/src/go/unit/nxt_go_port.h b/src/go/unit/nxt_go_port.h deleted file mode 100644 index ce9dbcc3..00000000 --- a/src/go/unit/nxt_go_port.h +++ /dev/null @@ -1,18 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#ifndef _NXT_GO_PORT_H_INCLUDED_ -#define _NXT_GO_PORT_H_INCLUDED_ - - -#include <sys/types.h> -#include "nxt_go_lib.h" - -nxt_go_request_t -nxt_go_port_on_read(void *buf, size_t buf_size, void *oob, size_t oob_size); - - -#endif /* _NXT_GO_PORT_H_INCLUDED_ */ diff --git a/src/go/unit/nxt_go_port_memory.c b/src/go/unit/nxt_go_port_memory.c deleted file mode 100644 index 85b46bed..00000000 --- a/src/go/unit/nxt_go_port_memory.c +++ /dev/null @@ -1,217 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#include "nxt_go_port_memory.h" -#include "nxt_go_process.h" -#include "nxt_go_array.h" -#include "nxt_go_log.h" - -#include "_cgo_export.h" - -#include <nxt_main.h> - -#if (NXT_HAVE_MEMFD_CREATE) - -#include <linux/memfd.h> -#include <unistd.h> -#include <sys/syscall.h> - -#endif - - -static nxt_port_mmap_header_t * -nxt_go_new_port_mmap(nxt_go_process_t *process, nxt_port_id_t id, - nxt_bool_t tracking) -{ - int name_len, rc; - void *mem; - char name[64]; - nxt_fd_t fd; - nxt_free_map_t *free_map; - nxt_port_msg_t port_msg; - nxt_go_port_mmap_t *port_mmap; - nxt_port_mmap_header_t *hdr; - - fd = -1; - - union { - struct cmsghdr cm; - char space[CMSG_SPACE(sizeof(int))]; - } cmsg; - - port_mmap = nxt_go_array_zero_add(&process->outgoing); - if (nxt_slow_path(port_mmap == NULL)) { - nxt_go_warn("failed to add port mmap to outgoing array"); - - return NULL; - } - - name_len = snprintf(name, nxt_length(name), - NXT_SHM_PREFIX "unit.go.%p", name); - -#if (NXT_HAVE_MEMFD_CREATE) - - fd = syscall(SYS_memfd_create, name, MFD_CLOEXEC); - - if (nxt_slow_path(fd == -1)) { - nxt_go_warn("memfd_create(%s) failed %d", name, errno); - - goto remove_fail; - } - - nxt_go_debug("memfd_create(%s): %d", name, fd); - -#elif (NXT_HAVE_SHM_OPEN_ANON) - - fd = shm_open(SHM_ANON, O_RDWR, S_IRUSR | S_IWUSR); - - nxt_go_debug("shm_open(SHM_ANON): %d", fd); - - if (nxt_slow_path(fd == -1)) { - nxt_go_warn("shm_open(SHM_ANON) failed %d", errno); - - goto remove_fail; - } - -#elif (NXT_HAVE_SHM_OPEN) - - /* Just in case. */ - shm_unlink((char *) name); - - fd = shm_open((char *) name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR); - - nxt_go_debug("shm_open(%s): %d", name, fd); - - if (nxt_slow_path(fd == -1)) { - nxt_go_warn("shm_open(%s) failed %d", name, errno); - - goto remove_fail; - } - - if (nxt_slow_path(shm_unlink((char *) name) == -1)) { - nxt_go_warn("shm_unlink(%s) failed %d", name, errno); - } - -#endif - - if (nxt_slow_path(ftruncate(fd, PORT_MMAP_SIZE) == -1)) { - nxt_go_warn("ftruncate() failed %d", errno); - - goto remove_fail; - } - - mem = mmap(NULL, PORT_MMAP_SIZE, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - - if (nxt_slow_path(mem == MAP_FAILED)) { - goto remove_fail; - } - - port_mmap->hdr = mem; - - /* Init segment header. */ - hdr = port_mmap->hdr; - - memset(hdr->free_map, 0xFFU, sizeof(hdr->free_map)); - memset(hdr->free_tracking_map, 0xFFU, sizeof(hdr->free_tracking_map)); - - hdr->id = process->outgoing.nelts - 1; - hdr->src_pid = getpid(); - hdr->dst_pid = process->pid; - hdr->sent_over = id; - - /* Mark first chunk as busy */ - free_map = tracking ? hdr->free_tracking_map : hdr->free_map; - - nxt_port_mmap_set_chunk_busy(free_map, 0); - - /* Mark as busy chunk followed the last available chunk. */ - nxt_port_mmap_set_chunk_busy(hdr->free_map, PORT_MMAP_CHUNK_COUNT); - nxt_port_mmap_set_chunk_busy(hdr->free_tracking_map, PORT_MMAP_CHUNK_COUNT); - - port_msg.stream = 0; - port_msg.pid = getpid(); - port_msg.reply_port = 0; - port_msg.type = _NXT_PORT_MSG_MMAP; - port_msg.last = 1; - port_msg.mmap = 0; - port_msg.nf = 0; - port_msg.mf = 0; - port_msg.tracking = 0; - - cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); - cmsg.cm.cmsg_level = SOL_SOCKET; - cmsg.cm.cmsg_type = SCM_RIGHTS; - - /* - * nxt_memcpy() is used instead of simple - * *(int *) CMSG_DATA(&cmsg.cm) = fd; - * because GCC 4.4 with -O2/3/s optimization may issue a warning: - * dereferencing type-punned pointer will break strict-aliasing rules - * - * Fortunately, GCC with -O1 compiles this nxt_memcpy() - * in the same simple assignment as in the code above. - */ - memcpy(CMSG_DATA(&cmsg.cm), &fd, sizeof(int)); - - rc = nxt_go_port_send(hdr->dst_pid, id, &port_msg, sizeof(port_msg), - &cmsg, sizeof(cmsg)); - - nxt_go_debug("new mmap #%d created for %d -> %d", - (int) hdr->id, (int) getpid(), (int) process->pid); - - close(fd); - - return hdr; - -remove_fail: - - if (fd != -1) { - close(fd); - } - - process->outgoing.nelts--; - - return NULL; -} - -nxt_port_mmap_header_t * -nxt_go_port_mmap_get(nxt_go_process_t *process, nxt_port_id_t port_id, - nxt_chunk_id_t *c, nxt_bool_t tracking) -{ - nxt_free_map_t *free_map; - nxt_go_port_mmap_t *port_mmap; - nxt_go_port_mmap_t *end_port_mmap; - nxt_port_mmap_header_t *hdr; - - nxt_go_mutex_lock(&process->outgoing_mutex); - - port_mmap = process->outgoing.elts; - end_port_mmap = port_mmap + process->outgoing.nelts; - - for (; port_mmap < end_port_mmap; port_mmap++) - { - hdr = port_mmap->hdr; - - if (hdr->sent_over != 0xFFFFu && hdr->sent_over != port_id) { - continue; - } - - free_map = tracking ? hdr->free_tracking_map : hdr->free_map; - - if (nxt_port_mmap_get_free_chunk(free_map, c)) { - goto unlock_return; - } - } - - hdr = nxt_go_new_port_mmap(process, port_id, tracking); - -unlock_return: - - nxt_go_mutex_unlock(&process->outgoing_mutex); - - return hdr; -} diff --git a/src/go/unit/nxt_go_port_memory.h b/src/go/unit/nxt_go_port_memory.h deleted file mode 100644 index 558eb68f..00000000 --- a/src/go/unit/nxt_go_port_memory.h +++ /dev/null @@ -1,30 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#ifndef _NXT_GO_PORT_MEMORY_H_INCLUDED_ -#define _NXT_GO_PORT_MEMORY_H_INCLUDED_ - - -#include <nxt_main.h> -#include <nxt_port_memory_int.h> - -#ifndef _NXT_GO_PROCESS_T_DEFINED_ -#define _NXT_GO_PROCESS_T_DEFINED_ -typedef struct nxt_go_process_s nxt_go_process_t; -#endif - -typedef struct nxt_go_port_mmap_s nxt_go_port_mmap_t; - -struct nxt_go_port_mmap_s { - nxt_port_mmap_header_t *hdr; -}; - -struct nxt_port_mmap_header_s * -nxt_go_port_mmap_get(nxt_go_process_t *process, nxt_port_id_t port_id, - nxt_chunk_id_t *c, nxt_bool_t tracking); - - -#endif /* _NXT_GO_PORT_MEMORY_H_INCLUDED_ */ diff --git a/src/go/unit/nxt_go_process.c b/src/go/unit/nxt_go_process.c deleted file mode 100644 index bb2d279c..00000000 --- a/src/go/unit/nxt_go_process.c +++ /dev/null @@ -1,148 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#include "nxt_go_process.h" -#include "nxt_go_array.h" -#include "nxt_go_mutex.h" -#include "nxt_go_log.h" -#include "nxt_go_port_memory.h" - -#include <nxt_port_memory_int.h> - - -static nxt_array_t processes; /* of nxt_go_process_t */ - -static nxt_go_process_t * -nxt_go_find_process(nxt_pid_t pid, uint32_t *pos) -{ - uint32_t l, r, i; - nxt_go_process_t *process; - - if (nxt_slow_path(processes.size == 0)) { - nxt_go_array_init(&processes, 1, sizeof(nxt_go_process_t)); - } - - l = 0; - r = processes.nelts; - i = (l + r) / 2; - - while (r > l) { - process = nxt_go_array_at(&processes, i); - - nxt_go_debug("compare process #%d (%p) at %d", - (int) process->pid, process, (int) i); - - if (pid == process->pid) { - nxt_go_debug("found process %d at %d", (int) pid, (int) i); - - if (pos != NULL) { - *pos = i; - } - - return process; - } - - if (pid < process->pid) { - r = i; - - } else { - l = i + 1; - } - - i = (l + r) / 2; - } - - if (pos != NULL) { - *pos = i; - } - - nxt_go_debug("process %d not found, best pos %d", (int) pid, (int) i); - - return NULL; -} - - -nxt_go_process_t * -nxt_go_get_process(nxt_pid_t pid) -{ - uint32_t pos; - nxt_go_process_t *process; - - process = nxt_go_find_process(pid, &pos); - - if (process == NULL) { - nxt_go_array_add(&processes); - process = nxt_go_array_at(&processes, pos); - - nxt_go_debug("init process #%d (%p) at %d", - (int) pid, process, (int) pos); - - if (pos < processes.nelts - 1) { - memmove(process + 1, process, - processes.size * (processes.nelts - 1 - pos)); - } - - process->pid = pid; - nxt_go_mutex_create(&process->incoming_mutex); - nxt_go_array_init(&process->incoming, 1, sizeof(nxt_go_port_mmap_t)); - nxt_go_mutex_create(&process->outgoing_mutex); - nxt_go_array_init(&process->outgoing, 1, sizeof(nxt_go_port_mmap_t)); - } - - return process; -} - - -void -nxt_go_new_incoming_mmap(nxt_pid_t pid, nxt_fd_t fd) -{ - void *mem; - struct stat mmap_stat; - nxt_go_process_t *process; - nxt_go_port_mmap_t *port_mmap; - - process = nxt_go_get_process(pid); - - nxt_go_debug("got new mmap fd #%d from process %d", - (int) fd, (int) pid); - - if (fstat(fd, &mmap_stat) == -1) { - nxt_go_warn("fstat(%d) failed %d", (int) fd, errno); - - return; - } - - nxt_go_mutex_lock(&process->incoming_mutex); - - port_mmap = nxt_go_array_zero_add(&process->incoming); - if (nxt_slow_path(port_mmap == NULL)) { - nxt_go_warn("failed to add mmap to incoming array"); - - goto fail; - } - - mem = mmap(NULL, mmap_stat.st_size, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - - if (nxt_slow_path(mem == MAP_FAILED)) { - nxt_go_warn("mmap() failed %d", errno); - - goto fail; - } - - port_mmap->hdr = mem; - - if (nxt_slow_path(port_mmap->hdr->id != process->incoming.nelts - 1)) { - nxt_go_warn("port mmap id mismatch (%d != %d)", - port_mmap->hdr->id, process->incoming.nelts - 1); - } - - port_mmap->hdr->sent_over = 0xFFFFu; - -fail: - - nxt_go_mutex_unlock(&process->incoming_mutex); -} diff --git a/src/go/unit/nxt_go_process.h b/src/go/unit/nxt_go_process.h deleted file mode 100644 index 197de1e9..00000000 --- a/src/go/unit/nxt_go_process.h +++ /dev/null @@ -1,33 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#ifndef _NXT_GO_PROCESS_H_INCLUDED_ -#define _NXT_GO_PROCESS_H_INCLUDED_ - - -#include <nxt_main.h> -#include "nxt_go_mutex.h" - -#ifndef _NXT_GO_PROCESS_T_DEFINED_ -#define _NXT_GO_PROCESS_T_DEFINED_ -typedef struct nxt_go_process_s nxt_go_process_t; -#endif - -struct nxt_go_process_s { - nxt_pid_t pid; - nxt_go_mutex_t incoming_mutex; - nxt_array_t incoming; /* of nxt_go_port_mmap_t */ - nxt_go_mutex_t outgoing_mutex; - nxt_array_t outgoing; /* of nxt_go_port_mmap_t */ -}; - -nxt_go_process_t *nxt_go_get_process(nxt_pid_t pid); - -void nxt_go_new_incoming_mmap(nxt_pid_t pid, nxt_fd_t fd); - - -#endif /* _NXT_GO_PROCESS_H_INCLUDED_ */ - diff --git a/src/go/unit/nxt_go_run_ctx.c b/src/go/unit/nxt_go_run_ctx.c deleted file mode 100644 index 6aa5db77..00000000 --- a/src/go/unit/nxt_go_run_ctx.c +++ /dev/null @@ -1,554 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#include "nxt_go_run_ctx.h" -#include "nxt_go_log.h" -#include "nxt_go_process.h" -#include "nxt_go_array.h" -#include "nxt_go_mutex.h" -#include "nxt_go_port_memory.h" - -#include "_cgo_export.h" - -#include <nxt_port_memory_int.h> -#include <nxt_main.h> - - -static nxt_int_t -nxt_go_ctx_msg_rbuf(nxt_go_run_ctx_t *ctx, nxt_go_msg_t *msg, nxt_buf_t *buf, - uint32_t n) -{ - size_t nchunks; - nxt_go_port_mmap_t *port_mmap; - nxt_port_mmap_msg_t *mmap_msg; - - if (nxt_slow_path(msg->mmap_msg == NULL)) { - if (n > 0) { - nxt_go_warn("failed to get plain buf #%d", (int) n); - - return NXT_ERROR; - } - - buf->mem.start = (u_char *) (msg->port_msg + 1); - buf->mem.pos = buf->mem.start; - buf->mem.end = buf->mem.start + msg->raw_size; - buf->mem.free = buf->mem.end; - - return NXT_OK; - } - - mmap_msg = msg->mmap_msg + n; - if (nxt_slow_path(mmap_msg >= msg->end)) { - nxt_go_warn("no more data in shm #%d", (int) n); - - return NXT_ERROR; - } - - if (nxt_slow_path(mmap_msg->mmap_id >= ctx->process->incoming.nelts)) { - nxt_go_warn("incoming shared memory segment #%d not found " - "for process %d", (int) mmap_msg->mmap_id, - (int) msg->port_msg->pid); - - return NXT_ERROR; - } - - nxt_go_mutex_lock(&ctx->process->incoming_mutex); - - port_mmap = nxt_go_array_at(&ctx->process->incoming, mmap_msg->mmap_id); - buf->mem.start = nxt_port_mmap_chunk_start(port_mmap->hdr, - mmap_msg->chunk_id); - buf->mem.pos = buf->mem.start; - buf->mem.free = buf->mem.start + mmap_msg->size; - - nxt_go_mutex_unlock(&ctx->process->incoming_mutex); - - nchunks = mmap_msg->size / PORT_MMAP_CHUNK_SIZE; - if ((mmap_msg->size % PORT_MMAP_CHUNK_SIZE) != 0) { - nchunks++; - } - - buf->mem.end = buf->mem.start + nchunks * PORT_MMAP_CHUNK_SIZE; - - return NXT_OK; -} - -static nxt_int_t -nxt_go_ctx_init_rbuf(nxt_go_run_ctx_t *ctx) -{ - return nxt_go_ctx_msg_rbuf(ctx, &ctx->msg, &ctx->rbuf, ctx->nrbuf); -} - -static void -nxt_go_ctx_init_msg(nxt_go_msg_t *msg, nxt_port_msg_t *port_msg, - size_t payload_size) -{ - void *data, *end; - nxt_port_mmap_msg_t *mmap_msg; - - memset(msg, 0, sizeof(nxt_go_msg_t)); - - msg->port_msg = port_msg; - msg->raw_size = payload_size; - - data = port_msg + 1; - end = nxt_pointer_to(data, payload_size); - - if (port_msg->tracking) { - msg->tracking = data; - data = msg->tracking + 1; - } - - if (nxt_fast_path(port_msg->mmap != 0)) { - msg->mmap_msg = data; - msg->end = end; - - mmap_msg = msg->mmap_msg; - while(mmap_msg < msg->end) { - msg->data_size += mmap_msg->size; - mmap_msg += 1; - } - - } else { - msg->mmap_msg = NULL; - msg->end = NULL; - msg->data_size = payload_size; - } -} - -void -nxt_go_ctx_release_msg(nxt_go_run_ctx_t *ctx, nxt_go_msg_t *msg) -{ - u_char *b, *e; - nxt_chunk_id_t c; - nxt_go_port_mmap_t *port_mmap; - nxt_port_mmap_msg_t *mmap_msg, *end; - - if (nxt_slow_path(msg->mmap_msg == NULL)) { - return; - } - - mmap_msg = msg->mmap_msg; - end = msg->end; - - nxt_go_mutex_lock(&ctx->process->incoming_mutex); - - for (; mmap_msg < end; mmap_msg++ ) { - port_mmap = nxt_go_array_at(&ctx->process->incoming, mmap_msg->mmap_id); - - c = mmap_msg->chunk_id; - b = nxt_port_mmap_chunk_start(port_mmap->hdr, c); - e = b + mmap_msg->size; - - while (b < e) { - nxt_port_mmap_set_chunk_free(port_mmap->hdr->free_map, c); - - b += PORT_MMAP_CHUNK_SIZE; - c++; - } - } - - nxt_go_mutex_unlock(&ctx->process->incoming_mutex); -} - - -nxt_int_t -nxt_go_ctx_init(nxt_go_run_ctx_t *ctx, nxt_port_msg_t *port_msg, - size_t payload_size) -{ - nxt_atomic_t *val; - nxt_go_port_mmap_t *port_mmap; - nxt_port_mmap_tracking_msg_t *tracking; - - memset(ctx, 0, sizeof(nxt_go_run_ctx_t)); - - ctx->process = nxt_go_get_process(port_msg->pid); - if (nxt_slow_path(ctx->process == NULL)) { - nxt_go_warn("failed to get process %d", port_msg->pid); - - return NXT_ERROR; - } - - nxt_go_ctx_init_msg(&ctx->msg, port_msg, payload_size); - - if (ctx->msg.tracking != NULL) { - tracking = ctx->msg.tracking; - - if (nxt_slow_path(tracking->mmap_id >= ctx->process->incoming.nelts)) { - nxt_go_warn("incoming shared memory segment #%d not found " - "for process %d", (int) tracking->mmap_id, - (int) port_msg->pid); - - return NXT_ERROR; - } - - nxt_go_mutex_lock(&ctx->process->incoming_mutex); - - port_mmap = nxt_go_array_at(&ctx->process->incoming, tracking->mmap_id); - - nxt_go_mutex_unlock(&ctx->process->incoming_mutex); - - val = port_mmap->hdr->tracking + tracking->tracking_id; - - ctx->cancelled = nxt_atomic_cmp_set(val, port_msg->stream, 0) == 0; - - if (ctx->cancelled) { - nxt_port_mmap_set_chunk_free(port_mmap->hdr->free_tracking_map, - tracking->tracking_id); - - return NXT_OK; - } - } - - ctx->msg_last = &ctx->msg; - - ctx->wport_msg.stream = port_msg->stream; - ctx->wport_msg.pid = getpid(); - ctx->wport_msg.type = _NXT_PORT_MSG_DATA; - ctx->wport_msg.mmap = 1; - - ctx->wmmap_msg = (nxt_port_mmap_msg_t *) ( &ctx->wport_msg + 1 ); - - return nxt_go_ctx_init_rbuf(ctx); -} - - -nxt_int_t -nxt_go_ctx_flush(nxt_go_run_ctx_t *ctx, int last) -{ - int i; - nxt_int_t rc; - - if (last != 0) { - ctx->wport_msg.last = 1; - } - - nxt_go_debug("flush buffers (%d)", last); - - for (i = 0; i < ctx->nwbuf; i++) { - nxt_port_mmap_msg_t *m = ctx->wmmap_msg + i; - - nxt_go_debug(" mmap_msg[%d]={%d, %d, %d}", i, - m->mmap_id, m->chunk_id, m->size); - } - - rc = nxt_go_port_send(ctx->msg.port_msg->pid, ctx->msg.port_msg->reply_port, - &ctx->wport_msg, sizeof(nxt_port_msg_t) + - ctx->nwbuf * sizeof(nxt_port_mmap_msg_t), NULL, 0); - - nxt_go_debug(" port send res = %d", rc); - - ctx->nwbuf = 0; - - memset(&ctx->wbuf, 0, sizeof(ctx->wbuf)); - - return rc; -} - - -nxt_buf_t * -nxt_go_port_mmap_get_buf(nxt_go_run_ctx_t *ctx, size_t size) -{ - size_t nchunks; - nxt_buf_t *buf; - nxt_chunk_id_t c; - nxt_go_port_mmap_t *port_mmap; - nxt_port_mmap_msg_t *mmap_msg; - nxt_port_mmap_header_t *hdr; - - c = 0; - - buf = &ctx->wbuf; - - hdr = nxt_go_port_mmap_get(ctx->process, ctx->msg.port_msg->reply_port, &c, - 0); - if (nxt_slow_path(hdr == NULL)) { - nxt_go_warn("failed to get port_mmap"); - - return NULL; - } - - buf->mem.start = nxt_port_mmap_chunk_start(hdr, c); - buf->mem.pos = buf->mem.start; - buf->mem.free = buf->mem.start; - buf->mem.end = buf->mem.start + PORT_MMAP_CHUNK_SIZE; - - buf->parent = hdr; - - mmap_msg = ctx->wmmap_msg + ctx->nwbuf; - mmap_msg->mmap_id = hdr->id; - mmap_msg->chunk_id = c; - mmap_msg->size = 0; - - nchunks = size / PORT_MMAP_CHUNK_SIZE; - if ((size % PORT_MMAP_CHUNK_SIZE) != 0 || nchunks == 0) { - nchunks++; - } - - c++; - nchunks--; - - /* Try to acquire as much chunks as required. */ - while (nchunks > 0) { - - if (nxt_port_mmap_chk_set_chunk_busy(hdr->free_map, c) == 0) { - break; - } - - buf->mem.end += PORT_MMAP_CHUNK_SIZE; - c++; - nchunks--; - } - - ctx->nwbuf++; - - return buf; -} - - -nxt_int_t -nxt_go_port_mmap_increase_buf(nxt_buf_t *b, size_t size, size_t min_size) -{ - size_t nchunks, free_size; - nxt_chunk_id_t c, start; - nxt_port_mmap_header_t *hdr; - - free_size = nxt_buf_mem_free_size(&b->mem); - - if (nxt_slow_path(size <= free_size)) { - return NXT_OK; - } - - hdr = b->parent; - - start = nxt_port_mmap_chunk_id(hdr, b->mem.end); - - size -= free_size; - - nchunks = size / PORT_MMAP_CHUNK_SIZE; - if ((size % PORT_MMAP_CHUNK_SIZE) != 0 || nchunks == 0) { - nchunks++; - } - - c = start; - - /* Try to acquire as much chunks as required. */ - while (nchunks > 0) { - - if (nxt_port_mmap_chk_set_chunk_busy(hdr->free_map, c) == 0) { - break; - } - - c++; - nchunks--; - } - - if (nchunks != 0 - && min_size > free_size + PORT_MMAP_CHUNK_SIZE * (c - start)) - { - c--; - while (c >= start) { - nxt_port_mmap_set_chunk_free(hdr->free_map, c); - c--; - } - - return NXT_ERROR; - - } else { - b->mem.end += PORT_MMAP_CHUNK_SIZE * (c - start); - - return NXT_OK; - } -} - - -nxt_int_t -nxt_go_ctx_write(nxt_go_run_ctx_t *ctx, void *data, size_t len) -{ - size_t free_size, copy_size; - nxt_buf_t *buf; - nxt_port_mmap_msg_t *mmap_msg; - - buf = &ctx->wbuf; - - while (len > 0) { - if (ctx->nwbuf == 0) { - buf = nxt_go_port_mmap_get_buf(ctx, len); - - if (nxt_slow_path(buf == NULL)) { - return NXT_ERROR; - } - } - - do { - free_size = nxt_buf_mem_free_size(&buf->mem); - - if (free_size > 0) { - copy_size = nxt_min(free_size, len); - - buf->mem.free = nxt_cpymem(buf->mem.free, data, copy_size); - - mmap_msg = ctx->wmmap_msg + ctx->nwbuf - 1; - mmap_msg->size += copy_size; - - len -= copy_size; - data = nxt_pointer_to(data, copy_size); - - if (len == 0) { - return NXT_OK; - } - } - - } while (nxt_go_port_mmap_increase_buf(buf, len, 1) == NXT_OK); - - if (ctx->nwbuf >= 8) { - nxt_go_ctx_flush(ctx, 0); - } - - buf = nxt_go_port_mmap_get_buf(ctx, len); - - if (nxt_slow_path(buf == NULL)) { - return NXT_ERROR; - } - } - - return NXT_OK; -} - - -static nxt_int_t -nxt_go_ctx_read_size_(nxt_go_run_ctx_t *ctx, size_t *size) -{ - nxt_buf_t *buf; - nxt_int_t rc; - - do { - buf = &ctx->rbuf; - - if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) < 1)) { - if (nxt_fast_path(nxt_buf_mem_used_size(&buf->mem) == 0)) { - - ctx->nrbuf++; - rc = nxt_go_ctx_init_rbuf(ctx); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_go_warn("read size: init rbuf failed"); - return rc; - } - - continue; - } - nxt_go_warn("read size: used size is not 0"); - return NXT_ERROR; - } - - if (buf->mem.pos[0] >= 128) { - if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) < 4)) { - nxt_go_warn("read size: used size < 4"); - return NXT_ERROR; - } - } - - break; - } while (1); - - buf->mem.pos = nxt_app_msg_read_length(buf->mem.pos, size); - - return NXT_OK; -} - - -nxt_int_t -nxt_go_ctx_read_size(nxt_go_run_ctx_t *ctx, size_t *size) -{ - nxt_int_t rc; - - rc = nxt_go_ctx_read_size_(ctx, size); - - if (nxt_fast_path(rc == NXT_OK)) { - nxt_go_debug("read_size: %d", (int)*size); - } - - return rc; -} - - -nxt_int_t -nxt_go_ctx_read_str(nxt_go_run_ctx_t *ctx, nxt_str_t *str) -{ - size_t length; - nxt_int_t rc; - nxt_buf_t *buf; - - rc = nxt_go_ctx_read_size_(ctx, &length); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_go_warn("read str: read size failed"); - return rc; - } - - buf = &ctx->rbuf; - - if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) < (intptr_t) length)) { - nxt_go_warn("read str: used size too small %d < %d", - (int) nxt_buf_mem_used_size(&buf->mem), (int) length); - return NXT_ERROR; - } - - if (length > 0) { - str->start = buf->mem.pos; - str->length = length - 1; - - buf->mem.pos += length; - - nxt_go_debug("read_str: %d %.*s", - (int) length - 1, (int) length - 1, str->start); - - } else { - str->start = NULL; - str->length = 0; - - nxt_go_debug("read_str: NULL"); - } - - return NXT_OK; -} - - -size_t -nxt_go_ctx_read_raw(nxt_go_run_ctx_t *ctx, void *dst, size_t size) -{ - size_t res, read_size; - nxt_int_t rc; - nxt_buf_t *buf; - - res = 0; - - while (size > 0) { - buf = &ctx->rbuf; - - if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) == 0)) { - ctx->nrbuf++; - rc = nxt_go_ctx_init_rbuf(ctx); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_go_warn("read raw: init rbuf failed"); - return res; - } - - continue; - } - - read_size = nxt_buf_mem_used_size(&buf->mem); - read_size = nxt_min(read_size, size); - - dst = nxt_cpymem(dst, buf->mem.pos, read_size); - - size -= read_size; - buf->mem.pos += read_size; - res += read_size; - } - - nxt_go_debug("read_raw: %d", (int) res); - - return res; -} diff --git a/src/go/unit/nxt_go_run_ctx.h b/src/go/unit/nxt_go_run_ctx.h deleted file mode 100644 index 42163295..00000000 --- a/src/go/unit/nxt_go_run_ctx.h +++ /dev/null @@ -1,78 +0,0 @@ - -/* - * Copyright (C) Max Romanov - * Copyright (C) NGINX, Inc. - */ - -#ifndef _NXT_GO_RUN_CTX_H_INCLUDED_ -#define _NXT_GO_RUN_CTX_H_INCLUDED_ - - -#include <nxt_main.h> -#include <nxt_router.h> -#include <nxt_port_memory_int.h> - -#ifndef _NXT_GO_PROCESS_T_DEFINED_ -#define _NXT_GO_PROCESS_T_DEFINED_ -typedef struct nxt_go_process_s nxt_go_process_t; -#endif - -typedef struct nxt_go_msg_s nxt_go_msg_t; - -struct nxt_go_msg_s { - off_t start_offset; - - nxt_port_msg_t *port_msg; - size_t raw_size; - size_t data_size; - - nxt_port_mmap_msg_t *mmap_msg; - nxt_port_mmap_msg_t *end; - - nxt_port_mmap_tracking_msg_t *tracking; - - nxt_go_msg_t *next; -}; - - -typedef struct { - nxt_go_msg_t msg; - - nxt_go_process_t *process; - nxt_port_mmap_msg_t *wmmap_msg; - nxt_bool_t cancelled; - - uint32_t nrbuf; - nxt_buf_t rbuf; - - uint32_t nwbuf; - nxt_buf_t wbuf; - nxt_port_msg_t wport_msg; - char wmmap_msg_buf[ sizeof(nxt_port_mmap_msg_t) * 8 ]; - - nxt_app_request_t request; - uintptr_t go_request; - - nxt_go_msg_t *msg_last; - - nxt_port_msg_t port_msg[]; -} nxt_go_run_ctx_t; - - -void nxt_go_ctx_release_msg(nxt_go_run_ctx_t *ctx, nxt_go_msg_t *msg); - -nxt_int_t nxt_go_ctx_init(nxt_go_run_ctx_t *ctx, nxt_port_msg_t *port_msg, - size_t payload_size); - -nxt_int_t nxt_go_ctx_flush(nxt_go_run_ctx_t *ctx, int last); - -nxt_int_t nxt_go_ctx_write(nxt_go_run_ctx_t *ctx, void *data, size_t len); - -nxt_int_t nxt_go_ctx_read_size(nxt_go_run_ctx_t *ctx, size_t *size); - -nxt_int_t nxt_go_ctx_read_str(nxt_go_run_ctx_t *ctx, nxt_str_t *str); - -size_t nxt_go_ctx_read_raw(nxt_go_run_ctx_t *ctx, void *dst, size_t size); - - -#endif /* _NXT_GO_RUN_CTX_H_INCLUDED_ */ diff --git a/src/go/unit/port.go b/src/go/unit/port.go index 51733176..f716c9ec 100644 --- a/src/go/unit/port.go +++ b/src/go/unit/port.go @@ -6,14 +6,12 @@ package unit /* -#include "nxt_go_lib.h" -#include "nxt_process_type.h" +#include "nxt_cgo_lib.h" */ import "C" import ( "net" - "net/http" "os" "sync" "unsafe" @@ -26,7 +24,6 @@ type port_key struct { type port struct { key port_key - t int rcv *net.UnixConn snd *net.UnixConn } @@ -34,7 +31,6 @@ type port struct { type port_registry struct { sync.RWMutex m map[port_key]*port - t [C.NXT_PROCESS_MAX]*port } var port_registry_ port_registry @@ -47,42 +43,14 @@ func find_port(key port_key) *port { return res } -func remove_by_pid(pid int) { - port_registry_.Lock() - if port_registry_.m != nil { - for k, p := range port_registry_.m { - if k.pid == pid { - if port_registry_.t[p.t] == p { - port_registry_.t[p.t] = nil - } - - delete(port_registry_.m, k) - } - } - } - - port_registry_.Unlock() -} - -func main_port() *port { - port_registry_.RLock() - res := port_registry_.t[C.NXT_PROCESS_MAIN] - port_registry_.RUnlock() - - return res -} - func add_port(p *port) { - nxt_go_debug("add_port: %d:%d", p.key.pid, p.key.id); - port_registry_.Lock() if port_registry_.m == nil { port_registry_.m = make(map[port_key]*port) } port_registry_.m[p.key] = p - port_registry_.t[p.t] = p port_registry_.Unlock() } @@ -120,14 +88,38 @@ func getUnixConn(fd int) *net.UnixConn { return uc } -//export nxt_go_new_port -func nxt_go_new_port(pid C.int, id C.int, t C.int, rcv C.int, snd C.int) { - new_port(int(pid), int(id), int(t), int(rcv), int(snd)) +//export nxt_go_add_port +func nxt_go_add_port(pid C.int, id C.int, rcv C.int, snd C.int) { + p := &port{ + key: port_key{ + pid: int(pid), + id: int(id), + }, + rcv: getUnixConn(int(rcv)), + snd: getUnixConn(int(snd)), + } + + add_port(p) +} + +//export nxt_go_remove_port +func nxt_go_remove_port(pid C.int, id C.int) { + key := port_key{ + pid: int(pid), + id: int(id), + } + + port_registry_.Lock() + if port_registry_.m != nil { + delete(port_registry_.m, key) + } + + port_registry_.Unlock() } //export nxt_go_port_send func nxt_go_port_send(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int, - oob unsafe.Pointer, oob_size C.int) C.int { + oob unsafe.Pointer, oob_size C.int) C.ssize_t { key := port_key{ pid: int(pid), @@ -141,83 +133,38 @@ func nxt_go_port_send(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int, return 0 } - n, oobn, err := p.snd.WriteMsgUnix(C.GoBytes(buf, buf_size), - C.GoBytes(oob, oob_size), nil) + n, oobn, err := p.snd.WriteMsgUnix(GoBytes(buf, buf_size), + GoBytes(oob, oob_size), nil) if err != nil { nxt_go_warn("write result %d (%d), %s", n, oobn, err) } - return C.int(n) - + return C.ssize_t(n) } -//export nxt_go_main_send -func nxt_go_main_send(buf unsafe.Pointer, buf_size C.int, oob unsafe.Pointer, - oob_size C.int) C.int { +//export nxt_go_port_recv +func nxt_go_port_recv(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int, + oob unsafe.Pointer, oob_size C.int) C.ssize_t { + + key := port_key{ + pid: int(pid), + id: int(id), + } - p := main_port() + p := find_port(key) if p == nil { + nxt_go_warn("port %d:%d not found", pid, id) return 0 } - n, oobn, err := p.snd.WriteMsgUnix(C.GoBytes(buf, buf_size), - C.GoBytes(oob, oob_size), nil) + n, oobn, _, _, err := p.rcv.ReadMsgUnix(GoBytes(buf, buf_size), + GoBytes(oob, oob_size)) if err != nil { nxt_go_warn("write result %d (%d), %s", n, oobn, err) } - return C.int(n) -} - -func new_port(pid int, id int, t int, rcv int, snd int) *port { - p := &port{ - key: port_key{ - pid: pid, - id: id, - }, - t: t, - rcv: getUnixConn(rcv), - snd: getUnixConn(snd), - } - - add_port(p) - - return p -} - -func (p *port) read(handler http.Handler) error { - var buf [16384]byte - var oob [1024]byte - var c_buf, c_oob cbuf - - n, oobn, _, _, err := p.rcv.ReadMsgUnix(buf[:], oob[:]) - - if err != nil { - return err - } - - c_buf.init(buf[:n]) - c_oob.init(oob[:oobn]) - - go_req := C.nxt_go_process_port_msg(c_buf.b, c_buf.s, c_oob.b, c_oob.s) - - if go_req == 0 { - return nil - } - - r := get_request(go_req) - - go func(r *request) { - if handler == nil { - handler = http.DefaultServeMux - } - - handler.ServeHTTP(r.response(), &r.req) - r.done() - }(r) - - return nil + return C.ssize_t(n) } diff --git a/src/go/unit/request.go b/src/go/unit/request.go index 7d99edaa..829a2c64 100644 --- a/src/go/unit/request.go +++ b/src/go/unit/request.go @@ -6,7 +6,7 @@ package unit /* -#include "nxt_go_lib.h" +#include "nxt_cgo_lib.h" */ import "C" @@ -20,15 +20,11 @@ import ( type request struct { req http.Request resp *response - c_req C.nxt_go_request_t - id C.uint32_t + c_req C.uintptr_t } func (r *request) Read(p []byte) (n int, err error) { - c := C.size_t(len(p)) - b := C.uintptr_t(uintptr(unsafe.Pointer(&p[0]))) - - res := C.nxt_go_request_read(r.c_req, b, c) + res := C.nxt_cgo_request_read(r.c_req, buf_ref(p), C.uint32_t(len(p))) if res == 0 && len(p) > 0 { return 0, io.EOF @@ -38,7 +34,7 @@ func (r *request) Read(p []byte) (n int, err error) { } func (r *request) Close() error { - C.nxt_go_request_close(r.c_req) + C.nxt_cgo_request_close(r.c_req) return nil } @@ -55,16 +51,16 @@ func (r *request) done() { if !resp.headerSent { resp.WriteHeader(http.StatusOK) } - C.nxt_go_request_done(r.c_req) + C.nxt_cgo_request_done(r.c_req, 0) } -func get_request(go_req C.nxt_go_request_t) *request { - return (*request)(unsafe.Pointer(uintptr(go_req))) +func get_request(go_req uintptr) *request { + return (*request)(unsafe.Pointer(go_req)) } -//export nxt_go_new_request -func nxt_go_new_request(c_req C.nxt_go_request_t, id C.uint32_t, - c_method *C.nxt_go_str_t, c_uri *C.nxt_go_str_t) uintptr { +//export nxt_go_request_create +func nxt_go_request_create(c_req C.uintptr_t, + c_method *C.nxt_cgo_str_t, c_uri *C.nxt_cgo_str_t) uintptr { uri := C.GoStringN(c_uri.start, c_uri.length) @@ -83,7 +79,6 @@ func nxt_go_new_request(c_req C.nxt_go_request_t, id C.uint32_t, RequestURI: uri, }, c_req: c_req, - id: id, } r.req.Body = r @@ -91,7 +86,7 @@ func nxt_go_new_request(c_req C.nxt_go_request_t, id C.uint32_t, } //export nxt_go_request_set_proto -func nxt_go_request_set_proto(go_req C.nxt_go_request_t, proto *C.nxt_go_str_t, +func nxt_go_request_set_proto(go_req uintptr, proto *C.nxt_cgo_str_t, maj C.int, min C.int) { r := get_request(go_req) @@ -101,8 +96,8 @@ func nxt_go_request_set_proto(go_req C.nxt_go_request_t, proto *C.nxt_go_str_t, } //export nxt_go_request_add_header -func nxt_go_request_add_header(go_req C.nxt_go_request_t, name *C.nxt_go_str_t, - value *C.nxt_go_str_t) { +func nxt_go_request_add_header(go_req uintptr, name *C.nxt_cgo_str_t, + value *C.nxt_cgo_str_t) { r := get_request(go_req) r.req.Header.Add(C.GoStringN(name.start, name.length), @@ -110,23 +105,33 @@ func nxt_go_request_add_header(go_req C.nxt_go_request_t, name *C.nxt_go_str_t, } //export nxt_go_request_set_content_length -func nxt_go_request_set_content_length(go_req C.nxt_go_request_t, l C.int64_t) { +func nxt_go_request_set_content_length(go_req uintptr, l C.int64_t) { get_request(go_req).req.ContentLength = int64(l) } //export nxt_go_request_set_host -func nxt_go_request_set_host(go_req C.nxt_go_request_t, host *C.nxt_go_str_t) { +func nxt_go_request_set_host(go_req uintptr, host *C.nxt_cgo_str_t) { get_request(go_req).req.Host = C.GoStringN(host.start, host.length) } //export nxt_go_request_set_url -func nxt_go_request_set_url(go_req C.nxt_go_request_t, scheme *C.char) { +func nxt_go_request_set_url(go_req uintptr, scheme *C.char) { get_request(go_req).req.URL.Scheme = C.GoString(scheme) } //export nxt_go_request_set_remote_addr -func nxt_go_request_set_remote_addr(go_req C.nxt_go_request_t, - addr *C.nxt_go_str_t) { +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_handler +func nxt_go_request_handler(go_req uintptr, h uintptr) { + r := get_request(go_req) + handler := *(*http.Handler)(unsafe.Pointer(h)) + + go func(r *request) { + handler.ServeHTTP(r.response(), &r.req) + r.done() + }(r) +} diff --git a/src/go/unit/response.go b/src/go/unit/response.go index 801a52e5..767d66b7 100644 --- a/src/go/unit/response.go +++ b/src/go/unit/response.go @@ -6,12 +6,11 @@ package unit /* -#include "nxt_go_lib.h" +#include "nxt_cgo_lib.h" */ import "C" import ( - "fmt" "net/http" ) @@ -19,10 +18,10 @@ type response struct { header http.Header headerSent bool req *http.Request - c_req C.nxt_go_request_t + c_req C.uintptr_t } -func new_response(c_req C.nxt_go_request_t, req *http.Request) *response { +func new_response(c_req C.uintptr_t, req *http.Request) *response { resp := &response{ header: http.Header{}, req: req, @@ -41,9 +40,7 @@ func (r *response) Write(p []byte) (n int, err error) { r.WriteHeader(http.StatusOK) } - l := C.size_t(len(p)) - b := buf_ref(p) - res := C.nxt_go_response_write(r.c_req, b, l) + res := C.nxt_cgo_response_write(r.c_req, buf_ref(p), C.uint32_t(len(p))) return int(res), nil } @@ -54,22 +51,37 @@ func (r *response) WriteHeader(code int) { return } r.headerSent = true - fmt.Fprintf(r, "Status: %d\r\n", code) // Set a default Content-Type if _, hasType := r.header["Content-Type"]; !hasType { r.header.Add("Content-Type", "text/html; charset=utf-8") } - r.header.Write(r) + fields := 0 + fields_size := 0 - r.Write([]byte("\r\n")) + for k, vv := range r.header { + for _, v := range vv { + fields++ + fields_size += len(k) + len(v) + } + } + + C.nxt_cgo_response_create(r.c_req, C.int(code), C.int(fields), + C.uint32_t(fields_size)) + + for k, vv := range r.header { + for _, v := range vv { + C.nxt_cgo_response_add_field(r.c_req, str_ref(k), C.uint8_t(len(k)), + str_ref(v), C.uint32_t(len(v))) + } + } + + C.nxt_cgo_response_send(r.c_req) } func (r *response) Flush() { if !r.headerSent { r.WriteHeader(http.StatusOK) } - - C.nxt_go_response_flush(r.c_req) } diff --git a/src/go/unit/unit.go b/src/go/unit/unit.go index 77be7612..06257768 100644 --- a/src/go/unit/unit.go +++ b/src/go/unit/unit.go @@ -6,17 +6,13 @@ package unit /* -#include "nxt_go_lib.h" +#include "nxt_cgo_lib.h" */ import "C" import ( - "errors" "fmt" "net/http" - "os" - "strconv" - "strings" "unsafe" ) @@ -33,104 +29,73 @@ func buf_ref(buf []byte) C.uintptr_t { return C.uintptr_t(uintptr(unsafe.Pointer(&buf[0]))) } -func (buf *cbuf) init(b []byte) { - buf.b = buf_ref(b) - buf.s = C.size_t(len(b)) +type StringHeader struct { + Data unsafe.Pointer + Len int } -func (buf *cbuf) GoBytes() []byte { - if buf == nil { - var b [0]byte - return b[:0] - } +func str_ref(s string) C.uintptr_t { + header := (*StringHeader)(unsafe.Pointer(&s)) - return C.GoBytes(unsafe.Pointer(uintptr(buf.b)), C.int(buf.s)) + return C.uintptr_t(uintptr(unsafe.Pointer(header.Data))) } -var nxt_go_quit bool = false - -//export nxt_go_set_quit -func nxt_go_set_quit() { - nxt_go_quit = true +func (buf *cbuf) init_bytes(b []byte) { + buf.b = buf_ref(b) + buf.s = C.size_t(len(b)) } -func nxt_go_warn(format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, "[go warn] " + format + "\n", args...) +func (buf *cbuf) init_string(s string) { + buf.b = str_ref(s) + buf.s = C.size_t(len(s)) } -func nxt_go_debug(format string, args ...interface{}) { - // fmt.Fprintf(os.Stderr, "[go debug] " + format + "\n", args...) +type SliceHeader struct { + Data unsafe.Pointer + Len int + Cap int } -func ListenAndServe(addr string, handler http.Handler) error { - var read_port *port - - go_ports_env := os.Getenv("NXT_GO_PORTS") - if go_ports_env == "" { - return http.ListenAndServe(addr, handler) +func (buf *cbuf) GoBytes() []byte { + if buf == nil { + var b [0]byte + return b[:0] } - nxt_go_debug("NXT_GO_PORTS=%s", go_ports_env) - - ports := strings.Split(go_ports_env, ";") - pid := os.Getpid() - - if len(ports) != 4 { - return errors.New("Invalid NXT_GO_PORTS format") + bytesHeader := &SliceHeader{ + Data: unsafe.Pointer(uintptr(buf.b)), + Len: int(buf.s), + Cap: int(buf.s), } - nxt_go_debug("version=%s", ports[0]) - - builtin_version := C.GoString(C.nxt_go_version()) - - if ports[0] != builtin_version { - return fmt.Errorf("Versions mismatch: Unit %s, while application is built with %s", - ports[0], builtin_version) - } + return *(*[]byte)(unsafe.Pointer(bytesHeader)) +} - stream, stream_err := strconv.Atoi(ports[1]) - if stream_err != nil { - return stream_err +func GoBytes(buf unsafe.Pointer, size C.int) []byte { + bytesHeader := &SliceHeader{ + Data: buf, + Len: int(size), + Cap: int(size), } - read_port = nil - - for _, port_str := range ports[2:] { - attrs := strings.Split(port_str, ",") - - if len(attrs) != 5 { - return fmt.Errorf("Invalid port format: unexpected port attributes number %d, while 5 expected", - len(attrs)) - } - - var attrsN [5]int - var err error - for i, attr := range attrs { - attrsN[i], err = strconv.Atoi(attr) - if err != nil { - return fmt.Errorf("Invalid port format: number attribute expected at %d position instead of '%s'", - i, attr); - } - } + return *(*[]byte)(unsafe.Pointer(bytesHeader)) +} - p := new_port(attrsN[0], attrsN[1], attrsN[2], attrsN[3], attrsN[4]) +func nxt_go_warn(format string, args ...interface{}) { + str := fmt.Sprintf("[go] " + format, args...) - if attrsN[0] == pid { - read_port = p - } - } + C.nxt_cgo_warn(str_ref(str), C.uint32_t(len(str))) +} - if read_port == nil { - return errors.New("Application read port not found"); +func ListenAndServe(addr string, handler http.Handler) error { + if handler == nil { + handler = http.DefaultServeMux } - C.nxt_go_ready(C.uint32_t(stream)) + rc := C.nxt_cgo_run(C.uintptr_t(uintptr(unsafe.Pointer(&handler)))) - for !nxt_go_quit { - err := read_port.read(handler) - if err != nil { - return err - } + if rc != 0 { + return http.ListenAndServe(addr, handler) } return nil diff --git a/src/nxt_application.c b/src/nxt_application.c index b2b7f9c0..3c62f7d4 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -12,6 +12,7 @@ #include <nxt_router.h> #include <nxt_http.h> #include <nxt_application.h> +#include <nxt_unit.h> #include <nxt_port_memory_int.h> #include <glob.h> @@ -46,10 +47,7 @@ static uint32_t compat[] = { nxt_str_t nxt_server = nxt_string(NXT_SERVER); -static nxt_thread_mutex_t nxt_app_mutex; -static nxt_thread_cond_t nxt_app_cond; - -static nxt_application_module_t *nxt_app; +static nxt_app_module_t *nxt_app; nxt_int_t @@ -188,13 +186,13 @@ static nxt_int_t nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, const char *name) { - void *dl; - nxt_str_t version; - nxt_int_t ret; - nxt_uint_t i, n; - nxt_module_t *module; - nxt_app_type_t type; - nxt_application_module_t *app; + void *dl; + nxt_str_t version; + nxt_int_t ret; + nxt_uint_t i, n; + nxt_module_t *module; + nxt_app_type_t type; + nxt_app_module_t *app; /* * Only memory allocation failure should return NXT_ERROR. @@ -353,14 +351,6 @@ nxt_app_start(nxt_task_t *task, void *data) return NXT_ERROR; } - if (nxt_slow_path(nxt_thread_mutex_create(&nxt_app_mutex) != NXT_OK)) { - return NXT_ERROR; - } - - if (nxt_slow_path(nxt_thread_cond_create(&nxt_app_cond) != NXT_OK)) { - return NXT_ERROR; - } - ret = nxt_app->init(task, data); if (nxt_slow_path(ret != NXT_OK)) { @@ -430,328 +420,6 @@ nxt_app_set_environment(nxt_conf_value_t *environment) } -void -nxt_app_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) -{ - if (nxt_app->atexit != NULL) { - nxt_app->atexit(task); - } - - nxt_worker_process_quit_handler(task, msg); -} - - -void -nxt_app_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) -{ - size_t dump_size; - nxt_int_t res; - nxt_buf_t *b; - nxt_port_t *port; - nxt_app_rmsg_t rmsg = { msg->buf }; - nxt_app_wmsg_t wmsg; - - b = msg->buf; - dump_size = b->mem.free - b->mem.pos; - - if (dump_size > 300) { - dump_size = 300; - } - - nxt_debug(task, "app data: %*s ...", dump_size, b->mem.pos); - - port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid, - msg->port_msg.reply_port); - if (nxt_slow_path(port == NULL)) { - nxt_debug(task, "stream #%uD: reply port %d not found", - msg->port_msg.stream, msg->port_msg.reply_port); - return; - } - - wmsg.port = port; - wmsg.write = NULL; - wmsg.buf = &wmsg.write; - wmsg.stream = msg->port_msg.stream; - - res = nxt_app->run(task, &rmsg, &wmsg); - - if (nxt_slow_path(res != NXT_OK)) { - nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, -1, - msg->port_msg.stream, 0, NULL); - } -} - - -u_char * -nxt_app_msg_write_get_buf(nxt_task_t *task, nxt_app_wmsg_t *msg, size_t size) -{ - size_t free_size; - u_char *res; - nxt_buf_t *b; - - res = NULL; - - do { - b = *msg->buf; - - if (b == NULL) { - b = nxt_port_mmap_get_buf(task, msg->port, size); - if (nxt_slow_path(b == NULL)) { - return NULL; - } - - *msg->buf = b; - } - - free_size = nxt_buf_mem_free_size(&b->mem); - - if (free_size >= size) { - res = b->mem.free; - b->mem.free += size; - - return res; - } - - if (nxt_port_mmap_increase_buf(task, b, size, size) == NXT_OK) { - res = b->mem.free; - b->mem.free += size; - - return res; - } - - msg->buf = &b->next; - } while(1); -} - - -nxt_int_t -nxt_app_msg_write(nxt_task_t *task, nxt_app_wmsg_t *msg, u_char *c, size_t size) -{ - u_char *dst; - size_t dst_length; - - if (c != NULL) { - dst_length = size + (size < 128 ? 1 : 4) + 1; - - dst = nxt_app_msg_write_get_buf(task, msg, dst_length); - if (nxt_slow_path(dst == NULL)) { - nxt_debug(task, "nxt_app_msg_write: get_buf(%uz) failed", - dst_length); - return NXT_ERROR; - } - - dst = nxt_app_msg_write_length(dst, size + 1); /* +1 for trailing 0 */ - - nxt_memcpy(dst, c, size); - dst[size] = 0; - - nxt_debug(task, "nxt_app_msg_write: %uz %*s", size, size, c); - - } else { - dst_length = 1; - - dst = nxt_app_msg_write_get_buf(task, msg, dst_length); - if (nxt_slow_path(dst == NULL)) { - nxt_debug(task, "nxt_app_msg_write: get_buf(%uz) failed", - dst_length); - return NXT_ERROR; - } - - dst = nxt_app_msg_write_length(dst, 0); - - nxt_debug(task, "nxt_app_msg_write: NULL"); - } - - return NXT_OK; -} - - -nxt_int_t -nxt_app_msg_write_prefixed_upcase(nxt_task_t *task, nxt_app_wmsg_t *msg, - const nxt_str_t *prefix, u_char *c, size_t size) -{ - u_char *dst, *src; - size_t i, length, dst_length; - - length = prefix->length + size; - - dst_length = length + (length < 128 ? 1 : 4) + 1; - - dst = nxt_app_msg_write_get_buf(task, msg, dst_length); - if (nxt_slow_path(dst == NULL)) { - return NXT_ERROR; - } - - dst = nxt_app_msg_write_length(dst, length + 1); /* +1 for trailing 0 */ - - nxt_memcpy(dst, prefix->start, prefix->length); - dst += prefix->length; - - src = c; - for (i = 0; i < size; i++, dst++, src++) { - - if (*src >= 'a' && *src <= 'z') { - *dst = *src & ~0x20; - continue; - } - - if (*src == '-') { - *dst = '_'; - continue; - } - - *dst = *src; - } - - *dst = 0; - - return NXT_OK; -} - - -nxt_inline nxt_int_t -nxt_app_msg_read_size_(nxt_task_t *task, nxt_app_rmsg_t *msg, size_t *size) -{ - nxt_buf_t *buf; - - do { - buf = msg->buf; - - if (nxt_slow_path(buf == NULL)) { - return NXT_DONE; - } - - if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) < 1)) { - if (nxt_fast_path(nxt_buf_mem_used_size(&buf->mem) == 0)) { - msg->buf = buf->next; - continue; - } - return NXT_ERROR; - } - - if (buf->mem.pos[0] >= 128) { - if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) < 4)) { - return NXT_ERROR; - } - } - - break; - } while (1); - - buf->mem.pos = nxt_app_msg_read_length(buf->mem.pos, size); - - return NXT_OK; -} - - -nxt_int_t -nxt_app_msg_read_str(nxt_task_t *task, nxt_app_rmsg_t *msg, nxt_str_t *str) -{ - size_t length; - nxt_int_t ret; - nxt_buf_t *buf; - - ret = nxt_app_msg_read_size_(task, msg, &length); - if (ret != NXT_OK) { - return ret; - } - - buf = msg->buf; - - if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) < (intptr_t) length)) { - return NXT_ERROR; - } - - if (length > 0) { - str->start = buf->mem.pos; - str->length = length - 1; - - buf->mem.pos += length; - - nxt_debug(task, "nxt_read_str: %uz %*s", length - 1, - length - 1, str->start); - - } else { - str->start = NULL; - str->length = 0; - - nxt_debug(task, "nxt_read_str: NULL"); - } - - return NXT_OK; -} - - -size_t -nxt_app_msg_read_raw(nxt_task_t *task, nxt_app_rmsg_t *msg, void *dst, - size_t size) -{ - size_t res, read_size; - nxt_buf_t *buf; - - res = 0; - - while (size > 0) { - buf = msg->buf; - - if (nxt_slow_path(buf == NULL)) { - break; - } - - if (nxt_slow_path(nxt_buf_mem_used_size(&buf->mem) == 0)) { - msg->buf = buf->next; - continue; - } - - read_size = nxt_buf_mem_used_size(&buf->mem); - read_size = nxt_min(read_size, size); - - dst = nxt_cpymem(dst, buf->mem.pos, read_size); - - size -= read_size; - buf->mem.pos += read_size; - res += read_size; - } - - nxt_debug(task, "nxt_read_raw: %uz", res); - - return res; -} - - -nxt_int_t -nxt_app_msg_read_nvp(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_str_t *n, - nxt_str_t *v) -{ - nxt_int_t rc; - - rc = nxt_app_msg_read_str(task, rmsg, n); - if (nxt_slow_path(rc != NXT_OK)) { - return rc; - } - - rc = nxt_app_msg_read_str(task, rmsg, v); - if (nxt_slow_path(rc != NXT_OK)) { - return rc; - } - - return rc; -} - - -nxt_int_t -nxt_app_msg_read_size(nxt_task_t *task, nxt_app_rmsg_t *msg, size_t *size) -{ - nxt_int_t ret; - - ret = nxt_app_msg_read_size_(task, msg, size); - - nxt_debug(task, "nxt_read_size: %d", (int) *size); - - return ret; -} - - nxt_int_t nxt_app_http_req_done(nxt_task_t *task, nxt_app_parse_ctx_t *ar) { @@ -778,92 +446,6 @@ nxt_app_http_release(nxt_task_t *task, void *obj, void *data) } -nxt_int_t -nxt_app_msg_flush(nxt_task_t *task, nxt_app_wmsg_t *msg, nxt_bool_t last) -{ - nxt_int_t rc; - nxt_buf_t *b; - - rc = NXT_OK; - - if (nxt_slow_path(last == 1)) { - do { - b = *msg->buf; - - if (b == NULL) { - b = nxt_buf_sync_alloc(msg->port->mem_pool, NXT_BUF_SYNC_LAST); - *msg->buf = b; - break; - } - - msg->buf = &b->next; - } while(1); - } - - if (nxt_slow_path(msg->write != NULL)) { - rc = nxt_port_socket_write(task, msg->port, NXT_PORT_MSG_DATA, - -1, msg->stream, 0, msg->write); - - msg->write = NULL; - msg->buf = &msg->write; - } - - return rc; -} - - -nxt_int_t -nxt_app_msg_write_raw(nxt_task_t *task, nxt_app_wmsg_t *msg, const u_char *c, - size_t size) -{ - size_t free_size, copy_size; - nxt_buf_t *b; - - nxt_debug(task, "nxt_app_msg_write_raw: %uz", size); - - while (size > 0) { - b = *msg->buf; - - if (b == NULL) { - free_size = nxt_min(size, PORT_MMAP_DATA_SIZE); - - b = nxt_port_mmap_get_buf(task, msg->port, free_size); - if (nxt_slow_path(b == NULL)) { - return NXT_ERROR; - } - - *msg->buf = b; - - } else { - free_size = nxt_buf_mem_free_size(&b->mem); - - if (free_size < size - && nxt_port_mmap_increase_buf(task, b, size, 1) == NXT_OK) - { - free_size = nxt_buf_mem_free_size(&b->mem); - } - } - - if (free_size > 0) { - copy_size = nxt_min(free_size, size); - - b->mem.free = nxt_cpymem(b->mem.free, c, copy_size); - - size -= copy_size; - c += copy_size; - - if (size == 0) { - return NXT_OK; - } - } - - msg->buf = &b->next; - } - - return NXT_OK; -} - - nxt_app_lang_module_t * nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name) { @@ -943,3 +525,43 @@ nxt_app_parse_type(u_char *p, size_t length) return NXT_APP_UNKNOWN; } + + +nxt_int_t +nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init) +{ + nxt_port_t *my_port, *main_port; + nxt_runtime_t *rt; + + nxt_memzero(init, sizeof(nxt_unit_init_t)); + + rt = task->thread->runtime; + + main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + if (nxt_slow_path(main_port == NULL)) { + return NXT_ERROR; + } + + my_port = nxt_runtime_port_find(rt, nxt_pid, 0); + if (nxt_slow_path(my_port == NULL)) { + return NXT_ERROR; + } + + init->ready_port.id.pid = main_port->pid; + init->ready_port.id.id = main_port->id; + init->ready_port.out_fd = main_port->pair[1]; + + nxt_fd_blocking(task, main_port->pair[1]); + + init->ready_stream = my_port->process->init->stream; + + init->read_port.id.pid = my_port->pid; + init->read_port.id.id = my_port->id; + init->read_port.in_fd = my_port->pair[0]; + + nxt_fd_blocking(task, my_port->pair[0]); + + init->log_fd = 2; + + return NXT_OK; +} diff --git a/src/nxt_application.h b/src/nxt_application.h index 1fca5dff..35346655 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -11,6 +11,8 @@ #include <nxt_conf.h> +#include <nxt_unit_typedefs.h> + typedef enum { NXT_APP_PYTHON, @@ -23,7 +25,6 @@ typedef enum { } nxt_app_type_t; -typedef struct nxt_app_module_s nxt_application_module_t; typedef struct nxt_app_module_s nxt_app_module_t; @@ -31,7 +32,7 @@ typedef struct { nxt_app_type_t type; u_char *version; char *file; - nxt_application_module_t *module; + nxt_app_module_t *module; } nxt_app_lang_module_t; @@ -129,6 +130,7 @@ typedef struct { 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; @@ -143,75 +145,6 @@ struct nxt_app_parse_ctx_s { nxt_int_t nxt_app_http_req_done(nxt_task_t *task, nxt_app_parse_ctx_t *ctx); -typedef struct nxt_app_wmsg_s nxt_app_wmsg_t; -typedef struct nxt_app_rmsg_s nxt_app_rmsg_t; - -struct nxt_app_wmsg_s { - nxt_port_t *port; /* where prepared buf will be sent */ - nxt_buf_t *write; - nxt_buf_t **buf; - uint32_t stream; -}; - - -struct nxt_app_rmsg_s { - nxt_buf_t *buf; /* current buffer to read */ -}; - - -nxt_inline u_char * -nxt_app_msg_write_length(u_char *dst, size_t length); - -/* TODO asynchronous mmap buffer assignment */ -NXT_EXPORT u_char *nxt_app_msg_write_get_buf(nxt_task_t *task, - nxt_app_wmsg_t *msg, size_t size); - -NXT_EXPORT nxt_int_t nxt_app_msg_write(nxt_task_t *task, nxt_app_wmsg_t *msg, - u_char *c, size_t size); - -NXT_EXPORT nxt_int_t nxt_app_msg_write_prefixed_upcase(nxt_task_t *task, - nxt_app_wmsg_t *msg, const nxt_str_t *prefix, u_char *c, size_t size); - -nxt_inline nxt_int_t -nxt_app_msg_write_nvp_(nxt_task_t *task, nxt_app_wmsg_t *msg, - u_char *n, size_t nsize, u_char *v, size_t vsize); - - -#define nxt_app_msg_write_const(task, msg, c) \ - nxt_app_msg_write((task), (msg), (u_char *) (c), nxt_length(c)) - -#define nxt_app_msg_write_str(task, msg, str) \ - nxt_app_msg_write((task), (msg), (str)->start, (str)->length) - -#define nxt_app_msg_write_cstr(task, msg, c) \ - nxt_app_msg_write((task), (msg), (c), nxt_strlen(c)) - -#define nxt_app_msg_write_nvp(task, msg, n, v) \ - nxt_app_msg_write_nvp_((task), (msg), (u_char *) (n), nxt_length(n), \ - (v)->start, (v)->length) - -nxt_inline nxt_int_t nxt_app_msg_write_size(nxt_task_t *task, - nxt_app_wmsg_t *msg, size_t size); - -NXT_EXPORT nxt_int_t nxt_app_msg_flush(nxt_task_t *task, nxt_app_wmsg_t *msg, - nxt_bool_t last); - -NXT_EXPORT nxt_int_t nxt_app_msg_write_raw(nxt_task_t *task, - nxt_app_wmsg_t *msg, const u_char *c, size_t size); - -NXT_EXPORT nxt_int_t nxt_app_msg_read_str(nxt_task_t *task, nxt_app_rmsg_t *msg, - nxt_str_t *str); - -NXT_EXPORT size_t nxt_app_msg_read_raw(nxt_task_t *task, - nxt_app_rmsg_t *msg, void *buf, size_t size); - -NXT_EXPORT nxt_int_t nxt_app_msg_read_nvp(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, nxt_str_t *n, nxt_str_t *v); - -NXT_EXPORT nxt_int_t nxt_app_msg_read_size(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, size_t *size); - - struct nxt_app_module_s { size_t compat_length; uint32_t *compat; @@ -221,94 +154,17 @@ struct nxt_app_module_s { nxt_int_t (*init)(nxt_task_t *task, nxt_common_app_conf_t *conf); - nxt_int_t (*run)(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, - nxt_app_wmsg_t *wmsg); - void (*atexit)(nxt_task_t *task); }; -nxt_int_t nxt_app_http_read_body(nxt_app_request_t *r, u_char *data, - size_t len); -nxt_int_t nxt_app_write(nxt_app_request_t *r, const u_char *data, size_t len); - -nxt_inline u_char * -nxt_app_msg_write_length(u_char *dst, size_t length) -{ - if (length < 128) { - *dst = length; - dst++; - - } else { - dst[0] = 0x80U | (length >> 24); - dst[1] = 0xFFU & (length >> 16); - dst[2] = 0xFFU & (length >> 8); - dst[3] = 0xFFU & length; - dst += 4; - } - - return dst; -} - - -nxt_inline nxt_int_t -nxt_app_msg_write_nvp_(nxt_task_t *task, nxt_app_wmsg_t *msg, - u_char *n, size_t nsize, u_char *v, size_t vsize) -{ - nxt_int_t rc; - - rc = nxt_app_msg_write(task, msg, n, nsize); - if (nxt_slow_path(rc != NXT_OK)) { - return rc; - } - - return nxt_app_msg_write(task, msg, v, vsize); -} - - -nxt_inline nxt_int_t -nxt_app_msg_write_size(nxt_task_t *task, nxt_app_wmsg_t *msg, size_t size) -{ - u_char *dst; - size_t dst_length; - - dst_length = size < 128 ? 1 : 4; - - dst = nxt_app_msg_write_get_buf(task, msg, dst_length); - if (nxt_slow_path(dst == NULL)) { - return NXT_ERROR; - } - - nxt_app_msg_write_length(dst, size); - - return NXT_OK; -} - - -nxt_inline u_char * -nxt_app_msg_read_length(u_char *src, size_t *length) -{ - if (src[0] < 128) { - *length = src[0]; - src++; - - } else { - *length = ((src[0] & 0x7FU) << 24) - + ( src[1] << 16) - + ( src[2] << 8) - + src[3]; - src += 4; - } - - return src; -} - - nxt_app_lang_module_t *nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name); nxt_app_type_t nxt_app_parse_type(u_char *p, size_t length); -NXT_EXPORT extern nxt_str_t nxt_server; -extern nxt_application_module_t nxt_go_module; +NXT_EXPORT extern nxt_str_t nxt_server; +extern nxt_app_module_t nxt_go_module; + +NXT_EXPORT nxt_int_t nxt_unit_default_init(nxt_task_t *task, + nxt_unit_init_t *init); #endif /* _NXT_APPLICATION_H_INCLIDED_ */ diff --git a/src/nxt_go.c b/src/nxt_go.c index 67a5afb6..4e7d0488 100644 --- a/src/nxt_go.c +++ b/src/nxt_go.c @@ -6,21 +6,17 @@ #include <nxt_main.h> #include <nxt_router.h> +#include <nxt_unit.h> static nxt_int_t nxt_go_init(nxt_task_t *task, nxt_common_app_conf_t *conf); -static nxt_int_t nxt_go_run(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *msg); - -nxt_application_module_t nxt_go_module = { +nxt_app_module_t nxt_go_module = { 0, NULL, nxt_string("go"), "*", nxt_go_init, - nxt_go_run, - NULL, }; @@ -51,6 +47,8 @@ nxt_go_fd_no_cloexec(nxt_task_t *task, nxt_socket_t fd) return NXT_ERROR; } + nxt_fd_blocking(task, fd); + return NXT_OK; } @@ -94,25 +92,26 @@ nxt_go_init(nxt_task_t *task, nxt_common_app_conf_t *conf) p = nxt_sprintf(buf, end, "%s;%uD;" - "%PI,%ud,%d,%d,%d;" - "%PI,%ud,%d,%d,%d%Z", + "%PI,%ud,%d;" + "%PI,%ud,%d;" + "%d,%Z", NXT_VERSION, my_port->process->init->stream, - main_port->pid, main_port->id, (int) main_port->type, - -1, main_port->pair[1], - my_port->pid, my_port->id, (int) my_port->type, - my_port->pair[0], -1); + main_port->pid, main_port->id, main_port->pair[1], + my_port->pid, my_port->id, my_port->pair[0], + 2); if (nxt_slow_path(p == end)) { - nxt_alert(task, "internal error: buffer too small for NXT_GO_PORTS"); + nxt_alert(task, "internal error: buffer too small for NXT_UNIT_INIT"); return NXT_ERROR; } - nxt_debug(task, "update NXT_GO_PORTS=%s", buf); + nxt_debug(task, "update "NXT_UNIT_INIT_ENV"=%s", buf); - rc = setenv("NXT_GO_PORTS", (char *) buf, 1); + rc = setenv(NXT_UNIT_INIT_ENV, (char *) buf, 1); if (nxt_slow_path(rc == -1)) { - nxt_alert(task, "setenv(NXT_GO_PORTS, %s) failed %E", buf, nxt_errno); + nxt_alert(task, "setenv("NXT_UNIT_INIT_ENV", %s) failed %E", buf, + nxt_errno); return NXT_ERROR; } @@ -174,11 +173,3 @@ nxt_go_init(nxt_task_t *task, nxt_common_app_conf_t *conf) return NXT_ERROR; } - - -static nxt_int_t -nxt_go_run(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *msg) -{ - return NXT_ERROR; -} diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 7065bb3f..413764f1 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -1,4 +1,3 @@ - /* * Copyright (C) Max Romanov * Copyright (C) Valentin V. Bartenev @@ -12,12 +11,19 @@ #include <nxt_main.h> #include <nxt_router.h> +#include <nxt_unit.h> +#include <nxt_unit_request.h> + +typedef struct nxt_php_run_ctx_s nxt_php_run_ctx_t; static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf); -static nxt_int_t nxt_php_run(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg); +static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t); +static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t); +nxt_inline u_char *nxt_realpath(const void *c); + +static void nxt_php_request_handler(nxt_unit_request_info_t *req); #if PHP_MAJOR_VERSION >= 7 # define NXT_PHP7 1 @@ -41,6 +47,12 @@ static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type); static int nxt_php_send_headers(sapi_headers_struct *sapi_headers); static char *nxt_php_read_cookies(void); +static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name, + nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC); +nxt_inline void nxt_php_set_str(nxt_unit_request_info_t *req, const char *name, + nxt_str_t *s, zval *track_vars_array TSRMLS_DC); +static void nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name, + char *str, uint32_t len, zval *track_vars_array TSRMLS_DC); static void nxt_php_register_variables(zval *track_vars_array); #ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE static void nxt_php_log_message(char *message, int syslog_type_int); @@ -57,8 +69,6 @@ static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC); static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC); #endif -static void nxt_php_flush(void *server_context); - static sapi_module_struct nxt_php_sapi_module = { @@ -72,7 +82,7 @@ static sapi_module_struct nxt_php_sapi_module = NULL, /* deactivate */ nxt_php_unbuffered_write, /* unbuffered write */ - nxt_php_flush, /* flush */ + NULL, /* flush */ NULL, /* get uid */ NULL, /* getenv */ @@ -120,19 +130,12 @@ static sapi_module_struct nxt_php_sapi_module = NULL /* input_filter_init */ }; -typedef struct { - nxt_task_t *task; - nxt_app_rmsg_t *rmsg; - nxt_app_request_t r; - nxt_str_t script; - nxt_app_wmsg_t *wmsg; - - size_t body_preread_size; -} nxt_php_run_ctx_t; -nxt_inline nxt_int_t nxt_php_write(nxt_php_run_ctx_t *ctx, - const u_char *data, size_t len, - nxt_bool_t flush, nxt_bool_t last); +struct nxt_php_run_ctx_s { + char *cookie; + nxt_str_t script; + nxt_unit_request_info_t *req; +}; static nxt_str_t nxt_php_path; @@ -141,58 +144,33 @@ static nxt_str_t nxt_php_script; static nxt_str_t nxt_php_index = nxt_string("index.php"); -static void -nxt_php_str_trim_trail(nxt_str_t *str, u_char t) -{ - while (str->length > 0 && str->start[str->length - 1] == t) { - str->length--; - } - - str->start[str->length] = '\0'; -} - - -static void -nxt_php_str_trim_lead(nxt_str_t *str, u_char t) -{ - while (str->length > 0 && str->start[0] == t) { - str->length--; - str->start++; - } -} - static uint32_t compat[] = { NXT_VERNUM, NXT_DEBUG, }; -NXT_EXPORT nxt_application_module_t nxt_app_module = { +NXT_EXPORT nxt_app_module_t nxt_app_module = { sizeof(compat), compat, nxt_string("php"), PHP_VERSION, nxt_php_init, - nxt_php_run, - NULL, }; static nxt_task_t *nxt_php_task; -nxt_inline u_char * -nxt_realpath(const void *c) -{ - return (u_char *) realpath(c, NULL); -} - - static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) { u_char *p; nxt_str_t rpath, ini_path; nxt_str_t *root, *path, *script, *index; + nxt_port_t *my_port, *main_port; + nxt_runtime_t *rt; + nxt_unit_ctx_t *unit_ctx; + nxt_unit_init_t php_init; nxt_conf_value_t *value; nxt_php_app_conf_t *c; @@ -314,6 +292,48 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_php_set_options(task, value, ZEND_INI_USER); } + nxt_memzero(&php_init, sizeof(nxt_unit_init_t)); + + rt = task->thread->runtime; + + main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + if (nxt_slow_path(main_port == NULL)) { + return NXT_ERROR; + } + + my_port = nxt_runtime_port_find(rt, nxt_pid, 0); + if (nxt_slow_path(my_port == NULL)) { + return NXT_ERROR; + } + + php_init.callbacks.request_handler = nxt_php_request_handler; + php_init.ready_port.id.pid = main_port->pid; + php_init.ready_port.id.id = main_port->id; + php_init.ready_port.out_fd = main_port->pair[1]; + + nxt_fd_blocking(task, main_port->pair[1]); + + php_init.ready_stream = my_port->process->init->stream; + + php_init.read_port.id.pid = my_port->pid; + php_init.read_port.id.id = my_port->id; + php_init.read_port.in_fd = my_port->pair[0]; + + nxt_fd_blocking(task, my_port->pair[0]); + + php_init.log_fd = 2; + + unit_ctx = nxt_unit_init(&php_init); + if (nxt_slow_path(unit_ctx == NULL)) { + return NXT_ERROR; + } + + nxt_unit_run(unit_ctx); + + nxt_unit_done(unit_ctx); + + exit(0); + return NXT_OK; } @@ -430,51 +450,57 @@ nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type) #endif -static nxt_int_t -nxt_php_read_request(nxt_task_t *task, nxt_app_rmsg_t *rmsg, - nxt_php_run_ctx_t *ctx) +static void +nxt_php_str_trim_trail(nxt_str_t *str, u_char t) { - u_char *p; - size_t s; - nxt_int_t rc; - nxt_str_t script_name; - nxt_app_request_header_t *h; - - h = &ctx->r.header; - -#define RC(S) \ - do { \ - rc = (S); \ - if (nxt_slow_path(rc != NXT_OK)) { \ - goto fail; \ - } \ - } while(0) - -#define NXT_READ(dst) \ - RC(nxt_app_msg_read_str(task, rmsg, (dst))) - - NXT_READ(&h->method); - NXT_READ(&h->target); - NXT_READ(&h->path); - - RC(nxt_app_msg_read_size(task, rmsg, &s)); - if (s > 0) { - s--; - h->query.start = h->target.start + s; - h->query.length = h->target.length - s; - - if (h->path.start == NULL) { - h->path.start = h->target.start; - h->path.length = s - 1; - } + while (str->length > 0 && str->start[str->length - 1] == t) { + str->length--; } - if (h->path.start == NULL) { - h->path = h->target; + str->start[str->length] = '\0'; +} + + +static void +nxt_php_str_trim_lead(nxt_str_t *str, u_char t) +{ + while (str->length > 0 && str->start[0] == t) { + str->length--; + str->start++; } +} + + +nxt_inline u_char * +nxt_realpath(const void *c) +{ + return (u_char *) realpath(c, NULL); +} + + +static void +nxt_php_request_handler(nxt_unit_request_info_t *req) +{ + int rc; + u_char *p; + nxt_str_t path, script_name; + nxt_unit_field_t *f; + zend_file_handle file_handle; + nxt_php_run_ctx_t run_ctx, *ctx; + nxt_unit_request_t *r; + + nxt_memzero(&run_ctx, sizeof(run_ctx)); + + ctx = &run_ctx; + ctx->req = req; + + r = req->request; + + path.length = r->path_length; + path.start = nxt_unit_sptr_get(&r->path); if (nxt_php_path.start == NULL) { - if (h->path.start[h->path.length - 1] == '/') { + if (path.start[path.length - 1] == '/') { script_name = nxt_php_index; } else { @@ -482,15 +508,17 @@ nxt_php_read_request(nxt_task_t *task, nxt_app_rmsg_t *rmsg, script_name.start = NULL; } - ctx->script.length = nxt_php_root.length + h->path.length + ctx->script.length = nxt_php_root.length + path.length + script_name.length; p = ctx->script.start = nxt_malloc(ctx->script.length + 1); if (nxt_slow_path(p == NULL)) { - return NXT_ERROR; + nxt_unit_request_done(req, NXT_UNIT_ERROR); + + return; } p = nxt_cpymem(p, nxt_php_root.start, nxt_php_root.length); - p = nxt_cpymem(p, h->path.start, h->path.length); + p = nxt_cpymem(p, path.start, path.length); if (script_name.length > 0) { p = nxt_cpymem(p, script_name.start, script_name.length); @@ -502,67 +530,26 @@ nxt_php_read_request(nxt_task_t *task, nxt_app_rmsg_t *rmsg, ctx->script = nxt_php_path; } - NXT_READ(&h->version); - - NXT_READ(&ctx->r.remote); - NXT_READ(&ctx->r.local); - - NXT_READ(&h->host); - NXT_READ(&h->cookie); - NXT_READ(&h->content_type); - NXT_READ(&h->content_length); - - RC(nxt_app_msg_read_size(task, rmsg, &s)); - h->parsed_content_length = s; - - RC(nxt_app_msg_read_size(task, ctx->rmsg, &ctx->body_preread_size)); + SG(server_context) = ctx; + SG(request_info).request_uri = nxt_unit_sptr_get(&r->target); + SG(request_info).request_method = nxt_unit_sptr_get(&r->method); -#undef NXT_READ -#undef RC - - /* Further headers read moved to nxt_php_register_variables. */ - return NXT_OK; - -fail: - - return rc; -} - - -static nxt_int_t -nxt_php_run(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) -{ - nxt_int_t rc; - zend_file_handle file_handle; - nxt_php_run_ctx_t run_ctx; - nxt_app_request_header_t *h; - - nxt_memzero(&run_ctx, sizeof(run_ctx)); - - run_ctx.task = task; - run_ctx.rmsg = rmsg; - run_ctx.wmsg = wmsg; + SG(request_info).proto_num = 1001; - h = &run_ctx.r.header; + SG(request_info).query_string = r->query.offset + ? nxt_unit_sptr_get(&r->query) : NULL; + SG(request_info).content_length = r->content_length; - rc = nxt_php_read_request(task, rmsg, &run_ctx); + if (r->content_type_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->content_type_field; - if (nxt_slow_path(rc != NXT_OK)) { - goto fail; + SG(request_info).content_type = nxt_unit_sptr_get(&f->value); } - SG(server_context) = &run_ctx; - SG(request_info).request_uri = (char *) h->target.start; - SG(request_info).request_method = (char *) h->method.start; - - SG(request_info).proto_num = 1001; - - SG(request_info).query_string = (char *) h->query.start; - SG(request_info).content_length = h->parsed_content_length; + if (r->cookie_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->cookie_field; - if (h->content_type.start != NULL) { - SG(request_info).content_type = (char *) h->content_type.start; + ctx->cookie = nxt_unit_sptr_get(&f->value); } SG(sapi_headers).http_response_code = 200; @@ -570,60 +557,41 @@ nxt_php_run(nxt_task_t *task, SG(request_info).path_translated = NULL; file_handle.type = ZEND_HANDLE_FILENAME; - file_handle.filename = (char *) run_ctx.script.start; + file_handle.filename = (char *) ctx->script.start; file_handle.free_filename = 0; file_handle.opened_path = NULL; - nxt_debug(task, "handle.filename = '%s'", run_ctx.script.start); + nxt_unit_req_debug(req, "handle.filename = '%s'", ctx->script.start); if (nxt_php_path.start != NULL) { - nxt_debug(task, "run script %V in absolute mode", &nxt_php_path); + nxt_unit_req_debug(req, "run script %.*s in absolute mode", + (int) nxt_php_path.length, + (char *) nxt_php_path.start); } else { - nxt_debug(task, "run script %V", &run_ctx.script); + nxt_unit_req_debug(req, "run script %.*s", (int) ctx->script.length, + (char *) ctx->script.start); } if (nxt_slow_path(php_request_startup() == FAILURE)) { - nxt_debug(task, "php_request_startup() failed"); - rc = NXT_ERROR; + nxt_unit_req_debug(req, "php_request_startup() failed"); + rc = NXT_UNIT_ERROR; + goto fail; } + rc = NXT_UNIT_OK; + php_execute_script(&file_handle TSRMLS_CC); php_request_shutdown(NULL); - nxt_app_msg_flush(task, wmsg, 1); - - rc = NXT_OK; - fail: - if (run_ctx.script.start != nxt_php_path.start) { - nxt_free(run_ctx.script.start); - } - - return rc; -} - - -nxt_inline nxt_int_t -nxt_php_write(nxt_php_run_ctx_t *ctx, const u_char *data, size_t len, - nxt_bool_t flush, nxt_bool_t last) -{ - nxt_int_t rc; - - if (len > 0) { - rc = nxt_app_msg_write_raw(ctx->task, ctx->wmsg, data, len); + nxt_unit_request_done(req, rc); - } else { - rc = NXT_OK; - } - - if (flush || last) { - rc = nxt_app_msg_flush(ctx->task, ctx->wmsg, last); + if (ctx->script.start != nxt_php_path.start) { + nxt_free(ctx->script.start); } - - return rc; } @@ -642,111 +610,105 @@ static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC) #endif { - nxt_int_t rc; + int rc; nxt_php_run_ctx_t *ctx; ctx = SG(server_context); - rc = nxt_php_write(ctx, (u_char *) str, str_length, 1, 0); - - if (nxt_fast_path(rc == NXT_OK)) { + rc = nxt_unit_response_write(ctx->req, str, str_length); + if (nxt_fast_path(rc == NXT_UNIT_OK)) { return str_length; } - // TODO handle NXT_AGAIN php_handle_aborted_connection(); return 0; } -static void -nxt_php_flush(void *server_context) -{ - nxt_php_run_ctx_t *ctx; - - ctx = server_context; - - (void) nxt_app_msg_flush(ctx->task, ctx->wmsg, 0); -} - - static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) { - size_t len; - u_char *status, buf[64]; - nxt_int_t rc; - nxt_php_run_ctx_t *ctx; - sapi_header_struct *h; - zend_llist_position zpos; - - static const u_char default_repsonse[] - = "Status: 200\r\n" - "\r\n"; - - static const u_char status_200[] = "Status: 200"; - static const u_char cr_lf[] = "\r\n"; + int rc, fields_count; + char *colon, *status_line, *value; + uint16_t status; + uint32_t resp_size; + nxt_php_run_ctx_t *ctx; + sapi_header_struct *h; + zend_llist_position zpos; + nxt_unit_request_info_t *req; ctx = SG(server_context); + req = ctx->req; -#define RC(S) \ - do { \ - rc = (S); \ - if (nxt_slow_path(rc != NXT_OK)) { \ - goto fail; \ - } \ - } while(0) + nxt_unit_req_debug(req, "nxt_php_send_headers"); if (SG(request_info).no_headers == 1) { - RC(nxt_php_write(ctx, default_repsonse, nxt_length(default_repsonse), - 1, 0)); + rc = nxt_unit_response_init(req, 200, 0, 0); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return SAPI_HEADER_SEND_FAILED; + } + return SAPI_HEADER_SENT_SUCCESSFULLY; } - if (SG(sapi_headers).http_status_line) { - status = (u_char *) SG(sapi_headers).http_status_line; - len = nxt_strlen(status); + resp_size = 0; + fields_count = zend_llist_count(&sapi_headers->headers); - if (len < 12) { - goto fail; - } + for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos); + h; + h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos)) + { + resp_size += h->header_len; + } - RC(nxt_php_write(ctx, status_200, sizeof(status_200) - 4, 0, 0)); - RC(nxt_php_write(ctx, status + 9, 3, 0, 0)); + if (SG(sapi_headers).http_status_line) { + status_line = SG(sapi_headers).http_status_line; - } else if (SG(sapi_headers).http_response_code) { - status = nxt_sprintf(buf, buf + sizeof(buf), "%03d", - SG(sapi_headers).http_response_code); - len = status - buf; + status = nxt_int_parse((u_char *) status_line + 9, 3); - RC(nxt_php_write(ctx, status_200, sizeof(status_200) - 4, 0, 0)); - RC(nxt_php_write(ctx, buf, len, 0, 0)); + } else if (SG(sapi_headers).http_response_code) { + status = SG(sapi_headers).http_response_code; } else { - RC(nxt_php_write(ctx, status_200, nxt_length(status_200), 0, 0)); + status = 200; + } + + rc = nxt_unit_response_init(req, status, fields_count, resp_size); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return SAPI_HEADER_SEND_FAILED; } - RC(nxt_php_write(ctx, cr_lf, nxt_length(cr_lf), 0, 0)); + for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos); + h; + h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos)) + { + nxt_unit_req_debug(req, "header: %.*s", (int) h->header_len, h->header); - h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos); + colon = memchr(h->header, ':', h->header_len); + if (nxt_slow_path(colon == NULL)) { + nxt_unit_req_warn(req, "colon not found in header '%.*s'", + (int) h->header_len, h->header); + continue; + } - while (h) { - RC(nxt_php_write(ctx, (u_char *) h->header, h->header_len, 0, 0)); - RC(nxt_php_write(ctx, cr_lf, nxt_length(cr_lf), 0, 0)); + value = colon + 1; + while(isspace(*value)) { + value++; + } - h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos); + nxt_unit_response_add_field(req, h->header, colon - h->header, + value, + h->header_len - (value - h->header)); } - RC(nxt_php_write(ctx, cr_lf, nxt_length(cr_lf), 1, 0)); + rc = nxt_unit_response_send(req); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_req_debug(req, "failed to send response"); -#undef RC + return SAPI_HEADER_SEND_FAILED; + } return SAPI_HEADER_SENT_SUCCESSFULLY; - -fail: - - // TODO handle NXT_AGAIN - return SAPI_HEADER_SEND_FAILED; } @@ -758,27 +720,13 @@ static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC) #endif { - size_t size, rest; - nxt_php_run_ctx_t *ctx; - nxt_app_request_header_t *h; + nxt_php_run_ctx_t *ctx; ctx = SG(server_context); - h = &ctx->r.header; - - rest = (size_t) h->parsed_content_length - SG(read_post_bytes); - nxt_debug(ctx->task, "nxt_php_read_post %O", rest); + nxt_unit_req_debug(ctx->req, "nxt_php_read_post %d", (int) count_bytes); - if (rest == 0) { - return 0; - } - - rest = nxt_min(ctx->body_preread_size, (size_t) count_bytes); - size = nxt_app_msg_read_raw(ctx->task, ctx->rmsg, buffer, rest); - - ctx->body_preread_size -= size; - - return size; + return nxt_unit_request_read(ctx->req, buffer, count_bytes); } @@ -789,44 +737,36 @@ nxt_php_read_cookies(TSRMLS_D) ctx = SG(server_context); - nxt_debug(ctx->task, "nxt_php_read_cookies"); + nxt_unit_req_debug(ctx->req, "nxt_php_read_cookies"); - return (char *) ctx->r.header.cookie.start; + return ctx->cookie; } static void nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) { - u_char *colon; - size_t rest, size; - nxt_str_t n, v; - nxt_int_t rc; - nxt_str_t host, server_name, server_port; - nxt_buf_t *b, buf; - nxt_task_t *task; - nxt_app_rmsg_t *rmsg, rmsg_tmp; - nxt_php_run_ctx_t *ctx; - nxt_app_request_header_t *h; - - static nxt_str_t def_host = nxt_string("localhost"); - static nxt_str_t def_port = nxt_string("80"); + char *host_start, *port_start; + uint32_t host_length, port_length; + const char *name; + nxt_unit_field_t *f, *f_end; + nxt_php_run_ctx_t *ctx; + nxt_unit_request_t *r; + nxt_unit_request_info_t *req; ctx = SG(server_context); - h = &ctx->r.header; - task = ctx->task; - - nxt_debug(task, "php register variables"); + req = ctx->req; + r = req->request; -#define NXT_PHP_SET(n, v) \ - nxt_debug(task, "php: register %s='%V'", n, &v); \ - php_register_variable_safe((char *) (n), (char *) (v).start, \ - (v).length, track_vars_array TSRMLS_CC) \ + nxt_unit_req_debug(req, "nxt_php_register_variables"); - NXT_PHP_SET("SERVER_SOFTWARE", nxt_server); + php_register_variable_safe((char *) "SERVER_SOFTWARE", + (char *) nxt_server.start, + nxt_server.length, track_vars_array TSRMLS_CC); - NXT_PHP_SET("SERVER_PROTOCOL", h->version); + nxt_php_set_sptr(req, "SERVER_PROTOCOL", &r->version, r->version_length, + track_vars_array TSRMLS_CC); /* * 'SCRIPT_NAME' @@ -857,102 +797,116 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) * If PHP is running as a command-line processor this variable contains the * script name since PHP 4.3.0. Previously it was not available. */ - NXT_PHP_SET("PHP_SELF", nxt_php_script); - NXT_PHP_SET("SCRIPT_NAME", nxt_php_script); + nxt_php_set_str(req, "PHP_SELF", &nxt_php_script, + track_vars_array TSRMLS_CC); + nxt_php_set_str(req, "SCRIPT_NAME", &nxt_php_script, + track_vars_array TSRMLS_CC); } else { - NXT_PHP_SET("PHP_SELF", h->path); - NXT_PHP_SET("SCRIPT_NAME", h->path); + nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length, + track_vars_array TSRMLS_CC); + nxt_php_set_sptr(req, "SCRIPT_NAME", &r->path, r->path_length, + track_vars_array TSRMLS_CC); } - NXT_PHP_SET("SCRIPT_FILENAME", ctx->script); - NXT_PHP_SET("DOCUMENT_ROOT", nxt_php_root); + nxt_php_set_str(req, "SCRIPT_FILENAME", &ctx->script, + track_vars_array TSRMLS_CC); + nxt_php_set_str(req, "DOCUMENT_ROOT", &nxt_php_root, + track_vars_array TSRMLS_CC); - NXT_PHP_SET("REQUEST_METHOD", h->method); - NXT_PHP_SET("REQUEST_URI", h->target); - - if (h->query.start != NULL) { - NXT_PHP_SET("QUERY_STRING", h->query); + nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length, + track_vars_array TSRMLS_CC); + nxt_php_set_sptr(req, "REQUEST_URI", &r->target, r->target_length, + track_vars_array TSRMLS_CC); + if (r->query.offset) { + nxt_php_set_sptr(req, "QUERY_STRING", &r->query, r->query_length, + track_vars_array TSRMLS_CC); } - if (h->content_type.start != NULL) { - NXT_PHP_SET("CONTENT_TYPE", h->content_type); - } + nxt_php_set_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length, + track_vars_array TSRMLS_CC); + nxt_php_set_sptr(req, "SERVER_ADDR", &r->local, r->local_length, + track_vars_array TSRMLS_CC); - if (h->content_length.start != NULL) { - NXT_PHP_SET("CONTENT_LENGTH", h->content_length); - } + f_end = r->fields + r->fields_count; + for (f = r->fields; f < f_end; f++) { + name = nxt_unit_sptr_get(&f->name); - host = h->host; - if (host.length == 0) { - host = def_host; + nxt_php_set_sptr(req, name, &f->value, f->value_length, + track_vars_array TSRMLS_CC); } - server_name = host; - colon = nxt_memchr(host.start, ':', host.length); + if (r->content_length_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->content_length_field; - if (colon != NULL) { - server_name.length = colon - host.start; + nxt_php_set_sptr(req, "CONTENT_LENGTH", &f->value, f->value_length, + track_vars_array TSRMLS_CC); + } - server_port.start = colon + 1; - server_port.length = host.length - server_name.length - 1; + if (r->content_type_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->content_type_field; - } else { - server_port = def_port; + nxt_php_set_sptr(req, "CONTENT_TYPE", &f->value, f->value_length, + track_vars_array TSRMLS_CC); } - NXT_PHP_SET("SERVER_NAME", server_name); - NXT_PHP_SET("SERVER_PORT", server_port); + if (r->host_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->host_field; - NXT_PHP_SET("REMOTE_ADDR", ctx->r.remote); - NXT_PHP_SET("SERVER_ADDR", ctx->r.local); + host_start = nxt_unit_sptr_get(&f->value); + host_length = f->value_length; - rmsg = ctx->rmsg; - rest = ctx->body_preread_size; + } else { + host_start = NULL; + host_length = 0; + } - if (rest != 0) { - /* Skipping request body. */ + nxt_unit_split_host(host_start, host_length, &host_start, &host_length, + &port_start, &port_length); - b = rmsg->buf; + nxt_php_set_cstr(req, "SERVER_NAME", host_start, host_length, + track_vars_array TSRMLS_CC); + nxt_php_set_cstr(req, "SERVER_PORT", port_start, port_length, + track_vars_array TSRMLS_CC); +} - do { - if (nxt_slow_path(b == NULL)) { - return; - } - size = nxt_buf_mem_used_size(&b->mem); +static void +nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name, + nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC) +{ + char *str; - if (rest < size) { - nxt_memcpy(&buf, b, NXT_BUF_MEM_SIZE); - buf.mem.pos += rest; - b = &buf; - break; - } + str = nxt_unit_sptr_get(v); - rest -= size; - b = b->next; + nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, str); - } while (rest != 0); + php_register_variable_safe((char *) name, str, len, + track_vars_array TSRMLS_CC); +} - rmsg_tmp = *rmsg; - rmsg_tmp.buf = b; - rmsg = &rmsg_tmp; - } - while (nxt_app_msg_read_str(task, rmsg, &n) == NXT_OK) { - if (nxt_slow_path(n.length == 0)) { - break; - } +nxt_inline void +nxt_php_set_str(nxt_unit_request_info_t *req, const char *name, + nxt_str_t *s, zval *track_vars_array TSRMLS_DC) +{ + nxt_php_set_cstr(req, name, (char *) s->start, s->length, + track_vars_array TSRMLS_CC); +} - rc = nxt_app_msg_read_str(task, rmsg, &v); - if (nxt_slow_path(rc != NXT_OK)) { - break; - } - NXT_PHP_SET(n.start, v); +static void +nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name, + char *cstr, uint32_t len, zval *track_vars_array TSRMLS_DC) +{ + if (nxt_slow_path(cstr == NULL)) { + return; } -#undef NXT_PHP_SET + nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, cstr); + + php_register_variable_safe((char *) name, cstr, len, + track_vars_array TSRMLS_CC); } diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c index e8af381f..774f1f33 100644 --- a/src/nxt_port_memory.c +++ b/src/nxt_port_memory.c @@ -255,7 +255,7 @@ fail: static nxt_port_mmap_handler_t * nxt_port_new_port_mmap(nxt_task_t *task, nxt_process_t *process, - nxt_port_t *port, nxt_int_t n, nxt_bool_t tracking) + nxt_port_t *port, nxt_bool_t tracking, nxt_int_t n) { void *mem; u_char *p, name[64]; @@ -459,7 +459,7 @@ nxt_port_mmap_get(nxt_task_t *task, nxt_port_t *port, nxt_chunk_id_t *c, /* TODO introduce port_mmap limit and release wait. */ *c = 0; - mmap_handler = nxt_port_new_port_mmap(task, process, port, n, tracking); + mmap_handler = nxt_port_new_port_mmap(task, process, port, tracking, n); unlock_return: diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c index 4b90483e..c3978764 100644 --- a/src/nxt_python_wsgi.c +++ b/src/nxt_python_wsgi.c @@ -14,6 +14,10 @@ #include <nxt_main.h> #include <nxt_runtime.h> #include <nxt_router.h> +#include <nxt_unit.h> +#include <nxt_unit_field.h> +#include <nxt_unit_request.h> +#include <nxt_unit_response.h> /* * According to "PEP 3333 / A Note On String Types" @@ -49,30 +53,33 @@ #define PyBytes_AS_STRING PyString_AS_STRING #endif +typedef struct nxt_python_run_ctx_s nxt_python_run_ctx_t; typedef struct { PyObject_HEAD - //nxt_app_request_t *request; } nxt_py_input_t; typedef struct { PyObject_HEAD - //nxt_app_request_t *request; } nxt_py_error_t; -typedef struct nxt_python_run_ctx_s nxt_python_run_ctx_t; - static nxt_int_t nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf); -static nxt_int_t nxt_python_run(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *msg); -static void nxt_python_atexit(nxt_task_t *task); +static void nxt_python_request_handler(nxt_unit_request_info_t *req); +static void nxt_python_atexit(void); static PyObject *nxt_python_create_environ(nxt_task_t *task); -static PyObject *nxt_python_get_environ(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, nxt_python_run_ctx_t *ctx); +static PyObject *nxt_python_get_environ(nxt_python_run_ctx_t *ctx); +static int nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, const char *name, + nxt_unit_sptr_t *sptr, uint32_t size); +static int nxt_python_add_str(nxt_python_run_ctx_t *ctx, const char *name, + char *str, uint32_t size); static PyObject *nxt_py_start_resp(PyObject *self, PyObject *args); +static int nxt_python_response_add_field(nxt_python_run_ctx_t *ctx, + PyObject *name, PyObject *value, int i); +static int nxt_python_str_buf(PyObject *str, char **buf, uint32_t *len, + PyObject **bytes); static PyObject *nxt_py_write(PyObject *self, PyObject *args); static void nxt_py_input_dealloc(nxt_py_input_t *self); @@ -80,35 +87,26 @@ static PyObject *nxt_py_input_read(nxt_py_input_t *self, PyObject *args); static PyObject *nxt_py_input_readline(nxt_py_input_t *self, PyObject *args); static PyObject *nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args); -struct nxt_python_run_ctx_s { - nxt_task_t *task; - nxt_app_rmsg_t *rmsg; - nxt_app_wmsg_t *wmsg; +static int nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes); - size_t body_preread_size; +struct nxt_python_run_ctx_s { + uint64_t content_length; + uint64_t bytes_sent; + PyObject *environ; + nxt_unit_request_info_t *req; }; -nxt_inline nxt_int_t nxt_python_write(nxt_python_run_ctx_t *ctx, - const u_char *data, size_t len, - nxt_bool_t flush, nxt_bool_t last); - -nxt_inline nxt_int_t nxt_python_write_py_str(nxt_python_run_ctx_t *ctx, - PyObject *str, nxt_bool_t flush, nxt_bool_t last); - - static uint32_t compat[] = { NXT_VERNUM, NXT_DEBUG, }; -NXT_EXPORT nxt_application_module_t nxt_app_module = { +NXT_EXPORT nxt_app_module_t nxt_app_module = { sizeof(compat), compat, nxt_string("python"), PY_VERSION, nxt_python_init, - nxt_python_run, - nxt_python_atexit, }; @@ -201,9 +199,12 @@ static nxt_python_run_ctx_t *nxt_python_run_ctx; static nxt_int_t nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) { + int rc; char *nxt_py_module; size_t len; PyObject *obj, *pypath, *module; + nxt_unit_ctx_t *unit_ctx; + nxt_unit_init_t python_init; nxt_python_app_conf_t *c; #if PY_MAJOR_VERSION == 3 char *path; @@ -378,6 +379,23 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_py_application = obj; + nxt_unit_default_init(task, &python_init); + + python_init.callbacks.request_handler = nxt_python_request_handler; + + unit_ctx = nxt_unit_init(&python_init); + if (nxt_slow_path(unit_ctx == NULL)) { + return NXT_ERROR; + } + + rc = nxt_unit_run(unit_ctx); + + nxt_unit_done(unit_ctx); + + nxt_python_atexit(); + + exit(rc); + return NXT_OK; fail: @@ -393,26 +411,26 @@ fail: } -static nxt_int_t -nxt_python_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) +static void +nxt_python_request_handler(nxt_unit_request_info_t *req) { - u_char *buf; - size_t size; - PyObject *result, *iterator, *item, *args, *environ; - nxt_python_run_ctx_t run_ctx = {task, rmsg, wmsg, 0}; - - environ = nxt_python_get_environ(task, rmsg, &run_ctx); + int rc; + PyObject *result, *iterator, *item, *args, *environ; + nxt_python_run_ctx_t run_ctx = {-1, 0, NULL, req}; + environ = nxt_python_get_environ(&run_ctx); if (nxt_slow_path(environ == NULL)) { - return NXT_ERROR; + nxt_unit_request_done(req, NXT_UNIT_ERROR); + + return; } args = PyTuple_New(2); - if (nxt_slow_path(args == NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "Python failed to create arguments tuple"); - return NXT_ERROR; + nxt_unit_req_error(req, "Python failed to create arguments tuple"); + + nxt_unit_request_done(req, NXT_UNIT_ERROR); + return; } PyTuple_SET_ITEM(args, 0, environ); @@ -427,70 +445,73 @@ nxt_python_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) Py_DECREF(args); if (nxt_slow_path(result == NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "Python failed to call the application"); + nxt_unit_req_error(req, "Python failed to call the application"); PyErr_Print(); - return NXT_ERROR; + + nxt_unit_request_done(req, NXT_UNIT_ERROR); + nxt_python_run_ctx = NULL; + + return; } item = NULL; iterator = NULL; /* Shortcut: avoid iterate over result string symbols. */ - if (PyBytes_Check(result) != 0) { - - size = PyBytes_GET_SIZE(result); - buf = (u_char *) PyBytes_AS_STRING(result); + if (PyBytes_Check(result)) { - nxt_python_write(&run_ctx, buf, size, 1, 1); + rc = nxt_python_write(&run_ctx, result); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } } else { iterator = PyObject_GetIter(result); if (nxt_slow_path(iterator == NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "the application returned not an iterable object"); + nxt_unit_req_error(req, "the application returned " + "not an iterable object"); goto fail; } - while((item = PyIter_Next(iterator))) { - - if (nxt_slow_path(PyBytes_Check(item) == 0)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "the application returned not a bytestring object"); + while (run_ctx.bytes_sent < run_ctx.content_length + && (item = PyIter_Next(iterator))) + { + if (nxt_slow_path(!PyBytes_Check(item))) { + nxt_unit_req_error(req, "the application returned " + "not a bytestring object"); goto fail; } - size = PyBytes_GET_SIZE(item); - buf = (u_char *) PyBytes_AS_STRING(item); - - nxt_debug(task, "nxt_app_write(fake): %uz", size); - - nxt_python_write(&run_ctx, buf, size, 1, 0); + rc = nxt_python_write(&run_ctx, item); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } Py_DECREF(item); } Py_DECREF(iterator); - nxt_python_write(&run_ctx, NULL, 0, 1, 1); - if (PyObject_HasAttrString(result, "close")) { PyObject_CallMethod(result, (char *) "close", NULL); } } if (nxt_slow_path(PyErr_Occurred() != NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, "an application error occurred"); + nxt_unit_req_error(req, "an application error occurred"); PyErr_Print(); } + nxt_unit_request_done(req, NXT_UNIT_OK); + Py_DECREF(result); + nxt_python_run_ctx = NULL; - return NXT_OK; + return; fail: @@ -509,12 +530,12 @@ fail: Py_DECREF(result); nxt_python_run_ctx = NULL; - return NXT_ERROR; + nxt_unit_request_done(req, NXT_UNIT_ERROR); } static void -nxt_python_atexit(nxt_task_t *task) +nxt_python_atexit(void) { Py_DECREF(nxt_py_application); Py_DECREF(nxt_py_start_resp_obj); @@ -673,189 +694,182 @@ fail: return NULL; } -nxt_inline nxt_int_t -nxt_python_add_env(nxt_task_t *task, PyObject *env, const char *name, - nxt_str_t *v) -{ - PyObject *value; - nxt_int_t rc; - value = PyString_FromStringAndSize((char *) v->start, v->length); - if (nxt_slow_path(value == NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "Python failed to create value string \"%V\"", v); - return NXT_ERROR; - } +static PyObject * +nxt_python_get_environ(nxt_python_run_ctx_t *ctx) +{ + int rc; + char *name, *host_start, *port_start; + uint32_t i, host_length, port_length; + PyObject *environ; + nxt_unit_field_t *f; + nxt_unit_request_t *r; - if (nxt_slow_path(PyDict_SetItemString(env, name, value) - != 0)) - { - nxt_log_error(NXT_LOG_ERR, task->log, - "Python failed to set the \"%s\" environ value", name); - rc = NXT_ERROR; + environ = PyDict_Copy(nxt_py_environ_ptyp); + if (nxt_slow_path(environ == NULL)) { + nxt_unit_req_error(ctx->req, + "Python failed to copy the \"environ\" dictionary"); - } else { - rc = NXT_OK; + return NULL; } - Py_DECREF(value); + ctx->environ = environ; - return rc; -} + r = ctx->req->request; +#define RC(S) \ + do { \ + rc = (S); \ + if (nxt_slow_path(rc != NXT_UNIT_OK)) { \ + goto fail; \ + } \ + } while(0) -nxt_inline nxt_int_t -nxt_python_read_add_env(nxt_task_t *task, nxt_app_rmsg_t *rmsg, - PyObject *env, const char *name, nxt_str_t *v) -{ - nxt_int_t rc; + RC(nxt_python_add_sptr(ctx, "REQUEST_METHOD", &r->method, + r->method_length)); + RC(nxt_python_add_sptr(ctx, "REQUEST_URI", &r->target, r->target_length)); - rc = nxt_app_msg_read_str(task, rmsg, v); - if (nxt_slow_path(rc != NXT_OK)) { - return rc; + if (r->query.offset) { + RC(nxt_python_add_sptr(ctx, "QUERY_STRING", &r->query, + r->query_length)); } + RC(nxt_python_add_sptr(ctx, "PATH_INFO", &r->path, r->path_length)); - if (v->start == NULL) { - return NXT_OK; - } - - return nxt_python_add_env(task, env, name, v); -} + 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)); + RC(nxt_python_add_sptr(ctx, "SERVER_PROTOCOL", &r->version, + r->version_length)); -static PyObject * -nxt_python_get_environ(nxt_task_t *task, nxt_app_rmsg_t *rmsg, - nxt_python_run_ctx_t *ctx) -{ - size_t s; - u_char *colon; - PyObject *environ; - nxt_int_t rc; - nxt_str_t n, v, target, path, query; - nxt_str_t host, server_name, server_port; + for (i = 0; i < r->fields_count; i++) { + f = r->fields + i; + name = nxt_unit_sptr_get(&f->name); - static nxt_str_t def_host = nxt_string("localhost"); - static nxt_str_t def_port = nxt_string("80"); + RC(nxt_python_add_sptr(ctx, name, &f->value, f->value_length)); + } - environ = PyDict_Copy(nxt_py_environ_ptyp); + if (r->content_length_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->content_length_field; - if (nxt_slow_path(environ == NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "Python failed to create the \"environ\" dictionary"); - return NULL; + RC(nxt_python_add_sptr(ctx, "CONTENT_LENGTH", &f->value, + f->value_length)); } -#define RC(S) \ - do { \ - rc = (S); \ - if (nxt_slow_path(rc != NXT_OK)) { \ - goto fail; \ - } \ - } while(0) + if (r->content_type_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->content_type_field; -#define NXT_READ(N) \ - RC(nxt_python_read_add_env(task, rmsg, environ, N, &v)) + RC(nxt_python_add_sptr(ctx, "CONTENT_TYPE", &f->value, + f->value_length)); + } - NXT_READ("REQUEST_METHOD"); - NXT_READ("REQUEST_URI"); + if (r->host_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->host_field; - target = v; - RC(nxt_app_msg_read_str(task, rmsg, &path)); + host_start = nxt_unit_sptr_get(&f->value); + host_length = f->value_length; - RC(nxt_app_msg_read_size(task, rmsg, &s)); // query length + 1 - if (s > 0) { - s--; + } else { + host_start = NULL; + host_length = 0; + } - query.start = target.start + s; - query.length = target.length - s; + nxt_unit_split_host(host_start, host_length, &host_start, &host_length, + &port_start, &port_length); - RC(nxt_python_add_env(task, environ, "QUERY_STRING", &query)); + RC(nxt_python_add_str(ctx, "SERVER_NAME", host_start, host_length)); + RC(nxt_python_add_str(ctx, "SERVER_PORT", port_start, port_length)); - if (path.start == NULL) { - path.start = target.start; - path.length = s - 1; - } - } +#undef RC - if (path.start == NULL) { - path = target; - } + return environ; + +fail: - RC(nxt_python_add_env(task, environ, "PATH_INFO", &path)); + Py_DECREF(environ); - NXT_READ("SERVER_PROTOCOL"); + return NULL; +} - NXT_READ("REMOTE_ADDR"); - NXT_READ("SERVER_ADDR"); - RC(nxt_app_msg_read_str(task, rmsg, &host)); +static int +nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, const char *name, + nxt_unit_sptr_t *sptr, uint32_t size) +{ + char *src; + PyObject *value; - if (host.length == 0) { - host = def_host; - } + src = nxt_unit_sptr_get(sptr); - server_name = host; - colon = nxt_memchr(host.start, ':', host.length); + value = PyString_FromStringAndSize(src, size); + if (nxt_slow_path(value == NULL)) { + nxt_unit_req_error(ctx->req, + "Python failed to create value string \"%.*s\"", + (int) size, src); - if (colon != NULL) { - server_name.length = colon - host.start; + return NXT_UNIT_ERROR; + } - server_port.start = colon + 1; - server_port.length = host.length - server_name.length - 1; + if (nxt_slow_path(PyDict_SetItemString(ctx->environ, name, value) != 0)) { + nxt_unit_req_error(ctx->req, + "Python failed to set the \"%s\" environ value", + name); + Py_DECREF(value); - } else { - server_port = def_port; + return NXT_UNIT_ERROR; } - RC(nxt_python_add_env(task, environ, "SERVER_NAME", &server_name)); - RC(nxt_python_add_env(task, environ, "SERVER_PORT", &server_port)); + Py_DECREF(value); - NXT_READ("CONTENT_TYPE"); - NXT_READ("CONTENT_LENGTH"); + return NXT_UNIT_OK; +} - while (nxt_app_msg_read_str(task, rmsg, &n) == NXT_OK) { - if (nxt_slow_path(n.length == 0)) { - break; - } - rc = nxt_app_msg_read_str(task, rmsg, &v); - if (nxt_slow_path(rc != NXT_OK)) { - break; - } +static int +nxt_python_add_str(nxt_python_run_ctx_t *ctx, const char *name, + char *str, uint32_t size) +{ + PyObject *value; - RC(nxt_python_add_env(task, environ, (char *) n.start, &v)); + if (nxt_slow_path(str == NULL)) { + return NXT_UNIT_OK; } - RC(nxt_app_msg_read_size(task, rmsg, &ctx->body_preread_size)); + value = PyString_FromStringAndSize(str, size); + if (nxt_slow_path(value == NULL)) { + nxt_unit_req_error(ctx->req, + "Python failed to create value string \"%.*s\"", + (int) size, str); -#undef NXT_READ -#undef RC + return NXT_UNIT_ERROR; + } - return environ; + if (nxt_slow_path(PyDict_SetItemString(ctx->environ, name, value) != 0)) { + nxt_unit_req_error(ctx->req, + "Python failed to set the \"%s\" environ value", + name); -fail: + Py_DECREF(value); - Py_DECREF(environ); + return NXT_UNIT_ERROR; + } - return NULL; + Py_DECREF(value); + + return NXT_UNIT_OK; } static PyObject * nxt_py_start_resp(PyObject *self, PyObject *args) { - PyObject *headers, *tuple, *string; - nxt_int_t rc; - nxt_uint_t i, n; + int rc, status; + char *status_str, *space_ptr; + uint32_t status_len; + PyObject *headers, *tuple, *string, *status_bytes; + Py_ssize_t i, n, fields_size, fields_count; nxt_python_run_ctx_t *ctx; - static const u_char status[] = "Status: "; - - static const u_char cr_lf[] = "\r\n"; - static const u_char sc_sp[] = ": "; - ctx = nxt_python_run_ctx; - if (nxt_slow_path(ctx == NULL)) { return PyErr_Format(PyExc_RuntimeError, "start_response() is called " @@ -869,25 +883,21 @@ nxt_py_start_resp(PyObject *self, PyObject *args) } string = PyTuple_GET_ITEM(args, 0); - - nxt_python_write(ctx, status, nxt_length(status), 0, 0); - - rc = nxt_python_write_py_str(ctx, string, 0, 0); - if (nxt_slow_path(rc != NXT_OK)) { + if (!PyBytes_Check(string) && !PyUnicode_Check(string)) { return PyErr_Format(PyExc_TypeError, "failed to write first argument (not a string?)"); } - nxt_python_write(ctx, cr_lf, nxt_length(cr_lf), 0, 0); - headers = PyTuple_GET_ITEM(args, 1); - if (!PyList_Check(headers)) { return PyErr_Format(PyExc_TypeError, "the second argument is not a response headers list"); } - for (i = 0; i < (nxt_uint_t) PyList_GET_SIZE(headers); i++) { + fields_size = 0; + fields_count = PyList_GET_SIZE(headers); + + for (i = 0; i < fields_count; i++) { tuple = PyList_GET_ITEM(headers, i); if (!PyTuple_Check(tuple)) { @@ -901,52 +911,182 @@ nxt_py_start_resp(PyObject *self, PyObject *args) } string = PyTuple_GET_ITEM(tuple, 0); + if (PyBytes_Check(string)) { + fields_size += PyBytes_GET_SIZE(string); - rc = nxt_python_write_py_str(ctx, string, 0, 0); - if (nxt_slow_path(rc != NXT_OK)) { + } else if (PyUnicode_Check(string)) { + fields_size += PyUnicode_GET_SIZE(string); + + } else { return PyErr_Format(PyExc_TypeError, - "failed to write response header name" - " (not a string?)"); + "header #%d name is not a string", (int) i); } - nxt_python_write(ctx, sc_sp, nxt_length(sc_sp), 0, 0); - string = PyTuple_GET_ITEM(tuple, 1); + if (PyBytes_Check(string)) { + fields_size += PyBytes_GET_SIZE(string); - rc = nxt_python_write_py_str(ctx, string, 0, 0); - if (nxt_slow_path(rc != NXT_OK)) { + } else if (PyUnicode_Check(string)) { + fields_size += PyUnicode_GET_SIZE(string); + + } else { return PyErr_Format(PyExc_TypeError, - "failed to write response header value" - " (not a string?)"); + "header #%d value is not a string", (int) i); } + } - nxt_python_write(ctx, cr_lf, nxt_length(cr_lf), 0, 0); + ctx->content_length = -1; + + string = PyTuple_GET_ITEM(args, 0); + rc = nxt_python_str_buf(string, &status_str, &status_len, &status_bytes); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return PyErr_Format(PyExc_TypeError, "status is not a string"); + } + + space_ptr = memchr(status_str, ' ', status_len); + if (space_ptr != NULL) { + status_len = space_ptr - status_str; + } + + status = nxt_int_parse((u_char *) status_str, status_len); + if (nxt_slow_path(status < 0)) { + return PyErr_Format(PyExc_TypeError, "failed to parse status code"); + } + + Py_XDECREF(status_bytes); + + /* + * PEP 3333: + * + * ... applications can replace their originally intended output with error + * output, up until the last possible moment. + */ + rc = nxt_unit_response_init(ctx->req, status, fields_count, fields_size); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return PyErr_Format(PyExc_RuntimeError, + "failed to allocate response object"); + } + + for (i = 0; i < fields_count; i++) { + tuple = PyList_GET_ITEM(headers, i); + + rc = nxt_python_response_add_field(ctx, PyTuple_GET_ITEM(tuple, 0), + PyTuple_GET_ITEM(tuple, 1), i); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return PyErr_Format(PyExc_RuntimeError, + "failed to add header #%d", (int) i); + } } - /* flush headers */ - nxt_python_write(ctx, cr_lf, nxt_length(cr_lf), 1, 0); + /* + * PEP 3333: + * + * However, the start_response callable must not actually transmit the + * response headers. Instead, it must store them for the server or gateway + * to transmit only after the first iteration of the application return + * value that yields a non-empty bytestring, or upon the application's + * first invocation of the write() callable. In other words, response + * headers must not be sent until there is actual body data available, or + * until the application's returned iterable is exhausted. (The only + * possible exception to this rule is if the response headers explicitly + * include a Content-Length of zero.) + */ + if (ctx->content_length == 0) { + rc = nxt_unit_response_send(ctx->req); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return PyErr_Format(PyExc_RuntimeError, + "failed to send response headers"); + } + } Py_INCREF(nxt_py_write_obj); return nxt_py_write_obj; } +static int +nxt_python_response_add_field(nxt_python_run_ctx_t *ctx, PyObject *name, + PyObject *value, int i) +{ + int rc; + char *name_str, *value_str; + uint32_t name_length, value_length; + PyObject *name_bytes, *value_bytes; + nxt_off_t content_length; + + name_bytes = NULL; + value_bytes = NULL; + + rc = nxt_python_str_buf(name, &name_str, &name_length, &name_bytes); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } + + rc = nxt_python_str_buf(value, &value_str, &value_length, &value_bytes); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } + + rc = nxt_unit_response_add_field(ctx->req, name_str, name_length, + value_str, value_length); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } + + if (ctx->req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) { + content_length = nxt_off_t_parse((u_char *) value_str, value_length); + if (nxt_slow_path(content_length < 0)) { + nxt_unit_req_error(ctx->req, "failed to parse Content-Length " + "value %.*s", (int) value_length, value_str); + + } else { + ctx->content_length = content_length; + } + } + +fail: + + Py_XDECREF(name_bytes); + Py_XDECREF(value_bytes); + + return rc; +} + + +static int +nxt_python_str_buf(PyObject *str, char **buf, uint32_t *len, PyObject **bytes) +{ + if (PyBytes_Check(str)) { + *buf = PyBytes_AS_STRING(str); + *len = PyBytes_GET_SIZE(str); + *bytes = NULL; + + } else { + *bytes = PyUnicode_AsLatin1String(str); + if (nxt_slow_path(*bytes == NULL)) { + return NXT_UNIT_ERROR; + } + + *buf = PyBytes_AS_STRING(*bytes); + *len = PyBytes_GET_SIZE(*bytes); + } + + return NXT_UNIT_OK; +} + + static PyObject * nxt_py_write(PyObject *self, PyObject *str) { - nxt_int_t rc; + int rc; if (nxt_fast_path(!PyBytes_Check(str))) { return PyErr_Format(PyExc_TypeError, "the argument is not a %s", NXT_PYTHON_BYTES_TYPE); } - rc = nxt_app_msg_write_raw(nxt_python_run_ctx->task, - nxt_python_run_ctx->wmsg, - (const u_char *) PyBytes_AS_STRING(str), - PyBytes_GET_SIZE(str)); - - if (nxt_slow_path(rc != NXT_OK)) { + rc = nxt_python_write(nxt_python_run_ctx, str); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { return PyErr_Format(PyExc_RuntimeError, "failed to write response value"); } @@ -965,22 +1105,19 @@ nxt_py_input_dealloc(nxt_py_input_t *self) static PyObject * nxt_py_input_read(nxt_py_input_t *self, PyObject *args) { - u_char *buf; - size_t copy_size; - PyObject *body, *obj; - Py_ssize_t size; - nxt_uint_t n; + char *buf; + PyObject *content, *obj; + Py_ssize_t size, n; nxt_python_run_ctx_t *ctx; ctx = nxt_python_run_ctx; - if (nxt_slow_path(ctx == NULL)) { return PyErr_Format(PyExc_RuntimeError, "wsgi.input.read() is called " "outside of WSGI request processing"); } - size = ctx->body_preread_size; + size = ctx->req->content_length; n = PyTuple_GET_SIZE(args); @@ -1002,25 +1139,21 @@ nxt_py_input_read(nxt_py_input_t *self, PyObject *args) "the read body size cannot be zero or less"); } - if (size == 0 || size > (Py_ssize_t) ctx->body_preread_size) { - size = ctx->body_preread_size; + if (size == 0 || size > (Py_ssize_t) ctx->req->content_length) { + size = ctx->req->content_length; } } - body = PyBytes_FromStringAndSize(NULL, size); - - if (nxt_slow_path(body == NULL)) { + content = PyBytes_FromStringAndSize(NULL, size); + if (nxt_slow_path(content == NULL)) { return NULL; } - buf = (u_char *) PyBytes_AS_STRING(body); + buf = PyBytes_AS_STRING(content); - copy_size = nxt_min((size_t) size, ctx->body_preread_size); - copy_size = nxt_app_msg_read_raw(ctx->task, ctx->rmsg, buf, copy_size); + size = nxt_unit_request_read(ctx->req, buf, size); - ctx->body_preread_size -= copy_size; - - return body; + return content; } @@ -1038,49 +1171,38 @@ nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args) } -nxt_inline nxt_int_t -nxt_python_write(nxt_python_run_ctx_t *ctx, const u_char *data, size_t len, - nxt_bool_t flush, nxt_bool_t last) +static int +nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes) { - nxt_int_t rc; + int rc; + char *str_buf; + uint32_t str_length; - rc = nxt_app_msg_write_raw(ctx->task, ctx->wmsg, data, len); + str_buf = PyBytes_AS_STRING(bytes); + str_length = PyBytes_GET_SIZE(bytes); - if (flush || last) { - rc = nxt_app_msg_flush(ctx->task, ctx->wmsg, last); + if (nxt_slow_path(str_length == 0)) { + return NXT_UNIT_OK; } - return rc; -} - - -nxt_inline nxt_int_t -nxt_python_write_py_str(nxt_python_run_ctx_t *ctx, PyObject *str, - nxt_bool_t flush, nxt_bool_t last) -{ - PyObject *bytes; - nxt_int_t rc; - - rc = NXT_OK; - - if (PyBytes_Check(str)) { - rc = nxt_python_write(ctx, (u_char *) PyBytes_AS_STRING(str), - PyBytes_GET_SIZE(str), flush, last); - - } else { - if (!PyUnicode_Check(str)) { - return NXT_ERROR; - } - - bytes = PyUnicode_AsLatin1String(str); - if (nxt_slow_path(bytes == NULL)) { - return NXT_ERROR; - } - - rc = nxt_python_write(ctx, (u_char *) PyBytes_AS_STRING(bytes), - PyBytes_GET_SIZE(bytes), flush, last); + /* + * PEP 3333: + * + * If the application supplies a Content-Length header, the server should + * not transmit more bytes to the client than the header allows, and should + * stop iterating over the response when enough data has been sent, or raise + * an error if the application tries to write() past that point. + */ + if (nxt_slow_path(str_length > ctx->content_length - ctx->bytes_sent)) { + nxt_unit_req_error(ctx->req, "content length %"PRIu64" exceeded", + ctx->content_length); + + return NXT_UNIT_ERROR; + } - Py_DECREF(bytes); + rc = nxt_unit_response_write(ctx->req, str_buf, str_length); + if (nxt_fast_path(rc == NXT_UNIT_OK)) { + ctx->bytes_sent += str_length; } return rc; diff --git a/src/nxt_router.c b/src/nxt_router.c index d9df48ec..4c25341f 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -8,6 +8,9 @@ #include <nxt_router.h> #include <nxt_conf.h> #include <nxt_http.h> +#include <nxt_port_memory_int.h> +#include <nxt_unit_request.h> +#include <nxt_unit_response.h> typedef struct { @@ -241,16 +244,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_int_t nxt_python_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, - nxt_app_wmsg_t *wmsg); -static nxt_int_t nxt_php_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, - nxt_app_wmsg_t *wmsg); -static nxt_int_t nxt_go_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, - nxt_app_wmsg_t *wmsg); -static nxt_int_t nxt_perl_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, - nxt_app_wmsg_t *wmsg); -static nxt_int_t nxt_ruby_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, - nxt_app_wmsg_t *wmsg); +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 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, @@ -265,13 +260,15 @@ static void nxt_http_request_send_body(nxt_task_t *task, void *obj, void *data); static nxt_router_t *nxt_router; +static const nxt_str_t http_prefix = nxt_string("HTTP_"); +static const nxt_str_t empty_prefix = nxt_string(""); -static nxt_app_prepare_msg_t nxt_app_prepare_msg[] = { - nxt_python_prepare_msg, - nxt_php_prepare_msg, - nxt_go_prepare_msg, - nxt_perl_prepare_msg, - nxt_ruby_prepare_msg, +static const nxt_str_t *nxt_app_msg_prefix[] = { + &http_prefix, + &http_prefix, + &empty_prefix, + &http_prefix, + &http_prefix, }; @@ -1459,7 +1456,6 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, app->live = 1; app->max_pending_responses = 2; app->max_requests = apcf.requests; - app->prepare_msg = nxt_app_prepare_msg[lang->type]; engine = task->thread->engine; @@ -3145,6 +3141,7 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, 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; @@ -3204,11 +3201,48 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_http_request_send_body(task, r, NULL); } else { + size_t b_size = nxt_buf_mem_used_size(&b->mem); + + if (nxt_slow_path(b_size < sizeof(*resp))) { + goto fail; + } + + resp = (void *) b->mem.pos; + if (nxt_slow_path(b_size < sizeof(*resp) + + resp->fields_count * sizeof(nxt_unit_field_t))) { + 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); + + if (nxt_slow_path(field == NULL)) { + goto fail; + } + + field->hash = f->hash; + field->skip = f->skip; + + field->name_length = f->name_length; + field->value_length = f->value_length; + field->name = nxt_unit_sptr_get(&f->name); + field->value = nxt_unit_sptr_get(&f->value); + + nxt_debug(task, "header: %*s: %*s", + (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; ret = nxt_http_fields_process(r->resp.fields, @@ -3217,6 +3251,14 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, goto fail; } + if (resp->piggyback_content_length != 0) { + b->mem.pos = nxt_unit_sptr_get(&resp->piggyback_content); + b->mem.free = b->mem.pos + resp->piggyback_content_length; + + } else { + b->mem.pos = b->mem.free; + } + if (nxt_buf_mem_used_size(&b->mem) == 0) { nxt_work_queue_add(&task->thread->engine->fast_work_queue, b->completion_handler, task, b, b->parent); @@ -4208,10 +4250,9 @@ static void nxt_router_app_prepare_request(nxt_task_t *task, nxt_req_app_link_t *ra) { uint32_t request_failed; - nxt_buf_t *b; + nxt_buf_t *buf; nxt_int_t res; nxt_port_t *port, *c_port, *reply_port; - nxt_app_wmsg_t wmsg; nxt_app_parse_ctx_t *ap; nxt_assert(ra->app_port != NULL); @@ -4236,32 +4277,30 @@ nxt_router_app_prepare_request(nxt_task_t *task, nxt_req_app_link_t *ra) nxt_process_connected_port_add(port->process, reply_port); } - wmsg.port = port; - wmsg.write = NULL; - wmsg.buf = &wmsg.write; - wmsg.stream = ra->stream; - - res = port->app->prepare_msg(task, &ap->r, &wmsg); + buf = nxt_router_prepare_msg(task, &ap->r, port, + nxt_app_msg_prefix[port->app->type]); - if (nxt_slow_path(res != NXT_OK)) { + if (nxt_slow_path(buf == NULL)) { nxt_router_ra_error(ra, 500, "Failed to prepare message for application"); goto release_port; } nxt_debug(task, "about to send %O bytes buffer to app process port %d", - nxt_buf_used_size(wmsg.write), - wmsg.port->socket.fd); + nxt_buf_used_size(buf), + port->socket.fd); request_failed = 0; - ra->msg_info.buf = wmsg.write; - ra->msg_info.completion_handler = wmsg.write->completion_handler; + ra->msg_info.buf = buf; + ra->msg_info.completion_handler = buf->completion_handler; - for (b = wmsg.write; b != NULL; b = b->next) { - b->completion_handler = nxt_router_dummy_buf_completion; + for (; buf; buf = buf->next) { + buf->completion_handler = nxt_router_dummy_buf_completion; } + buf = ra->msg_info.buf; + res = nxt_port_mmap_get_tracking(task, port, &ra->msg_info.tracking, ra->stream); if (nxt_slow_path(res != NXT_OK)) { @@ -4270,8 +4309,8 @@ nxt_router_app_prepare_request(nxt_task_t *task, nxt_req_app_link_t *ra) goto release_port; } - res = nxt_port_socket_twrite(task, wmsg.port, NXT_PORT_MSG_DATA, - -1, ra->stream, reply_port->id, wmsg.write, + res = nxt_port_socket_twrite(task, port, NXT_PORT_MSG_DATA, + -1, ra->stream, reply_port->id, buf, &ra->msg_info.tracking); if (nxt_slow_path(res != NXT_OK)) { @@ -4288,421 +4327,317 @@ release_port: } -static nxt_int_t -nxt_python_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, - nxt_app_wmsg_t *wmsg) -{ - nxt_int_t rc; - nxt_buf_t *b; - nxt_http_field_t *field; - nxt_app_request_header_t *h; - - static const nxt_str_t prefix = nxt_string("HTTP_"); - static const nxt_str_t eof = nxt_null_string; - - h = &r->header; - -#define RC(S) \ - do { \ - rc = (S); \ - if (nxt_slow_path(rc != NXT_OK)) { \ - goto fail; \ - } \ - } while(0) - -#define NXT_WRITE(N) \ - RC(nxt_app_msg_write_str(task, wmsg, N)) - - /* TODO error handle, async mmap buffer assignment */ +struct nxt_fields_iter_s { + nxt_list_part_t *part; + nxt_http_field_t *field; +}; - NXT_WRITE(&h->method); - NXT_WRITE(&h->target); +typedef struct nxt_fields_iter_s nxt_fields_iter_t; - if (h->path.start == h->target.start) { - NXT_WRITE(&eof); - } else { - NXT_WRITE(&h->path); - } - - if (h->query.start != NULL) { - RC(nxt_app_msg_write_size(task, wmsg, - h->query.start - h->target.start + 1)); - } else { - RC(nxt_app_msg_write_size(task, wmsg, 0)); +static nxt_http_field_t * +nxt_fields_part_first(nxt_list_part_t *part, nxt_fields_iter_t *i) +{ + if (part == NULL) { + return NULL; } - NXT_WRITE(&h->version); - - NXT_WRITE(&r->remote); - NXT_WRITE(&r->local); - - NXT_WRITE(&h->host); - NXT_WRITE(&h->content_type); - NXT_WRITE(&h->content_length); - - nxt_list_each(field, h->fields) { - RC(nxt_app_msg_write_prefixed_upcase(task, wmsg, &prefix, field->name, - field->name_length)); - RC(nxt_app_msg_write(task, wmsg, field->value, field->value_length)); - - } nxt_list_loop; - - /* end-of-headers mark */ - NXT_WRITE(&eof); - - RC(nxt_app_msg_write_size(task, wmsg, r->body.preread_size)); - - for (b = r->body.buf; b != NULL; b = b->next) { - RC(nxt_app_msg_write_raw(task, wmsg, b->mem.pos, - nxt_buf_mem_used_size(&b->mem))); + while (part->nelts == 0) { + part = part->next; + if (part == NULL) { + return NULL; + } } -#undef NXT_WRITE -#undef RC - - return NXT_OK; - -fail: + i->part = part; + i->field = nxt_list_data(i->part); - return NXT_ERROR; + return i->field; } -static nxt_int_t -nxt_php_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, - nxt_app_wmsg_t *wmsg) +static nxt_http_field_t * +nxt_fields_first(nxt_list_t *fields, nxt_fields_iter_t *i) { - nxt_int_t rc; - nxt_buf_t *b; - nxt_http_field_t *field; - nxt_app_request_header_t *h; - - static const nxt_str_t prefix = nxt_string("HTTP_"); - static const nxt_str_t eof = nxt_null_string; - - h = &r->header; - -#define RC(S) \ - do { \ - rc = (S); \ - if (nxt_slow_path(rc != NXT_OK)) { \ - goto fail; \ - } \ - } while(0) - -#define NXT_WRITE(N) \ - RC(nxt_app_msg_write_str(task, wmsg, N)) - - /* TODO error handle, async mmap buffer assignment */ - - NXT_WRITE(&h->method); - NXT_WRITE(&h->target); - - if (h->path.start == h->target.start) { - NXT_WRITE(&eof); - - } else { - NXT_WRITE(&h->path); - } - - if (h->query.start != NULL) { - RC(nxt_app_msg_write_size(task, wmsg, - h->query.start - h->target.start + 1)); - } else { - RC(nxt_app_msg_write_size(task, wmsg, 0)); - } - - NXT_WRITE(&h->version); - - // PHP_SELF - // SCRIPT_NAME - // SCRIPT_FILENAME - // DOCUMENT_ROOT + return nxt_fields_part_first(nxt_list_part(fields), i); +} - NXT_WRITE(&r->remote); - NXT_WRITE(&r->local); - NXT_WRITE(&h->host); - NXT_WRITE(&h->cookie); - NXT_WRITE(&h->content_type); - NXT_WRITE(&h->content_length); +static nxt_http_field_t * +nxt_fields_next(nxt_fields_iter_t *i) +{ + nxt_http_field_t *end = nxt_list_data(i->part); - RC(nxt_app_msg_write_size(task, wmsg, h->parsed_content_length)); - RC(nxt_app_msg_write_size(task, wmsg, r->body.preread_size)); + end += i->part->nelts; + i->field++; - for (b = r->body.buf; b != NULL; b = b->next) { - RC(nxt_app_msg_write_raw(task, wmsg, b->mem.pos, - nxt_buf_mem_used_size(&b->mem))); + if (i->field < end) { + return i->field; } - nxt_list_each(field, h->fields) { - RC(nxt_app_msg_write_prefixed_upcase(task, wmsg, &prefix, field->name, - field->name_length)); - RC(nxt_app_msg_write(task, wmsg, field->value, field->value_length)); - - } nxt_list_loop; - - /* end-of-headers mark */ - NXT_WRITE(&eof); - -#undef NXT_WRITE -#undef RC - - return NXT_OK; - -fail: - - return NXT_ERROR; + return nxt_fields_part_first(i->part->next, i); } -static nxt_int_t -nxt_go_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, nxt_app_wmsg_t *wmsg) +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) { - nxt_int_t rc; - nxt_buf_t *b; - nxt_http_field_t *field; + 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; - static const nxt_str_t eof = nxt_null_string; - h = &r->header; -#define RC(S) \ - do { \ - rc = (S); \ - if (nxt_slow_path(rc != NXT_OK)) { \ - goto fail; \ - } \ - } while(0) - -#define NXT_WRITE(N) \ - RC(nxt_app_msg_write_str(task, wmsg, N)) - - /* TODO error handle, async mmap buffer assignment */ - - NXT_WRITE(&h->method); - NXT_WRITE(&h->target); - - if (h->path.start == h->target.start) { - NXT_WRITE(&eof); - - } else { - NXT_WRITE(&h->path); - } - - if (h->query.start != NULL) { - RC(nxt_app_msg_write_size(task, wmsg, - h->query.start - h->target.start + 1)); - } else { - RC(nxt_app_msg_write_size(task, wmsg, 0)); - } - - NXT_WRITE(&h->version); - NXT_WRITE(&r->remote); - - NXT_WRITE(&h->host); - NXT_WRITE(&h->cookie); - NXT_WRITE(&h->content_type); - NXT_WRITE(&h->content_length); + req_size = sizeof(nxt_unit_request_t) + + h->method.length + 1 + + h->version.length + 1 + + r->remote.length + 1 + + r->local.length + 1 + + h->target.length + 1 + + (h->path.start != h->target.start ? h->path.length + 1 : 0); - RC(nxt_app_msg_write_size(task, wmsg, h->parsed_content_length)); + fields_count = 0; nxt_list_each(field, h->fields) { - RC(nxt_app_msg_write(task, wmsg, field->name, field->name_length)); - RC(nxt_app_msg_write(task, wmsg, field->value, field->value_length)); + fields_count++; + req_size += field->name_length + prefix->length + 1 + + field->value_length + 1; } nxt_list_loop; - /* end-of-headers mark */ - NXT_WRITE(&eof); + req_size += fields_count * sizeof(nxt_unit_field_t); - RC(nxt_app_msg_write_size(task, wmsg, r->body.preread_size)); + if (nxt_slow_path(req_size > PORT_MMAP_DATA_SIZE)) { + nxt_alert(task, "headers to big to fit in shared memory (%d)", + (int) req_size); - for (b = r->body.buf; b != NULL; b = b->next) { - RC(nxt_app_msg_write_raw(task, wmsg, b->mem.pos, - nxt_buf_mem_used_size(&b->mem))); + return NULL; } -#undef NXT_WRITE -#undef RC - - return NXT_OK; - -fail: - - return NXT_ERROR; -} + out = nxt_port_mmap_get_buf(task, port, + nxt_min(req_size + r->body.preread_size, PORT_MMAP_DATA_SIZE)); + if (nxt_slow_path(out == NULL)) { + return NULL; + } + req = (nxt_unit_request_t *) out->mem.free; + out->mem.free += req_size; -static nxt_int_t -nxt_perl_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, - nxt_app_wmsg_t *wmsg) -{ - nxt_int_t rc; - nxt_str_t str; - nxt_buf_t *b; - nxt_http_field_t *field; - nxt_app_request_header_t *h; + req->content_length = h->parsed_content_length; - static const nxt_str_t prefix = nxt_string("HTTP_"); - static const nxt_str_t eof = nxt_null_string; + p = (u_char *) (req->fields + fields_count); - h = &r->header; + nxt_debug(task, "fields_count=%d", (int) fields_count); -#define RC(S) \ - do { \ - rc = (S); \ - if (nxt_slow_path(rc != NXT_OK)) { \ - goto fail; \ - } \ - } while(0) + req->method_length = h->method.length; + nxt_unit_sptr_set(&req->method, p); + p = nxt_cpymem(p, h->method.start, h->method.length); + *p++ = '\0'; -#define NXT_WRITE(N) \ - RC(nxt_app_msg_write_str(task, wmsg, N)) + req->version_length = h->version.length; + nxt_unit_sptr_set(&req->version, p); + p = nxt_cpymem(p, h->version.start, h->version.length); + *p++ = '\0'; - /* TODO error handle, async mmap buffer assignment */ + req->remote_length = r->remote.length; + nxt_unit_sptr_set(&req->remote, p); + p = nxt_cpymem(p, r->remote.start, r->remote.length); + *p++ = '\0'; - NXT_WRITE(&h->method); - NXT_WRITE(&h->target); + req->local_length = r->local.length; + nxt_unit_sptr_set(&req->local, p); + p = nxt_cpymem(p, r->local.start, r->local.length); + *p++ = '\0'; - if (h->query.length) { - str.start = h->target.start; - str.length = (h->target.length - h->query.length) - 1; + target_pos = p; + req->target_length = h->target.length; + nxt_unit_sptr_set(&req->target, p); + p = nxt_cpymem(p, h->target.start, h->target.length); + *p++ = '\0'; - RC(nxt_app_msg_write_str(task, wmsg, &str)); + req->path_length = h->path.length; + if (h->path.start == h->target.start) { + nxt_unit_sptr_set(&req->path, target_pos); } else { - NXT_WRITE(&eof); + nxt_unit_sptr_set(&req->path, p); + p = nxt_cpymem(p, h->path.start, h->path.length); + *p++ = '\0'; } + req->query_length = h->query.length; if (h->query.start != NULL) { - RC(nxt_app_msg_write_size(task, wmsg, - h->query.start - h->target.start + 1)); + query_pos = nxt_pointer_to(target_pos, + h->query.start - h->target.start); + + nxt_unit_sptr_set(&req->query, query_pos); + } else { - RC(nxt_app_msg_write_size(task, wmsg, 0)); + req->query.offset = 0; } - NXT_WRITE(&h->version); + req->host_field = NXT_UNIT_NONE_FIELD; + req->content_length_field = NXT_UNIT_NONE_FIELD; + req->content_type_field = NXT_UNIT_NONE_FIELD; + req->cookie_field = NXT_UNIT_NONE_FIELD; - NXT_WRITE(&r->remote); - NXT_WRITE(&r->local); + dst_field = req->fields; - NXT_WRITE(&h->host); - NXT_WRITE(&h->content_type); - NXT_WRITE(&h->content_length); + for (field = nxt_fields_first(h->fields, &iter); + field != NULL; + field = nxt_fields_next(&iter)) + { + if (field->skip) { + continue; + } - nxt_list_each(field, h->fields) { - RC(nxt_app_msg_write_prefixed_upcase(task, wmsg, &prefix, - field->name, field->name_length)); - RC(nxt_app_msg_write(task, wmsg, field->value, field->value_length)); - } nxt_list_loop; + dst_field->hash = field->hash; + dst_field->skip = 0; + dst_field->name_length = field->name_length + prefix->length; + dst_field->value_length = field->value_length; - /* end-of-headers mark */ - NXT_WRITE(&eof); + if (field->value == h->host.start) { + req->host_field = dst_field - req->fields; - RC(nxt_app_msg_write_size(task, wmsg, r->body.preread_size)); + } else if (field->value == h->content_length.start) { + req->content_length_field = dst_field - req->fields; - for (b = r->body.buf; b != NULL; b = b->next) { + } else if (field->value == h->content_type.start) { + req->content_type_field = dst_field - req->fields; - RC(nxt_app_msg_write_raw(task, wmsg, b->mem.pos, - nxt_buf_mem_used_size(&b->mem))); - } + } else if (field->value == h->cookie.start) { + req->cookie_field = dst_field - req->fields; + } -#undef NXT_WRITE -#undef RC + nxt_debug(task, "add field 0x%04Xd, %d, %d, %p : %d %p", + (int) field->hash, (int) field->skip, + (int) field->name_length, field->name, + (int) field->value_length, field->value); - return NXT_OK; + if (prefix->length != 0) { + nxt_unit_sptr_set(&dst_field->name, p); + p = nxt_cpymem(p, prefix->start, prefix->length); -fail: + end = field->name + field->name_length; + for (pos = field->name; pos < end; pos++) { + c = *pos; - return NXT_ERROR; -} + if (c >= 'a' && c <= 'z') { + *p++ = (c & ~0x20); + continue; + } + if (c == '-') { + *p++ = '_'; + continue; + } -static nxt_int_t -nxt_ruby_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, - nxt_app_wmsg_t *wmsg) -{ - nxt_int_t rc; - nxt_str_t str; - nxt_buf_t *b; - nxt_http_field_t *field; - nxt_app_request_header_t *h; + *p++ = c; + } - static const nxt_str_t prefix = nxt_string("HTTP_"); - static const nxt_str_t eof = nxt_null_string; + } else { + nxt_unit_sptr_set(&dst_field->name, p); + p = nxt_cpymem(p, field->name, field->name_length); + } - h = &r->header; + *p++ = '\0'; -#define RC(S) \ - do { \ - rc = (S); \ - if (nxt_slow_path(rc != NXT_OK)) { \ - goto fail; \ - } \ - } while(0) + nxt_unit_sptr_set(&dst_field->value, p); + p = nxt_cpymem(p, field->value, field->value_length); -#define NXT_WRITE(N) \ - RC(nxt_app_msg_write_str(task, wmsg, N)) + if (prefix->length != 0) { + dup_iter = iter; - /* TODO error handle, async mmap buffer assignment */ + for (dup = nxt_fields_next(&dup_iter); + dup != NULL; + dup = nxt_fields_next(&dup_iter)) + { + if (dup->name_length != field->name_length + || dup->skip + || dup->hash != field->hash + || nxt_memcasecmp(dup->name, field->name, dup->name_length)) + { + continue; + } - NXT_WRITE(&h->method); - NXT_WRITE(&h->target); + p = nxt_cpymem(p, ", ", 2); + p = nxt_cpymem(p, dup->value, dup->value_length); - if (h->query.length) { - str.start = h->target.start; - str.length = (h->target.length - h->query.length) - 1; + dst_field->value_length += 2 + dup->value_length; - RC(nxt_app_msg_write_str(task, wmsg, &str)); + dup->skip = 1; + } + } - } else { - NXT_WRITE(&eof); - } + *p++ = '\0'; - if (h->query.start != NULL) { - RC(nxt_app_msg_write_size(task, wmsg, - h->query.start - h->target.start + 1)); - } else { - RC(nxt_app_msg_write_size(task, wmsg, 0)); + dst_field++; } - NXT_WRITE(&h->version); + req->fields_count = dst_field - req->fields; - NXT_WRITE(&r->remote); - NXT_WRITE(&r->local); + nxt_unit_sptr_set(&req->preread_content, out->mem.free); - NXT_WRITE(&h->host); - NXT_WRITE(&h->content_type); - NXT_WRITE(&h->content_length); + buf = out; + tail = &buf->next; - nxt_list_each(field, h->fields) { - RC(nxt_app_msg_write_prefixed_upcase(task, wmsg, &prefix, - field->name, field->name_length)); - RC(nxt_app_msg_write(task, wmsg, field->value, field->value_length)); - } nxt_list_loop; + for (b = r->body.buf; b != NULL; b = b->next) { + size = nxt_buf_mem_used_size(&b->mem); + pos = b->mem.pos; + + while (size > 0) { + if (buf == NULL) { + free_size = nxt_min(size, PORT_MMAP_DATA_SIZE); + + buf = nxt_port_mmap_get_buf(task, port, free_size); + if (nxt_slow_path(buf == NULL)) { + while (out != NULL) { + buf = out->next; + out->completion_handler(task, out, out->parent); + out = buf; + } + return NULL; + } - /* end-of-headers mark */ - NXT_WRITE(&eof); + *tail = buf; + tail = &buf->next; - RC(nxt_app_msg_write_size(task, wmsg, r->body.preread_size)); + } else { + free_size = nxt_buf_mem_free_size(&buf->mem); + if (free_size < size + && nxt_port_mmap_increase_buf(task, buf, size, 1) + == NXT_OK) + { + free_size = nxt_buf_mem_free_size(&buf->mem); + } + } - for (b = r->body.buf; b != NULL; b = b->next) { + if (free_size > 0) { + copy_size = nxt_min(free_size, size); - RC(nxt_app_msg_write_raw(task, wmsg, b->mem.pos, - nxt_buf_mem_used_size(&b->mem))); - } + buf->mem.free = nxt_cpymem(buf->mem.free, pos, copy_size); -#undef NXT_WRITE -#undef RC + size -= copy_size; + pos += copy_size; - return NXT_OK; + if (size == 0) { + break; + } + } -fail: + buf = NULL; + } + } - return NXT_ERROR; + return out; } diff --git a/src/nxt_router.h b/src/nxt_router.h index 4d82cb31..57c526f3 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -81,11 +81,6 @@ typedef struct { } nxt_joint_job_t; - -typedef nxt_int_t (*nxt_app_prepare_msg_t)(nxt_task_t *task, - nxt_app_request_t *r, nxt_app_wmsg_t *wmsg); - - struct nxt_app_s { nxt_thread_mutex_t mutex; /* Protects ports queue. */ nxt_queue_t ports; /* of nxt_port_t.app_link */ @@ -120,7 +115,6 @@ struct nxt_app_s { nxt_queue_link_t link; nxt_str_t conf; - nxt_app_prepare_msg_t prepare_msg; nxt_atomic_t use_count; }; diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h index dbad16b0..124a64fd 100644 --- a/src/nxt_runtime.h +++ b/src/nxt_runtime.h @@ -106,7 +106,7 @@ void nxt_runtime_port_add(nxt_task_t *task, nxt_port_t *port); void nxt_runtime_port_remove(nxt_task_t *task, nxt_port_t *port); -nxt_port_t *nxt_runtime_port_find(nxt_runtime_t *rt, nxt_pid_t pid, +NXT_EXPORT nxt_port_t *nxt_runtime_port_find(nxt_runtime_t *rt, nxt_pid_t pid, nxt_port_id_t port_id); @@ -129,9 +129,6 @@ void nxt_cdecl nxt_log_time_handler(nxt_uint_t level, nxt_log_t *log, void nxt_stream_connection_init(nxt_task_t *task, void *obj, void *data); -void nxt_app_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); -void nxt_app_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); - #define nxt_runtime_process_each(rt, process) \ do { \ diff --git a/src/nxt_unit.c b/src/nxt_unit.c new file mode 100644 index 00000000..2694de7d --- /dev/null +++ b/src/nxt_unit.c @@ -0,0 +1,3630 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#include <stdlib.h> + +#include "nxt_main.h" +#include "nxt_port_memory_int.h" + +#include "nxt_unit.h" +#include "nxt_unit_request.h" +#include "nxt_unit_response.h" + +#if (NXT_HAVE_MEMFD_CREATE) +#include <linux/memfd.h> +#endif + +typedef struct nxt_unit_impl_s nxt_unit_impl_t; +typedef struct nxt_unit_mmap_s nxt_unit_mmap_t; +typedef struct nxt_unit_mmaps_s nxt_unit_mmaps_t; +typedef struct nxt_unit_process_s nxt_unit_process_t; +typedef struct nxt_unit_mmap_buf_s nxt_unit_mmap_buf_t; +typedef struct nxt_unit_recv_msg_s nxt_unit_recv_msg_t; +typedef struct nxt_unit_ctx_impl_s nxt_unit_ctx_impl_t; +typedef struct nxt_unit_port_impl_s nxt_unit_port_impl_t; +typedef struct nxt_unit_request_info_impl_s nxt_unit_request_info_impl_t; + +static nxt_unit_impl_t *nxt_unit_create(nxt_unit_init_t *init); +static void nxt_unit_ctx_init(nxt_unit_impl_t *lib, + nxt_unit_ctx_impl_t *ctx_impl, void *data); +static int nxt_unit_read_env(nxt_unit_port_t *ready_port, + nxt_unit_port_t *read_port, int *log_fd, uint32_t *stream); +static int nxt_unit_ready(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, + uint32_t stream); +static nxt_unit_request_info_impl_t *nxt_unit_request_info_get( + nxt_unit_ctx_t *ctx); +static void nxt_unit_request_info_release(nxt_unit_request_info_t *req); +static void nxt_unit_request_info_free(nxt_unit_request_info_impl_t *req); +static nxt_unit_process_t *nxt_unit_msg_get_process(nxt_unit_ctx_t *ctx, + nxt_unit_recv_msg_t *recv_msg); +static nxt_unit_mmap_buf_t *nxt_unit_mmap_buf_get(nxt_unit_ctx_t *ctx); +static void nxt_unit_mmap_buf_release(nxt_unit_mmap_buf_t *mmap_buf); +static int nxt_unit_mmap_buf_send(nxt_unit_ctx_t *ctx, uint32_t stream, + nxt_unit_mmap_buf_t *mmap_buf, int last); +static nxt_port_mmap_header_t *nxt_unit_mmap_get(nxt_unit_ctx_t *ctx, + nxt_unit_process_t *process, nxt_unit_port_id_t *port_id, + nxt_chunk_id_t *c, int n); +static nxt_unit_mmap_t *nxt_unit_mmap_at(nxt_unit_mmaps_t *mmaps, uint32_t i); +static nxt_port_mmap_header_t *nxt_unit_new_mmap(nxt_unit_ctx_t *ctx, + nxt_unit_process_t *process, nxt_unit_port_id_t *port_id, int n); +static int nxt_unit_send_mmap(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, + int fd); +static int nxt_unit_get_outgoing_buf(nxt_unit_ctx_t *ctx, + nxt_unit_process_t *process, nxt_unit_port_id_t *port_id, uint32_t size, + nxt_unit_mmap_buf_t *mmap_buf); +static int nxt_unit_incoming_mmap(nxt_unit_ctx_t *ctx, pid_t pid, int fd); + +static void nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps); +static void nxt_unit_process_use(nxt_unit_ctx_t *ctx, + nxt_unit_process_t *process, int i); +static void nxt_unit_mmaps_destroy(nxt_unit_mmaps_t *mmaps); +static nxt_port_mmap_header_t *nxt_unit_get_incoming_mmap(nxt_unit_ctx_t *ctx, + nxt_unit_process_t *process, uint32_t id); +static int nxt_unit_tracking_read(nxt_unit_ctx_t *ctx, + nxt_unit_recv_msg_t *recv_msg); +static int nxt_unit_mmap_read(nxt_unit_ctx_t *ctx, + nxt_unit_recv_msg_t *recv_msg, nxt_queue_t *incoming_buf); +static int nxt_unit_mmap_release(nxt_port_mmap_header_t *hdr, void *start, + uint32_t size); + +static nxt_unit_process_t *nxt_unit_process_get(nxt_unit_ctx_t *ctx, + pid_t pid); +static nxt_unit_process_t *nxt_unit_process_find(nxt_unit_ctx_t *ctx, + pid_t pid, int remove); +static nxt_unit_process_t *nxt_unit_process_pop_first(nxt_unit_impl_t *lib); +static int nxt_unit_run_once(nxt_unit_ctx_t *ctx); +static int nxt_unit_create_port(nxt_unit_ctx_t *ctx, + nxt_unit_port_id_t *port_id, int *fd); + +static int nxt_unit_send_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *dst, + nxt_unit_port_id_t *new_port, int fd); + +static void nxt_unit_remove_port_unsafe(nxt_unit_ctx_t *ctx, + nxt_unit_port_id_t *port_id, nxt_unit_port_t *r_port, + nxt_unit_process_t **process); +static void nxt_unit_remove_process(nxt_unit_ctx_t *ctx, + nxt_unit_process_t *process); + +static ssize_t nxt_unit_port_send_default(nxt_unit_ctx_t *ctx, + nxt_unit_port_id_t *port_id, const void *buf, size_t buf_size, + const void *oob, size_t oob_size); +static ssize_t nxt_unit_port_recv_default(nxt_unit_ctx_t *ctx, + nxt_unit_port_id_t *port_id, void *buf, size_t buf_size, + void *oob, size_t oob_size); + +static int nxt_unit_port_hash_add(nxt_lvlhsh_t *port_hash, + nxt_unit_port_t *port); +static nxt_unit_port_impl_t *nxt_unit_port_hash_find(nxt_lvlhsh_t *port_hash, + nxt_unit_port_id_t *port_id, int remove); + +static char * nxt_unit_snprint_prefix(char *p, char *end, pid_t pid, int level); + + +struct nxt_unit_mmap_buf_s { + nxt_unit_buf_t buf; + + nxt_port_mmap_header_t *hdr; + nxt_queue_link_t link; + nxt_unit_port_id_t port_id; + nxt_unit_request_info_t *req; + nxt_unit_ctx_impl_t *ctx_impl; +}; + + +struct nxt_unit_recv_msg_s { + nxt_port_msg_t port_msg; + + void *start; + uint32_t size; + + nxt_unit_process_t *process; +}; + + +typedef enum { + NXT_UNIT_RS_START = 0, + NXT_UNIT_RS_RESPONSE_INIT, + NXT_UNIT_RS_RESPONSE_HAS_CONTENT, + NXT_UNIT_RS_RESPONSE_SENT, + NXT_UNIT_RS_DONE, +} nxt_unit_req_state_t; + + +struct nxt_unit_request_info_impl_s { + nxt_unit_request_info_t req; + + nxt_unit_recv_msg_t recv_msg; + nxt_queue_t outgoing_buf; /* of nxt_unit_mmap_buf_t */ + nxt_queue_t incoming_buf; /* of nxt_unit_mmap_buf_t */ + + nxt_unit_req_state_t state; + + nxt_queue_link_t link; + + char extra_data[]; +}; + + +struct nxt_unit_ctx_impl_s { + nxt_unit_ctx_t ctx; + + nxt_unit_port_id_t read_port_id; + int read_port_fd; + + nxt_queue_link_t link; + + nxt_queue_t free_buf; /* of nxt_unit_mmap_buf_t */ + + /* of nxt_unit_request_info_impl_t */ + nxt_queue_t free_req; + + /* of nxt_unit_request_info_impl_t */ + nxt_queue_t active_req; + + nxt_unit_mmap_buf_t ctx_buf[2]; + + nxt_unit_request_info_impl_t req; +}; + + +struct nxt_unit_impl_s { + nxt_unit_t unit; + nxt_unit_callbacks_t callbacks; + + uint32_t request_data_size; + + pthread_mutex_t mutex; + + nxt_lvlhsh_t processes; /* of nxt_unit_process_t */ + nxt_lvlhsh_t ports; /* of nxt_unit_port_impl_t */ + + nxt_unit_port_id_t ready_port_id; + + nxt_queue_t contexts; /* of nxt_unit_ctx_impl_t */ + + pid_t pid; + int log_fd; + int online; + + nxt_unit_ctx_impl_t main_ctx; +}; + + +struct nxt_unit_port_impl_s { + nxt_unit_port_t port; + + nxt_queue_link_t link; + nxt_unit_process_t *process; +}; + + +struct nxt_unit_mmap_s { + nxt_port_mmap_header_t *hdr; +}; + + +struct nxt_unit_mmaps_s { + pthread_mutex_t mutex; + uint32_t size; + uint32_t cap; + nxt_unit_mmap_t *elts; +}; + + +struct nxt_unit_process_s { + pid_t pid; + + nxt_queue_t ports; + + nxt_unit_mmaps_t incoming; + nxt_unit_mmaps_t outgoing; + + nxt_unit_impl_t *lib; + + nxt_atomic_t use_count; + + uint32_t next_port_id; +}; + + +/* Explicitly using 32 bit types to avoid possible alignment. */ +typedef struct { + int32_t pid; + uint32_t id; +} nxt_unit_port_hash_id_t; + + +nxt_unit_ctx_t * +nxt_unit_init(nxt_unit_init_t *init) +{ + int rc; + uint32_t ready_stream; + nxt_unit_ctx_t *ctx; + nxt_unit_impl_t *lib; + nxt_unit_port_t ready_port, read_port; + + lib = nxt_unit_create(init); + if (nxt_slow_path(lib == NULL)) { + return NULL; + } + + if (init->ready_port.id.pid != 0 + && init->ready_stream != 0 + && init->read_port.id.pid != 0) + { + ready_port = init->ready_port; + ready_stream = init->ready_stream; + read_port = init->read_port; + lib->log_fd = init->log_fd; + + nxt_unit_port_id_init(&ready_port.id, ready_port.id.pid, + ready_port.id.id); + nxt_unit_port_id_init(&read_port.id, read_port.id.pid, + read_port.id.id); + } else { + rc = nxt_unit_read_env(&ready_port, &read_port, &lib->log_fd, + &ready_stream); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } + } + + ctx = &lib->main_ctx.ctx; + + rc = lib->callbacks.add_port(ctx, &ready_port); + if (rc != NXT_UNIT_OK) { + nxt_unit_alert(NULL, "failed to add ready_port"); + + goto fail; + } + + rc = lib->callbacks.add_port(ctx, &read_port); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_alert(NULL, "failed to add read_port"); + + goto fail; + } + + lib->main_ctx.read_port_id = read_port.id; + lib->ready_port_id = ready_port.id; + + rc = nxt_unit_ready(ctx, &ready_port.id, ready_stream); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_alert(NULL, "failed to send READY message"); + + goto fail; + } + + return ctx; + +fail: + + free(lib); + + return NULL; +} + + +static nxt_unit_impl_t * +nxt_unit_create(nxt_unit_init_t *init) +{ + int rc; + nxt_unit_impl_t *lib; + nxt_unit_callbacks_t *cb; + + lib = malloc(sizeof(nxt_unit_impl_t) + init->request_data_size); + if (nxt_slow_path(lib == NULL)) { + nxt_unit_alert(NULL, "failed to allocate unit struct"); + + return NULL; + } + + rc = pthread_mutex_init(&lib->mutex, NULL); + if (nxt_slow_path(rc != 0)) { + nxt_unit_alert(NULL, "failed to initialize mutex (%d)", rc); + + goto fail; + } + + lib->unit.data = init->data; + lib->callbacks = init->callbacks; + + lib->request_data_size = init->request_data_size; + + lib->processes.slot = NULL; + lib->ports.slot = NULL; + + lib->pid = getpid(); + lib->log_fd = STDERR_FILENO; + lib->online = 1; + + nxt_queue_init(&lib->contexts); + + nxt_unit_ctx_init(lib, &lib->main_ctx, init->ctx_data); + + cb = &lib->callbacks; + + if (cb->request_handler == NULL) { + nxt_unit_alert(NULL, "request_handler is NULL"); + + goto fail; + } + + if (cb->add_port == NULL) { + cb->add_port = nxt_unit_add_port; + } + + if (cb->remove_port == NULL) { + cb->remove_port = nxt_unit_remove_port; + } + + if (cb->remove_pid == NULL) { + cb->remove_pid = nxt_unit_remove_pid; + } + + if (cb->quit == NULL) { + cb->quit = nxt_unit_quit; + } + + if (cb->port_send == NULL) { + cb->port_send = nxt_unit_port_send_default; + } + + if (cb->port_recv == NULL) { + cb->port_recv = nxt_unit_port_recv_default; + } + + return lib; + +fail: + + free(lib); + + return NULL; +} + + +static void +nxt_unit_ctx_init(nxt_unit_impl_t *lib, nxt_unit_ctx_impl_t *ctx_impl, + void *data) +{ + ctx_impl->ctx.data = data; + ctx_impl->ctx.unit = &lib->unit; + + nxt_queue_insert_tail(&lib->contexts, &ctx_impl->link); + + nxt_queue_init(&ctx_impl->free_buf); + nxt_queue_init(&ctx_impl->free_req); + nxt_queue_init(&ctx_impl->active_req); + + nxt_queue_insert_tail(&ctx_impl->free_buf, &ctx_impl->ctx_buf[0].link); + nxt_queue_insert_tail(&ctx_impl->free_buf, &ctx_impl->ctx_buf[1].link); + nxt_queue_insert_tail(&ctx_impl->free_req, &ctx_impl->req.link); + + ctx_impl->req.req.ctx = &ctx_impl->ctx; + ctx_impl->req.req.unit = &lib->unit; + + ctx_impl->read_port_fd = -1; +} + + +static int +nxt_unit_read_env(nxt_unit_port_t *ready_port, nxt_unit_port_t *read_port, + int *log_fd, uint32_t *stream) +{ + int rc; + int ready_fd, read_fd; + char *unit_init, *version_end; + long version_length; + int64_t ready_pid, read_pid; + uint32_t ready_stream, ready_id, read_id; + + unit_init = getenv(NXT_UNIT_INIT_ENV); + if (nxt_slow_path(unit_init == NULL)) { + nxt_unit_alert(NULL, "%s is not in the current environment", + NXT_UNIT_INIT_ENV); + + return NXT_UNIT_ERROR; + } + + nxt_unit_debug(NULL, "%s='%s'", NXT_UNIT_INIT_ENV, unit_init); + + version_length = nxt_length(NXT_VERSION); + + version_end = strchr(unit_init, ';'); + if (version_end == NULL + || version_end - unit_init != version_length + || memcmp(unit_init, NXT_VERSION, version_length) != 0) + { + nxt_unit_alert(NULL, "version check error"); + + return NXT_UNIT_ERROR; + } + + rc = sscanf(version_end + 1, + "%"PRIu32";" + "%"PRId64",%"PRIu32",%d;" + "%"PRId64",%"PRIu32",%d;" + "%d", + &ready_stream, + &ready_pid, &ready_id, &ready_fd, + &read_pid, &read_id, &read_fd, + log_fd); + + if (nxt_slow_path(rc != 8)) { + nxt_unit_alert(NULL, "failed to scan variables"); + + return NXT_UNIT_ERROR; + } + + nxt_unit_port_id_init(&ready_port->id, (pid_t) ready_pid, ready_id); + + ready_port->in_fd = -1; + ready_port->out_fd = ready_fd; + ready_port->data = NULL; + + nxt_unit_port_id_init(&read_port->id, (pid_t) read_pid, read_id); + + read_port->in_fd = read_fd; + read_port->out_fd = -1; + read_port->data = NULL; + + *stream = ready_stream; + + return NXT_UNIT_OK; +} + + +static int +nxt_unit_ready(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, + uint32_t stream) +{ + ssize_t res; + nxt_port_msg_t msg; + nxt_unit_impl_t *lib; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + msg.stream = stream; + msg.pid = lib->pid; + msg.reply_port = 0; + msg.type = _NXT_PORT_MSG_PROCESS_READY; + msg.last = 1; + msg.mmap = 0; + msg.nf = 0; + msg.mf = 0; + msg.tracking = 0; + + res = lib->callbacks.port_send(ctx, port_id, &msg, sizeof(msg), NULL, 0); + if (res != sizeof(msg)) { + return NXT_UNIT_ERROR; + } + + return NXT_UNIT_OK; +} + + +int +nxt_unit_process_msg(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, + void *buf, size_t buf_size, void *oob, size_t oob_size) +{ + int fd, rc; + pid_t pid; + nxt_queue_t incoming_buf; + struct cmsghdr *cm; + nxt_port_msg_t *port_msg; + nxt_unit_impl_t *lib; + nxt_unit_port_t new_port; + nxt_queue_link_t *lnk; + nxt_unit_request_t *r; + nxt_unit_mmap_buf_t *b; + nxt_unit_recv_msg_t recv_msg; + nxt_unit_callbacks_t *cb; + nxt_port_msg_new_port_t *new_port_msg; + nxt_unit_request_info_t *req; + nxt_unit_request_info_impl_t *req_impl; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + rc = NXT_UNIT_ERROR; + fd = -1; + recv_msg.process = NULL; + port_msg = buf; + cm = oob; + + if (oob_size >= CMSG_SPACE(sizeof(int)) + && cm->cmsg_len == CMSG_LEN(sizeof(int)) + && cm->cmsg_level == SOL_SOCKET + && cm->cmsg_type == SCM_RIGHTS) + { + memcpy(&fd, CMSG_DATA(cm), sizeof(int)); + } + + if (nxt_slow_path(buf_size < sizeof(nxt_port_msg_t))) { + nxt_unit_warn(ctx, "message too small (%d bytes)", (int) buf_size); + goto fail; + } + + recv_msg.port_msg = *port_msg; + recv_msg.start = port_msg + 1; + recv_msg.size = buf_size - sizeof(nxt_port_msg_t); + + if (nxt_slow_path(port_msg->type >= NXT_PORT_MSG_MAX)) { + nxt_unit_warn(ctx, "#%"PRIu32": unknown message type (%d)", + port_msg->stream, (int) port_msg->type); + goto fail; + } + + if (port_msg->tracking && nxt_unit_tracking_read(ctx, &recv_msg) == 0) { + rc = NXT_UNIT_OK; + + goto fail; + } + + /* Fragmentation is unsupported. */ + if (nxt_slow_path(port_msg->nf != 0 || port_msg->mf != 0)) { + nxt_unit_warn(ctx, "#%"PRIu32": fragmented message type (%d)", + port_msg->stream, (int) port_msg->type); + goto fail; + } + + if (port_msg->mmap) { + nxt_queue_init(&incoming_buf); + + if (nxt_unit_mmap_read(ctx, &recv_msg, &incoming_buf) != NXT_UNIT_OK) { + goto fail; + } + } + + cb = &lib->callbacks; + + switch (port_msg->type) { + + case _NXT_PORT_MSG_QUIT: + nxt_unit_debug(ctx, "#%"PRIu32": quit", port_msg->stream); + + cb->quit(ctx); + rc = NXT_UNIT_OK; + break; + + case _NXT_PORT_MSG_NEW_PORT: + if (nxt_slow_path(recv_msg.size != sizeof(nxt_port_msg_new_port_t))) { + nxt_unit_warn(ctx, "#%"PRIu32": new_port: " + "invalid message size (%d)", + port_msg->stream, (int) recv_msg.size); + + goto fail; + } + + new_port_msg = recv_msg.start; + + nxt_unit_debug(ctx, "#%"PRIu32": new_port: %d,%d fd %d", + port_msg->stream, (int) new_port_msg->pid, + (int) new_port_msg->id, fd); + + nxt_unit_port_id_init(&new_port.id, new_port_msg->pid, + new_port_msg->id); + + new_port.in_fd = -1; + new_port.out_fd = fd; + new_port.data = NULL; + + fd = -1; + + rc = cb->add_port(ctx, &new_port); + break; + + case _NXT_PORT_MSG_CHANGE_FILE: + nxt_unit_debug(ctx, "#%"PRIu32": change_file: fd %d", + port_msg->stream, fd); + break; + + case _NXT_PORT_MSG_MMAP: + rc = nxt_unit_incoming_mmap(ctx, port_msg->pid, fd); + break; + + case _NXT_PORT_MSG_DATA: + if (nxt_slow_path(port_msg->mmap == 0)) { + nxt_unit_warn(ctx, "#%"PRIu32": data is not in shared memory", + port_msg->stream); + + goto fail; + } + + if (nxt_slow_path(recv_msg.size < sizeof(nxt_unit_request_t))) { + nxt_unit_warn(ctx, "#%"PRIu32": data too short: %d while at least " + "%d expected", port_msg->stream, (int) recv_msg.size, + (int) sizeof(nxt_unit_request_t)); + + goto fail; + } + + req_impl = nxt_unit_request_info_get(ctx); + if (nxt_slow_path(req_impl == NULL)) { + nxt_unit_warn(ctx, "#%"PRIu32": request info allocation failed", + port_msg->stream); + + goto fail; + } + + req = &req_impl->req; + + req->request_port = *port_id; + + nxt_unit_port_id_init(&req->response_port, port_msg->pid, + port_msg->reply_port); + + req->request = recv_msg.start; + + lnk = nxt_queue_first(&incoming_buf); + b = nxt_container_of(lnk, nxt_unit_mmap_buf_t, link); + + req->request_buf = &b->buf; + req->response = NULL; + req->response_buf = NULL; + + r = req->request; + + req->content_length = r->content_length; + + req->content_buf = req->request_buf; + req->content_buf->free = nxt_unit_sptr_get(&r->preread_content); + + /* Move process to req_impl. */ + req_impl->recv_msg = recv_msg; + + recv_msg.process = NULL; + + nxt_queue_init(&req_impl->outgoing_buf); + nxt_queue_init(&req_impl->incoming_buf); + + nxt_queue_each(b, &incoming_buf, nxt_unit_mmap_buf_t, link) + { + b->req = req; + } nxt_queue_loop; + + nxt_queue_add(&req_impl->incoming_buf, &incoming_buf); + nxt_queue_init(&incoming_buf); + + req->response_max_fields = 0; + req_impl->state = NXT_UNIT_RS_START; + + nxt_unit_debug(ctx, "#%"PRIu32": %.*s %.*s (%d)", port_msg->stream, + (int) r->method_length, nxt_unit_sptr_get(&r->method), + (int) r->target_length, nxt_unit_sptr_get(&r->target), + (int) r->content_length); + + cb->request_handler(req); + + rc = NXT_UNIT_OK; + break; + + case _NXT_PORT_MSG_REMOVE_PID: + if (nxt_slow_path(recv_msg.size != sizeof(pid))) { + nxt_unit_warn(ctx, "#%"PRIu32": remove_pid: invalid message size " + "(%d != %d)", port_msg->stream, (int) recv_msg.size, + (int) sizeof(pid)); + + goto fail; + } + + memcpy(&pid, recv_msg.start, sizeof(pid)); + + nxt_unit_debug(ctx, "#%"PRIu32": remove_pid: %d", + port_msg->stream, (int) pid); + + cb->remove_pid(ctx, pid); + + rc = NXT_UNIT_OK; + break; + + default: + nxt_unit_debug(ctx, "#%"PRIu32": ignore message type: %d", + port_msg->stream, (int) port_msg->type); + + goto fail; + } + +fail: + + if (fd != -1) { + close(fd); + } + + if (port_msg->mmap) { + nxt_queue_each(b, &incoming_buf, nxt_unit_mmap_buf_t, link) + { + nxt_unit_mmap_release(b->hdr, b->buf.start, + b->buf.end - b->buf.start); + + nxt_unit_mmap_buf_release(b); + } nxt_queue_loop; + } + + if (recv_msg.process != NULL) { + nxt_unit_process_use(ctx, recv_msg.process, -1); + } + + return rc; +} + + +static nxt_unit_request_info_impl_t * +nxt_unit_request_info_get(nxt_unit_ctx_t *ctx) +{ + nxt_unit_impl_t *lib; + nxt_queue_link_t *lnk; + nxt_unit_ctx_impl_t *ctx_impl; + nxt_unit_request_info_impl_t *req_impl; + + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + if (nxt_queue_is_empty(&ctx_impl->free_req)) { + req_impl = malloc(sizeof(nxt_unit_request_info_impl_t) + + lib->request_data_size); + if (nxt_slow_path(req_impl == NULL)) { + nxt_unit_warn(ctx, "request info allocation failed"); + + return NULL; + } + + req_impl->req.unit = ctx->unit; + req_impl->req.ctx = ctx; + + } else { + lnk = nxt_queue_first(&ctx_impl->free_req); + nxt_queue_remove(lnk); + + req_impl = nxt_container_of(lnk, nxt_unit_request_info_impl_t, link); + } + + nxt_queue_insert_tail(&ctx_impl->active_req, &req_impl->link); + + req_impl->req.data = lib->request_data_size ? req_impl->extra_data : NULL; + + return req_impl; +} + + +static void +nxt_unit_request_info_release(nxt_unit_request_info_t *req) +{ + nxt_unit_mmap_buf_t *b; + nxt_unit_ctx_impl_t *ctx_impl; + nxt_unit_recv_msg_t *recv_msg; + nxt_unit_request_info_impl_t *req_impl; + + ctx_impl = nxt_container_of(req->ctx, nxt_unit_ctx_impl_t, ctx); + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + req->response = NULL; + req->response_buf = NULL; + + recv_msg = &req_impl->recv_msg; + + if (recv_msg->process != NULL) { + nxt_unit_process_use(req->ctx, recv_msg->process, -1); + + recv_msg->process = NULL; + } + + nxt_queue_each(b, &req_impl->outgoing_buf, nxt_unit_mmap_buf_t, link) { + + nxt_unit_buf_free(&b->buf); + + } nxt_queue_loop; + + nxt_queue_each(b, &req_impl->incoming_buf, nxt_unit_mmap_buf_t, link) { + + nxt_unit_mmap_release(b->hdr, b->buf.start, b->buf.end - b->buf.start); + nxt_unit_mmap_buf_release(b); + + } nxt_queue_loop; + + nxt_queue_remove(&req_impl->link); + + nxt_queue_insert_tail(&ctx_impl->free_req, &req_impl->link); +} + + +static void +nxt_unit_request_info_free(nxt_unit_request_info_impl_t *req_impl) +{ + nxt_unit_ctx_impl_t *ctx_impl; + + ctx_impl = nxt_container_of(req_impl->req.ctx, nxt_unit_ctx_impl_t, ctx); + + nxt_queue_remove(&req_impl->link); + + if (req_impl != &ctx_impl->req) { + free(req_impl); + } +} + + +uint16_t +nxt_unit_field_hash(const char *name, size_t name_length) +{ + u_char ch; + uint32_t hash; + const char *p, *end; + + hash = 159406; /* Magic value copied from nxt_http_parse.c */ + end = name + name_length; + + for (p = name; p < end; p++) { + ch = *p; + hash = (hash << 4) + hash + nxt_lowcase(ch); + } + + hash = (hash >> 16) ^ hash; + + return hash; +} + + +void +nxt_unit_split_host(char *host, uint32_t host_length, + char **name, uint32_t *name_length, char **port, uint32_t *port_length) +{ + char *cpos; + + static char default_host[] = "localhost"; + static char default_port[] = "80"; + + if (nxt_slow_path(host == NULL || host_length == 0)) { + *name = default_host; + *name_length = nxt_length(default_host); + + *port = default_port; + *port_length = nxt_length(default_port); + + return; + } + + cpos = memchr(host, ':', host_length); + + if (nxt_slow_path(cpos == NULL)) { + *name = host; + *name_length = host_length; + + *port = default_port; + *port_length = nxt_length(default_port); + + return; + } + + if (nxt_slow_path(cpos == host)) { + *name = default_host; + *name_length = nxt_length(default_host); + + } else { + *name = host; + *name_length = cpos - host; + } + + cpos++; + + if (nxt_slow_path(host + host_length == cpos)) { + *port = default_port; + *port_length = nxt_length(default_port); + + } else { + *port = cpos; + *port_length = host_length - (cpos - host); + } +} + + +void +nxt_unit_request_group_dup_fields(nxt_unit_request_info_t *req) +{ + uint32_t i, j; + nxt_unit_field_t *fields, f; + nxt_unit_request_t *r; + + nxt_unit_req_debug(req, "group_dup_fields"); + + r = req->request; + fields = r->fields; + + for (i = 0; i < r->fields_count; i++) { + + switch (fields[i].hash) { + case NXT_UNIT_HASH_HOST: + r->host_field = i; + break; + + case NXT_UNIT_HASH_CONTENT_LENGTH: + r->content_length_field = i; + break; + + case NXT_UNIT_HASH_CONTENT_TYPE: + r->content_type_field = i; + break; + + case NXT_UNIT_HASH_COOKIE: + r->cookie_field = i; + break; + }; + + for (j = i + 1; j < r->fields_count; j++) { + if (fields[i].hash != fields[j].hash) { + continue; + } + + if (j == i + 1) { + continue; + } + + f = fields[j]; + f.name.offset += (j - (i + 1)) * sizeof(f); + f.value.offset += (j - (i + 1)) * sizeof(f); + + while (j > i + 1) { + fields[j] = fields[j - 1]; + fields[j].name.offset -= sizeof(f); + fields[j].value.offset -= sizeof(f); + j--; + } + + fields[j] = f; + + i++; + } + } +} + + +int +nxt_unit_response_init(nxt_unit_request_info_t *req, + uint16_t status, uint32_t max_fields_count, uint32_t max_fields_size) +{ + uint32_t buf_size; + nxt_unit_buf_t *buf; + nxt_unit_request_info_impl_t *req_impl; + + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + if (nxt_slow_path(req_impl->state >= NXT_UNIT_RS_RESPONSE_SENT)) { + nxt_unit_req_warn(req, "init: response already sent"); + + return NXT_UNIT_ERROR; + } + + nxt_unit_req_debug(req, "init: %d, max fields %d/%d", (int) status, + (int) max_fields_count, (int) max_fields_size); + + if (nxt_slow_path(req_impl->state >= NXT_UNIT_RS_RESPONSE_INIT)) { + nxt_unit_req_debug(req, "duplicate response init"); + } + + buf_size = sizeof(nxt_unit_response_t) + + max_fields_count * sizeof(nxt_unit_field_t) + + max_fields_size; + + if (nxt_slow_path(req->response_buf != NULL)) { + buf = req->response_buf; + + if (nxt_fast_path(buf_size <= (uint32_t) (buf->end - buf->start))) { + goto init_response; + } + + nxt_unit_buf_free(buf); + + req->response_buf = NULL; + req->response = NULL; + req->response_max_fields = 0; + + req_impl->state = NXT_UNIT_RS_START; + } + + buf = nxt_unit_response_buf_alloc(req, buf_size); + if (nxt_slow_path(buf == NULL)) { + return NXT_UNIT_ERROR; + } + +init_response: + + memset(buf->start, 0, sizeof(nxt_unit_response_t)); + + req->response_buf = buf; + + req->response = (nxt_unit_response_t *) buf->start; + req->response->status = status; + + buf->free = buf->start + sizeof(nxt_unit_response_t) + + max_fields_count * sizeof(nxt_unit_field_t); + + req->response_max_fields = max_fields_count; + req_impl->state = NXT_UNIT_RS_RESPONSE_INIT; + + return NXT_UNIT_OK; +} + + +int +nxt_unit_response_realloc(nxt_unit_request_info_t *req, + uint32_t max_fields_count, uint32_t max_fields_size) +{ + char *p; + uint32_t i, buf_size; + nxt_unit_buf_t *buf; + nxt_unit_field_t *f, *src; + nxt_unit_response_t *resp; + nxt_unit_request_info_impl_t *req_impl; + + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + if (nxt_slow_path(req_impl->state < NXT_UNIT_RS_RESPONSE_INIT)) { + nxt_unit_req_warn(req, "realloc: response not init"); + + return NXT_UNIT_ERROR; + } + + if (nxt_slow_path(req_impl->state >= NXT_UNIT_RS_RESPONSE_SENT)) { + nxt_unit_req_warn(req, "realloc: response already sent"); + + return NXT_UNIT_ERROR; + } + + if (nxt_slow_path(max_fields_count < req->response->fields_count)) { + nxt_unit_req_warn(req, "realloc: new max_fields_count is too small"); + + return NXT_UNIT_ERROR; + } + + buf_size = sizeof(nxt_unit_response_t) + + max_fields_count * sizeof(nxt_unit_field_t) + + max_fields_size; + + buf = nxt_unit_response_buf_alloc(req, buf_size); + if (nxt_slow_path(buf == NULL)) { + return NXT_UNIT_ERROR; + } + + resp = (nxt_unit_response_t *) buf->start; + + memset(resp, 0, sizeof(nxt_unit_response_t)); + + resp->status = req->response->status; + resp->content_length = req->response->content_length; + + p = buf->start + max_fields_count * sizeof(nxt_unit_field_t); + f = resp->fields; + + for (i = 0; i < req->response->fields_count; i++) { + src = req->request->fields + i; + + if (nxt_slow_path(src->skip != 0)) { + continue; + } + + if (nxt_slow_path(src->name_length + src->value_length + > (uint32_t) (buf->end - p))) + { + goto fail; + } + + nxt_unit_sptr_set(&f->name, p); + p = nxt_cpymem(p, nxt_unit_sptr_get(&src->name), src->name_length); + + nxt_unit_sptr_set(&f->value, p); + p = nxt_cpymem(p, nxt_unit_sptr_get(&src->value), src->value_length); + + f->hash = src->hash; + f->skip = 0; + f->name_length = src->name_length; + f->value_length = src->value_length; + + resp->fields_count++; + f++; + } + + if (req->response->piggyback_content_length > 0) { + if (nxt_slow_path(req->response->piggyback_content_length + > (uint32_t) (buf->end - p))) + { + goto fail; + } + + resp->piggyback_content_length = req->response->piggyback_content_length; + + nxt_unit_sptr_set(&resp->piggyback_content, p); + p = nxt_cpymem(p, nxt_unit_sptr_get(&req->response->piggyback_content), + req->response->piggyback_content_length); + } + + buf->free = p; + + nxt_unit_buf_free(req->response_buf); + + req->response = resp; + req->response_buf = buf; + req->response_max_fields = max_fields_count; + + return NXT_UNIT_OK; + +fail: + + nxt_unit_buf_free(buf); + + return NXT_UNIT_ERROR; +} + + +int +nxt_unit_response_is_init(nxt_unit_request_info_t *req) +{ + nxt_unit_request_info_impl_t *req_impl; + + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + return req_impl->state >= NXT_UNIT_RS_RESPONSE_INIT; +} + + +int +nxt_unit_response_add_field(nxt_unit_request_info_t *req, + const char *name, uint8_t name_length, + const char *value, uint32_t value_length) +{ + nxt_unit_buf_t *buf; + nxt_unit_field_t *f; + nxt_unit_response_t *resp; + nxt_unit_request_info_impl_t *req_impl; + + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + if (nxt_slow_path(req_impl->state != NXT_UNIT_RS_RESPONSE_INIT)) { + nxt_unit_req_warn(req, "add_field: response not initialized or " + "already sent"); + + return NXT_UNIT_ERROR; + } + + resp = req->response; + + if (nxt_slow_path(resp->fields_count >= req->response_max_fields)) { + nxt_unit_req_warn(req, "add_field: too many response fields"); + + return NXT_UNIT_ERROR; + } + + buf = req->response_buf; + + if (nxt_slow_path(name_length + value_length + > (uint32_t) (buf->end - buf->free))) + { + nxt_unit_req_warn(req, "add_field: response buffer overflow"); + + return NXT_UNIT_ERROR; + } + + nxt_unit_req_debug(req, "add_field #%"PRIu32": %.*s: %.*s", + resp->fields_count, + (int) name_length, name, + (int) value_length, value); + + f = resp->fields + resp->fields_count; + + nxt_unit_sptr_set(&f->name, buf->free); + buf->free = nxt_cpymem(buf->free, name, name_length); + + nxt_unit_sptr_set(&f->value, buf->free); + buf->free = nxt_cpymem(buf->free, value, value_length); + + f->hash = nxt_unit_field_hash(name, name_length); + f->skip = 0; + f->name_length = name_length; + f->value_length = value_length; + + resp->fields_count++; + + return NXT_UNIT_OK; +} + + +int +nxt_unit_response_add_content(nxt_unit_request_info_t *req, + const void* src, uint32_t size) +{ + nxt_unit_buf_t *buf; + nxt_unit_response_t *resp; + nxt_unit_request_info_impl_t *req_impl; + + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + if (nxt_slow_path(req_impl->state < NXT_UNIT_RS_RESPONSE_INIT)) { + nxt_unit_req_warn(req, "add_content: response not initialized yet"); + + return NXT_UNIT_ERROR; + } + + if (nxt_slow_path(req_impl->state >= NXT_UNIT_RS_RESPONSE_SENT)) { + nxt_unit_req_warn(req, "add_content: response already sent"); + + return NXT_UNIT_ERROR; + } + + buf = req->response_buf; + + if (nxt_slow_path(size > (uint32_t) (buf->end - buf->free))) { + nxt_unit_req_warn(req, "add_content: buffer overflow"); + + return NXT_UNIT_ERROR; + } + + resp = req->response; + + if (resp->piggyback_content_length == 0) { + nxt_unit_sptr_set(&resp->piggyback_content, buf->free); + req_impl->state = NXT_UNIT_RS_RESPONSE_HAS_CONTENT; + } + + resp->piggyback_content_length += size; + + buf->free = nxt_cpymem(buf->free, src, size); + + return NXT_UNIT_OK; +} + + +int +nxt_unit_response_send(nxt_unit_request_info_t *req) +{ + int rc; + nxt_unit_mmap_buf_t *mmap_buf; + nxt_unit_request_info_impl_t *req_impl; + + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + if (nxt_slow_path(req_impl->state < NXT_UNIT_RS_RESPONSE_INIT)) { + nxt_unit_req_warn(req, "send: response is not initialized yet"); + + return NXT_UNIT_ERROR; + } + + if (nxt_slow_path(req_impl->state >= NXT_UNIT_RS_RESPONSE_SENT)) { + nxt_unit_req_warn(req, "send: response already sent"); + + return NXT_UNIT_ERROR; + } + + nxt_unit_req_debug(req, "send: %"PRIu32" fields, %d bytes", + req->response->fields_count, + (int) (req->response_buf->free + - req->response_buf->start)); + + mmap_buf = nxt_container_of(req->response_buf, nxt_unit_mmap_buf_t, buf); + + rc = nxt_unit_mmap_buf_send(req->ctx, + req_impl->recv_msg.port_msg.stream, + mmap_buf, 0); + if (nxt_fast_path(rc == NXT_UNIT_OK)) { + req->response = NULL; + req->response_buf = NULL; + req_impl->state = NXT_UNIT_RS_RESPONSE_SENT; + + nxt_unit_mmap_buf_release(mmap_buf); + } + + return rc; +} + + +int +nxt_unit_response_is_sent(nxt_unit_request_info_t *req) +{ + nxt_unit_request_info_impl_t *req_impl; + + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + return req_impl->state >= NXT_UNIT_RS_RESPONSE_SENT; +} + + +nxt_unit_buf_t * +nxt_unit_response_buf_alloc(nxt_unit_request_info_t *req, uint32_t size) +{ + int rc; + nxt_unit_process_t *process; + nxt_unit_mmap_buf_t *mmap_buf; + nxt_unit_request_info_impl_t *req_impl; + + if (nxt_slow_path(size > PORT_MMAP_DATA_SIZE)) { + nxt_unit_req_warn(req, "response_buf_alloc: " + "requested buffer (%"PRIu32") too big", size); + + return NULL; + } + + nxt_unit_req_debug(req, "response_buf_alloc: %"PRIu32, size); + + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + process = nxt_unit_msg_get_process(req->ctx, &req_impl->recv_msg); + if (nxt_slow_path(process == NULL)) { + return NULL; + } + + mmap_buf = nxt_unit_mmap_buf_get(req->ctx); + if (nxt_slow_path(mmap_buf == NULL)) { + return NULL; + } + + mmap_buf->req = req; + + nxt_queue_insert_tail(&req_impl->outgoing_buf, &mmap_buf->link); + + rc = nxt_unit_get_outgoing_buf(req->ctx, process, &req->response_port, + size, mmap_buf); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_mmap_buf_release(mmap_buf); + + return NULL; + } + + return &mmap_buf->buf; +} + + +static nxt_unit_process_t * +nxt_unit_msg_get_process(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg) +{ + nxt_unit_impl_t *lib; + + if (recv_msg->process != NULL) { + return recv_msg->process; + } + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + pthread_mutex_lock(&lib->mutex); + + recv_msg->process = nxt_unit_process_find(ctx, recv_msg->port_msg.pid, 0); + + pthread_mutex_unlock(&lib->mutex); + + if (recv_msg->process == NULL) { + nxt_unit_warn(ctx, "#%"PRIu32": process %d not found", + recv_msg->port_msg.stream, (int) recv_msg->port_msg.pid); + } + + return recv_msg->process; +} + + +static nxt_unit_mmap_buf_t * +nxt_unit_mmap_buf_get(nxt_unit_ctx_t *ctx) +{ + nxt_queue_link_t *lnk; + nxt_unit_mmap_buf_t *mmap_buf; + nxt_unit_ctx_impl_t *ctx_impl; + + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + + if (nxt_queue_is_empty(&ctx_impl->free_buf)) { + mmap_buf = malloc(sizeof(nxt_unit_mmap_buf_t)); + if (nxt_slow_path(mmap_buf == NULL)) { + nxt_unit_warn(ctx, "failed to allocate buf"); + } + + } else { + lnk = nxt_queue_first(&ctx_impl->free_buf); + nxt_queue_remove(lnk); + + mmap_buf = nxt_container_of(lnk, nxt_unit_mmap_buf_t, link); + } + + mmap_buf->ctx_impl = ctx_impl; + + return mmap_buf; +} + + +static void +nxt_unit_mmap_buf_release(nxt_unit_mmap_buf_t *mmap_buf) +{ + nxt_queue_remove(&mmap_buf->link); + + nxt_queue_insert_tail(&mmap_buf->ctx_impl->free_buf, &mmap_buf->link); +} + + +int +nxt_unit_buf_send(nxt_unit_buf_t *buf) +{ + int rc; + nxt_unit_mmap_buf_t *mmap_buf; + nxt_unit_request_info_t *req; + nxt_unit_request_info_impl_t *req_impl; + + mmap_buf = nxt_container_of(buf, nxt_unit_mmap_buf_t, buf); + + req = mmap_buf->req; + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + nxt_unit_req_debug(req, "buf_send: %d bytes", + (int) (buf->free - buf->start)); + + if (nxt_slow_path(req_impl->state < NXT_UNIT_RS_RESPONSE_INIT)) { + nxt_unit_req_warn(req, "buf_send: response not initialized yet"); + + return NXT_UNIT_ERROR; + } + + if (nxt_slow_path(req_impl->state < NXT_UNIT_RS_RESPONSE_SENT)) { + nxt_unit_req_warn(req, "buf_send: headers not sent yet"); + + return NXT_UNIT_ERROR; + } + + if (nxt_fast_path(buf->free > buf->start)) { + rc = nxt_unit_mmap_buf_send(req->ctx, + req_impl->recv_msg.port_msg.stream, + mmap_buf, 0); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return rc; + } + } + + nxt_unit_mmap_buf_release(mmap_buf); + + return NXT_UNIT_OK; +} + + +static void +nxt_unit_buf_send_done(nxt_unit_buf_t *buf) +{ + int rc; + nxt_unit_mmap_buf_t *mmap_buf; + nxt_unit_request_info_t *req; + nxt_unit_request_info_impl_t *req_impl; + + mmap_buf = nxt_container_of(buf, nxt_unit_mmap_buf_t, buf); + + req = mmap_buf->req; + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + rc = nxt_unit_mmap_buf_send(req->ctx, + req_impl->recv_msg.port_msg.stream, + mmap_buf, 1); + + if (nxt_slow_path(rc == NXT_UNIT_OK)) { + nxt_unit_mmap_buf_release(mmap_buf); + + nxt_unit_request_info_release(req); + + } else { + nxt_unit_request_done(req, rc); + } +} + + +static int +nxt_unit_mmap_buf_send(nxt_unit_ctx_t *ctx, uint32_t stream, + nxt_unit_mmap_buf_t *mmap_buf, int last) +{ + struct { + nxt_port_msg_t msg; + nxt_port_mmap_msg_t mmap_msg; + } m; + + u_char *end, *last_used, *first_free; + ssize_t res; + nxt_chunk_id_t first_free_chunk; + nxt_unit_buf_t *buf; + nxt_unit_impl_t *lib; + nxt_port_mmap_header_t *hdr; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + buf = &mmap_buf->buf; + + m.mmap_msg.size = buf->free - buf->start; + + m.msg.stream = stream; + m.msg.pid = lib->pid; + m.msg.reply_port = 0; + m.msg.type = _NXT_PORT_MSG_DATA; + m.msg.last = last != 0; + m.msg.mmap = m.mmap_msg.size > 0; + m.msg.nf = 0; + m.msg.mf = 0; + m.msg.tracking = 0; + + hdr = mmap_buf->hdr; + + m.mmap_msg.mmap_id = hdr->id; + m.mmap_msg.chunk_id = nxt_port_mmap_chunk_id(hdr, (u_char *) buf->start); + + nxt_unit_debug(ctx, "#%"PRIu32": send mmap: (%d,%d,%d)", + stream, + (int) m.mmap_msg.mmap_id, + (int) m.mmap_msg.chunk_id, + (int) m.mmap_msg.size); + + res = lib->callbacks.port_send(ctx, &mmap_buf->port_id, &m, + m.mmap_msg.size > 0 ? sizeof(m) + : sizeof(m.msg), + NULL, 0); + if (nxt_slow_path(res != sizeof(m))) { + return NXT_UNIT_ERROR; + } + + if (buf->end - buf->free >= PORT_MMAP_CHUNK_SIZE) { + last_used = (u_char *) buf->free - 1; + + first_free_chunk = nxt_port_mmap_chunk_id(hdr, last_used) + 1; + first_free = nxt_port_mmap_chunk_start(hdr, first_free_chunk); + end = (u_char *) buf->end; + + nxt_unit_mmap_release(hdr, first_free, (uint32_t) (end - first_free)); + + buf->end = (char *) first_free; + } + + return NXT_UNIT_OK; +} + + +void +nxt_unit_buf_free(nxt_unit_buf_t *buf) +{ + nxt_unit_mmap_buf_t *mmap_buf; + + mmap_buf = nxt_container_of(buf, nxt_unit_mmap_buf_t, buf); + + nxt_unit_mmap_release(mmap_buf->hdr, buf->start, buf->end - buf->start); + + nxt_unit_mmap_buf_release(mmap_buf); +} + + +nxt_unit_buf_t * +nxt_unit_buf_next(nxt_unit_buf_t *buf) +{ + nxt_queue_link_t *lnk; + nxt_unit_mmap_buf_t *mmap_buf; + nxt_unit_request_info_impl_t *req_impl; + + mmap_buf = nxt_container_of(buf, nxt_unit_mmap_buf_t, buf); + req_impl = nxt_container_of(mmap_buf->req, nxt_unit_request_info_impl_t, + req); + + lnk = &mmap_buf->link; + + if (lnk == nxt_queue_last(&req_impl->incoming_buf)) { + return NULL; + } + + lnk = nxt_queue_next(lnk); + mmap_buf = nxt_container_of(lnk, nxt_unit_mmap_buf_t, link); + + return &mmap_buf->buf; +} + + +uint32_t +nxt_unit_buf_max(void) +{ + return PORT_MMAP_DATA_SIZE; +} + + +uint32_t +nxt_unit_buf_min(void) +{ + return PORT_MMAP_CHUNK_SIZE; +} + + +int +nxt_unit_response_write(nxt_unit_request_info_t *req, const void *start, + size_t size) +{ + int rc; + uint32_t part_size; + const char *part_start; + nxt_unit_process_t *process; + nxt_unit_mmap_buf_t mmap_buf; + nxt_unit_request_info_impl_t *req_impl; + + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + part_start = start; + + /* Check if response is not send yet. */ + if (nxt_slow_path(req->response_buf)) { + part_size = req->response_buf->end - req->response_buf->free; + part_size = nxt_min(size, part_size); + + rc = nxt_unit_response_add_content(req, part_start, part_size); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return rc; + } + + rc = nxt_unit_response_send(req); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return rc; + } + + size -= part_size; + part_start += part_size; + } + + process = nxt_unit_msg_get_process(req->ctx, &req_impl->recv_msg); + if (nxt_slow_path(process == NULL)) { + return NXT_UNIT_ERROR; + } + + while (size > 0) { + part_size = nxt_min(size, PORT_MMAP_DATA_SIZE); + + rc = nxt_unit_get_outgoing_buf(req->ctx, process, &req->response_port, + part_size, &mmap_buf); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return rc; + } + + mmap_buf.buf.free = nxt_cpymem(mmap_buf.buf.free, + part_start, part_size); + + rc = nxt_unit_mmap_buf_send(req->ctx, + req_impl->recv_msg.port_msg.stream, + &mmap_buf, 0); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_mmap_release(mmap_buf.hdr, mmap_buf.buf.start, + mmap_buf.buf.end - mmap_buf.buf.start); + + return rc; + } + + size -= part_size; + part_start += part_size; + } + + return NXT_UNIT_OK; +} + + +int +nxt_unit_response_write_cb(nxt_unit_request_info_t *req, + nxt_unit_read_info_t *read_info) +{ + int rc; + ssize_t n; + nxt_unit_buf_t *buf; + + /* Check if response is not send yet. */ + if (nxt_slow_path(req->response_buf)) { + + /* Enable content in headers buf. */ + rc = nxt_unit_response_add_content(req, "", 0); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_req_error(req, "Failed to add piggyback content"); + + return rc; + } + + buf = req->response_buf; + + while (buf->end - buf->free > 0) { + n = read_info->read(read_info, buf->free, buf->end - buf->free); + if (nxt_slow_path(n < 0)) { + nxt_unit_req_error(req, "Read error"); + + return NXT_UNIT_ERROR; + } + + /* Manually increase sizes. */ + buf->free += n; + req->response->piggyback_content_length += n; + + if (read_info->eof) { + break; + } + } + + rc = nxt_unit_response_send(req); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_req_error(req, "Failed to send headers with content"); + + return rc; + } + + if (read_info->eof) { + return NXT_UNIT_OK; + } + } + + while (!read_info->eof) { + buf = nxt_unit_response_buf_alloc(req, nxt_min(read_info->buf_size, + PORT_MMAP_DATA_SIZE)); + if (nxt_slow_path(buf == NULL)) { + nxt_unit_req_error(req, "Failed to allocate buf for content"); + + return NXT_UNIT_ERROR; + } + + while (!read_info->eof && buf->end > buf->free) { + n = read_info->read(read_info, buf->free, buf->end - buf->free); + if (nxt_slow_path(n < 0)) { + nxt_unit_req_error(req, "Read error"); + + nxt_unit_buf_free(buf); + + return NXT_UNIT_ERROR; + } + + buf->free += n; + } + + rc = nxt_unit_buf_send(buf); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_req_error(req, "Failed to send content"); + + return rc; + } + } + + return NXT_UNIT_OK; +} + + +ssize_t +nxt_unit_request_read(nxt_unit_request_info_t *req, void *dst, size_t size) +{ + u_char *p; + size_t rest, copy, read; + nxt_unit_buf_t *buf; + + p = dst; + rest = size; + + buf = req->content_buf; + + while (buf != NULL) { + copy = buf->end - buf->free; + copy = nxt_min(rest, copy); + + p = nxt_cpymem(p, buf->free, copy); + + buf->free += copy; + rest -= copy; + + if (rest == 0) { + if (buf->end == buf->free) { + buf = nxt_unit_buf_next(buf); + } + + break; + } + + buf = nxt_unit_buf_next(buf); + } + + req->content_buf = buf; + + read = size - rest; + + req->content_length -= read; + + return read; +} + + +void +nxt_unit_request_done(nxt_unit_request_info_t *req, int rc) +{ + ssize_t res; + uint32_t size; + nxt_port_msg_t msg; + nxt_unit_impl_t *lib; + nxt_unit_request_info_impl_t *req_impl; + + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + nxt_unit_req_debug(req, "done: %d", rc); + + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto skip_response_send; + } + + if (nxt_slow_path(req_impl->state < NXT_UNIT_RS_RESPONSE_INIT)) { + + size = nxt_length("Content-Type") + nxt_length("text/plain"); + + rc = nxt_unit_response_init(req, 200, 1, size); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto skip_response_send; + } + + rc = nxt_unit_response_add_field(req, "Content-Type", + nxt_length("Content-Type"), + "text/plain", nxt_length("text/plain")); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto skip_response_send; + } + } + + if (nxt_slow_path(req_impl->state < NXT_UNIT_RS_RESPONSE_SENT)) { + + req_impl->state = NXT_UNIT_RS_RESPONSE_SENT; + + nxt_unit_buf_send_done(req->response_buf); + + return; + } + +skip_response_send: + + lib = nxt_container_of(req->unit, nxt_unit_impl_t, unit); + + msg.stream = req_impl->recv_msg.port_msg.stream; + msg.pid = lib->pid; + msg.reply_port = 0; + msg.type = (rc == NXT_UNIT_OK) ? _NXT_PORT_MSG_DATA + : _NXT_PORT_MSG_RPC_ERROR; + msg.last = 1; + msg.mmap = 0; + msg.nf = 0; + msg.mf = 0; + msg.tracking = 0; + + res = lib->callbacks.port_send(req->ctx, &req->response_port, + &msg, sizeof(msg), NULL, 0); + if (nxt_slow_path(res != sizeof(msg))) { + nxt_unit_req_alert(req, "last message send failed: %s (%d)", + strerror(errno), errno); + } + + nxt_unit_request_info_release(req); +} + + +static nxt_port_mmap_header_t * +nxt_unit_mmap_get(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process, + nxt_unit_port_id_t *port_id, nxt_chunk_id_t *c, int n) +{ + int res, nchunks, i; + nxt_unit_mmap_t *mm, *mm_end; + nxt_port_mmap_header_t *hdr; + + pthread_mutex_lock(&process->outgoing.mutex); + + mm_end = process->outgoing.elts + process->outgoing.size; + + for (mm = process->outgoing.elts; mm < mm_end; mm++) { + hdr = mm->hdr; + + if (hdr->sent_over != 0xFFFFu && hdr->sent_over != port_id->id) { + continue; + } + + *c = 0; + + while (nxt_port_mmap_get_free_chunk(hdr->free_map, c)) { + nchunks = 1; + + while (nchunks < n) { + res = nxt_port_mmap_chk_set_chunk_busy(hdr->free_map, + *c + nchunks); + + if (res == 0) { + for (i = 0; i < nchunks; i++) { + nxt_port_mmap_set_chunk_free(hdr->free_map, *c + i); + } + + *c += nchunks + 1; + nchunks = 0; + break; + } + + nchunks++; + } + + if (nchunks == n) { + goto unlock; + } + } + } + + *c = 0; + hdr = nxt_unit_new_mmap(ctx, process, port_id, n); + +unlock: + + pthread_mutex_unlock(&process->outgoing.mutex); + + return hdr; +} + + +static nxt_unit_mmap_t * +nxt_unit_mmap_at(nxt_unit_mmaps_t *mmaps, uint32_t i) +{ + uint32_t cap; + + cap = mmaps->cap; + + if (cap == 0) { + cap = i + 1; + } + + while (i + 1 > cap) { + + if (cap < 16) { + cap = cap * 2; + + } else { + cap = cap + cap / 2; + } + } + + if (cap != mmaps->cap) { + + mmaps->elts = realloc(mmaps->elts, cap * sizeof(*mmaps->elts)); + if (nxt_slow_path(mmaps->elts == NULL)) { + return NULL; + } + + memset(mmaps->elts + mmaps->cap, 0, + sizeof(*mmaps->elts) * (cap - mmaps->cap)); + + mmaps->cap = cap; + } + + if (i + 1 > mmaps->size) { + mmaps->size = i + 1; + } + + return mmaps->elts + i; +} + + +static nxt_port_mmap_header_t * +nxt_unit_new_mmap(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process, + nxt_unit_port_id_t *port_id, int n) +{ + int i, fd, rc; + void *mem; + char name[64]; + nxt_unit_mmap_t *mm; + nxt_unit_impl_t *lib; + nxt_port_mmap_header_t *hdr; + + lib = process->lib; + + mm = nxt_unit_mmap_at(&process->outgoing, process->outgoing.size); + if (nxt_slow_path(mm == NULL)) { + nxt_unit_warn(ctx, "failed to add mmap to outgoing array"); + + return NULL; + } + + snprintf(name, sizeof(name), NXT_SHM_PREFIX "unit.%d.%p", + lib->pid, (void *) pthread_self()); + +#if (NXT_HAVE_MEMFD_CREATE) + + fd = syscall(SYS_memfd_create, name, MFD_CLOEXEC); + if (nxt_slow_path(fd == -1)) { + nxt_unit_alert(ctx, "memfd_create(%s) failed: %s (%d)", name, + strerror(errno), errno); + + goto remove_fail; + } + + nxt_unit_debug(ctx, "memfd_create(%s): %d", name, fd); + +#elif (NXT_HAVE_SHM_OPEN_ANON) + + fd = shm_open(SHM_ANON, O_RDWR, S_IRUSR | S_IWUSR); + if (nxt_slow_path(fd == -1)) { + nxt_unit_alert(ctx, "shm_open(SHM_ANON) failed: %s (%d)", + strerror(errno), errno); + + goto remove_fail; + } + +#elif (NXT_HAVE_SHM_OPEN) + + /* Just in case. */ + shm_unlink(name); + + fd = shm_open(name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR); + if (nxt_slow_path(fd == -1)) { + nxt_unit_alert(ctx, "shm_open(%s) failed: %s (%d)", name, + strerror(errno), errno); + + goto remove_fail; + } + + if (nxt_slow_path(shm_unlink(name) == -1)) { + nxt_unit_warn(ctx, "shm_unlink(%s) failed: %s (%d)", name, + strerror(errno), errno); + } + +#else + +#error No working shared memory implementation. + +#endif + + if (nxt_slow_path(ftruncate(fd, PORT_MMAP_SIZE) == -1)) { + nxt_unit_alert(ctx, "ftruncate(%d) failed: %s (%d)", fd, + strerror(errno), errno); + + goto remove_fail; + } + + mem = mmap(NULL, PORT_MMAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (nxt_slow_path(mem == MAP_FAILED)) { + nxt_unit_alert(ctx, "mmap(%d) failed: %s (%d)", fd, + strerror(errno), errno); + + goto remove_fail; + } + + mm->hdr = mem; + hdr = mem; + + memset(hdr->free_map, 0xFFU, sizeof(hdr->free_map)); + memset(hdr->free_tracking_map, 0xFFU, sizeof(hdr->free_tracking_map)); + + hdr->id = process->outgoing.size - 1; + hdr->src_pid = lib->pid; + hdr->dst_pid = process->pid; + hdr->sent_over = port_id->id; + + /* Mark first n chunk(s) as busy */ + for (i = 0; i < n; i++) { + nxt_port_mmap_set_chunk_busy(hdr->free_map, i); + } + + /* Mark as busy chunk followed the last available chunk. */ + nxt_port_mmap_set_chunk_busy(hdr->free_map, PORT_MMAP_CHUNK_COUNT); + nxt_port_mmap_set_chunk_busy(hdr->free_tracking_map, PORT_MMAP_CHUNK_COUNT); + + pthread_mutex_unlock(&process->outgoing.mutex); + + rc = nxt_unit_send_mmap(ctx, port_id, fd); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + munmap(mem, PORT_MMAP_SIZE); + hdr = NULL; + + } else { + nxt_unit_debug(ctx, "new mmap #%"PRIu32" created for %d -> %d", + hdr->id, (int) lib->pid, (int) process->pid); + } + + close(fd); + + pthread_mutex_lock(&process->outgoing.mutex); + + if (nxt_fast_path(hdr != NULL)) { + return hdr; + } + +remove_fail: + + process->outgoing.size--; + + return NULL; +} + + +static int +nxt_unit_send_mmap(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, int fd) +{ + ssize_t res; + nxt_port_msg_t msg; + nxt_unit_impl_t *lib; + union { + struct cmsghdr cm; + char space[CMSG_SPACE(sizeof(int))]; + } cmsg; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + msg.stream = 0; + msg.pid = lib->pid; + msg.reply_port = 0; + msg.type = _NXT_PORT_MSG_MMAP; + msg.last = 0; + msg.mmap = 0; + msg.nf = 0; + msg.mf = 0; + msg.tracking = 0; + +#if (NXT_VALGRIND) + memset(&cmsg, 0, sizeof(cmsg)); +#endif + + cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); + cmsg.cm.cmsg_level = SOL_SOCKET; + cmsg.cm.cmsg_type = SCM_RIGHTS; + + /* + * memcpy() is used instead of simple + * *(int *) CMSG_DATA(&cmsg.cm) = fd; + * because GCC 4.4 with -O2/3/s optimization may issue a warning: + * dereferencing type-punned pointer will break strict-aliasing rules + * + * Fortunately, GCC with -O1 compiles this nxt_memcpy() + * in the same simple assignment as in the code above. + */ + memcpy(CMSG_DATA(&cmsg.cm), &fd, sizeof(int)); + + res = lib->callbacks.port_send(ctx, port_id, &msg, sizeof(msg), + &cmsg, sizeof(cmsg)); + if (nxt_slow_path(res != sizeof(msg))) { + nxt_unit_warn(ctx, "failed to send shm to %d: %s (%d)", + (int) port_id->pid, strerror(errno), errno); + + return NXT_UNIT_ERROR; + } + + return NXT_UNIT_OK; +} + + +static int +nxt_unit_get_outgoing_buf(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process, + nxt_unit_port_id_t *port_id, uint32_t size, + nxt_unit_mmap_buf_t *mmap_buf) +{ + uint32_t nchunks; + nxt_chunk_id_t c; + nxt_port_mmap_header_t *hdr; + + nchunks = (size + PORT_MMAP_CHUNK_SIZE - 1) / PORT_MMAP_CHUNK_SIZE; + + hdr = nxt_unit_mmap_get(ctx, process, port_id, &c, nchunks); + if (nxt_slow_path(hdr == NULL)) { + return NXT_UNIT_ERROR; + } + + mmap_buf->hdr = hdr; + mmap_buf->buf.start = (char *) nxt_port_mmap_chunk_start(hdr, c); + mmap_buf->buf.free = mmap_buf->buf.start; + mmap_buf->buf.end = mmap_buf->buf.start + nchunks * PORT_MMAP_CHUNK_SIZE; + mmap_buf->port_id = *port_id; + + nxt_unit_debug(ctx, "outgoing mmap allocation: (%d,%d,%d)", + (int) hdr->id, (int) c, + (int) (nchunks * PORT_MMAP_CHUNK_SIZE)); + + return NXT_UNIT_OK; +} + + +static int +nxt_unit_incoming_mmap(nxt_unit_ctx_t *ctx, pid_t pid, int fd) +{ + int rc; + void *mem; + struct stat mmap_stat; + nxt_unit_mmap_t *mm; + nxt_unit_impl_t *lib; + nxt_unit_process_t *process; + nxt_port_mmap_header_t *hdr; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + nxt_unit_debug(ctx, "incoming_mmap: fd %d from process %d", fd, (int) pid); + + pthread_mutex_lock(&lib->mutex); + + process = nxt_unit_process_find(ctx, pid, 0); + + pthread_mutex_unlock(&lib->mutex); + + if (nxt_slow_path(process == NULL)) { + nxt_unit_warn(ctx, "incoming_mmap: process %d not found, fd %d", + (int) pid, fd); + + return NXT_UNIT_ERROR; + } + + rc = NXT_UNIT_ERROR; + + if (fstat(fd, &mmap_stat) == -1) { + nxt_unit_warn(ctx, "incoming_mmap: fstat(%d) failed: %s (%d)", fd, + strerror(errno), errno); + + goto fail; + } + + mem = mmap(NULL, mmap_stat.st_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (nxt_slow_path(mem == MAP_FAILED)) { + nxt_unit_warn(ctx, "incoming_mmap: mmap() failed: %s (%d)", + strerror(errno), errno); + + goto fail; + } + + hdr = mem; + + if (nxt_slow_path(hdr->src_pid != pid || hdr->dst_pid != lib->pid)) { + + nxt_unit_warn(ctx, "incoming_mmap: unexpected pid in mmap header " + "detected: %d != %d or %d != %d", (int) hdr->src_pid, + (int) pid, (int) hdr->dst_pid, (int) lib->pid); + + munmap(mem, PORT_MMAP_SIZE); + + goto fail; + } + + pthread_mutex_lock(&process->incoming.mutex); + + mm = nxt_unit_mmap_at(&process->incoming, hdr->id); + if (nxt_slow_path(mm == NULL)) { + nxt_unit_warn(ctx, "incoming_mmap: failed to add to incoming array"); + + munmap(mem, PORT_MMAP_SIZE); + + } else { + mm->hdr = hdr; + + hdr->sent_over = 0xFFFFu; + + rc = NXT_UNIT_OK; + } + + pthread_mutex_unlock(&process->incoming.mutex); + +fail: + + nxt_unit_process_use(ctx, process, -1); + + return rc; +} + + +static void +nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps) +{ + pthread_mutex_init(&mmaps->mutex, NULL); + + mmaps->size = 0; + mmaps->cap = 0; + mmaps->elts = NULL; +} + + +static void +nxt_unit_process_use(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process, int i) +{ + long c; + + c = nxt_atomic_fetch_add(&process->use_count, i); + + if (i < 0 && c == -i) { + nxt_unit_debug(ctx, "destroy process #%d", (int) process->pid); + + nxt_unit_mmaps_destroy(&process->incoming); + nxt_unit_mmaps_destroy(&process->outgoing); + + free(process); + } +} + + +static void +nxt_unit_mmaps_destroy(nxt_unit_mmaps_t *mmaps) +{ + nxt_unit_mmap_t *mm, *end; + + if (mmaps->elts != NULL) { + end = mmaps->elts + mmaps->size; + + for (mm = mmaps->elts; mm < end; mm++) { + munmap(mm->hdr, PORT_MMAP_SIZE); + } + + free(mmaps->elts); + } + + pthread_mutex_destroy(&mmaps->mutex); +} + + +static nxt_port_mmap_header_t * +nxt_unit_get_incoming_mmap(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process, + uint32_t id) +{ + nxt_port_mmap_header_t *hdr; + + if (nxt_fast_path(process->incoming.size > id)) { + hdr = process->incoming.elts[id].hdr; + + } else { + hdr = NULL; + } + + return hdr; +} + + +static int +nxt_unit_tracking_read(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg) +{ + int rc; + nxt_chunk_id_t c; + nxt_unit_process_t *process; + nxt_port_mmap_header_t *hdr; + nxt_port_mmap_tracking_msg_t *tracking_msg; + + if (recv_msg->size < (int) sizeof(nxt_port_mmap_tracking_msg_t)) { + nxt_unit_warn(ctx, "#%"PRIu32": tracking_read: too small message (%d)", + recv_msg->port_msg.stream, (int) recv_msg->size); + + return 0; + } + + tracking_msg = recv_msg->start; + + recv_msg->start = tracking_msg + 1; + recv_msg->size -= sizeof(nxt_port_mmap_tracking_msg_t); + + process = nxt_unit_msg_get_process(ctx, recv_msg); + if (nxt_slow_path(process == NULL)) { + return 0; + } + + pthread_mutex_lock(&process->incoming.mutex); + + hdr = nxt_unit_get_incoming_mmap(ctx, process, tracking_msg->mmap_id); + if (nxt_slow_path(hdr == NULL)) { + pthread_mutex_unlock(&process->incoming.mutex); + + nxt_unit_warn(ctx, "#%"PRIu32": tracking_read: " + "invalid mmap id %d,%"PRIu32, + recv_msg->port_msg.stream, + (int) process->pid, tracking_msg->mmap_id); + + return 0; + } + + c = tracking_msg->tracking_id; + rc = nxt_atomic_cmp_set(hdr->tracking + c, recv_msg->port_msg.stream, 0); + + if (rc == 0) { + nxt_unit_debug(ctx, "#%"PRIu32": tracking cancelled", + recv_msg->port_msg.stream); + + nxt_port_mmap_set_chunk_free(hdr->free_tracking_map, c); + } + + pthread_mutex_unlock(&process->incoming.mutex); + + return rc; +} + + +static int +nxt_unit_mmap_read(nxt_unit_ctx_t *ctx, nxt_unit_recv_msg_t *recv_msg, + nxt_queue_t *incoming_buf) +{ + void *start; + uint32_t size; + nxt_unit_process_t *process; + nxt_unit_mmap_buf_t *b; + nxt_port_mmap_msg_t *mmap_msg, *end; + nxt_port_mmap_header_t *hdr; + + if (nxt_slow_path(recv_msg->size < sizeof(nxt_port_mmap_msg_t))) { + nxt_unit_warn(ctx, "#%"PRIu32": mmap_read: too small message (%d)", + recv_msg->port_msg.stream, (int) recv_msg->size); + + return NXT_UNIT_ERROR; + } + + process = nxt_unit_msg_get_process(ctx, recv_msg); + if (nxt_slow_path(process == NULL)) { + return NXT_UNIT_ERROR; + } + + mmap_msg = recv_msg->start; + end = nxt_pointer_to(recv_msg->start, recv_msg->size); + + pthread_mutex_lock(&process->incoming.mutex); + + for (; mmap_msg < end; mmap_msg++) { + hdr = nxt_unit_get_incoming_mmap(ctx, process, mmap_msg->mmap_id); + if (nxt_slow_path(hdr == NULL)) { + pthread_mutex_unlock(&process->incoming.mutex); + + nxt_unit_warn(ctx, "#%"PRIu32": mmap_read: " + "invalid mmap id %d,%"PRIu32, + recv_msg->port_msg.stream, + (int) process->pid, mmap_msg->mmap_id); + + return NXT_UNIT_ERROR; + } + + start = nxt_port_mmap_chunk_start(hdr, mmap_msg->chunk_id); + size = mmap_msg->size; + + if (recv_msg->start == mmap_msg) { + nxt_unit_warn(ctx, "#%"PRIu32": mmap_read: " + "move start %p -> %p", + recv_msg->port_msg.stream, + recv_msg->start, start); + + recv_msg->start = start; + recv_msg->size = size; + } + + b = nxt_unit_mmap_buf_get(ctx); + if (nxt_slow_path(b == NULL)) { + pthread_mutex_unlock(&process->incoming.mutex); + + nxt_unit_warn(ctx, "#%"PRIu32": mmap_read: " + "failed to allocate buf", + recv_msg->port_msg.stream); + + nxt_unit_mmap_release(hdr, start, size); + + return NXT_UNIT_ERROR; + } + + nxt_queue_insert_tail(incoming_buf, &b->link); + + b->buf.start = start; + b->buf.free = start; + b->buf.end = b->buf.start + size; + b->hdr = hdr; + + nxt_unit_debug(ctx, "#%"PRIu32": mmap_read: [%p,%d] %d->%d,(%d,%d,%d)\n" + "%.*s", + recv_msg->port_msg.stream, + start, (int) size, + (int) hdr->src_pid, (int) hdr->dst_pid, + (int) hdr->id, (int) mmap_msg->chunk_id, + (int) mmap_msg->size, + (int) size, (char *) start); + } + + pthread_mutex_unlock(&process->incoming.mutex); + + return NXT_UNIT_OK; +} + + +static int +nxt_unit_mmap_release(nxt_port_mmap_header_t *hdr, void *start, uint32_t size) +{ + u_char *p, *end; + nxt_chunk_id_t c; + + memset(start, 0xA5, size); + + p = start; + end = p + size; + c = nxt_port_mmap_chunk_id(hdr, p); + + while (p < end) { + nxt_port_mmap_set_chunk_free(hdr->free_map, c); + + p += PORT_MMAP_CHUNK_SIZE; + c++; + } + + return NXT_UNIT_OK; +} + + +static nxt_int_t +nxt_unit_lvlhsh_pid_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + nxt_process_t *process; + + process = data; + + if (lhq->key.length == sizeof(pid_t) + && *(pid_t *) lhq->key.start == process->pid) + { + return NXT_OK; + } + + return NXT_DECLINED; +} + + +static const nxt_lvlhsh_proto_t lvlhsh_processes_proto nxt_aligned(64) = { + NXT_LVLHSH_DEFAULT, + nxt_unit_lvlhsh_pid_test, + nxt_lvlhsh_alloc, + nxt_lvlhsh_free, +}; + + +static inline void +nxt_unit_process_lhq_pid(nxt_lvlhsh_query_t *lhq, pid_t *pid) +{ + lhq->key_hash = nxt_murmur_hash2(pid, sizeof(*pid)); + lhq->key.length = sizeof(*pid); + lhq->key.start = (u_char *) pid; + lhq->proto = &lvlhsh_processes_proto; +} + + +static nxt_unit_process_t * +nxt_unit_process_get(nxt_unit_ctx_t *ctx, pid_t pid) +{ + nxt_unit_impl_t *lib; + nxt_unit_process_t *process; + nxt_lvlhsh_query_t lhq; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + nxt_unit_process_lhq_pid(&lhq, &pid); + + if (nxt_lvlhsh_find(&lib->processes, &lhq) == NXT_OK) { + process = lhq.value; + nxt_unit_process_use(ctx, process, 1); + + return process; + } + + process = malloc(sizeof(nxt_unit_process_t)); + if (nxt_slow_path(process == NULL)) { + nxt_unit_warn(ctx, "failed to allocate process for #%d", (int) pid); + + return NULL; + } + + process->pid = pid; + process->use_count = 1; + process->next_port_id = 0; + process->lib = lib; + + nxt_queue_init(&process->ports); + + nxt_unit_mmaps_init(&process->incoming); + nxt_unit_mmaps_init(&process->outgoing); + + lhq.replace = 0; + lhq.value = process; + + switch (nxt_lvlhsh_insert(&lib->processes, &lhq)) { + + case NXT_OK: + break; + + default: + nxt_unit_warn(ctx, "process %d insert failed", (int) pid); + + pthread_mutex_destroy(&process->outgoing.mutex); + pthread_mutex_destroy(&process->incoming.mutex); + free(process); + process = NULL; + break; + } + + nxt_unit_process_use(ctx, process, 1); + + return process; +} + + +static nxt_unit_process_t * +nxt_unit_process_find(nxt_unit_ctx_t *ctx, pid_t pid, int remove) +{ + int rc; + nxt_unit_impl_t *lib; + nxt_unit_process_t *process; + nxt_lvlhsh_query_t lhq; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + nxt_unit_process_lhq_pid(&lhq, &pid); + + if (remove) { + rc = nxt_lvlhsh_delete(&lib->processes, &lhq); + + } else { + rc = nxt_lvlhsh_find(&lib->processes, &lhq); + } + + if (rc == NXT_OK) { + process = lhq.value; + + if (!remove) { + nxt_unit_process_use(ctx, process, 1); + } + + return process; + } + + return NULL; +} + + +static nxt_unit_process_t * +nxt_unit_process_pop_first(nxt_unit_impl_t *lib) +{ + return nxt_lvlhsh_retrieve(&lib->processes, &lvlhsh_processes_proto, NULL); +} + + +int +nxt_unit_run(nxt_unit_ctx_t *ctx) +{ + int rc; + nxt_unit_impl_t *lib; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + rc = NXT_UNIT_OK; + + while (nxt_fast_path(lib->online)) { + rc = nxt_unit_run_once(ctx); + } + + return rc; +} + + +static int +nxt_unit_run_once(nxt_unit_ctx_t *ctx) +{ + int rc; + char buf[4096]; + char oob[256]; + ssize_t rsize; + nxt_unit_impl_t *lib; + nxt_unit_ctx_impl_t *ctx_impl; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + + memset(oob, 0, sizeof(struct cmsghdr)); + + if (ctx_impl->read_port_fd != -1) { + rsize = nxt_unit_port_recv(ctx, ctx_impl->read_port_fd, + buf, sizeof(buf), + oob, sizeof(oob)); + } else { + rsize = lib->callbacks.port_recv(ctx, &ctx_impl->read_port_id, + buf, sizeof(buf), + oob, sizeof(oob)); + } + + if (nxt_fast_path(rsize > 0)) { + rc = nxt_unit_process_msg(ctx, &ctx_impl->read_port_id, buf, rsize, + oob, sizeof(oob)); + } else { + rc = NXT_UNIT_ERROR; + } + + return rc; +} + + +void +nxt_unit_done(nxt_unit_ctx_t *ctx) +{ + nxt_unit_impl_t *lib; + nxt_unit_process_t *process; + nxt_unit_ctx_impl_t *ctx_impl; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + nxt_queue_each(ctx_impl, &lib->contexts, nxt_unit_ctx_impl_t, link) { + + nxt_unit_ctx_free(&ctx_impl->ctx); + + } nxt_queue_loop; + + for ( ;; ) { + process = nxt_unit_process_pop_first(lib); + if (process == NULL) { + break; + } + + nxt_unit_remove_process(ctx, process); + } + + free(lib); +} + + +nxt_unit_ctx_t * +nxt_unit_ctx_alloc(nxt_unit_ctx_t *ctx, void *data) +{ + int rc, fd; + nxt_unit_impl_t *lib; + nxt_unit_port_id_t new_port_id; + nxt_unit_ctx_impl_t *new_ctx; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + new_ctx = malloc(sizeof(nxt_unit_ctx_impl_t) + lib->request_data_size); + if (nxt_slow_path(new_ctx == NULL)) { + nxt_unit_warn(ctx, "failed to allocate context"); + + return NULL; + } + + rc = nxt_unit_create_port(ctx, &new_port_id, &fd); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + free(new_ctx); + + return NULL; + } + + rc = nxt_unit_send_port(ctx, &lib->ready_port_id, &new_port_id, fd); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + lib->callbacks.remove_port(ctx, &new_port_id); + + close(fd); + + free(new_ctx); + + return NULL; + } + + close(fd); + + nxt_unit_ctx_init(lib, new_ctx, data); + + new_ctx->read_port_id = new_port_id; + + return &new_ctx->ctx; +} + + +void +nxt_unit_ctx_free(nxt_unit_ctx_t *ctx) +{ + nxt_unit_impl_t *lib; + nxt_unit_ctx_impl_t *ctx_impl; + nxt_unit_mmap_buf_t *mmap_buf; + nxt_unit_request_info_impl_t *req_impl; + + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + nxt_queue_each(req_impl, &ctx_impl->active_req, + nxt_unit_request_info_impl_t, link) + { + nxt_unit_req_warn(&req_impl->req, "active request on ctx free"); + + nxt_unit_request_done(&req_impl->req, NXT_UNIT_ERROR); + + } nxt_queue_loop; + + nxt_queue_remove(&ctx_impl->ctx_buf[0].link); + nxt_queue_remove(&ctx_impl->ctx_buf[1].link); + + nxt_queue_each(mmap_buf, &ctx_impl->free_buf, nxt_unit_mmap_buf_t, link) { + + nxt_queue_remove(&mmap_buf->link); + free(mmap_buf); + + } nxt_queue_loop; + + nxt_queue_each(req_impl, &ctx_impl->free_req, + nxt_unit_request_info_impl_t, link) + { + nxt_unit_request_info_free(req_impl); + + } nxt_queue_loop; + + nxt_queue_remove(&ctx_impl->link); + + if (ctx_impl != &lib->main_ctx) { + free(ctx_impl); + } +} + + +/* SOCK_SEQPACKET is disabled to test SOCK_DGRAM on all platforms. */ +#if (0 || NXT_HAVE_AF_UNIX_SOCK_SEQPACKET) +#define NXT_UNIX_SOCKET SOCK_SEQPACKET +#else +#define NXT_UNIX_SOCKET SOCK_DGRAM +#endif + + +void +nxt_unit_port_id_init(nxt_unit_port_id_t *port_id, pid_t pid, uint16_t id) +{ + nxt_unit_port_hash_id_t port_hash_id; + + port_hash_id.pid = pid; + port_hash_id.id = id; + + port_id->pid = pid; + port_id->hash = nxt_murmur_hash2(&port_hash_id, sizeof(port_hash_id)); + port_id->id = id; +} + + +int +nxt_unit_create_send_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *dst, + nxt_unit_port_id_t *port_id) +{ + int rc, fd; + nxt_unit_impl_t *lib; + nxt_unit_port_id_t new_port_id; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + rc = nxt_unit_create_port(ctx, &new_port_id, &fd); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return rc; + } + + rc = nxt_unit_send_port(ctx, dst, &new_port_id, fd); + + if (nxt_fast_path(rc == NXT_UNIT_OK)) { + *port_id = new_port_id; + + } else { + lib->callbacks.remove_port(ctx, &new_port_id); + } + + close(fd); + + return rc; +} + + +static int +nxt_unit_create_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, int *fd) +{ + int rc, port_sockets[2]; + nxt_unit_impl_t *lib; + nxt_unit_port_t new_port; + nxt_unit_process_t *process; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + rc = socketpair(AF_UNIX, NXT_UNIX_SOCKET, 0, port_sockets); + if (nxt_slow_path(rc != 0)) { + nxt_unit_warn(ctx, "create_port: socketpair() failed: %s (%d)", + strerror(errno), errno); + + return NXT_UNIT_ERROR; + } + + nxt_unit_debug(ctx, "create_port: new socketpair: %d->%d", + port_sockets[0], port_sockets[1]); + + pthread_mutex_lock(&lib->mutex); + + process = nxt_unit_process_get(ctx, lib->pid); + if (nxt_slow_path(process == NULL)) { + pthread_mutex_unlock(&lib->mutex); + + close(port_sockets[0]); + close(port_sockets[1]); + + return NXT_UNIT_ERROR; + } + + nxt_unit_port_id_init(&new_port.id, lib->pid, process->next_port_id++); + + new_port.in_fd = port_sockets[0]; + new_port.out_fd = -1; + new_port.data = NULL; + + pthread_mutex_unlock(&lib->mutex); + + nxt_unit_process_use(ctx, process, -1); + + rc = lib->callbacks.add_port(ctx, &new_port); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_warn(ctx, "create_port: add_port() failed"); + + close(port_sockets[0]); + close(port_sockets[1]); + + return rc; + } + + *port_id = new_port.id; + *fd = port_sockets[1]; + + return rc; +} + + +static int +nxt_unit_send_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *dst, + nxt_unit_port_id_t *new_port, int fd) +{ + ssize_t res; + nxt_unit_impl_t *lib; + + struct { + nxt_port_msg_t msg; + nxt_port_msg_new_port_t new_port; + } m; + + union { + struct cmsghdr cm; + char space[CMSG_SPACE(sizeof(int))]; + } cmsg; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + m.msg.stream = 0; + m.msg.pid = lib->pid; + m.msg.reply_port = 0; + m.msg.type = _NXT_PORT_MSG_NEW_PORT; + m.msg.last = 0; + m.msg.mmap = 0; + m.msg.nf = 0; + m.msg.mf = 0; + m.msg.tracking = 0; + + m.new_port.id = new_port->id; + m.new_port.pid = new_port->pid; + m.new_port.type = NXT_PROCESS_WORKER; + m.new_port.max_size = 16 * 1024; + m.new_port.max_share = 64 * 1024; + +#if (NXT_VALGRIND) + memset(&cmsg, 0, sizeof(cmsg)); +#endif + + cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); + cmsg.cm.cmsg_level = SOL_SOCKET; + cmsg.cm.cmsg_type = SCM_RIGHTS; + + /* + * memcpy() is used instead of simple + * *(int *) CMSG_DATA(&cmsg.cm) = fd; + * because GCC 4.4 with -O2/3/s optimization may issue a warning: + * dereferencing type-punned pointer will break strict-aliasing rules + * + * Fortunately, GCC with -O1 compiles this nxt_memcpy() + * in the same simple assignment as in the code above. + */ + memcpy(CMSG_DATA(&cmsg.cm), &fd, sizeof(int)); + + res = lib->callbacks.port_send(ctx, dst, &m, sizeof(m), + &cmsg, sizeof(cmsg)); + + return res == sizeof(m) ? NXT_UNIT_OK : NXT_UNIT_ERROR; +} + + +int +nxt_unit_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) +{ + int rc; + nxt_unit_impl_t *lib; + nxt_unit_process_t *process; + nxt_unit_port_impl_t *new_port; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + nxt_unit_debug(ctx, "add_port: %d,%d in_fd %d out_fd %d", + port->id.pid, port->id.id, + port->in_fd, port->out_fd); + + pthread_mutex_lock(&lib->mutex); + + process = nxt_unit_process_get(ctx, port->id.pid); + if (nxt_slow_path(process == NULL)) { + rc = NXT_UNIT_ERROR; + goto unlock; + } + + if (port->id.id >= process->next_port_id) { + process->next_port_id = port->id.id + 1; + } + + new_port = malloc(sizeof(nxt_unit_port_impl_t)); + if (nxt_slow_path(new_port == NULL)) { + rc = NXT_UNIT_ERROR; + goto unlock; + } + + new_port->port = *port; + + rc = nxt_unit_port_hash_add(&lib->ports, &new_port->port); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto unlock; + } + + nxt_queue_insert_tail(&process->ports, &new_port->link); + + rc = NXT_UNIT_OK; + + new_port->process = process; + +unlock: + + pthread_mutex_unlock(&lib->mutex); + + if (nxt_slow_path(process != NULL && rc != NXT_UNIT_OK)) { + nxt_unit_process_use(ctx, process, -1); + } + + return rc; +} + + +void +nxt_unit_remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id) +{ + nxt_unit_find_remove_port(ctx, port_id, NULL); +} + + +void +nxt_unit_find_remove_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, + nxt_unit_port_t *r_port) +{ + nxt_unit_impl_t *lib; + nxt_unit_process_t *process; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + pthread_mutex_lock(&lib->mutex); + + process = NULL; + + nxt_unit_remove_port_unsafe(ctx, port_id, r_port, &process); + + pthread_mutex_unlock(&lib->mutex); + + if (nxt_slow_path(process != NULL)) { + nxt_unit_process_use(ctx, process, -1); + } +} + + +static void +nxt_unit_remove_port_unsafe(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, + nxt_unit_port_t *r_port, nxt_unit_process_t **process) +{ + nxt_unit_impl_t *lib; + nxt_unit_port_impl_t *port; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + port = nxt_unit_port_hash_find(&lib->ports, port_id, 1); + if (nxt_slow_path(port == NULL)) { + nxt_unit_debug(ctx, "remove_port: port %d,%d not found", + (int) port_id->pid, (int) port_id->id); + + return; + } + + nxt_unit_debug(ctx, "remove_port: port %d,%d, fds %d,%d, data %p", + (int) port_id->pid, (int) port_id->id, + port->port.in_fd, port->port.out_fd, port->port.data); + + if (port->port.in_fd != -1) { + close(port->port.in_fd); + } + + if (port->port.out_fd != -1) { + close(port->port.out_fd); + } + + if (port->process != NULL) { + nxt_queue_remove(&port->link); + } + + if (process != NULL) { + *process = port->process; + } + + if (r_port != NULL) { + *r_port = port->port; + } + + free(port); +} + + +void +nxt_unit_remove_pid(nxt_unit_ctx_t *ctx, pid_t pid) +{ + nxt_unit_impl_t *lib; + nxt_unit_process_t *process; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + pthread_mutex_lock(&lib->mutex); + + process = nxt_unit_process_find(ctx, pid, 1); + if (nxt_slow_path(process == NULL)) { + nxt_unit_debug(ctx, "remove_pid: process %d not found", (int) pid); + + pthread_mutex_unlock(&lib->mutex); + + return; + } + + nxt_unit_remove_process(ctx, process); +} + + +static void +nxt_unit_remove_process(nxt_unit_ctx_t *ctx, nxt_unit_process_t *process) +{ + nxt_queue_t ports; + nxt_unit_impl_t *lib; + nxt_unit_port_impl_t *port; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + nxt_queue_init(&ports); + + nxt_queue_add(&ports, &process->ports); + + nxt_queue_each(port, &ports, nxt_unit_port_impl_t, link) { + + nxt_unit_process_use(ctx, process, -1); + port->process = NULL; + + /* Shortcut for default callback. */ + if (lib->callbacks.remove_port == nxt_unit_remove_port) { + nxt_queue_remove(&port->link); + + nxt_unit_remove_port_unsafe(ctx, &port->port.id, NULL, NULL); + } + + } nxt_queue_loop; + + pthread_mutex_unlock(&lib->mutex); + + nxt_queue_each(port, &ports, nxt_unit_port_impl_t, link) { + + nxt_queue_remove(&port->link); + + lib->callbacks.remove_port(ctx, &port->port.id); + + } nxt_queue_loop; + + nxt_unit_process_use(ctx, process, -1); +} + + +void +nxt_unit_quit(nxt_unit_ctx_t *ctx) +{ + nxt_unit_impl_t *lib; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + lib->online = 0; +} + + +static ssize_t +nxt_unit_port_send_default(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, + const void *buf, size_t buf_size, const void *oob, size_t oob_size) +{ + int fd; + nxt_unit_impl_t *lib; + nxt_unit_port_impl_t *port; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + pthread_mutex_lock(&lib->mutex); + + port = nxt_unit_port_hash_find(&lib->ports, port_id, 0); + + if (nxt_fast_path(port != NULL)) { + fd = port->port.out_fd; + + } else { + nxt_unit_warn(ctx, "port_send: port %d,%d not found", + (int) port_id->pid, (int) port_id->id); + fd = -1; + } + + pthread_mutex_unlock(&lib->mutex); + + if (nxt_slow_path(fd == -1)) { + if (port != NULL) { + nxt_unit_warn(ctx, "port_send: port %d,%d: fd == -1", + (int) port_id->pid, (int) port_id->id); + } + + return -1; + } + + nxt_unit_debug(ctx, "port_send: found port %d,%d fd %d", + (int) port_id->pid, (int) port_id->id, fd); + + return nxt_unit_port_send(ctx, fd, buf, buf_size, oob, oob_size); +} + + +ssize_t +nxt_unit_port_send(nxt_unit_ctx_t *ctx, int fd, + const void *buf, size_t buf_size, const void *oob, size_t oob_size) +{ + ssize_t res; + struct iovec iov[1]; + struct msghdr msg; + + iov[0].iov_base = (void *) buf; + iov[0].iov_len = buf_size; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = (void *) oob; + msg.msg_controllen = oob_size; + + res = sendmsg(fd, &msg, 0); + + if (nxt_slow_path(res == -1)) { + nxt_unit_warn(ctx, "port_send(%d, %d) failed: %s (%d)", + fd, (int) buf_size, strerror(errno), errno); + + } else { + nxt_unit_debug(ctx, "port_send(%d, %d): %d", fd, (int) buf_size, + (int) res); + } + + return res; +} + + +static ssize_t +nxt_unit_port_recv_default(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *port_id, + void *buf, size_t buf_size, void *oob, size_t oob_size) +{ + int fd; + nxt_unit_impl_t *lib; + nxt_unit_ctx_impl_t *ctx_impl; + nxt_unit_port_impl_t *port; + + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + pthread_mutex_lock(&lib->mutex); + + port = nxt_unit_port_hash_find(&lib->ports, port_id, 0); + + if (nxt_fast_path(port != NULL)) { + fd = port->port.in_fd; + + } else { + nxt_unit_debug(ctx, "port_recv: port %d,%d not found", + (int) port_id->pid, (int) port_id->id); + fd = -1; + } + + pthread_mutex_unlock(&lib->mutex); + + if (nxt_slow_path(fd == -1)) { + return -1; + } + + nxt_unit_debug(ctx, "port_recv: found port %d,%d, fd %d", + (int) port_id->pid, (int) port_id->id, fd); + + ctx_impl = nxt_container_of(ctx, nxt_unit_ctx_impl_t, ctx); + + if (nxt_fast_path(port_id == &ctx_impl->read_port_id)) { + ctx_impl->read_port_fd = fd; + } + + return nxt_unit_port_recv(ctx, fd, buf, buf_size, oob, oob_size); +} + + +ssize_t +nxt_unit_port_recv(nxt_unit_ctx_t *ctx, int fd, void *buf, size_t buf_size, + void *oob, size_t oob_size) +{ + ssize_t res; + struct iovec iov[1]; + struct msghdr msg; + + iov[0].iov_base = buf; + iov[0].iov_len = buf_size; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = oob; + msg.msg_controllen = oob_size; + + res = recvmsg(fd, &msg, 0); + + if (nxt_slow_path(res == -1)) { + nxt_unit_warn(ctx, "port_recv(%d) failed: %s (%d)", + fd, strerror(errno), errno); + + } else { + nxt_unit_debug(ctx, "port_recv(%d): %d", fd, (int) res); + } + + return res; +} + + +static nxt_int_t +nxt_unit_port_hash_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + nxt_unit_port_t *port; + nxt_unit_port_hash_id_t *port_id; + + port = data; + port_id = (nxt_unit_port_hash_id_t *) lhq->key.start; + + if (lhq->key.length == sizeof(nxt_unit_port_hash_id_t) + && port_id->pid == port->id.pid + && port_id->id == port->id.id) + { + return NXT_OK; + } + + return NXT_DECLINED; +} + + +static const nxt_lvlhsh_proto_t lvlhsh_ports_proto nxt_aligned(64) = { + NXT_LVLHSH_DEFAULT, + nxt_unit_port_hash_test, + nxt_lvlhsh_alloc, + nxt_lvlhsh_free, +}; + + +static inline void +nxt_unit_port_hash_lhq(nxt_lvlhsh_query_t *lhq, + nxt_unit_port_hash_id_t *port_hash_id, + nxt_unit_port_id_t *port_id) +{ + port_hash_id->pid = port_id->pid; + port_hash_id->id = port_id->id; + + if (nxt_fast_path(port_id->hash != 0)) { + lhq->key_hash = port_id->hash; + + } else { + lhq->key_hash = nxt_murmur_hash2(port_hash_id, sizeof(*port_hash_id)); + + port_id->hash = lhq->key_hash; + + nxt_unit_debug(NULL, "calculate hash for port_id (%d, %d): %04X", + (int) port_id->pid, (int) port_id->id, + (int) port_id->hash); + } + + lhq->key.length = sizeof(nxt_unit_port_hash_id_t); + lhq->key.start = (u_char *) port_hash_id; + lhq->proto = &lvlhsh_ports_proto; + lhq->pool = NULL; +} + + +static int +nxt_unit_port_hash_add(nxt_lvlhsh_t *port_hash, nxt_unit_port_t *port) +{ + nxt_int_t res; + nxt_lvlhsh_query_t lhq; + nxt_unit_port_hash_id_t port_hash_id; + + nxt_unit_port_hash_lhq(&lhq, &port_hash_id, &port->id); + lhq.replace = 0; + lhq.value = port; + + res = nxt_lvlhsh_insert(port_hash, &lhq); + + switch (res) { + + case NXT_OK: + return NXT_UNIT_OK; + + default: + return NXT_UNIT_ERROR; + } +} + + +static nxt_unit_port_impl_t * +nxt_unit_port_hash_find(nxt_lvlhsh_t *port_hash, nxt_unit_port_id_t *port_id, + int remove) +{ + nxt_int_t res; + nxt_lvlhsh_query_t lhq; + nxt_unit_port_hash_id_t port_hash_id; + + nxt_unit_port_hash_lhq(&lhq, &port_hash_id, port_id); + + if (remove) { + res = nxt_lvlhsh_delete(port_hash, &lhq); + + } else { + res = nxt_lvlhsh_find(port_hash, &lhq); + } + + switch (res) { + + case NXT_OK: + return lhq.value; + + default: + return NULL; + } +} + + +void +nxt_unit_log(nxt_unit_ctx_t *ctx, int level, const char *fmt, ...) +{ + int log_fd, n; + char msg[NXT_MAX_ERROR_STR], *p, *end; + pid_t pid; + va_list ap; + nxt_unit_impl_t *lib; + + if (nxt_fast_path(ctx != NULL)) { + lib = nxt_container_of(ctx->unit, nxt_unit_impl_t, unit); + + pid = lib->pid; + log_fd = lib->log_fd; + + } else { + pid = getpid(); + log_fd = STDERR_FILENO; + } + + p = msg; + end = p + sizeof(msg) - 1; + + p = nxt_unit_snprint_prefix(p, end, pid, level); + + va_start(ap, fmt); + p += vsnprintf(p, end - p, fmt, ap); + va_end(ap); + + if (nxt_slow_path(p > end)) { + memcpy(end - 5, "[...]", 5); + p = end; + } + + *p++ = '\n'; + + n = write(log_fd, msg, p - msg); + if (nxt_slow_path(n < 0)) { + fprintf(stderr, "Failed to write log: %.*s", (int) (p - msg), msg); + } +} + + +void +nxt_unit_req_log(nxt_unit_request_info_t *req, int level, const char *fmt, ...) +{ + int log_fd, n; + char msg[NXT_MAX_ERROR_STR], *p, *end; + pid_t pid; + va_list ap; + nxt_unit_impl_t *lib; + nxt_unit_request_info_impl_t *req_impl; + + if (nxt_fast_path(req != NULL)) { + lib = nxt_container_of(req->ctx->unit, nxt_unit_impl_t, unit); + + pid = lib->pid; + log_fd = lib->log_fd; + + } else { + pid = getpid(); + log_fd = STDERR_FILENO; + } + + p = msg; + end = p + sizeof(msg) - 1; + + p = nxt_unit_snprint_prefix(p, end, pid, level); + + if (nxt_fast_path(req != NULL)) { + req_impl = nxt_container_of(req, nxt_unit_request_info_impl_t, req); + + p += snprintf(p, end - p, + "#%"PRIu32": ", req_impl->recv_msg.port_msg.stream); + } + + va_start(ap, fmt); + p += vsnprintf(p, end - p, fmt, ap); + va_end(ap); + + if (nxt_slow_path(p > end)) { + memcpy(end - 5, "[...]", 5); + p = end; + } + + *p++ = '\n'; + + n = write(log_fd, msg, p - msg); + if (nxt_slow_path(n < 0)) { + fprintf(stderr, "Failed to write log: %.*s", (int) (p - msg), msg); + } +} + + +static const char * nxt_unit_log_levels[] = { + "alert", + "error", + "warn", + "notice", + "info", + "debug", +}; + + +static char * +nxt_unit_snprint_prefix(char *p, char *end, pid_t pid, int level) +{ + struct tm tm; + struct timespec ts; + + (void) clock_gettime(CLOCK_REALTIME, &ts); + +#if (NXT_HAVE_LOCALTIME_R) + (void) localtime_r(&ts.tv_sec, &tm); +#else + tm = *localtime(&ts.tv_sec); +#endif + + p += snprintf(p, end - p, + "%4d/%02d/%02d %02d:%02d:%02d.%03d ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + (int) ts.tv_nsec / 1000000); + + p += snprintf(p, end - p, + "[%s] %d#%"PRIu64" [unit] ", nxt_unit_log_levels[level], + (int) pid, + (uint64_t) (uintptr_t) nxt_thread_get_tid()); + + return p; +} + + +/* The function required by nxt_lvlhsh_alloc() and nxt_lvlvhsh_free(). */ + +void * +nxt_memalign(size_t alignment, size_t size) +{ + void *p; + nxt_err_t err; + + err = posix_memalign(&p, alignment, size); + + if (nxt_fast_path(err == 0)) { + return p; + } + + return NULL; +} + +#if (NXT_DEBUG) + +void +nxt_free(void *p) +{ + free(p); +} + +#endif diff --git a/src/nxt_unit.h b/src/nxt_unit.h new file mode 100644 index 00000000..1b4923a2 --- /dev/null +++ b/src/nxt_unit.h @@ -0,0 +1,355 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_UNIT_H_INCLUDED_ +#define _NXT_UNIT_H_INCLUDED_ + + +#include <inttypes.h> +#include <sys/types.h> +#include <string.h> + +#include "nxt_unit_typedefs.h" + +enum { + NXT_UNIT_OK = 0, + NXT_UNIT_ERROR = 1, +}; + +enum { + NXT_UNIT_LOG_ALERT = 0, + NXT_UNIT_LOG_ERR = 1, + NXT_UNIT_LOG_WARN = 2, + NXT_UNIT_LOG_NOTICE = 3, + NXT_UNIT_LOG_INFO = 4, + NXT_UNIT_LOG_DEBUG = 5, +}; + +#define NXT_UNIT_INIT_ENV "NXT_UNIT_INIT" + +/* + * Mostly opaque structure with library state. + * + * Only user defined 'data' pointer exposed here. The rest is unit + * implementation specific and hidden. + */ +struct nxt_unit_s { + void *data; /* User defined data. */ +}; + +/* + * Thread context. + * + * First (main) context is provided 'for free'. To receive and process + * requests in other thread, one need to allocate context and use it + * further in this thread. + */ +struct nxt_unit_ctx_s { + void *data; /* User context-specific data. */ + nxt_unit_t *unit; +}; + +/* + * Unit port identification structure. + * + * Each port can be uniquely identified by listen process id (pid) and port id. + * This identification is required to refer the port from different process. + */ +struct nxt_unit_port_id_s { + pid_t pid; + uint32_t hash; + uint16_t id; +}; + +/* + * unit provides port storage which is able to store and find the following + * data structures. + */ +struct nxt_unit_port_s { + nxt_unit_port_id_t id; + + int in_fd; + int out_fd; + + void *data; +}; + + +struct nxt_unit_buf_s { + char *start; + char *free; + char *end; +}; + + +struct nxt_unit_request_info_s { + nxt_unit_t *unit; + nxt_unit_ctx_t *ctx; + + nxt_unit_port_id_t request_port; + nxt_unit_port_id_t response_port; + + nxt_unit_request_t *request; + nxt_unit_buf_t *request_buf; + + nxt_unit_response_t *response; + nxt_unit_buf_t *response_buf; + uint32_t response_max_fields; + + nxt_unit_buf_t *content_buf; + uint64_t content_length; + + void *data; +}; + +/* + * Set of application-specific callbacks. Application may leave all optional + * callbacks as NULL. + */ +struct nxt_unit_callbacks_s { + /* + * Process request data. Unlike all other callback, this callback + * need to be defined by application. + */ + void (*request_handler)(nxt_unit_request_info_t *req); + + /* Add new Unit port to communicate with process pid. Optional. */ + int (*add_port)(nxt_unit_ctx_t *, nxt_unit_port_t *port); + + /* Remove previously added port. Optional. */ + void (*remove_port)(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id); + + /* Remove all data associated with process pid including ports. Optional. */ + void (*remove_pid)(nxt_unit_ctx_t *, pid_t pid); + + /* Gracefully quit the application. Optional. */ + void (*quit)(nxt_unit_ctx_t *); + + /* Send data and control to process pid using port id. Optional. */ + ssize_t (*port_send)(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id, + const void *buf, size_t buf_size, + const void *oob, size_t oob_size); + + /* Receive data on port id. Optional. */ + ssize_t (*port_recv)(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id, + void *buf, size_t buf_size, void *oob, size_t oob_size); + +}; + + +struct nxt_unit_init_s { + void *data; /* Opaque pointer to user-defined data. */ + void *ctx_data; /* Opaque pointer to user-defined data. */ + int max_pending_requests; + + uint32_t request_data_size; + + nxt_unit_callbacks_t callbacks; + + nxt_unit_port_t ready_port; + uint32_t ready_stream; + nxt_unit_port_t read_port; + int log_fd; +}; + + +typedef ssize_t (*nxt_unit_read_func_t)(nxt_unit_read_info_t *read_info, + void *dst, size_t size); + + +struct nxt_unit_read_info_s { + nxt_unit_read_func_t read; + int eof; + uint32_t buf_size; + void *data; +}; + + +/* + * Initialize Unit application library with necessary callbacks and + * ready/reply port parameters, send 'READY' response to master. + */ +nxt_unit_ctx_t *nxt_unit_init(nxt_unit_init_t *); + +/* + * Process received message, invoke configured callbacks. + * + * If application implements it's own event loop, each datagram received + * from port socket should be initially processed by unit. This function + * may invoke other application-defined callback for message processing. + */ +int nxt_unit_process_msg(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id, + void *buf, size_t buf_size, void *oob, size_t oob_size); + +/* + * Main function useful in case when application does not have it's own + * event loop. nxt_unit_run() starts infinite message wait and process loop. + * + * for (;;) { + * app_lib->port_recv(...); + * nxt_unit_process_msg(...); + * } + * + * The normally function returns when QUIT message received from Unit. + */ +int nxt_unit_run(nxt_unit_ctx_t *); + +/* Destroy application library object. */ +void nxt_unit_done(nxt_unit_ctx_t *); + +/* + * Allocate and initialize new execution context with new listen port to + * process requests in other thread. + */ +nxt_unit_ctx_t *nxt_unit_ctx_alloc(nxt_unit_ctx_t *, void *); + +/* Free unused context. It is not required to free main context. */ +void nxt_unit_ctx_free(nxt_unit_ctx_t *); + +/* Initialize port_id, calculate hash. */ +void nxt_unit_port_id_init(nxt_unit_port_id_t *port_id, pid_t pid, uint16_t id); + +/* + * Create extra incoming port, perform all required actions to propogate + * the port to Unit server. Fills structure referenced by port_id with + * current pid and new port id. + */ +int nxt_unit_create_send_port(nxt_unit_ctx_t *, nxt_unit_port_id_t *dst, + nxt_unit_port_id_t *port_id); + +/* Default 'add_port' implementation. */ +int nxt_unit_add_port(nxt_unit_ctx_t *, nxt_unit_port_t *port); + +/* Find previously added port. */ +nxt_unit_port_t *nxt_unit_find_port(nxt_unit_ctx_t *, + nxt_unit_port_id_t *port_id); + +/* Find, fill output 'port' and remove port from storage. */ +void nxt_unit_find_remove_port(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id, + nxt_unit_port_t *port); + +/* Default 'remove_port' implementation. */ +void nxt_unit_remove_port(nxt_unit_ctx_t *, nxt_unit_port_id_t *port_id); + +/* Default 'remove_pid' implementation. */ +void nxt_unit_remove_pid(nxt_unit_ctx_t *, pid_t pid); + +/* Default 'quit' implementation. */ +void nxt_unit_quit(nxt_unit_ctx_t *); + +/* Default 'port_send' implementation. */ +ssize_t nxt_unit_port_send(nxt_unit_ctx_t *, int fd, + const void *buf, size_t buf_size, + const void *oob, size_t oob_size); + +/* Default 'port_recv' implementation. */ +ssize_t nxt_unit_port_recv(nxt_unit_ctx_t *, int fd, void *buf, size_t buf_size, + void *oob, size_t oob_size); + +/* Calculates hash for given field name. */ +uint16_t nxt_unit_field_hash(const char* name, size_t name_length); + +/* Split host for server name and port. */ +void nxt_unit_split_host(char *host_start, uint32_t host_length, + char **name, uint32_t *name_length, char **port, uint32_t *port_length); + +/* Group duplicate fields for easy enumeration. */ +void nxt_unit_request_group_dup_fields(nxt_unit_request_info_t *req); + +/* + * Allocate response structure capable to store limited numer of fields. + * The structure may be accessed directly via req->response pointer or + * filled step-by-step using functions add_field and add_content. + */ +int nxt_unit_response_init(nxt_unit_request_info_t *req, + uint16_t status, uint32_t max_fields_count, uint32_t max_fields_size); + +int nxt_unit_response_realloc(nxt_unit_request_info_t *req, + uint32_t max_fields_count, uint32_t max_fields_size); + +int nxt_unit_response_is_init(nxt_unit_request_info_t *req); + +int nxt_unit_response_add_field(nxt_unit_request_info_t *req, + const char* name, uint8_t name_length, + const char* value, uint32_t value_length); + +int nxt_unit_response_add_content(nxt_unit_request_info_t *req, + const void* src, uint32_t size); + +/* + * Send prepared response to Unit server. Response structure destroyed during + * this call. + */ +int nxt_unit_response_send(nxt_unit_request_info_t *req); + +int nxt_unit_response_is_sent(nxt_unit_request_info_t *req); + +nxt_unit_buf_t *nxt_unit_response_buf_alloc(nxt_unit_request_info_t *req, + uint32_t size); + +int nxt_unit_buf_send(nxt_unit_buf_t *buf); + +void nxt_unit_buf_free(nxt_unit_buf_t *buf); + +nxt_unit_buf_t *nxt_unit_buf_next(nxt_unit_buf_t *buf); + +uint32_t nxt_unit_buf_max(void); + +uint32_t nxt_unit_buf_min(void); + +int nxt_unit_response_write(nxt_unit_request_info_t *req, const void *start, + size_t size); + +int nxt_unit_response_write_cb(nxt_unit_request_info_t *req, + nxt_unit_read_info_t *read_info); + +ssize_t nxt_unit_request_read(nxt_unit_request_info_t *req, void *dst, + size_t size); + +void nxt_unit_request_done(nxt_unit_request_info_t *req, int rc); + + +void nxt_unit_log(nxt_unit_ctx_t *ctx, int level, const char* fmt, ...); + +void nxt_unit_req_log(nxt_unit_request_info_t *req, int level, + const char* fmt, ...); + +#if (NXT_DEBUG) + +#define nxt_unit_debug(ctx, fmt, ARGS...) \ + nxt_unit_log(ctx, NXT_UNIT_LOG_DEBUG, fmt, ##ARGS) + +#define nxt_unit_req_debug(req, fmt, ARGS...) \ + nxt_unit_req_log(req, NXT_UNIT_LOG_DEBUG, fmt, ##ARGS) + +#else + +#define nxt_unit_debug(ctx, fmt, ARGS...) + +#define nxt_unit_req_debug(req, fmt, ARGS...) + +#endif + + +#define nxt_unit_warn(ctx, fmt, ARGS...) \ + nxt_unit_log(ctx, NXT_UNIT_LOG_WARN, fmt, ##ARGS) + +#define nxt_unit_req_warn(req, fmt, ARGS...) \ + nxt_unit_req_log(req, NXT_UNIT_LOG_WARN, fmt, ##ARGS) + +#define nxt_unit_error(ctx, fmt, ARGS...) \ + nxt_unit_log(ctx, NXT_UNIT_LOG_ERR, fmt, ##ARGS) + +#define nxt_unit_req_error(req, fmt, ARGS...) \ + nxt_unit_req_log(req, NXT_UNIT_LOG_ERR, fmt, ##ARGS) + +#define nxt_unit_alert(ctx, fmt, ARGS...) \ + nxt_unit_log(ctx, NXT_UNIT_LOG_ALERT, fmt, ##ARGS) + +#define nxt_unit_req_alert(req, fmt, ARGS...) \ + nxt_unit_req_log(req, NXT_UNIT_LOG_ALERT, fmt, ##ARGS) + + +#endif /* _NXT_UNIT_H_INCLUDED_ */ diff --git a/src/nxt_unit_field.h b/src/nxt_unit_field.h new file mode 100644 index 00000000..0d490f31 --- /dev/null +++ b/src/nxt_unit_field.h @@ -0,0 +1,34 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_UNIT_FIELD_H_INCLUDED_ +#define _NXT_UNIT_FIELD_H_INCLUDED_ + + +#include <inttypes.h> + +#include "nxt_unit_sptr.h" + +enum { + NXT_UNIT_HASH_HOST = 0xE6EB, + NXT_UNIT_HASH_CONTENT_LENGTH = 0x1EA0, + NXT_UNIT_HASH_CONTENT_TYPE = 0x5F7D, + NXT_UNIT_HASH_COOKIE = 0x23F2, +}; + + +/* Name and Value field aka HTTP header. */ +struct nxt_unit_field_s { + uint16_t hash; + uint8_t skip; + uint8_t name_length; + uint32_t value_length; + + nxt_unit_sptr_t name; + nxt_unit_sptr_t value; +}; + + +#endif /* _NXT_UNIT_FIELD_H_INCLUDED_ */ diff --git a/src/nxt_unit_request.h b/src/nxt_unit_request.h new file mode 100644 index 00000000..af5c29a1 --- /dev/null +++ b/src/nxt_unit_request.h @@ -0,0 +1,48 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_UNIT_REQUEST_H_INCLUDED_ +#define _NXT_UNIT_REQUEST_H_INCLUDED_ + + +#include <inttypes.h> + +#include "nxt_unit_sptr.h" +#include "nxt_unit_field.h" + +#define NXT_UNIT_NONE_FIELD 0xFFFFFFFFU + +struct nxt_unit_request_s { + uint8_t method_length; + uint8_t version_length; + uint8_t remote_length; + uint8_t local_length; + uint32_t target_length; + uint32_t path_length; + uint32_t query_length; + uint32_t fields_count; + + uint32_t host_field; + uint32_t content_length_field; + uint32_t content_type_field; + uint32_t cookie_field; + + uint64_t content_length; + + nxt_unit_sptr_t method; + nxt_unit_sptr_t version; + nxt_unit_sptr_t remote; + nxt_unit_sptr_t local; + nxt_unit_sptr_t target; + nxt_unit_sptr_t path; + nxt_unit_sptr_t query; + nxt_unit_sptr_t preread_content; + + nxt_unit_field_t fields[]; +}; + + +#endif /* _NXT_UNIT_REQUEST_H_INCLUDED_ */ + diff --git a/src/nxt_unit_response.h b/src/nxt_unit_response.h new file mode 100644 index 00000000..481ed701 --- /dev/null +++ b/src/nxt_unit_response.h @@ -0,0 +1,27 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_UNIT_RESPONSE_H_INCLUDED_ +#define _NXT_UNIT_RESPONSE_H_INCLUDED_ + + +#include <inttypes.h> + +#include "nxt_unit_sptr.h" +#include "nxt_unit_field.h" + +struct nxt_unit_response_s { + uint64_t content_length; + uint32_t fields_count; + uint32_t piggyback_content_length; + uint16_t status; + + nxt_unit_sptr_t piggyback_content; + + nxt_unit_field_t fields[]; +}; + + +#endif /* _NXT_UNIT_RESPONSE_H_INCLUDED_ */ diff --git a/src/nxt_unit_sptr.h b/src/nxt_unit_sptr.h new file mode 100644 index 00000000..314416e4 --- /dev/null +++ b/src/nxt_unit_sptr.h @@ -0,0 +1,38 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_UNIT_SPTR_H_INCLUDED_ +#define _NXT_UNIT_SPTR_H_INCLUDED_ + + +#include <inttypes.h> +#include <stddef.h> +#include <string.h> + +#include "nxt_unit_typedefs.h" + + +/* Serialized pointer. */ +union nxt_unit_sptr_u { + uint8_t base[1]; + uint32_t offset; +}; + + +static inline void +nxt_unit_sptr_set(nxt_unit_sptr_t *sptr, void *ptr) +{ + sptr->offset = (uint8_t *) ptr - sptr->base; +} + + +static inline void * +nxt_unit_sptr_get(nxt_unit_sptr_t *sptr) +{ + return sptr->base + sptr->offset; +} + + +#endif /* _NXT_UNIT_SPTR_H_INCLUDED_ */ diff --git a/src/nxt_unit_typedefs.h b/src/nxt_unit_typedefs.h new file mode 100644 index 00000000..871ce25b --- /dev/null +++ b/src/nxt_unit_typedefs.h @@ -0,0 +1,25 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_UNIT_TYPEDEFS_H_INCLUDED_ +#define _NXT_UNIT_TYPEDEFS_H_INCLUDED_ + + +typedef struct nxt_unit_s nxt_unit_t; +typedef struct nxt_unit_ctx_s nxt_unit_ctx_t; +typedef struct nxt_unit_port_id_s nxt_unit_port_id_t; +typedef struct nxt_unit_port_s nxt_unit_port_t; +typedef struct nxt_unit_buf_s nxt_unit_buf_t; +typedef struct nxt_unit_request_info_s nxt_unit_request_info_t; +typedef struct nxt_unit_callbacks_s nxt_unit_callbacks_t; +typedef struct nxt_unit_init_s nxt_unit_init_t; +typedef union nxt_unit_sptr_u nxt_unit_sptr_t; +typedef struct nxt_unit_field_s nxt_unit_field_t; +typedef struct nxt_unit_request_s nxt_unit_request_t; +typedef struct nxt_unit_response_s nxt_unit_response_t; +typedef struct nxt_unit_read_info_s nxt_unit_read_info_t; + + +#endif /* _NXT_UNIT_TYPEDEFS_H_INCLUDED_ */ diff --git a/src/nxt_worker_process.c b/src/nxt_worker_process.c index 7ba92dea..8bcfb467 100644 --- a/src/nxt_worker_process.c +++ b/src/nxt_worker_process.c @@ -19,13 +19,10 @@ static void nxt_worker_process_sigterm_handler(nxt_task_t *task, void *obj, static void nxt_worker_process_sigquit_handler(nxt_task_t *task, void *obj, void *data); - nxt_port_handlers_t nxt_app_process_port_handlers = { - .quit = nxt_app_quit_handler, .new_port = nxt_port_new_port_handler, .change_file = nxt_port_change_log_file_handler, .mmap = nxt_port_mmap_handler, - .data = nxt_app_data_handler, .remove_pid = nxt_port_remove_pid_handler, }; diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c index 3619dcd0..b2a9c97f 100644 --- a/src/perl/nxt_perl_psgi.c +++ b/src/perl/nxt_perl_psgi.c @@ -11,25 +11,22 @@ #include <nxt_runtime.h> #include <nxt_application.h> #include <nxt_file.h> +#include <nxt_unit.h> +#include <nxt_unit_request.h> +#include <nxt_unit_response.h> typedef struct { - PerlInterpreter *my_perl; - - nxt_task_t *task; - nxt_app_rmsg_t *rmsg; - nxt_app_wmsg_t *wmsg; - - size_t body_preread_size; + PerlInterpreter *my_perl; + nxt_unit_request_info_t *req; } nxt_perl_psgi_input_t; -nxt_inline nxt_int_t nxt_perl_psgi_write(nxt_task_t *task, nxt_app_wmsg_t *wmsg, - const u_char *data, size_t len, - nxt_bool_t flush, nxt_bool_t last); +typedef struct { + PerlInterpreter *my_perl; + SV *app; +} nxt_perl_psgi_module_t; -nxt_inline nxt_int_t nxt_perl_psgi_http_write_status_str(nxt_task_t *task, - nxt_app_wmsg_t *wmsg, nxt_str_t *http_status); static long nxt_perl_psgi_io_input_read(PerlInterpreter *my_perl, nxt_perl_psgi_io_arg_t *arg, void *vbuf, size_t length); @@ -53,7 +50,7 @@ static void nxt_perl_psgi_xs_core_global_changes(PerlInterpreter *my_perl, static void nxt_perl_psgi_xs_init(pTHX); static SV *nxt_perl_psgi_call_var_application(PerlInterpreter *my_perl, - SV *env, nxt_task_t *task); + SV *env, SV *app, nxt_unit_request_info_t *req); /* For currect load XS modules */ EXTERN_C void boot_DynaLoader(pTHX_ CV *cv); @@ -64,44 +61,42 @@ static nxt_int_t nxt_perl_psgi_io_error_init(PerlInterpreter *my_perl, nxt_perl_psgi_io_arg_t *arg); static PerlInterpreter *nxt_perl_psgi_interpreter_init(nxt_task_t *task, - char *script); - -nxt_inline nxt_int_t nxt_perl_psgi_env_append_str(PerlInterpreter *my_perl, - HV *hash_env, const char *name, nxt_str_t *str); -nxt_inline nxt_int_t nxt_perl_psgi_env_append(PerlInterpreter *my_perl, - HV *hash_env, const char *name, void *value); + char *script, SV **app); -static SV *nxt_perl_psgi_env_create(PerlInterpreter *my_perl, nxt_task_t *task, - nxt_app_rmsg_t *rmsg, size_t *body_preread_size); +static SV *nxt_perl_psgi_env_create(PerlInterpreter *my_perl, + nxt_unit_request_info_t *req, nxt_perl_psgi_input_t *input); +nxt_inline int nxt_perl_psgi_add_sptr(PerlInterpreter *my_perl, HV *hash_env, + const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len); +nxt_inline int nxt_perl_psgi_add_str(PerlInterpreter *my_perl, HV *hash_env, + const char *name, uint32_t name_len, char *str, uint32_t len); +nxt_inline int nxt_perl_psgi_add_value(PerlInterpreter *my_perl, HV *hash_env, + const char *name, uint32_t name_len, void *value); -nxt_inline nxt_int_t nxt_perl_psgi_read_add_env(PerlInterpreter *my_perl, - nxt_task_t *task, nxt_app_rmsg_t *rmsg, HV *hash_env, - const char *name, nxt_str_t *str); static u_char *nxt_perl_psgi_module_create(nxt_task_t *task, const char *script); -static nxt_str_t nxt_perl_psgi_result_status(PerlInterpreter *my_perl, +static nxt_int_t nxt_perl_psgi_result_status(PerlInterpreter *my_perl, SV *result); -static nxt_int_t nxt_perl_psgi_result_head(PerlInterpreter *my_perl, - SV *sv_head, nxt_task_t *task, nxt_app_wmsg_t *wmsg); -static nxt_int_t nxt_perl_psgi_result_body(PerlInterpreter *my_perl, - SV *result, nxt_task_t *task, nxt_app_wmsg_t *wmsg); -static nxt_int_t nxt_perl_psgi_result_body_ref(PerlInterpreter *my_perl, - SV *sv_body, nxt_task_t *task, nxt_app_wmsg_t *wmsg); -static nxt_int_t nxt_perl_psgi_result_array(PerlInterpreter *my_perl, - SV *result, nxt_task_t *task, nxt_app_wmsg_t *wmsg); +static int nxt_perl_psgi_result_head(PerlInterpreter *my_perl, + SV *sv_head, nxt_unit_request_info_t *req, uint16_t status); +static int nxt_perl_psgi_result_body(PerlInterpreter *my_perl, + SV *result, nxt_unit_request_info_t *req); +static int nxt_perl_psgi_result_body_ref(PerlInterpreter *my_perl, + SV *sv_body, nxt_unit_request_info_t *req); +static ssize_t nxt_perl_psgi_io_read(nxt_unit_read_info_t *read_info, + void *dst, size_t size); +static int nxt_perl_psgi_result_array(PerlInterpreter *my_perl, + SV *result, nxt_unit_request_info_t *req); static nxt_int_t nxt_perl_psgi_init(nxt_task_t *task, nxt_common_app_conf_t *conf); -static nxt_int_t nxt_perl_psgi_run(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg); -static void nxt_perl_psgi_atexit(nxt_task_t *task); +static void nxt_perl_psgi_request_handler(nxt_unit_request_info_t *req); +static void nxt_perl_psgi_atexit(void); typedef SV *(*nxt_perl_psgi_callback_f)(PerlInterpreter *my_perl, SV *env, nxt_task_t *task); -static SV *nxt_perl_psgi_app; static PerlInterpreter *nxt_perl_psgi; static nxt_perl_psgi_io_arg_t nxt_perl_psgi_arg_input, nxt_perl_psgi_arg_error; @@ -109,85 +104,24 @@ static uint32_t nxt_perl_psgi_compat[] = { NXT_VERNUM, NXT_DEBUG, }; -NXT_EXPORT nxt_application_module_t nxt_app_module = { +NXT_EXPORT nxt_app_module_t nxt_app_module = { sizeof(nxt_perl_psgi_compat), nxt_perl_psgi_compat, nxt_string("perl"), PERL_VERSION_STRING, nxt_perl_psgi_init, - nxt_perl_psgi_run, - nxt_perl_psgi_atexit, }; -nxt_inline nxt_int_t -nxt_perl_psgi_write(nxt_task_t *task, nxt_app_wmsg_t *wmsg, - const u_char *data, size_t len, - nxt_bool_t flush, nxt_bool_t last) -{ - nxt_int_t rc; - - rc = nxt_app_msg_write_raw(task, wmsg, data, len); - - if (nxt_slow_path(rc != NXT_OK)) { - return rc; - } - - if (flush || last) { - rc = nxt_app_msg_flush(task, wmsg, last); - } - - return rc; -} - - -nxt_inline nxt_int_t -nxt_perl_psgi_http_write_status_str(nxt_task_t *task, nxt_app_wmsg_t *wmsg, - nxt_str_t *http_status) -{ - nxt_int_t rc; - - rc = NXT_OK; - -#define RC_WRT(DATA, DATALEN, FLUSH) \ - do { \ - rc = nxt_perl_psgi_write(task, wmsg, DATA, \ - DATALEN, FLUSH, 0); \ - if (nxt_slow_path(rc != NXT_OK)) \ - return rc; \ - \ - } while (0) - - RC_WRT((const u_char *) "Status: ", nxt_length("Status: "), 0); - RC_WRT(http_status->start, http_status->length, 0); - RC_WRT((u_char *) "\r\n", nxt_length("\r\n"), 0); - -#undef RC_WRT - - return rc; -} - - static long nxt_perl_psgi_io_input_read(PerlInterpreter *my_perl, nxt_perl_psgi_io_arg_t *arg, void *vbuf, size_t length) { - size_t copy_size; nxt_perl_psgi_input_t *input; input = (nxt_perl_psgi_input_t *) arg->ctx; - if (input->body_preread_size == 0) { - return 0; - } - - copy_size = nxt_min(length, input->body_preread_size); - copy_size = nxt_app_msg_read_raw(input->task, input->rmsg, - vbuf, copy_size); - - input->body_preread_size -= copy_size; - - return copy_size; + return nxt_unit_request_read(input->req, vbuf, length); } @@ -222,7 +156,7 @@ nxt_perl_psgi_io_error_write(PerlInterpreter *my_perl, nxt_perl_psgi_input_t *input; input = (nxt_perl_psgi_input_t *) arg->ctx; - nxt_log_error(NXT_LOG_ERR, input->task->log, "Perl: %s", vbuf); + nxt_unit_req_error(input->req, "Perl: %s", vbuf); return (long) length; } @@ -284,7 +218,7 @@ nxt_perl_psgi_xs_init(pTHX) static SV * nxt_perl_psgi_call_var_application(PerlInterpreter *my_perl, - SV *env, nxt_task_t *task) + SV *env, SV *app, nxt_unit_request_info_t *req) { SV *result; @@ -297,14 +231,13 @@ nxt_perl_psgi_call_var_application(PerlInterpreter *my_perl, XPUSHs(env); PUTBACK; - call_sv(nxt_perl_psgi_app, G_EVAL|G_SCALAR); + call_sv(app, G_EVAL|G_SCALAR); SPAGAIN; if (SvTRUE(ERRSV)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to run Perl Application: \n%s", - SvPV_nolen(ERRSV)); + nxt_unit_req_error(req, "PSGI: Failed to run Perl Application: \n%s", + SvPV_nolen(ERRSV)); } result = POPs; @@ -418,7 +351,7 @@ nxt_perl_psgi_io_error_init(PerlInterpreter *my_perl, static PerlInterpreter * -nxt_perl_psgi_interpreter_init(nxt_task_t *task, char *script) +nxt_perl_psgi_interpreter_init(nxt_task_t *task, char *script, SV **app) { int status, pargc; char **pargv, **penv; @@ -485,7 +418,7 @@ nxt_perl_psgi_interpreter_init(nxt_task_t *task, char *script) goto fail; } - nxt_perl_psgi_app = eval_pv((const char *) run_module, FALSE); + *app = eval_pv((const char *) run_module, FALSE); if (SvTRUE(ERRSV)) { nxt_alert(task, "PSGI: Failed to parse script: %s\n%s", @@ -511,108 +444,41 @@ fail: } -nxt_inline nxt_int_t -nxt_perl_psgi_env_append_str(PerlInterpreter *my_perl, HV *hash_env, - const char *name, nxt_str_t *str) -{ - SV **ha; - - ha = hv_store(hash_env, name, (I32) strlen(name), - newSVpv((const char *) str->start, (STRLEN) str->length), 0); - - if (nxt_slow_path(ha == NULL)) { - return NXT_ERROR; - } - - return NXT_OK; -} - - -nxt_inline nxt_int_t -nxt_perl_psgi_env_append(PerlInterpreter *my_perl, HV *hash_env, - const char *name, void *value) -{ - SV **ha; - - ha = hv_store(hash_env, name, (I32) strlen(name), value, 0); - - if (nxt_slow_path(ha == NULL)) { - return NXT_ERROR; - } - - return NXT_OK; -} - - -nxt_inline nxt_int_t -nxt_perl_psgi_read_add_env(PerlInterpreter *my_perl, nxt_task_t *task, - nxt_app_rmsg_t *rmsg, HV *hash_env, - const char *name, nxt_str_t *str) -{ - nxt_int_t rc; - - rc = nxt_app_msg_read_str(task, rmsg, str); - - if (nxt_slow_path(rc != NXT_OK)) { - return rc; - } - - if (str->start == NULL) { - return NXT_OK; - } - - return nxt_perl_psgi_env_append_str(my_perl, hash_env, name, str); -} - - static SV * -nxt_perl_psgi_env_create(PerlInterpreter *my_perl, nxt_task_t *task, - nxt_app_rmsg_t *rmsg, size_t *body_preread_size) +nxt_perl_psgi_env_create(PerlInterpreter *my_perl, + nxt_unit_request_info_t *req, nxt_perl_psgi_input_t *input) { - HV *hash_env; - AV *array_version; - u_char *colon; - size_t query_size; - nxt_int_t rc; - nxt_str_t str, value, path, target; - nxt_str_t host, server_name, server_port; - - static nxt_str_t def_host = nxt_string("localhost"); - static nxt_str_t def_port = nxt_string("80"); + HV *hash_env; + AV *array_version; + char *host_start, *port_start; + uint32_t i, host_length, port_length; + nxt_unit_field_t *f; + nxt_unit_request_t *r; hash_env = newHV(); - if (nxt_slow_path(hash_env == NULL)) { return NULL; } -#define RC(FNS) \ - do { \ - if (nxt_slow_path((FNS) != NXT_OK)) \ - goto fail; \ - } while (0) - -#define GET_STR(ATTR) \ - RC(nxt_perl_psgi_read_add_env(my_perl, task, rmsg, \ - hash_env, ATTR, &str)) - - RC(nxt_perl_psgi_env_append_str(my_perl, hash_env, - "SERVER_SOFTWARE", &nxt_server)); - - GET_STR("REQUEST_METHOD"); - GET_STR("REQUEST_URI"); +#define RC(FNS) \ + do { \ + if (nxt_slow_path((FNS) != NXT_UNIT_OK)) \ + goto fail; \ + } while (0) - target = str; +#define NL(S) (S), sizeof(S)-1 - RC(nxt_app_msg_read_str(task, rmsg, &path)); - RC(nxt_app_msg_read_size(task, rmsg, &query_size)); + r = req->request; - if (path.start == NULL || path.length == 0) { - path = target; - } + RC(nxt_perl_psgi_add_str(my_perl, hash_env, NL("SERVER_SOFTWARE"), + (char *) nxt_server.start, nxt_server.length)); - RC(nxt_perl_psgi_env_append_str(my_perl, hash_env, "PATH_INFO", - &path)); + RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("REQUEST_METHOD"), + &r->method, r->method_length)); + RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("REQUEST_URI"), + &r->target, r->target_length)); + RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("PATH_INFO"), + &r->path, r->path_length)); array_version = newAV(); @@ -623,111 +489,136 @@ nxt_perl_psgi_env_create(PerlInterpreter *my_perl, nxt_task_t *task, av_push(array_version, newSViv(1)); av_push(array_version, newSViv(1)); - RC(nxt_perl_psgi_env_append(my_perl, hash_env, "psgi.version", + RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.version"), newRV_noinc((SV *) array_version))); - RC(nxt_perl_psgi_env_append(my_perl, hash_env, "psgi.url_scheme", + RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.url_scheme"), newSVpv("http", 4))); - RC(nxt_perl_psgi_env_append(my_perl, hash_env, "psgi.input", + 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_env_append(my_perl, hash_env, "psgi.errors", + RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.errors"), SvREFCNT_inc(nxt_perl_psgi_arg_error.io))); - RC(nxt_perl_psgi_env_append(my_perl, hash_env, "psgi.multithread", + RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.multithread"), &PL_sv_no)); - RC(nxt_perl_psgi_env_append(my_perl, hash_env, "psgi.multiprocess", + RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.multiprocess"), &PL_sv_yes)); - RC(nxt_perl_psgi_env_append(my_perl, hash_env, "psgi.run_once", + RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.run_once"), &PL_sv_no)); - RC(nxt_perl_psgi_env_append(my_perl, hash_env, "psgi.nonblocking", + RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.nonblocking"), &PL_sv_no)); - RC(nxt_perl_psgi_env_append(my_perl, hash_env, "psgi.streaming", + RC(nxt_perl_psgi_add_value(my_perl, hash_env, NL("psgi.streaming"), &PL_sv_no)); - if (query_size > 0) { - query_size--; - - if (nxt_slow_path(target.length < query_size)) { - goto fail; - } + if (r->query.offset) { + RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("QUERY_STRING"), + &r->query, r->query_length)); + } + RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("SERVER_PROTOCOL"), + &r->version, r->version_length)); + RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("REMOTE_ADDR"), + &r->remote, r->remote_length)); + RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("SERVER_ADDR"), + &r->local, r->local_length)); - str.start = &target.start[query_size]; - str.length = target.length - query_size; + for (i = 0; i < r->fields_count; i++) { + f = r->fields + i; - RC(nxt_perl_psgi_env_append_str(my_perl, hash_env, - "QUERY_STRING", &str)); + RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, + nxt_unit_sptr_get(&f->name), f->name_length, + &f->value, f->value_length)); } - GET_STR("SERVER_PROTOCOL"); - GET_STR("REMOTE_ADDR"); - GET_STR("SERVER_ADDR"); + if (r->content_length_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->content_length_field; - RC(nxt_app_msg_read_str(task, rmsg, &host)); - - if (host.length == 0) { - host = def_host; + RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("CONTENT_LENGTH"), + &f->value, f->value_length)); } - colon = nxt_memchr(host.start, ':', host.length); - server_name = host; + if (r->content_type_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->content_type_field; + + RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("CONTENT_TYPE"), + &f->value, f->value_length)); + } - if (colon != NULL) { - server_name.length = colon - host.start; + if (r->host_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->host_field; - server_port.start = colon + 1; - server_port.length = host.length - server_name.length - 1; + host_start = nxt_unit_sptr_get(&f->value); + host_length = f->value_length; } else { - server_port = def_port; + host_start = NULL; + host_length = 0; } - RC(nxt_perl_psgi_env_append_str(my_perl, hash_env, - "SERVER_NAME", &server_name)); - RC(nxt_perl_psgi_env_append_str(my_perl, hash_env, - "SERVER_PORT", &server_port)); + nxt_unit_split_host(host_start, host_length, &host_start, &host_length, + &port_start, &port_length); - GET_STR("CONTENT_TYPE"); - GET_STR("CONTENT_LENGTH"); + RC(nxt_perl_psgi_add_str(my_perl, hash_env, NL("SERVER_NAME"), + host_start, host_length)); + RC(nxt_perl_psgi_add_str(my_perl, hash_env, NL("SERVER_PORT"), + port_start, port_length)); - for ( ;; ) { - rc = nxt_app_msg_read_str(task, rmsg, &str); +#undef NL +#undef RC - if (nxt_slow_path(rc != NXT_OK)) { - goto fail; - } + return newRV_noinc((SV *) hash_env); - if (nxt_slow_path(str.length == 0)) { - break; - } +fail: - rc = nxt_app_msg_read_str(task, rmsg, &value); + SvREFCNT_dec(hash_env); - if (nxt_slow_path(rc != NXT_OK)) { - break; - } + return NULL; +} - RC(nxt_perl_psgi_env_append_str(my_perl, hash_env, - (char *) str.start, &value)); - } - RC(nxt_app_msg_read_size(task, rmsg, body_preread_size)); +nxt_inline int +nxt_perl_psgi_add_sptr(PerlInterpreter *my_perl, HV *hash_env, + const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len) +{ + return nxt_perl_psgi_add_str(my_perl, hash_env, name, name_len, + nxt_unit_sptr_get(sptr), len); +} -#undef GET_STR -#undef RC - return newRV_noinc((SV *) hash_env); +nxt_inline int +nxt_perl_psgi_add_str(PerlInterpreter *my_perl, HV *hash_env, + const char *name, uint32_t name_len, char *str, uint32_t len) +{ + SV **ha; -fail: + ha = hv_store(hash_env, name, (I32) name_len, + newSVpv(str, (STRLEN) len), 0); + if (nxt_slow_path(ha == NULL)) { + return NXT_UNIT_ERROR; + } - SvREFCNT_dec(hash_env); + return NXT_UNIT_OK; +} - return NULL; + +nxt_inline int +nxt_perl_psgi_add_value(PerlInterpreter *my_perl, HV *hash_env, + const char *name, uint32_t name_len, void *value) +{ + SV **ha; + + ha = hv_store(hash_env, name, (I32) name_len, value, 0); + if (nxt_slow_path(ha == NULL)) { + return NXT_UNIT_ERROR; + } + + return NXT_UNIT_OK; } -static nxt_str_t +static nxt_int_t nxt_perl_psgi_result_status(PerlInterpreter *my_perl, SV *result) { SV **sv_status; AV *array; + u_char *space; nxt_str_t status; array = (AV *) SvRV(result); @@ -735,106 +626,111 @@ nxt_perl_psgi_result_status(PerlInterpreter *my_perl, SV *result) status.start = (u_char *) SvPV(*sv_status, status.length); - return status; + space = nxt_memchr(status.start, ' ', status.length); + if (space != NULL) { + status.length = space - status.start; + } + + return nxt_int_parse(status.start, status.length); } -static nxt_int_t +static int nxt_perl_psgi_result_head(PerlInterpreter *my_perl, SV *sv_head, - nxt_task_t *task, nxt_app_wmsg_t *wmsg) + nxt_unit_request_info_t *req, uint16_t status) { AV *array_head; SV **entry; + int rc; long i, array_len; - nxt_int_t rc; - nxt_str_t body; + char *name, *value; + STRLEN name_len, value_len; + uint32_t fields, size; if (nxt_slow_path(SvROK(sv_head) == 0 || SvTYPE(SvRV(sv_head)) != SVt_PVAV)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: An unsupported format was received from " - "Perl Application for head part"); + nxt_unit_req_error(req, + "PSGI: An unsupported format was received from " + "Perl Application for head part"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } array_head = (AV *) SvRV(sv_head); array_len = av_len(array_head); if (array_len < 1) { - return NXT_OK; + return nxt_unit_response_init(req, status, 0, 0); } if (nxt_slow_path((array_len % 2) == 0)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Bad format for head from " - "Perl Application"); + nxt_unit_req_error(req, "PSGI: Bad format for head from " + "Perl Application"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } + fields = 0; + size = 0; + for (i = 0; i <= array_len; i++) { entry = av_fetch(array_head, i, 0); if (nxt_fast_path(entry == NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to get head entry from " - "Perl Application"); + nxt_unit_req_error(req, "PSGI: Failed to get head entry from " + "Perl Application"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } - body.start = (u_char *) SvPV(*entry, body.length); - - rc = nxt_app_msg_write_raw(task, wmsg, - (u_char *) body.start, body.length); - - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to write head " - "from Perl Application"); - return rc; - } + value = SvPV(*entry, value_len); + size += value_len; if ((i % 2) == 0) { - rc = nxt_app_msg_write_raw(task, wmsg, - (u_char *) ": ", nxt_length(": ")); - } else { - rc = nxt_app_msg_write_raw(task, wmsg, - (u_char *) "\r\n", nxt_length("\r\n")); + fields++; } + } + + rc = nxt_unit_response_init(req, status, fields, size); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return rc; + } + + for (i = 0; i <= array_len; i += 2) { + entry = av_fetch(array_head, i, 0); + name = SvPV(*entry, name_len); + + entry = av_fetch(array_head, i + 1, 0); + value = SvPV(*entry, value_len); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to write head from " - "Perl Application"); + rc = nxt_unit_response_add_field(req, name, name_len, value, value_len); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { return rc; } } - return NXT_OK; + return NXT_UNIT_OK; } -static nxt_int_t +static int nxt_perl_psgi_result_body(PerlInterpreter *my_perl, SV *sv_body, - nxt_task_t *task, nxt_app_wmsg_t *wmsg) + nxt_unit_request_info_t *req) { SV **entry; AV *body_array; + int rc; long i; - nxt_int_t rc; nxt_str_t body; if (nxt_slow_path(SvROK(sv_body) == 0 || SvTYPE(SvRV(sv_body)) != SVt_PVAV)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: An unsupported format was received from " - "Perl Application for a body part"); + nxt_unit_req_error(req, "PSGI: An unsupported format was received from " + "Perl Application for a body part"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } body_array = (AV *) SvRV(sv_body); @@ -844,10 +740,10 @@ nxt_perl_psgi_result_body(PerlInterpreter *my_perl, SV *sv_body, entry = av_fetch(body_array, i, 0); if (nxt_fast_path(entry == NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to get body entry from " - "Perl Application"); - return NXT_ERROR; + nxt_unit_req_error(req, "PSGI: Failed to get body entry from " + "Perl Application"); + + return NXT_UNIT_ERROR; } body.start = (u_char *) SvPV(*entry, body.length); @@ -856,241 +752,215 @@ nxt_perl_psgi_result_body(PerlInterpreter *my_perl, SV *sv_body, continue; } - rc = nxt_app_msg_write_raw(task, wmsg, - (u_char *) body.start, body.length); + rc = nxt_unit_response_write(req, body.start, body.length); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to write 'body' from " - "Perl Application"); - return rc; - } - - rc = nxt_app_msg_flush(task, wmsg, 0); - - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to flush data for a 'body' " - "part from Perl Application"); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_req_error(req, "PSGI: Failed to write content from " + "Perl Application"); return rc; } } - return NXT_OK; + return NXT_UNIT_OK; } -static nxt_int_t +typedef struct { + PerlInterpreter *my_perl; + PerlIO *fp; +} nxt_perl_psgi_io_ctx_t; + + +static int nxt_perl_psgi_result_body_ref(PerlInterpreter *my_perl, SV *sv_body, - nxt_task_t *task, nxt_app_wmsg_t *wmsg) + nxt_unit_request_info_t *req) { - IO *io; - PerlIO *fp; - SSize_t n; - nxt_int_t rc; - u_char vbuf[8192]; + IO *io; + nxt_unit_read_info_t read_info; + nxt_perl_psgi_io_ctx_t io_ctx; io = GvIO(SvRV(sv_body)); if (io == NULL) { - return NXT_OK; + return NXT_UNIT_OK; } - fp = IoIFP(io); + io_ctx.my_perl = my_perl; + io_ctx.fp = IoIFP(io); - for ( ;; ) { - n = PerlIO_read(fp, vbuf, 8192); + read_info.read = nxt_perl_psgi_io_read; + read_info.eof = PerlIO_eof(io_ctx.fp); + read_info.buf_size = 8192; + read_info.data = &io_ctx; - if (n < 1) { - break; - } - - rc = nxt_app_msg_write_raw(task, wmsg, - (u_char *) vbuf, (size_t) n); + return nxt_unit_response_write_cb(req, &read_info); +} - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to write 'body' from " - "Perl Application"); - return rc; - } +static ssize_t +nxt_perl_psgi_io_read(nxt_unit_read_info_t *read_info, void *dst, size_t size) +{ + ssize_t res; + PerlInterpreter *my_perl; + nxt_perl_psgi_io_ctx_t *ctx; - rc = nxt_app_msg_flush(task, wmsg, 0); + ctx = read_info->data; + my_perl = ctx->my_perl; - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to flush data for a body " - "part from Perl Application"); + res = PerlIO_read(ctx->fp, dst, size); - return rc; - } - } + read_info->eof = PerlIO_eof(ctx->fp); - return NXT_OK; + return res; } -static nxt_int_t +static int nxt_perl_psgi_result_array(PerlInterpreter *my_perl, SV *result, - nxt_task_t *task, nxt_app_wmsg_t *wmsg) + nxt_unit_request_info_t *req) { AV *array; SV **sv_temp; + int rc; long array_len; - nxt_int_t rc; - nxt_str_t http_status; + nxt_int_t status; array = (AV *) SvRV(result); array_len = av_len(array); if (nxt_slow_path(array_len < 0)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Invalid result format from Perl Application"); + nxt_unit_req_error(req, + "PSGI: Invalid result format from Perl Application"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } - http_status = nxt_perl_psgi_result_status(nxt_perl_psgi, result); + status = nxt_perl_psgi_result_status(my_perl, result); - if (nxt_slow_path(http_status.start == NULL || http_status.length == 0)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: An unexpected status was received " - "from Perl Application"); + if (nxt_slow_path(status < 0)) { + nxt_unit_req_error(req, + "PSGI: An unexpected status was received " + "from Perl Application"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } - rc = nxt_perl_psgi_http_write_status_str(task, wmsg, &http_status); - - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to write HTTP Status"); - - return rc; - } + if (array_len >= 1) { + sv_temp = av_fetch(array, 1, 0); - if (array_len < 1) { - rc = nxt_app_msg_write_raw(task, wmsg, - (u_char *) "\r\n", nxt_length("\r\n")); + if (nxt_slow_path(sv_temp == NULL)) { + nxt_unit_req_error(req, "PSGI: Failed to get head from " + "Perl ARRAY variable"); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to write HTTP Headers"); + return NXT_UNIT_ERROR; + } + rc = nxt_perl_psgi_result_head(my_perl, *sv_temp, req, status); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { return rc; } - return NXT_OK; - } - - sv_temp = av_fetch(array, 1, 0); - - if (nxt_slow_path(sv_temp == NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to get head from Perl ARRAY variable"); - - return NXT_ERROR; - } - - rc = nxt_perl_psgi_result_head(nxt_perl_psgi, *sv_temp, task, wmsg); - - if (nxt_slow_path(rc != NXT_OK)) { - return rc; - } - - rc = nxt_app_msg_write_raw(task, wmsg, - (u_char *) "\r\n", nxt_length("\r\n")); - - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to write HTTP Headers"); - - return rc; + } else { + return nxt_unit_response_init(req, status, 0, 0); } if (nxt_fast_path(array_len < 2)) { - return NXT_OK; + return NXT_UNIT_OK; } sv_temp = av_fetch(array, 2, 0); if (nxt_slow_path(sv_temp == NULL || SvROK(*sv_temp) == FALSE)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to get body from Perl ARRAY variable"); + nxt_unit_req_error(req, + "PSGI: Failed to get body from " + "Perl ARRAY variable"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } if (SvTYPE(SvRV(*sv_temp)) == SVt_PVAV) { - rc = nxt_perl_psgi_result_body(nxt_perl_psgi, *sv_temp, task, wmsg); - - } else { - rc = nxt_perl_psgi_result_body_ref(nxt_perl_psgi, *sv_temp, - task, wmsg); - } - - if (nxt_slow_path(rc != NXT_OK)) { - return rc; + return nxt_perl_psgi_result_body(my_perl, *sv_temp, req); } - return NXT_OK; + return nxt_perl_psgi_result_body_ref(my_perl, *sv_temp, req); } static nxt_int_t nxt_perl_psgi_init(nxt_task_t *task, nxt_common_app_conf_t *conf) { - PerlInterpreter *my_perl; + int rc; + nxt_unit_ctx_t *unit_ctx; + nxt_unit_init_t perl_init; + PerlInterpreter *my_perl; + nxt_perl_psgi_module_t module; - my_perl = nxt_perl_psgi_interpreter_init(task, conf->u.perl.script); + my_perl = nxt_perl_psgi_interpreter_init(task, conf->u.perl.script, + &module.app); if (nxt_slow_path(my_perl == NULL)) { return NXT_ERROR; } + module.my_perl = my_perl; nxt_perl_psgi = my_perl; + nxt_unit_default_init(task, &perl_init); + + perl_init.callbacks.request_handler = nxt_perl_psgi_request_handler; + perl_init.data = &module; + + unit_ctx = nxt_unit_init(&perl_init); + if (nxt_slow_path(unit_ctx == NULL)) { + return NXT_ERROR; + } + + rc = nxt_unit_run(unit_ctx); + + nxt_unit_done(unit_ctx); + + nxt_perl_psgi_atexit(); + + exit(rc); + return NXT_OK; } -static nxt_int_t -nxt_perl_psgi_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) +static void +nxt_perl_psgi_request_handler(nxt_unit_request_info_t *req) { - SV *env, *result; - size_t body_preread_size; - nxt_int_t rc; - nxt_perl_psgi_input_t input; + SV *env, *result; + nxt_int_t rc; + PerlInterpreter *my_perl; + nxt_perl_psgi_input_t input; + nxt_perl_psgi_module_t *module; - dTHXa(nxt_perl_psgi); + module = req->unit->data; + my_perl = module->my_perl; + + input.my_perl = my_perl; + input.req = req; /* * Create environ variable for perl sub "application". * > sub application { * > my ($environ) = @_; */ - env = nxt_perl_psgi_env_create(nxt_perl_psgi, task, rmsg, - &body_preread_size); - + env = nxt_perl_psgi_env_create(my_perl, req, &input); if (nxt_slow_path(env == NULL)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: Failed to create 'env' for Perl Application"); + nxt_unit_req_error(req, + "PSGI: Failed to create 'env' for Perl Application"); + nxt_unit_request_done(req, NXT_UNIT_ERROR); - return NXT_ERROR; + return; } - input.my_perl = nxt_perl_psgi; - input.task = task; - input.rmsg = rmsg; - input.wmsg = wmsg; - input.body_preread_size = body_preread_size; - nxt_perl_psgi_arg_input.ctx = &input; nxt_perl_psgi_arg_error.ctx = &input; /* Call perl sub and get result as SV*. */ - result = nxt_perl_psgi_call_var_application(nxt_perl_psgi, env, task); + result = nxt_perl_psgi_call_var_application(my_perl, env, module->app, req); /* * We expect ARRAY ref like a @@ -1099,40 +969,24 @@ nxt_perl_psgi_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) if (nxt_slow_path(SvOK(result) == 0 || SvROK(result) == 0 || SvTYPE(SvRV(result)) != SVt_PVAV)) { - nxt_log_error(NXT_LOG_ERR, task->log, - "PSGI: An unexpected response was received from " - "Perl Application"); - goto fail; - } - - rc = nxt_perl_psgi_result_array(nxt_perl_psgi, result, task, wmsg); + nxt_unit_req_error(req, "PSGI: An unexpected response was received " + "from Perl Application"); - if (nxt_slow_path(rc != NXT_OK)) { - goto fail; - } + rc = NXT_UNIT_ERROR; - rc = nxt_app_msg_flush(task, wmsg, 1); - - if (nxt_slow_path(rc != NXT_OK)) { - goto fail; + } else { + rc = nxt_perl_psgi_result_array(my_perl, result, req); } - SvREFCNT_dec(result); - SvREFCNT_dec(env); - - return NXT_OK; - -fail: + nxt_unit_request_done(req, rc); SvREFCNT_dec(result); SvREFCNT_dec(env); - - return NXT_ERROR; } static void -nxt_perl_psgi_atexit(nxt_task_t *task) +nxt_perl_psgi_atexit(void) { dTHXa(nxt_perl_psgi); diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index ea05b133..a08b8189 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -5,6 +5,9 @@ #include <ruby/nxt_ruby.h> +#include <nxt_unit.h> +#include <nxt_unit_request.h> + #define NXT_RUBY_RACK_API_VERSION_MAJOR 1 #define NXT_RUBY_RACK_API_VERSION_MINOR 3 @@ -35,29 +38,26 @@ static VALUE nxt_ruby_bundler_setup(VALUE arg); static VALUE nxt_ruby_require_rack(VALUE arg); static VALUE nxt_ruby_rack_parse_script(VALUE ctx); static VALUE nxt_ruby_rack_env_create(VALUE arg); -static nxt_int_t nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, - nxt_app_wmsg_t *wmsg); +static void nxt_ruby_request_handler(nxt_unit_request_info_t *req); static VALUE nxt_ruby_rack_app_run(VALUE arg); -static nxt_int_t nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx, - VALUE hash_env); -nxt_inline nxt_int_t nxt_ruby_read_add_env(nxt_task_t *task, - nxt_app_rmsg_t *rmsg, VALUE hash_env, const char *name, nxt_str_t *str); +static int nxt_ruby_read_request(VALUE hash_env); +nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, + const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len); +nxt_inline void nxt_ruby_add_str(VALUE hash_env, + const char *name, uint32_t name_len, char *str, uint32_t len); static nxt_int_t nxt_ruby_rack_result_status(VALUE result); -nxt_inline nxt_int_t nxt_ruby_write(nxt_task_t *task, nxt_app_wmsg_t *wmsg, - const u_char *data, size_t len, nxt_bool_t flush, nxt_bool_t last); -static nxt_int_t nxt_ruby_rack_result_headers(VALUE result); -static int nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg); -static nxt_int_t nxt_ruby_head_send_part(const char *key, size_t key_size, - const char *value, size_t value_size); -static nxt_int_t nxt_ruby_rack_result_body(VALUE result); -static nxt_int_t nxt_ruby_rack_result_body_file_write(VALUE filepath); +static int nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status); +static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg); +static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg); +static int nxt_ruby_rack_result_body(VALUE result); +static int nxt_ruby_rack_result_body_file_write(VALUE filepath); static VALUE nxt_ruby_rack_result_body_each(VALUE body); static void nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc); -static void nxt_ruby_atexit(nxt_task_t *task); +static void nxt_ruby_atexit(void); static uint32_t compat[] = { @@ -71,22 +71,22 @@ static VALUE nxt_ruby_io_input; static VALUE nxt_ruby_io_error; static nxt_ruby_run_ctx_t nxt_ruby_run_ctx; -NXT_EXPORT nxt_application_module_t nxt_app_module = { +NXT_EXPORT nxt_app_module_t nxt_app_module = { sizeof(compat), compat, nxt_string("ruby"), ruby_version, nxt_ruby_init, - nxt_ruby_run, - nxt_ruby_atexit, }; static nxt_int_t nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf) { - int state; + int state, rc; VALUE dummy, res; + nxt_unit_ctx_t *unit_ctx; + nxt_unit_init_t ruby_unit_init; nxt_ruby_rack_init_t rack_init; ruby_init(); @@ -128,6 +128,27 @@ nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf) rb_gc_register_address(&nxt_ruby_call); rb_gc_register_address(&nxt_ruby_env); + nxt_unit_default_init(task, &ruby_unit_init); + + ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler; + + unit_ctx = nxt_unit_init(&ruby_unit_init); + if (nxt_slow_path(unit_ctx == NULL)) { + return NXT_ERROR; + } + + nxt_ruby_run_ctx.unit_ctx = unit_ctx; + + rc = nxt_unit_run(unit_ctx); + + nxt_ruby_atexit(); + + nxt_ruby_run_ctx.unit_ctx = NULL; + + nxt_unit_done(unit_ctx); + + exit(rc); + return NXT_OK; } @@ -319,82 +340,75 @@ nxt_ruby_rack_env_create(VALUE arg) } -static nxt_int_t -nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) +static void +nxt_ruby_request_handler(nxt_unit_request_info_t *req) { int state; VALUE res; - nxt_ruby_run_ctx.task = task; - nxt_ruby_run_ctx.rmsg = rmsg; - nxt_ruby_run_ctx.wmsg = wmsg; + nxt_ruby_run_ctx.req = req; res = rb_protect(nxt_ruby_rack_app_run, Qnil, &state); - if (nxt_slow_path(state != 0)) { - nxt_ruby_exception_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, + if (nxt_slow_path(res == Qnil || state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, "Failed to run ruby script"); - return NXT_ERROR; - } - - if (nxt_slow_path(res == Qnil)) { - return NXT_ERROR; } - - return NXT_OK; } static VALUE nxt_ruby_rack_app_run(VALUE arg) { + int rc; VALUE env, result; - nxt_int_t rc; + nxt_int_t status; env = rb_hash_dup(nxt_ruby_env); - rc = nxt_ruby_read_request(&nxt_ruby_run_ctx, env); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_alert(nxt_ruby_run_ctx.task, - "Ruby: Failed to process incoming request"); + rc = nxt_ruby_read_request(env); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_req_alert(nxt_ruby_run_ctx.req, + "Ruby: Failed to process incoming request"); goto fail; } result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env); if (nxt_slow_path(TYPE(result) != T_ARRAY)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Invalid response format from application"); + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Invalid response format from application"); goto fail; } if (nxt_slow_path(RARRAY_LEN(result) != 3)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Invalid response format from application. " - "Need 3 entries [Status, Headers, Body]"); + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Invalid response format from application. " + "Need 3 entries [Status, Headers, Body]"); goto fail; } - rc = nxt_ruby_rack_result_status(result); - if (nxt_slow_path(rc != NXT_OK)) { + status = nxt_ruby_rack_result_status(result); + if (nxt_slow_path(status < 0)) { + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Invalid response status from application."); + goto fail; } - rc = nxt_ruby_rack_result_headers(result); - if (nxt_slow_path(rc != NXT_OK)) { + rc = nxt_ruby_rack_result_headers(result, status); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { goto fail; } rc = nxt_ruby_rack_result_body(result); - if (nxt_slow_path(rc != NXT_OK)) { + if (nxt_slow_path(rc != NXT_UNIT_OK)) { goto fail; } - rc = nxt_app_msg_flush(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, 1); - if (nxt_slow_path(rc != NXT_OK)) { - goto fail; - } + nxt_unit_request_done(nxt_ruby_run_ctx.req, rc); + nxt_ruby_run_ctx.req = NULL; rb_hash_delete(env, rb_obj_id(env)); @@ -402,296 +416,194 @@ nxt_ruby_rack_app_run(VALUE arg) fail: + nxt_unit_request_done(nxt_ruby_run_ctx.req, NXT_UNIT_ERROR); + nxt_ruby_run_ctx.req = NULL; + rb_hash_delete(env, rb_obj_id(env)); return Qnil; } -static nxt_int_t -nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx, VALUE hash_env) +static int +nxt_ruby_read_request(VALUE hash_env) { - u_char *colon; - size_t query_size; - nxt_int_t rc; - nxt_str_t str, value, path, target; - nxt_str_t host, server_name, server_port; - nxt_task_t *task; - nxt_app_rmsg_t *rmsg; + char *host_start, *port_start; + uint32_t i, host_length, port_length; + nxt_unit_field_t *f; + nxt_unit_request_t *r; - static nxt_str_t def_host = nxt_string("localhost"); - static nxt_str_t def_port = nxt_string("80"); + r = nxt_ruby_run_ctx.req->request; - task = run_ctx->task; - rmsg = run_ctx->rmsg; +#define NL(S) (S), sizeof(S)-1 - rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_METHOD", &str); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; + nxt_ruby_add_sptr(hash_env, NL("REQUEST_METHOD"), &r->method, + r->method_length); + nxt_ruby_add_sptr(hash_env, NL("REQUEST_URI"), &r->target, + r->target_length); + nxt_ruby_add_sptr(hash_env, NL("PATH_INFO"), &r->path, r->path_length); + if (r->query.offset) { + nxt_ruby_add_sptr(hash_env, NL("QUERY_STRING"), &r->query, + r->query_length); } + nxt_ruby_add_sptr(hash_env, NL("SERVER_PROTOCOL"), &r->version, + r->version_length); + nxt_ruby_add_sptr(hash_env, NL("REMOTE_ADDR"), &r->remote, + r->remote_length); + nxt_ruby_add_sptr(hash_env, NL("SERVER_ADDR"), &r->local, r->local_length); - rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_URI", &target); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } + for (i = 0; i < r->fields_count; i++) { + f = r->fields + i; - rc = nxt_app_msg_read_str(task, rmsg, &path); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; + nxt_ruby_add_sptr(hash_env, nxt_unit_sptr_get(&f->name), f->name_length, + &f->value, f->value_length); } - rc = nxt_app_msg_read_size(task, rmsg, &query_size); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } + if (r->content_length_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->content_length_field; - if (path.start == NULL || path.length == 0) { - path = target; + nxt_ruby_add_sptr(hash_env, NL("CONTENT_LENGTH"), + &f->value, f->value_length); } - rb_hash_aset(hash_env, rb_str_new2("PATH_INFO"), - rb_str_new((const char *) path.start, (long) path.length)); + if (r->content_type_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->content_type_field; - if (query_size > 0) { - query_size--; - - if (nxt_slow_path(target.length < query_size)) { - return NXT_ERROR; - } - - str.start = &target.start[query_size]; - str.length = target.length - query_size; - - rb_hash_aset(hash_env, rb_str_new2("QUERY_STRING"), - rb_str_new((const char *) str.start, (long) str.length)); + nxt_ruby_add_sptr(hash_env, NL("CONTENT_TYPE"), + &f->value, f->value_length); } - rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_PROTOCOL", &str); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } + if (r->host_field != NXT_UNIT_NONE_FIELD) { + f = r->fields + r->host_field; - rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REMOTE_ADDR", &str); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } - - rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_ADDR", &str); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } - - rc = nxt_app_msg_read_str(task, rmsg, &host); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } - - if (host.length == 0) { - host = def_host; - } - - colon = nxt_memchr(host.start, ':', host.length); - server_name = host; - - if (colon != NULL) { - server_name.length = colon - host.start; - - server_port.start = colon + 1; - server_port.length = host.length - server_name.length - 1; + host_start = nxt_unit_sptr_get(&f->value); + host_length = f->value_length; } else { - server_port = def_port; - } - - rb_hash_aset(hash_env, rb_str_new2("SERVER_NAME"), - rb_str_new((const char *) server_name.start, - (long) server_name.length)); - - rb_hash_aset(hash_env, rb_str_new2("SERVER_PORT"), - rb_str_new((const char *) server_port.start, - (long) server_port.length)); - - rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_TYPE", &str); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } - - rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_LENGTH", &str); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; + host_start = NULL; + host_length = 0; } - for ( ;; ) { - rc = nxt_app_msg_read_str(task, rmsg, &str); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } + nxt_unit_split_host(host_start, host_length, &host_start, &host_length, + &port_start, &port_length); - if (nxt_slow_path(str.length == 0)) { - break; - } + nxt_ruby_add_str(hash_env, NL("SERVER_NAME"), host_start, host_length); + nxt_ruby_add_str(hash_env, NL("SERVER_PORT"), port_start, port_length); - rc = nxt_app_msg_read_str(task, rmsg, &value); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } - - rb_hash_aset(hash_env, - rb_str_new((char *) str.start, (long) str.length), - rb_str_new((const char *) value.start, - (long) value.length)); - } +#undef NL - rc = nxt_app_msg_read_size(task, rmsg, &run_ctx->body_preread_size); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } - - return NXT_OK; + return NXT_UNIT_OK; } -nxt_inline nxt_int_t -nxt_ruby_read_add_env(nxt_task_t *task, nxt_app_rmsg_t *rmsg, VALUE hash_env, - const char *name, nxt_str_t *str) +nxt_inline void +nxt_ruby_add_sptr(VALUE hash_env, + const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len) { - nxt_int_t rc; + char *str; - rc = nxt_app_msg_read_str(task, rmsg, str); - if (nxt_slow_path(rc != NXT_OK)) { - return rc; - } + str = nxt_unit_sptr_get(sptr); - if (str->start == NULL) { - rb_hash_aset(hash_env, rb_str_new2(name), Qnil); - return NXT_OK; - } + rb_hash_aset(hash_env, rb_str_new(name, name_len), rb_str_new(str, len)); +} - rb_hash_aset(hash_env, rb_str_new2(name), - rb_str_new((const char *) str->start, (long) str->length)); - return NXT_OK; +nxt_inline void +nxt_ruby_add_str(VALUE hash_env, + const char *name, uint32_t name_len, char *str, uint32_t len) +{ + rb_hash_aset(hash_env, rb_str_new(name, name_len), rb_str_new(str, len)); } static nxt_int_t nxt_ruby_rack_result_status(VALUE result) { - VALUE status; - u_char *p; - size_t len; - nxt_int_t rc; - u_char buf[3]; + VALUE status; status = rb_ary_entry(result, 0); if (TYPE(status) == T_FIXNUM) { - nxt_sprintf(buf, buf + 3, "%03d", FIX2INT(status)); - - p = buf; - len = 3; - - } else if (TYPE(status) == T_STRING) { - p = (u_char *) RSTRING_PTR(status); - len = RSTRING_LEN(status); - - } else { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Invalid response 'status' format from application"); - - return NXT_ERROR; + return FIX2INT(status); } - rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, - (u_char *) "Status: ", nxt_length("Status: "), 0, 0); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; + if (TYPE(status) == T_STRING) { + return nxt_int_parse((u_char *) RSTRING_PTR(status), + RSTRING_LEN(status)); } - rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, - p, len, 0, 0); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } - - rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, - (u_char *) "\r\n", nxt_length("\r\n"), 0, 0); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; - } + nxt_unit_req_error(nxt_ruby_run_ctx.req, "Ruby: Invalid response 'status' " + "format from application"); - return NXT_OK; + return -2; } -nxt_inline nxt_int_t -nxt_ruby_write(nxt_task_t *task, nxt_app_wmsg_t *wmsg, - const u_char *data, size_t len, nxt_bool_t flush, nxt_bool_t last) -{ - nxt_int_t rc; - - rc = nxt_app_msg_write_raw(task, wmsg, data, len); - if (nxt_slow_path(rc != NXT_OK)) { - return rc; - } - - if (flush || last) { - rc = nxt_app_msg_flush(task, wmsg, last); - } - - return rc; -} +typedef struct { + int rc; + uint32_t fields; + uint32_t size; +} nxt_ruby_headers_info_t; -static nxt_int_t -nxt_ruby_rack_result_headers(VALUE result) +static int +nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status) { - VALUE headers; - nxt_int_t rc; + int rc; + VALUE headers; + nxt_ruby_headers_info_t headers_info; headers = rb_ary_entry(result, 1); if (nxt_slow_path(TYPE(headers) != T_HASH)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Invalid response 'headers' format from application"); + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Invalid response 'headers' format from " + "application"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } - rc = NXT_OK; + rc = NXT_UNIT_OK; - rb_hash_foreach(headers, nxt_ruby_hash_foreach, (VALUE) (uintptr_t) &rc); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; + headers_info.rc = NXT_UNIT_OK; + headers_info.fields = 0; + headers_info.size = 0; + + rb_hash_foreach(headers, nxt_ruby_hash_info, + (VALUE) (uintptr_t) &headers_info); + if (nxt_slow_path(headers_info.rc != NXT_UNIT_OK)) { + return headers_info.rc; } - rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, - (u_char *) "\r\n", nxt_length("\r\n"), 0, 0); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; + rc = nxt_unit_response_init(nxt_ruby_run_ctx.req, status, + headers_info.fields, headers_info.size); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return rc; } - return NXT_OK; + rb_hash_foreach(headers, nxt_ruby_hash_add, (VALUE) (uintptr_t) &rc); + + return rc; } static int -nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg) +nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg) { - nxt_int_t rc, *rc_p; - const char *value, *value_end, *pos; + const char *value, *value_end, *pos; + nxt_ruby_headers_info_t *headers_info; - rc_p = (nxt_int_t *) (uintptr_t) arg; + headers_info = (void *) (uintptr_t) arg; if (nxt_slow_path(TYPE(r_key) != T_STRING)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Wrong header entry 'key' from application"); + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Wrong header entry 'key' from application"); goto fail; } if (nxt_slow_path(TYPE(r_value) != T_STRING)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Wrong header entry 'value' from application"); + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Wrong header entry 'value' from application"); goto fail; } @@ -708,70 +620,86 @@ nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg) break; } - rc = nxt_ruby_head_send_part(RSTRING_PTR(r_key), RSTRING_LEN(r_key), - value, pos - value); - if (nxt_slow_path(rc != NXT_OK)) { - goto fail; - } + headers_info->fields++; + headers_info->size += RSTRING_LEN(r_key) + (pos - value); pos++; value = pos; } if (value <= value_end) { - rc = nxt_ruby_head_send_part(RSTRING_PTR(r_key), RSTRING_LEN(r_key), - value, value_end - value); - if (nxt_slow_path(rc != NXT_OK)) { - goto fail; - } + headers_info->fields++; + headers_info->size += RSTRING_LEN(r_key) + (value_end - value); } - *rc_p = NXT_OK; - return ST_CONTINUE; fail: - *rc_p = NXT_ERROR; + headers_info->rc = NXT_UNIT_ERROR; return ST_STOP; } -static nxt_int_t -nxt_ruby_head_send_part(const char *key, size_t key_size, - const char *value, size_t value_size) +static int +nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) { - nxt_int_t rc; + int *rc; + uint32_t key_len; + const char *value, *value_end, *pos; - rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, - (u_char *) key, key_size); - if (nxt_slow_path(rc != NXT_OK)) { - return rc; - } + rc = (int *) (uintptr_t) arg; - rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, - (u_char *) ": ", nxt_length(": ")); - if (nxt_slow_path(rc != NXT_OK)) { - return rc; + value = RSTRING_PTR(r_value); + value_end = value + RSTRING_LEN(r_value); + + key_len = RSTRING_LEN(r_key); + + pos = value; + + for ( ;; ) { + pos = strchr(pos, '\n'); + + if (pos == NULL) { + break; + } + + *rc = nxt_unit_response_add_field(nxt_ruby_run_ctx.req, + RSTRING_PTR(r_key), key_len, + value, pos - value); + if (nxt_slow_path(*rc != NXT_UNIT_OK)) { + goto fail; + } + + pos++; + value = pos; } - rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, - (u_char *) value, value_size); - if (nxt_slow_path(rc != NXT_OK)) { - return rc; + if (value <= value_end) { + *rc = nxt_unit_response_add_field(nxt_ruby_run_ctx.req, + RSTRING_PTR(r_key), key_len, + value, value_end - value); + if (nxt_slow_path(*rc != NXT_UNIT_OK)) { + goto fail; + } } - return nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, - (u_char *) "\r\n", nxt_length("\r\n")); + return ST_CONTINUE; + +fail: + + *rc = NXT_UNIT_ERROR; + + return ST_STOP; } -static nxt_int_t +static int nxt_ruby_rack_result_body(VALUE result) { - VALUE fn, body; - nxt_int_t rc; + int rc; + VALUE fn, body; body = rb_ary_entry(result, 2); @@ -779,120 +707,134 @@ nxt_ruby_rack_result_body(VALUE result) fn = rb_funcall(body, rb_intern("to_path"), 0); if (nxt_slow_path(TYPE(fn) != T_STRING)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Failed to get 'body' file path from application"); + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Failed to get 'body' file path from " + "application"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } rc = nxt_ruby_rack_result_body_file_write(fn); - if (nxt_slow_path(rc != NXT_OK)) { - return NXT_ERROR; + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + return rc; } } else if (rb_respond_to(body, rb_intern("each"))) { rb_iterate(rb_each, body, nxt_ruby_rack_result_body_each, 0); } else { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Invalid response 'body' format from application"); + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Invalid response 'body' format " + "from application"); - return NXT_ERROR; + return NXT_UNIT_ERROR; } if (rb_respond_to(body, rb_intern("close"))) { rb_funcall(body, rb_intern("close"), 0); } - return NXT_OK; + return NXT_UNIT_OK; } -static nxt_int_t -nxt_ruby_rack_result_body_file_write(VALUE filepath) +typedef struct { + int fd; + off_t pos; + off_t rest; +} nxt_ruby_rack_file_t; + + +static ssize_t +nxt_ruby_rack_file_read(nxt_unit_read_info_t *read_info, void *dst, size_t size) { - size_t len; - ssize_t n; - nxt_off_t rest; - nxt_int_t rc; - nxt_file_t file; - nxt_file_info_t finfo; - u_char buf[8192]; + ssize_t res; + nxt_ruby_rack_file_t *file; - nxt_memzero(&file, sizeof(nxt_file_t)); + file = read_info->data; - file.name = (nxt_file_name_t *) RSTRING_PTR(filepath); + size = nxt_min(size, (size_t) file->rest); - rc = nxt_file_open(nxt_ruby_run_ctx.task, &file, NXT_FILE_RDONLY, - NXT_FILE_OPEN, 0); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Failed to open 'body' file: %s", - (const char *) file.name); + res = pread(file->fd, dst, size, file->pos); - return NXT_ERROR; + if (res >= 0) { + file->pos += res; + file->rest -= res; + + if (size > (size_t) res) { + file->rest = 0; + } } - rc = nxt_file_info(&file, &finfo); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Failed to get 'body' file information: %s", - (const char *) file.name); + read_info->eof = file->rest == 0; - goto fail; - } + return res; +} - rest = nxt_file_size(&finfo); - while (rest != 0) { - len = nxt_min(rest, (nxt_off_t) sizeof(buf)); +static int +nxt_ruby_rack_result_body_file_write(VALUE filepath) +{ + int fd, rc; + struct stat finfo; + nxt_ruby_rack_file_t ruby_file; + nxt_unit_read_info_t read_info; - n = nxt_file_read(&file, buf, len, nxt_file_size(&finfo) - rest); - if (nxt_slow_path(n != (ssize_t) len)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Failed to read 'body' file"); + fd = open(RSTRING_PTR(filepath), O_RDONLY, 0); + if (nxt_slow_path(fd == -1)) { + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Failed to open content file \"%s\": %s (%d)", + RSTRING_PTR(filepath), strerror(errno), errno); - goto fail; - } + return NXT_UNIT_ERROR; + } - rest -= len; + rc = fstat(fd, &finfo); + if (nxt_slow_path(rc == -1)) { + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Content file fstat(\"%s\") failed: %s (%d)", + RSTRING_PTR(filepath), strerror(errno), errno); - rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, - buf, len); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Failed to write 'body' from application"); + close(fd); - goto fail; - } + return NXT_UNIT_ERROR; } - nxt_file_close(nxt_ruby_run_ctx.task, &file); + ruby_file.fd = fd; + ruby_file.pos = 0; + ruby_file.rest = finfo.st_size; - return NXT_OK; + read_info.read = nxt_ruby_rack_file_read; + read_info.eof = ruby_file.rest == 0; + read_info.buf_size = ruby_file.rest; + read_info.data = &ruby_file; -fail: + rc = nxt_unit_response_write_cb(nxt_ruby_run_ctx.req, &read_info); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Failed to write content file."); + } - nxt_file_close(nxt_ruby_run_ctx.task, &file); + close(fd); - return NXT_ERROR; + return rc; } static VALUE nxt_ruby_rack_result_body_each(VALUE body) { - nxt_int_t rc; + int rc; if (TYPE(body) != T_STRING) { return Qnil; } - rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, - (u_char *) RSTRING_PTR(body), RSTRING_LEN(body)); - if (nxt_slow_path(rc != NXT_OK)) { - nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, - "Ruby: Failed to write 'body' from application"); + rc = nxt_unit_response_write(nxt_ruby_run_ctx.req, RSTRING_PTR(body), + RSTRING_LEN(body)); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + nxt_unit_req_error(nxt_ruby_run_ctx.req, + "Ruby: Failed to write 'body' from application"); } return Qnil; @@ -905,30 +847,51 @@ nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc) int i; VALUE err, ary, eclass, msg; - nxt_log(task, level, "Ruby: %s", desc); + if (task != NULL) { + nxt_log(task, level, "Ruby: %s", desc); + + } else { + nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "Ruby: %s", desc); + } err = rb_errinfo(); - ary = rb_funcall(err, rb_intern("backtrace"), 0); + if (nxt_slow_path(err == Qnil)) { + return; + } - if (RARRAY_LEN(ary) == 0) { + ary = rb_funcall(err, rb_intern("backtrace"), 0); + if (nxt_slow_path(RARRAY_LEN(ary) == 0)) { return; } eclass = rb_class_name(rb_class_of(err)); msg = rb_funcall(err, rb_intern("message"), 0); - nxt_log(task, level, "Ruby: %s: %s (%s)", - RSTRING_PTR(RARRAY_PTR(ary)[0]), - RSTRING_PTR(msg), RSTRING_PTR(eclass)); + if (task != NULL) { + nxt_log(task, level, "Ruby: %s: %s (%s)", + RSTRING_PTR(RARRAY_PTR(ary)[0]), + RSTRING_PTR(msg), RSTRING_PTR(eclass)); + + } else { + nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "Ruby: %s: %s (%s)", + RSTRING_PTR(RARRAY_PTR(ary)[0]), + RSTRING_PTR(msg), RSTRING_PTR(eclass)); + } for (i = 1; i < RARRAY_LEN(ary); i++) { - nxt_log(task, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i])); + if (task != NULL) { + nxt_log(task, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i])); + + } else { + nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "from %s", + RSTRING_PTR(RARRAY_PTR(ary)[i])); + } } } static void -nxt_ruby_atexit(nxt_task_t *task) +nxt_ruby_atexit(void) { rb_gc_unregister_address(&nxt_ruby_io_input); rb_gc_unregister_address(&nxt_ruby_io_error); diff --git a/src/ruby/nxt_ruby.h b/src/ruby/nxt_ruby.h index 9a6be0d4..77a65894 100644 --- a/src/ruby/nxt_ruby.h +++ b/src/ruby/nxt_ruby.h @@ -17,14 +17,12 @@ #include <nxt_router.h> #include <nxt_runtime.h> #include <nxt_application.h> +#include <nxt_unit_typedefs.h> typedef struct { - nxt_task_t *task; - nxt_app_rmsg_t *rmsg; - nxt_app_wmsg_t *wmsg; - - size_t body_preread_size; + nxt_unit_ctx_t *unit_ctx; + nxt_unit_request_info_t *req; } nxt_ruby_run_ctx_t; diff --git a/src/ruby/nxt_ruby_stream_io.c b/src/ruby/nxt_ruby_stream_io.c index eee11c3c..3f6cac89 100644 --- a/src/ruby/nxt_ruby_stream_io.c +++ b/src/ruby/nxt_ruby_stream_io.c @@ -5,12 +5,12 @@ */ #include <ruby/nxt_ruby.h> +#include <nxt_unit.h> static VALUE nxt_ruby_stream_io_new(VALUE class, VALUE wrap); static VALUE nxt_ruby_stream_io_initialize(int argc, VALUE *argv, VALUE self); static VALUE nxt_ruby_stream_io_gets(VALUE obj, VALUE args); -static size_t nxt_ruby_stream_io_read_line(nxt_app_rmsg_t *rmsg, VALUE str); static VALUE nxt_ruby_stream_io_each(VALUE obj, VALUE args); static VALUE nxt_ruby_stream_io_read(VALUE obj, VALUE args); static VALUE nxt_ruby_stream_io_rewind(VALUE obj, VALUE args); @@ -85,63 +85,47 @@ nxt_ruby_stream_io_initialize(int argc, VALUE *argv, VALUE self) static VALUE nxt_ruby_stream_io_gets(VALUE obj, VALUE args) { - VALUE buf; - nxt_ruby_run_ctx_t *run_ctx; + VALUE buf; + char *p; + size_t size, b_size; + nxt_unit_buf_t *b; + nxt_ruby_run_ctx_t *run_ctx; + nxt_unit_request_info_t *req; Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx); - if (run_ctx->body_preread_size == 0) { - return Qnil; - } - - buf = rb_str_buf_new(1); + req = run_ctx->req; - if (buf == Qnil) { + if (req->content_length == 0) { return Qnil; } - run_ctx->body_preread_size -= nxt_ruby_stream_io_read_line(run_ctx->rmsg, - buf); - - return buf; -} - - -static size_t -nxt_ruby_stream_io_read_line(nxt_app_rmsg_t *rmsg, VALUE str) -{ - size_t len, size; - u_char *p; - nxt_buf_t *buf; - - len = 0; + size = 0; - for (buf = rmsg->buf; buf != NULL; buf = buf->next) { - - size = nxt_buf_mem_used_size(&buf->mem); - p = memchr(buf->mem.pos, '\n', size); + for (b = req->content_buf; b; b = nxt_unit_buf_next(b)) { + b_size = b->end - b->free; + p = memchr(b->free, '\n', b_size); if (p != NULL) { p++; - size = p - buf->mem.pos; - - rb_str_cat(str, (const char *) buf->mem.pos, size); - - len += size; - buf->mem.pos = p; - + size += p - b->free; break; } - rb_str_cat(str, (const char *) buf->mem.pos, size); + size += b_size; + } + + buf = rb_str_buf_new(size); - len += size; - buf->mem.pos = buf->mem.free; + if (buf == Qnil) { + return Qnil; } - rmsg->buf = buf; + size = nxt_unit_request_read(req, RSTRING_PTR(buf), size); + + rb_str_set_len(buf, size); - return len; + return buf; } @@ -173,12 +157,11 @@ nxt_ruby_stream_io_read(VALUE obj, VALUE args) { VALUE buf; long copy_size, u_size; - size_t len; nxt_ruby_run_ctx_t *run_ctx; Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx); - copy_size = run_ctx->body_preread_size; + copy_size = run_ctx->req->content_length; if (RARRAY_LEN(args) > 0 && TYPE(RARRAY_PTR(args)[0]) == T_FIXNUM) { u_size = NUM2LONG(RARRAY_PTR(args)[0]); @@ -202,8 +185,8 @@ nxt_ruby_stream_io_read(VALUE obj, VALUE args) return Qnil; } - len = nxt_app_msg_read_raw(run_ctx->task, run_ctx->rmsg, - RSTRING_PTR(buf), (size_t) copy_size); + copy_size = nxt_unit_request_read(run_ctx->req, RSTRING_PTR(buf), + copy_size); if (RARRAY_LEN(args) > 1 && TYPE(RARRAY_PTR(args)[1]) == T_STRING) { @@ -211,9 +194,7 @@ nxt_ruby_stream_io_read(VALUE obj, VALUE args) rb_str_cat(RARRAY_PTR(args)[1], RSTRING_PTR(buf), copy_size); } - rb_str_set_len(buf, (long) len); - - run_ctx->body_preread_size -= len; + rb_str_set_len(buf, copy_size); return buf; } @@ -276,8 +257,7 @@ nxt_ruby_stream_io_s_write(nxt_ruby_run_ctx_t *run_ctx, VALUE val) } } - nxt_log_error(NXT_LOG_ERR, run_ctx->task->log, "Ruby: %s", - RSTRING_PTR(val)); + nxt_unit_req_error(run_ctx->req, "Ruby: %s", RSTRING_PTR(val)); return RSTRING_LEN(val); } diff --git a/src/test/nxt_unit_app_test.c b/src/test/nxt_unit_app_test.c new file mode 100644 index 00000000..4ecb8d6b --- /dev/null +++ b/src/test/nxt_unit_app_test.c @@ -0,0 +1,191 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_unit.h> +#include <nxt_unit_request.h> +#include <nxt_clang.h> + + +#define CONTENT_TYPE "Content-Type" +#define TEXT_PLAIN "text/plain" +#define HELLO_WORLD "Hello world!\n" + +#define NEW_LINE "\n" + +#define REQUEST_DATA "Request data:\n" +#define METHOD " Method: " +#define PROTOCOL " Protocol: " +#define REMOTE_ADDR " Remote addr: " +#define LOCAL_ADDR " Local addr: " +#define TARGET " Target: " +#define PATH " Path: " +#define QUERY " Query: " +#define FIELDS " Fields:\n" +#define FIELD_PAD " " +#define FIELD_SEP ": " +#define BODY " Body:\n" + + +static inline char * +copy(char *p, const void *src, uint32_t len) +{ + memcpy(p, src, len); + + return p + len; +} + + +static void +greeting_app_request_handler(nxt_unit_request_info_t *req) +{ + int rc; + char *p; + ssize_t res; + uint32_t i; + nxt_unit_buf_t *buf; + nxt_unit_field_t *f; + nxt_unit_request_t *r; + + rc = nxt_unit_response_init(req, 200 /* Status code. */, + 1 /* Number of response headers. */, + nxt_length(CONTENT_TYPE) + + nxt_length(TEXT_PLAIN) + + nxt_length(HELLO_WORLD)); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } + + rc = nxt_unit_response_add_field(req, + CONTENT_TYPE, nxt_length(CONTENT_TYPE), + TEXT_PLAIN, nxt_length(TEXT_PLAIN)); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } + + rc = nxt_unit_response_add_content(req, HELLO_WORLD, + nxt_length(HELLO_WORLD)); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } + + rc = nxt_unit_response_send(req); + if (nxt_slow_path(rc != NXT_UNIT_OK)) { + goto fail; + } + + r = req->request; + + buf = nxt_unit_response_buf_alloc(req, (req->request_buf->end + - req->request_buf->start) + + nxt_length(REQUEST_DATA) + + nxt_length(METHOD) + + nxt_length(NEW_LINE) + + nxt_length(PROTOCOL) + + nxt_length(NEW_LINE) + + nxt_length(REMOTE_ADDR) + + nxt_length(NEW_LINE) + + nxt_length(LOCAL_ADDR) + + nxt_length(NEW_LINE) + + nxt_length(TARGET) + + nxt_length(NEW_LINE) + + nxt_length(PATH) + + nxt_length(NEW_LINE) + + nxt_length(QUERY) + + nxt_length(NEW_LINE) + + nxt_length(FIELDS) + + r->fields_count * ( + nxt_length(FIELD_PAD) + + nxt_length(FIELD_SEP)) + + nxt_length(BODY)); + if (nxt_slow_path(buf == NULL)) { + rc = NXT_UNIT_ERROR; + + goto fail; + } + + p = buf->free; + + p = copy(p, REQUEST_DATA, nxt_length(REQUEST_DATA)); + + p = copy(p, METHOD, nxt_length(METHOD)); + p = copy(p, nxt_unit_sptr_get(&r->method), r->method_length); + *p++ = '\n'; + + p = copy(p, PROTOCOL, nxt_length(PROTOCOL)); + p = copy(p, nxt_unit_sptr_get(&r->version), r->version_length); + *p++ = '\n'; + + p = copy(p, REMOTE_ADDR, nxt_length(REMOTE_ADDR)); + p = copy(p, nxt_unit_sptr_get(&r->remote), r->remote_length); + *p++ = '\n'; + + p = copy(p, LOCAL_ADDR, nxt_length(LOCAL_ADDR)); + p = copy(p, nxt_unit_sptr_get(&r->local), r->local_length); + *p++ = '\n'; + + p = copy(p, TARGET, nxt_length(TARGET)); + p = copy(p, nxt_unit_sptr_get(&r->target), r->target_length); + *p++ = '\n'; + + p = copy(p, PATH, nxt_length(PATH)); + p = copy(p, nxt_unit_sptr_get(&r->path), r->path_length); + *p++ = '\n'; + + if (r->query.offset) { + p = copy(p, QUERY, nxt_length(QUERY)); + p = copy(p, nxt_unit_sptr_get(&r->query), r->query_length); + *p++ = '\n'; + } + + p = copy(p, FIELDS, nxt_length(FIELDS)); + + for (i = 0; i < r->fields_count; i++) { + f = r->fields + i; + + p = copy(p, FIELD_PAD, nxt_length(FIELD_PAD)); + p = copy(p, nxt_unit_sptr_get(&f->name), f->name_length); + p = copy(p, FIELD_SEP, nxt_length(FIELD_SEP)); + p = copy(p, nxt_unit_sptr_get(&f->value), f->value_length); + *p++ = '\n'; + } + + if (r->content_length > 0) { + p = copy(p, BODY, nxt_length(BODY)); + + res = nxt_unit_request_read(req, buf->free, buf->end - buf->free); + buf->free += res; + + } + + buf->free = p; + + rc = nxt_unit_buf_send(buf); + +fail: + + nxt_unit_request_done(req, rc); +} + +int +main() +{ + nxt_unit_ctx_t *ctx; + nxt_unit_init_t init; + + memset(&init, 0, sizeof(nxt_unit_init_t)); + + init.callbacks.request_handler = greeting_app_request_handler; + + ctx = nxt_unit_init(&init); + if (ctx == NULL) { + return 1; + } + + nxt_unit_run(ctx); + + nxt_unit_done(ctx); + + return 0; +} |