summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorMax Romanov <max.romanov@nginx.com>2018-08-06 17:27:33 +0300
committerMax Romanov <max.romanov@nginx.com>2018-08-06 17:27:33 +0300
commit1bb22d1e922c87d3c86c67bdce626767ee48fb5c (patch)
tree6e067a82b309c3a0d0f592f037f26d886a7f8c13 /src
parentb6ce2da65c9c5229d744b2d964623b2d0f731ee9 (diff)
downloadunit-1bb22d1e922c87d3c86c67bdce626767ee48fb5c.tar.gz
unit-1bb22d1e922c87d3c86c67bdce626767ee48fb5c.tar.bz2
Unit application library.
Library now used in all language modules. Old 'nxt_app_*' code removed. See src/test/nxt_unit_app_test.c for usage sample.
Diffstat (limited to 'src')
-rw-r--r--src/go/unit/nxt_cgo_lib.c207
-rw-r--r--src/go/unit/nxt_cgo_lib.h40
-rw-r--r--src/go/unit/nxt_go_array.c62
-rw-r--r--src/go/unit/nxt_go_array.h36
-rw-r--r--src/go/unit/nxt_go_lib.c143
-rw-r--r--src/go/unit/nxt_go_lib.h40
-rw-r--r--src/go/unit/nxt_go_log.h34
-rw-r--r--src/go/unit/nxt_go_mutex.h21
-rw-r--r--src/go/unit/nxt_go_port.c216
-rw-r--r--src/go/unit/nxt_go_port.h18
-rw-r--r--src/go/unit/nxt_go_port_memory.c217
-rw-r--r--src/go/unit/nxt_go_port_memory.h30
-rw-r--r--src/go/unit/nxt_go_process.c148
-rw-r--r--src/go/unit/nxt_go_process.h33
-rw-r--r--src/go/unit/nxt_go_run_ctx.c554
-rw-r--r--src/go/unit/nxt_go_run_ctx.h78
-rw-r--r--src/go/unit/port.go143
-rw-r--r--src/go/unit/request.go51
-rw-r--r--src/go/unit/response.go36
-rw-r--r--src/go/unit/unit.go123
-rw-r--r--src/nxt_application.c476
-rw-r--r--src/nxt_application.h162
-rw-r--r--src/nxt_go.c39
-rw-r--r--src/nxt_php_sapi.c656
-rw-r--r--src/nxt_port_memory.c4
-rw-r--r--src/nxt_python_wsgi.c672
-rw-r--r--src/nxt_router.c655
-rw-r--r--src/nxt_router.h6
-rw-r--r--src/nxt_runtime.h5
-rw-r--r--src/nxt_unit.c3630
-rw-r--r--src/nxt_unit.h355
-rw-r--r--src/nxt_unit_field.h34
-rw-r--r--src/nxt_unit_request.h48
-rw-r--r--src/nxt_unit_response.h27
-rw-r--r--src/nxt_unit_sptr.h38
-rw-r--r--src/nxt_unit_typedefs.h25
-rw-r--r--src/nxt_worker_process.c3
-rw-r--r--src/perl/nxt_perl_psgi.c794
-rw-r--r--src/ruby/nxt_ruby.c693
-rw-r--r--src/ruby/nxt_ruby.h8
-rw-r--r--src/ruby/nxt_ruby_stream_io.c78
-rw-r--r--src/test/nxt_unit_app_test.c191
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;
+}