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.
This commit is contained in:
29
auto/make
29
auto/make
@@ -54,6 +54,18 @@ done
|
||||
|
||||
$echo >> $NXT_MAKEFILE
|
||||
|
||||
$echo "NXT_LIB_UNIT_OBJS = \\" >> $NXT_MAKEFILE
|
||||
$echo " $NXT_BUILD_DIR/src/nxt_lvlhsh.o \\" >> $NXT_MAKEFILE
|
||||
$echo " $NXT_BUILD_DIR/src/nxt_murmur_hash.o \\" >> $NXT_MAKEFILE
|
||||
|
||||
for nxt_src in $NXT_LIB_UNIT_SRCS
|
||||
do
|
||||
nxt_obj=${nxt_src%.c}.o
|
||||
$echo " $NXT_BUILD_DIR/$nxt_obj \\" >> $NXT_MAKEFILE
|
||||
done
|
||||
|
||||
$echo >> $NXT_MAKEFILE
|
||||
|
||||
|
||||
# Shared and static library.
|
||||
|
||||
@@ -70,12 +82,17 @@ $NXT_BUILD_DIR/$NXT_LIB_STATIC: \$(NXT_LIB_OBJS)
|
||||
$NXT_STATIC_LINK $NXT_BUILD_DIR/$NXT_LIB_STATIC \\
|
||||
\$(NXT_LIB_OBJS)
|
||||
|
||||
$NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC: \$(NXT_LIB_UNIT_OBJS)
|
||||
$NXT_STATIC_LINK $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC \\
|
||||
\$(NXT_LIB_UNIT_OBJS)
|
||||
|
||||
END
|
||||
|
||||
|
||||
# Object files.
|
||||
|
||||
for nxt_src in $NXT_LIB_SRCS $NXT_TEST_SRCS
|
||||
for nxt_src in $NXT_LIB_SRCS $NXT_TEST_SRCS $NXT_LIB_UNIT_SRCS \
|
||||
src/test/nxt_unit_app_test.c
|
||||
do
|
||||
nxt_obj=${nxt_src%.c}.o
|
||||
nxt_dep=${nxt_src%.c}.dep
|
||||
@@ -129,6 +146,13 @@ $NXT_BUILD_DIR/utf8_file_name_test: $NXT_LIB_UTF8_FILE_NAME_TEST_SRCS \\
|
||||
$NXT_BUILD_DIR/$NXT_LIB_STATIC \\
|
||||
$NXT_LD_OPT $NXT_LIBM $NXT_LIBS
|
||||
|
||||
$NXT_BUILD_DIR/unit_app_test: $NXT_BUILD_DIR/src/test/nxt_unit_app_test.o \\
|
||||
$NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC
|
||||
\$(NXT_EXEC_LINK) -o $NXT_BUILD_DIR/unit_app_test \\
|
||||
\$(CFLAGS) $NXT_BUILD_DIR/src/test/nxt_unit_app_test.o \\
|
||||
$NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC \\
|
||||
$NXT_LD_OPT $NXT_LIBM $NXT_LIBS $NXT_LIB_AUX_LIBS
|
||||
|
||||
END
|
||||
|
||||
|
||||
@@ -241,7 +265,8 @@ cat << END > Makefile
|
||||
include $NXT_MAKEFILE
|
||||
|
||||
.PHONY: tests
|
||||
tests: $NXT_BUILD_DIR/tests $NXT_BUILD_DIR/utf8_file_name_test
|
||||
tests: $NXT_BUILD_DIR/tests $NXT_BUILD_DIR/utf8_file_name_test \\
|
||||
$NXT_BUILD_DIR/unit_app_test
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
||||
@@ -106,6 +106,7 @@ ${NXT_GO}-install: ${NXT_GO}-install-build
|
||||
${NXT_GO}-install-src:
|
||||
install -d \$(DESTDIR)\$(NXT_GO_DST)/src/nginx/unit
|
||||
install -p -m644 ./src/*.h ./build/*.h ./src/go/unit/* \
|
||||
./src/nxt_unit.c ./src/nxt_lvlhsh.c ./src/nxt_murmur_hash.c \
|
||||
\$(DESTDIR)\$(NXT_GO_DST)/src/nginx/unit/
|
||||
|
||||
${NXT_GO}-install-build: ${NXT_GO}-install-src
|
||||
|
||||
@@ -155,7 +155,7 @@ NXT_PERL_MODULE_SRCS=" \
|
||||
|
||||
# The Perl module object files.
|
||||
|
||||
nxt_objs=
|
||||
nxt_objs=$NXT_BUILD_DIR/src/nxt_unit.o
|
||||
|
||||
for nxt_src in $NXT_PERL_MODULE_SRCS; do
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ NXT_PHP_MODULE_SRCS=" \
|
||||
|
||||
# The php module object files.
|
||||
|
||||
nxt_objs=
|
||||
nxt_objs=$NXT_BUILD_DIR/src/nxt_unit.o
|
||||
|
||||
for nxt_src in $NXT_PHP_MODULE_SRCS; do
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ NXT_PYTHON_MODULE_SRCS=" \
|
||||
|
||||
# The python module object files.
|
||||
|
||||
nxt_objs=
|
||||
nxt_objs=$NXT_BUILD_DIR/src/nxt_unit.o
|
||||
|
||||
for nxt_src in $NXT_PYTHON_MODULE_SRCS; do
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ NXT_RUBY_MODULE_SRCS=" \
|
||||
|
||||
# The Ruby module object files.
|
||||
|
||||
nxt_objs=
|
||||
nxt_objs=$NXT_BUILD_DIR/src/nxt_unit.o
|
||||
|
||||
for nxt_src in $NXT_RUBY_MODULE_SRCS; do
|
||||
|
||||
|
||||
22
auto/os/conf
22
auto/os/conf
@@ -35,6 +35,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.so"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
NXT_LIBM="-lm"
|
||||
NXT_LIBS="$NXT_LIBRT $NXT_LIBDL $NXT_PTHREAD"
|
||||
;;
|
||||
@@ -57,6 +59,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.so"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
NXT_LIBM="-lm"
|
||||
NXT_LIBS="$NXT_LIBRT $NXT_PTHREAD"
|
||||
;;
|
||||
@@ -86,6 +90,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.so"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
NXT_EXEC_LINK="\$(CC)"
|
||||
NXT_SHARED_LOCAL_EXEC_LINK=
|
||||
|
||||
@@ -113,6 +119,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.dylib"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.dylib"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
# MacOSX libm.dylib is a symlink to libSystem.dylib.
|
||||
NXT_LIBM=
|
||||
NXT_LIBS=
|
||||
@@ -135,6 +143,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.so"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
NXT_LIBM="-lm"
|
||||
NXT_LIBS="$NXT_LIBRT $NXT_PTHREAD"
|
||||
;;
|
||||
@@ -155,6 +165,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.so"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
NXT_LIBM="-lm"
|
||||
NXT_LIBS="$NXT_PTHREAD"
|
||||
;;
|
||||
@@ -175,6 +187,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.so"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
NXT_LIBM="-lm"
|
||||
NXT_LIBS="$NXT_LIBRT $NXT_PTHREAD"
|
||||
;;
|
||||
@@ -194,6 +208,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.so"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
NXT_LIBM="-lm"
|
||||
NXT_LIBS="$NXT_PTHREAD"
|
||||
;;
|
||||
@@ -213,6 +229,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.so"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
NXT_LIBM="-lm"
|
||||
NXT_LIBS="$NXT_PTHREAD $NXT_LIBHG"
|
||||
;;
|
||||
@@ -232,6 +250,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.so"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
NXT_LIBM="-lm"
|
||||
NXT_LIBS="$NXT_PTHREAD"
|
||||
;;
|
||||
@@ -250,6 +270,8 @@ case "$NXT_SYSTEM" in
|
||||
NXT_LIB_SHARED="libnxt.so"
|
||||
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
|
||||
|
||||
NXT_LIB_UNIT_STATIC="libunit.a"
|
||||
|
||||
NXT_LIBM="-lm"
|
||||
NXT_LIBS="$NXT_LIBRT $NXT_LIBDL $NXT_PTHREAD"
|
||||
;;
|
||||
|
||||
@@ -102,6 +102,8 @@ NXT_LIB_SRC0=" \
|
||||
src/nxt_mem_pool_cleanup.c \
|
||||
"
|
||||
|
||||
NXT_LIB_UNIT_SRCS="src/nxt_unit.c"
|
||||
|
||||
|
||||
NXT_LIB_SSLTLS_DEPS="src/nxt_ssltls.h"
|
||||
NXT_LIB_SSLTLS_SRCS="src/nxt_ssltls.c"
|
||||
|
||||
207
src/go/unit/nxt_cgo_lib.c
Normal file
207
src/go/unit/nxt_cgo_lib.c
Normal file
@@ -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);
|
||||
}
|
||||
40
src/go/unit/nxt_cgo_lib.h
Normal file
40
src/go/unit/nxt_cgo_lib.h
Normal file
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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_ */
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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 {
|
||||
|
||||
p := main_port()
|
||||
key := port_key{
|
||||
pid: int(pid),
|
||||
id: int(id),
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,105 +29,74 @@ func buf_ref(buf []byte) C.uintptr_t {
|
||||
return C.uintptr_t(uintptr(unsafe.Pointer(&buf[0])))
|
||||
}
|
||||
|
||||
func (buf *cbuf) init(b []byte) {
|
||||
type StringHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
}
|
||||
|
||||
func str_ref(s string) C.uintptr_t {
|
||||
header := (*StringHeader)(unsafe.Pointer(&s))
|
||||
|
||||
return C.uintptr_t(uintptr(unsafe.Pointer(header.Data)))
|
||||
}
|
||||
|
||||
func (buf *cbuf) init_bytes(b []byte) {
|
||||
buf.b = buf_ref(b)
|
||||
buf.s = C.size_t(len(b))
|
||||
}
|
||||
|
||||
func (buf *cbuf) init_string(s string) {
|
||||
buf.b = str_ref(s)
|
||||
buf.s = C.size_t(len(s))
|
||||
}
|
||||
|
||||
type SliceHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
|
||||
func (buf *cbuf) GoBytes() []byte {
|
||||
if buf == nil {
|
||||
var b [0]byte
|
||||
return b[:0]
|
||||
}
|
||||
|
||||
return C.GoBytes(unsafe.Pointer(uintptr(buf.b)), C.int(buf.s))
|
||||
bytesHeader := &SliceHeader{
|
||||
Data: unsafe.Pointer(uintptr(buf.b)),
|
||||
Len: int(buf.s),
|
||||
Cap: int(buf.s),
|
||||
}
|
||||
|
||||
var nxt_go_quit bool = false
|
||||
return *(*[]byte)(unsafe.Pointer(bytesHeader))
|
||||
}
|
||||
|
||||
//export nxt_go_set_quit
|
||||
func nxt_go_set_quit() {
|
||||
nxt_go_quit = true
|
||||
func GoBytes(buf unsafe.Pointer, size C.int) []byte {
|
||||
bytesHeader := &SliceHeader{
|
||||
Data: buf,
|
||||
Len: int(size),
|
||||
Cap: int(size),
|
||||
}
|
||||
|
||||
return *(*[]byte)(unsafe.Pointer(bytesHeader))
|
||||
}
|
||||
|
||||
func nxt_go_warn(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "[go warn] " + format + "\n", args...)
|
||||
}
|
||||
str := fmt.Sprintf("[go] " + format, args...)
|
||||
|
||||
func nxt_go_debug(format string, args ...interface{}) {
|
||||
// fmt.Fprintf(os.Stderr, "[go debug] " + format + "\n", args...)
|
||||
C.nxt_cgo_warn(str_ref(str), C.uint32_t(len(str)))
|
||||
}
|
||||
|
||||
func ListenAndServe(addr string, handler http.Handler) error {
|
||||
var read_port *port
|
||||
if handler == nil {
|
||||
handler = http.DefaultServeMux
|
||||
}
|
||||
|
||||
go_ports_env := os.Getenv("NXT_GO_PORTS")
|
||||
if go_ports_env == "" {
|
||||
rc := C.nxt_cgo_run(C.uintptr_t(uintptr(unsafe.Pointer(&handler))))
|
||||
|
||||
if rc != 0 {
|
||||
return http.ListenAndServe(addr, handler)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
stream, stream_err := strconv.Atoi(ports[1])
|
||||
if stream_err != nil {
|
||||
return stream_err
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
p := new_port(attrsN[0], attrsN[1], attrsN[2], attrsN[3], attrsN[4])
|
||||
|
||||
if attrsN[0] == pid {
|
||||
read_port = p
|
||||
}
|
||||
}
|
||||
|
||||
if read_port == nil {
|
||||
return errors.New("Application read port not found");
|
||||
}
|
||||
|
||||
C.nxt_go_ready(C.uint32_t(stream))
|
||||
|
||||
for !nxt_go_quit {
|
||||
err := read_port.read(handler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -194,7 +192,7 @@ nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules,
|
||||
nxt_uint_t i, n;
|
||||
nxt_module_t *module;
|
||||
nxt_app_type_t type;
|
||||
nxt_application_module_t *app;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
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_ */
|
||||
|
||||
39
src/nxt_go.c
39
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
struct nxt_php_run_ctx_s {
|
||||
char *cookie;
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
size_t s;
|
||||
nxt_int_t rc;
|
||||
nxt_str_t script_name;
|
||||
nxt_app_request_header_t *h;
|
||||
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;
|
||||
|
||||
h = &ctx->r.header;
|
||||
nxt_memzero(&run_ctx, sizeof(run_ctx));
|
||||
|
||||
#define RC(S) \
|
||||
do { \
|
||||
rc = (S); \
|
||||
if (nxt_slow_path(rc != NXT_OK)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
} while(0)
|
||||
ctx = &run_ctx;
|
||||
ctx->req = req;
|
||||
|
||||
#define NXT_READ(dst) \
|
||||
RC(nxt_app_msg_read_str(task, rmsg, (dst)))
|
||||
r = req->request;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (h->path.start == NULL) {
|
||||
h->path = h->target;
|
||||
}
|
||||
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));
|
||||
|
||||
#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;
|
||||
|
||||
h = &run_ctx.r.header;
|
||||
|
||||
rc = nxt_php_read_request(task, rmsg, &run_ctx);
|
||||
|
||||
if (nxt_slow_path(rc != NXT_OK)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
SG(server_context) = &run_ctx;
|
||||
SG(request_info).request_uri = (char *) h->target.start;
|
||||
SG(request_info).request_method = (char *) h->method.start;
|
||||
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);
|
||||
|
||||
SG(request_info).proto_num = 1001;
|
||||
|
||||
SG(request_info).query_string = (char *) h->query.start;
|
||||
SG(request_info).content_length = h->parsed_content_length;
|
||||
SG(request_info).query_string = r->query.offset
|
||||
? nxt_unit_sptr_get(&r->query) : NULL;
|
||||
SG(request_info).content_length = r->content_length;
|
||||
|
||||
if (h->content_type.start != NULL) {
|
||||
SG(request_info).content_type = (char *) h->content_type.start;
|
||||
if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
|
||||
f = r->fields + r->content_type_field;
|
||||
|
||||
SG(request_info).content_type = nxt_unit_sptr_get(&f->value);
|
||||
}
|
||||
|
||||
if (r->cookie_field != NXT_UNIT_NONE_FIELD) {
|
||||
f = r->fields + r->cookie_field;
|
||||
|
||||
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);
|
||||
nxt_unit_request_done(req, rc);
|
||||
|
||||
if (ctx->script.start != nxt_php_path.start) {
|
||||
nxt_free(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);
|
||||
|
||||
} else {
|
||||
rc = NXT_OK;
|
||||
}
|
||||
|
||||
if (flush || last) {
|
||||
rc = nxt_app_msg_flush(ctx->task, ctx->wmsg, last);
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
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";
|
||||
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;
|
||||
}
|
||||
|
||||
resp_size = 0;
|
||||
fields_count = zend_llist_count(&sapi_headers->headers);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (SG(sapi_headers).http_status_line) {
|
||||
status = (u_char *) SG(sapi_headers).http_status_line;
|
||||
len = nxt_strlen(status);
|
||||
status_line = SG(sapi_headers).http_status_line;
|
||||
|
||||
if (len < 12) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
RC(nxt_php_write(ctx, status_200, sizeof(status_200) - 4, 0, 0));
|
||||
RC(nxt_php_write(ctx, status + 9, 3, 0, 0));
|
||||
status = nxt_int_parse((u_char *) status_line + 9, 3);
|
||||
|
||||
} 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;
|
||||
|
||||
RC(nxt_php_write(ctx, status_200, sizeof(status_200) - 4, 0, 0));
|
||||
RC(nxt_php_write(ctx, buf, len, 0, 0));
|
||||
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_php_write(ctx, cr_lf, nxt_length(cr_lf), 0, 0));
|
||||
|
||||
h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
|
||||
|
||||
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));
|
||||
|
||||
h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos);
|
||||
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), 1, 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);
|
||||
|
||||
#undef RC
|
||||
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;
|
||||
}
|
||||
|
||||
value = colon + 1;
|
||||
while(isspace(*value)) {
|
||||
value++;
|
||||
}
|
||||
|
||||
nxt_unit_response_add_field(req, h->header, colon - h->header,
|
||||
value,
|
||||
h->header_len - (value - h->header));
|
||||
}
|
||||
|
||||
rc = nxt_unit_response_send(req);
|
||||
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
|
||||
nxt_unit_req_debug(req, "failed to send response");
|
||||
|
||||
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;
|
||||
|
||||
ctx = SG(server_context);
|
||||
h = &ctx->r.header;
|
||||
|
||||
rest = (size_t) h->parsed_content_length - SG(read_post_bytes);
|
||||
nxt_unit_req_debug(ctx->req, "nxt_php_read_post %d", (int) count_bytes);
|
||||
|
||||
nxt_debug(ctx->task, "nxt_php_read_post %O", rest);
|
||||
|
||||
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;
|
||||
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_app_request_header_t *h;
|
||||
|
||||
static nxt_str_t def_host = nxt_string("localhost");
|
||||
static nxt_str_t def_port = nxt_string("80");
|
||||
nxt_unit_request_t *r;
|
||||
nxt_unit_request_info_t *req;
|
||||
|
||||
ctx = SG(server_context);
|
||||
|
||||
h = &ctx->r.header;
|
||||
task = ctx->task;
|
||||
req = ctx->req;
|
||||
r = req->request;
|
||||
|
||||
nxt_debug(task, "php register variables");
|
||||
nxt_unit_req_debug(req, "nxt_php_register_variables");
|
||||
|
||||
#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) \
|
||||
php_register_variable_safe((char *) "SERVER_SOFTWARE",
|
||||
(char *) nxt_server.start,
|
||||
nxt_server.length, track_vars_array TSRMLS_CC);
|
||||
|
||||
NXT_PHP_SET("SERVER_SOFTWARE", nxt_server);
|
||||
|
||||
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);
|
||||
|
||||
f_end = r->fields + r->fields_count;
|
||||
for (f = r->fields; f < f_end; f++) {
|
||||
name = nxt_unit_sptr_get(&f->name);
|
||||
|
||||
nxt_php_set_sptr(req, name, &f->value, f->value_length,
|
||||
track_vars_array TSRMLS_CC);
|
||||
}
|
||||
|
||||
if (h->content_length.start != NULL) {
|
||||
NXT_PHP_SET("CONTENT_LENGTH", h->content_length);
|
||||
if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
|
||||
f = r->fields + r->content_length_field;
|
||||
|
||||
nxt_php_set_sptr(req, "CONTENT_LENGTH", &f->value, f->value_length,
|
||||
track_vars_array TSRMLS_CC);
|
||||
}
|
||||
|
||||
host = h->host;
|
||||
if (host.length == 0) {
|
||||
host = def_host;
|
||||
if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
|
||||
f = r->fields + r->content_type_field;
|
||||
|
||||
nxt_php_set_sptr(req, "CONTENT_TYPE", &f->value, f->value_length,
|
||||
track_vars_array TSRMLS_CC);
|
||||
}
|
||||
|
||||
server_name = host;
|
||||
colon = nxt_memchr(host.start, ':', host.length);
|
||||
if (r->host_field != NXT_UNIT_NONE_FIELD) {
|
||||
f = r->fields + r->host_field;
|
||||
|
||||
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;
|
||||
host_start = NULL;
|
||||
host_length = 0;
|
||||
}
|
||||
|
||||
NXT_PHP_SET("SERVER_NAME", server_name);
|
||||
NXT_PHP_SET("SERVER_PORT", server_port);
|
||||
nxt_unit_split_host(host_start, host_length, &host_start, &host_length,
|
||||
&port_start, &port_length);
|
||||
|
||||
NXT_PHP_SET("REMOTE_ADDR", ctx->r.remote);
|
||||
NXT_PHP_SET("SERVER_ADDR", ctx->r.local);
|
||||
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);
|
||||
}
|
||||
|
||||
rmsg = ctx->rmsg;
|
||||
rest = ctx->body_preread_size;
|
||||
|
||||
if (rest != 0) {
|
||||
/* Skipping request body. */
|
||||
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;
|
||||
|
||||
b = rmsg->buf;
|
||||
str = nxt_unit_sptr_get(v);
|
||||
|
||||
do {
|
||||
if (nxt_slow_path(b == NULL)) {
|
||||
nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, str);
|
||||
|
||||
php_register_variable_safe((char *) name, str, len,
|
||||
track_vars_array TSRMLS_CC);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size = nxt_buf_mem_used_size(&b->mem);
|
||||
nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, cstr);
|
||||
|
||||
if (rest < size) {
|
||||
nxt_memcpy(&buf, b, NXT_BUF_MEM_SIZE);
|
||||
buf.mem.pos += rest;
|
||||
b = &buf;
|
||||
break;
|
||||
}
|
||||
|
||||
rest -= size;
|
||||
b = b->next;
|
||||
|
||||
} while (rest != 0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
rc = nxt_app_msg_read_str(task, rmsg, &v);
|
||||
if (nxt_slow_path(rc != NXT_OK)) {
|
||||
break;
|
||||
}
|
||||
|
||||
NXT_PHP_SET(n.start, v);
|
||||
}
|
||||
|
||||
#undef NXT_PHP_SET
|
||||
php_register_variable_safe((char *) name, cstr, len,
|
||||
track_vars_array TSRMLS_CC);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
static int nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes);
|
||||
|
||||
struct nxt_python_run_ctx_s {
|
||||
nxt_task_t *task;
|
||||
nxt_app_rmsg_t *rmsg;
|
||||
nxt_app_wmsg_t *wmsg;
|
||||
|
||||
size_t body_preread_size;
|
||||
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;
|
||||
int rc;
|
||||
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);
|
||||
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) {
|
||||
if (PyBytes_Check(result)) {
|
||||
|
||||
size = PyBytes_GET_SIZE(result);
|
||||
buf = (u_char *) PyBytes_AS_STRING(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,162 +694,91 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
} else {
|
||||
rc = NXT_OK;
|
||||
}
|
||||
|
||||
Py_DECREF(value);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
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_app_msg_read_str(task, rmsg, v);
|
||||
if (nxt_slow_path(rc != NXT_OK)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (v->start == NULL) {
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
return nxt_python_add_env(task, env, name, v);
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
nxt_python_get_environ(nxt_task_t *task, nxt_app_rmsg_t *rmsg,
|
||||
nxt_python_run_ctx_t *ctx)
|
||||
nxt_python_get_environ(nxt_python_run_ctx_t *ctx)
|
||||
{
|
||||
size_t s;
|
||||
u_char *colon;
|
||||
int rc;
|
||||
char *name, *host_start, *port_start;
|
||||
uint32_t i, host_length, port_length;
|
||||
PyObject *environ;
|
||||
nxt_int_t rc;
|
||||
nxt_str_t n, v, target, path, query;
|
||||
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");
|
||||
nxt_unit_field_t *f;
|
||||
nxt_unit_request_t *r;
|
||||
|
||||
environ = PyDict_Copy(nxt_py_environ_ptyp);
|
||||
|
||||
if (nxt_slow_path(environ == NULL)) {
|
||||
nxt_log_error(NXT_LOG_ERR, task->log,
|
||||
"Python failed to create the \"environ\" dictionary");
|
||||
nxt_unit_req_error(ctx->req,
|
||||
"Python failed to copy the \"environ\" dictionary");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->environ = environ;
|
||||
|
||||
r = ctx->req->request;
|
||||
|
||||
#define RC(S) \
|
||||
do { \
|
||||
rc = (S); \
|
||||
if (nxt_slow_path(rc != NXT_OK)) { \
|
||||
if (nxt_slow_path(rc != NXT_UNIT_OK)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define NXT_READ(N) \
|
||||
RC(nxt_python_read_add_env(task, rmsg, environ, N, &v))
|
||||
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));
|
||||
|
||||
NXT_READ("REQUEST_METHOD");
|
||||
NXT_READ("REQUEST_URI");
|
||||
|
||||
target = v;
|
||||
RC(nxt_app_msg_read_str(task, rmsg, &path));
|
||||
|
||||
RC(nxt_app_msg_read_size(task, rmsg, &s)); // query length + 1
|
||||
if (s > 0) {
|
||||
s--;
|
||||
|
||||
query.start = target.start + s;
|
||||
query.length = target.length - s;
|
||||
|
||||
RC(nxt_python_add_env(task, environ, "QUERY_STRING", &query));
|
||||
|
||||
if (path.start == NULL) {
|
||||
path.start = target.start;
|
||||
path.length = s - 1;
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
for (i = 0; i < r->fields_count; i++) {
|
||||
f = r->fields + i;
|
||||
name = nxt_unit_sptr_get(&f->name);
|
||||
|
||||
RC(nxt_python_add_sptr(ctx, name, &f->value, f->value_length));
|
||||
}
|
||||
|
||||
if (path.start == NULL) {
|
||||
path = target;
|
||||
if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
|
||||
f = r->fields + r->content_length_field;
|
||||
|
||||
RC(nxt_python_add_sptr(ctx, "CONTENT_LENGTH", &f->value,
|
||||
f->value_length));
|
||||
}
|
||||
|
||||
RC(nxt_python_add_env(task, environ, "PATH_INFO", &path));
|
||||
if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
|
||||
f = r->fields + r->content_type_field;
|
||||
|
||||
NXT_READ("SERVER_PROTOCOL");
|
||||
|
||||
NXT_READ("REMOTE_ADDR");
|
||||
NXT_READ("SERVER_ADDR");
|
||||
|
||||
RC(nxt_app_msg_read_str(task, rmsg, &host));
|
||||
|
||||
if (host.length == 0) {
|
||||
host = def_host;
|
||||
RC(nxt_python_add_sptr(ctx, "CONTENT_TYPE", &f->value,
|
||||
f->value_length));
|
||||
}
|
||||
|
||||
server_name = host;
|
||||
colon = nxt_memchr(host.start, ':', host.length);
|
||||
if (r->host_field != NXT_UNIT_NONE_FIELD) {
|
||||
f = r->fields + r->host_field;
|
||||
|
||||
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;
|
||||
host_start = NULL;
|
||||
host_length = 0;
|
||||
}
|
||||
|
||||
RC(nxt_python_add_env(task, environ, "SERVER_NAME", &server_name));
|
||||
RC(nxt_python_add_env(task, environ, "SERVER_PORT", &server_port));
|
||||
nxt_unit_split_host(host_start, host_length, &host_start, &host_length,
|
||||
&port_start, &port_length);
|
||||
|
||||
NXT_READ("CONTENT_TYPE");
|
||||
NXT_READ("CONTENT_LENGTH");
|
||||
RC(nxt_python_add_str(ctx, "SERVER_NAME", host_start, host_length));
|
||||
RC(nxt_python_add_str(ctx, "SERVER_PORT", port_start, port_length));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
RC(nxt_python_add_env(task, environ, (char *) n.start, &v));
|
||||
}
|
||||
|
||||
RC(nxt_app_msg_read_size(task, rmsg, &ctx->body_preread_size));
|
||||
|
||||
#undef NXT_READ
|
||||
#undef RC
|
||||
|
||||
return environ;
|
||||
@@ -841,21 +791,85 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
src = nxt_unit_sptr_get(sptr);
|
||||
|
||||
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);
|
||||
|
||||
return NXT_UNIT_ERROR;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return NXT_UNIT_ERROR;
|
||||
}
|
||||
|
||||
Py_DECREF(value);
|
||||
|
||||
return NXT_UNIT_OK;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
nxt_python_add_str(nxt_python_run_ctx_t *ctx, const char *name,
|
||||
char *str, uint32_t size)
|
||||
{
|
||||
PyObject *value;
|
||||
|
||||
if (nxt_slow_path(str == NULL)) {
|
||||
return NXT_UNIT_OK;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return NXT_UNIT_ERROR;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return NXT_UNIT_ERROR;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
/* flush headers */
|
||||
nxt_python_write(ctx, cr_lf, nxt_length(cr_lf), 1, 0);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Py_DECREF(bytes);
|
||||
if (nxt_slow_path(str_length == 0)) {
|
||||
return NXT_UNIT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
719
src/nxt_router.c
719
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;
|
||||
buf = nxt_router_prepare_msg(task, &ap->r, port,
|
||||
nxt_app_msg_prefix[port->app->type]);
|
||||
|
||||
res = port->app->prepare_msg(task, &ap->r, &wmsg);
|
||||
|
||||
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;
|
||||
struct nxt_fields_iter_s {
|
||||
nxt_list_part_t *part;
|
||||
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;
|
||||
typedef struct nxt_fields_iter_s nxt_fields_iter_t;
|
||||
|
||||
|
||||
static nxt_http_field_t *
|
||||
nxt_fields_part_first(nxt_list_part_t *part, nxt_fields_iter_t *i)
|
||||
{
|
||||
if (part == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (part->nelts == 0) {
|
||||
part = part->next;
|
||||
if (part == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
i->part = part;
|
||||
i->field = nxt_list_data(i->part);
|
||||
|
||||
return i->field;
|
||||
}
|
||||
|
||||
|
||||
static nxt_http_field_t *
|
||||
nxt_fields_first(nxt_list_t *fields, nxt_fields_iter_t *i)
|
||||
{
|
||||
return nxt_fields_part_first(nxt_list_part(fields), i);
|
||||
}
|
||||
|
||||
|
||||
static nxt_http_field_t *
|
||||
nxt_fields_next(nxt_fields_iter_t *i)
|
||||
{
|
||||
nxt_http_field_t *end = nxt_list_data(i->part);
|
||||
|
||||
end += i->part->nelts;
|
||||
i->field++;
|
||||
|
||||
if (i->field < end) {
|
||||
return i->field;
|
||||
}
|
||||
|
||||
return nxt_fields_part_first(i->part->next, i);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
void *target_pos, *query_pos;
|
||||
u_char *pos, *end, *p, c;
|
||||
size_t fields_count, req_size, size, free_size;
|
||||
size_t copy_size;
|
||||
nxt_buf_t *b, *buf, *out, **tail;
|
||||
nxt_http_field_t *field, *dup;
|
||||
nxt_unit_field_t *dst_field;
|
||||
nxt_fields_iter_t iter, dup_iter;
|
||||
nxt_unit_request_t *req;
|
||||
nxt_app_request_header_t *h;
|
||||
|
||||
h = &r->header;
|
||||
|
||||
#define RC(S) \
|
||||
do { \
|
||||
rc = (S); \
|
||||
if (nxt_slow_path(rc != NXT_OK)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
} while(0)
|
||||
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);
|
||||
|
||||
#define NXT_WRITE(N) \
|
||||
RC(nxt_app_msg_write_str(task, wmsg, N))
|
||||
fields_count = 0;
|
||||
|
||||
/* TODO error handle, async mmap buffer assignment */
|
||||
nxt_list_each(field, h->fields) {
|
||||
fields_count++;
|
||||
|
||||
NXT_WRITE(&h->method);
|
||||
NXT_WRITE(&h->target);
|
||||
req_size += field->name_length + prefix->length + 1
|
||||
+ field->value_length + 1;
|
||||
} nxt_list_loop;
|
||||
|
||||
req_size += fields_count * sizeof(nxt_unit_field_t);
|
||||
|
||||
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);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
req->content_length = h->parsed_content_length;
|
||||
|
||||
p = (u_char *) (req->fields + fields_count);
|
||||
|
||||
nxt_debug(task, "fields_count=%d", (int) fields_count);
|
||||
|
||||
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';
|
||||
|
||||
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';
|
||||
|
||||
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';
|
||||
|
||||
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';
|
||||
|
||||
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';
|
||||
|
||||
req->path_length = h->path.length;
|
||||
if (h->path.start == h->target.start) {
|
||||
NXT_WRITE(&eof);
|
||||
nxt_unit_sptr_set(&req->path, target_pos);
|
||||
|
||||
} else {
|
||||
NXT_WRITE(&h->path);
|
||||
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);
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
#undef NXT_WRITE
|
||||
#undef RC
|
||||
|
||||
return NXT_OK;
|
||||
|
||||
fail:
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_php_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
|
||||
nxt_app_wmsg_t *wmsg)
|
||||
for (field = nxt_fields_first(h->fields, &iter);
|
||||
field != NULL;
|
||||
field = nxt_fields_next(&iter))
|
||||
{
|
||||
nxt_int_t rc;
|
||||
nxt_buf_t *b;
|
||||
nxt_http_field_t *field;
|
||||
nxt_app_request_header_t *h;
|
||||
if (field->skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
static const nxt_str_t prefix = nxt_string("HTTP_");
|
||||
static const nxt_str_t eof = nxt_null_string;
|
||||
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;
|
||||
|
||||
h = &r->header;
|
||||
if (field->value == h->host.start) {
|
||||
req->host_field = dst_field - req->fields;
|
||||
|
||||
#define RC(S) \
|
||||
do { \
|
||||
rc = (S); \
|
||||
if (nxt_slow_path(rc != NXT_OK)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
} while(0)
|
||||
} else if (field->value == h->content_length.start) {
|
||||
req->content_length_field = dst_field - req->fields;
|
||||
|
||||
#define NXT_WRITE(N) \
|
||||
RC(nxt_app_msg_write_str(task, wmsg, N))
|
||||
} else if (field->value == h->content_type.start) {
|
||||
req->content_type_field = dst_field - req->fields;
|
||||
|
||||
/* TODO error handle, async mmap buffer assignment */
|
||||
} else if (field->value == h->cookie.start) {
|
||||
req->cookie_field = dst_field - req->fields;
|
||||
}
|
||||
|
||||
NXT_WRITE(&h->method);
|
||||
NXT_WRITE(&h->target);
|
||||
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);
|
||||
|
||||
if (h->path.start == h->target.start) {
|
||||
NXT_WRITE(&eof);
|
||||
if (prefix->length != 0) {
|
||||
nxt_unit_sptr_set(&dst_field->name, p);
|
||||
p = nxt_cpymem(p, prefix->start, prefix->length);
|
||||
|
||||
end = field->name + field->name_length;
|
||||
for (pos = field->name; pos < end; pos++) {
|
||||
c = *pos;
|
||||
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
*p++ = (c & ~0x20);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '-') {
|
||||
*p++ = '_';
|
||||
continue;
|
||||
}
|
||||
|
||||
*p++ = c;
|
||||
}
|
||||
|
||||
} else {
|
||||
NXT_WRITE(&h->path);
|
||||
nxt_unit_sptr_set(&dst_field->name, p);
|
||||
p = nxt_cpymem(p, field->name, field->name_length);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
*p++ = '\0';
|
||||
|
||||
NXT_WRITE(&h->version);
|
||||
nxt_unit_sptr_set(&dst_field->value, p);
|
||||
p = nxt_cpymem(p, field->value, field->value_length);
|
||||
|
||||
// PHP_SELF
|
||||
// SCRIPT_NAME
|
||||
// SCRIPT_FILENAME
|
||||
// DOCUMENT_ROOT
|
||||
if (prefix->length != 0) {
|
||||
dup_iter = iter;
|
||||
|
||||
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);
|
||||
|
||||
RC(nxt_app_msg_write_size(task, wmsg, h->parsed_content_length));
|
||||
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)));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_go_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, nxt_app_wmsg_t *wmsg)
|
||||
for (dup = nxt_fields_next(&dup_iter);
|
||||
dup != NULL;
|
||||
dup = nxt_fields_next(&dup_iter))
|
||||
{
|
||||
nxt_int_t rc;
|
||||
nxt_buf_t *b;
|
||||
nxt_http_field_t *field;
|
||||
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);
|
||||
|
||||
RC(nxt_app_msg_write_size(task, wmsg, h->parsed_content_length));
|
||||
|
||||
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));
|
||||
|
||||
} 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)));
|
||||
}
|
||||
|
||||
#undef NXT_WRITE
|
||||
#undef RC
|
||||
|
||||
return NXT_OK;
|
||||
|
||||
fail:
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_perl_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
|
||||
nxt_app_wmsg_t *wmsg)
|
||||
if (dup->name_length != field->name_length
|
||||
|| dup->skip
|
||||
|| dup->hash != field->hash
|
||||
|| nxt_memcasecmp(dup->name, field->name, dup->name_length))
|
||||
{
|
||||
nxt_int_t rc;
|
||||
nxt_str_t str;
|
||||
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->query.length) {
|
||||
str.start = h->target.start;
|
||||
str.length = (h->target.length - h->query.length) - 1;
|
||||
|
||||
RC(nxt_app_msg_write_str(task, wmsg, &str));
|
||||
|
||||
} else {
|
||||
NXT_WRITE(&eof);
|
||||
continue;
|
||||
}
|
||||
|
||||
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));
|
||||
p = nxt_cpymem(p, ", ", 2);
|
||||
p = nxt_cpymem(p, dup->value, dup->value_length);
|
||||
|
||||
dst_field->value_length += 2 + dup->value_length;
|
||||
|
||||
dup->skip = 1;
|
||||
}
|
||||
}
|
||||
|
||||
NXT_WRITE(&h->version);
|
||||
*p++ = '\0';
|
||||
|
||||
NXT_WRITE(&r->remote);
|
||||
NXT_WRITE(&r->local);
|
||||
dst_field++;
|
||||
}
|
||||
|
||||
NXT_WRITE(&h->host);
|
||||
NXT_WRITE(&h->content_type);
|
||||
NXT_WRITE(&h->content_length);
|
||||
req->fields_count = dst_field - req->fields;
|
||||
|
||||
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;
|
||||
nxt_unit_sptr_set(&req->preread_content, out->mem.free);
|
||||
|
||||
/* end-of-headers mark */
|
||||
NXT_WRITE(&eof);
|
||||
|
||||
RC(nxt_app_msg_write_size(task, wmsg, r->body.preread_size));
|
||||
buf = out;
|
||||
tail = &buf->next;
|
||||
|
||||
for (b = r->body.buf; b != NULL; b = b->next) {
|
||||
size = nxt_buf_mem_used_size(&b->mem);
|
||||
pos = b->mem.pos;
|
||||
|
||||
RC(nxt_app_msg_write_raw(task, wmsg, b->mem.pos,
|
||||
nxt_buf_mem_used_size(&b->mem)));
|
||||
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;
|
||||
}
|
||||
|
||||
#undef NXT_WRITE
|
||||
#undef RC
|
||||
*tail = buf;
|
||||
tail = &buf->next;
|
||||
|
||||
return NXT_OK;
|
||||
|
||||
fail:
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_ruby_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
|
||||
nxt_app_wmsg_t *wmsg)
|
||||
} 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)
|
||||
{
|
||||
nxt_int_t rc;
|
||||
nxt_str_t str;
|
||||
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->query.length) {
|
||||
str.start = h->target.start;
|
||||
str.length = (h->target.length - h->query.length) - 1;
|
||||
|
||||
RC(nxt_app_msg_write_str(task, wmsg, &str));
|
||||
|
||||
} else {
|
||||
NXT_WRITE(&eof);
|
||||
free_size = nxt_buf_mem_free_size(&buf->mem);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
if (free_size > 0) {
|
||||
copy_size = nxt_min(free_size, size);
|
||||
|
||||
buf->mem.free = nxt_cpymem(buf->mem.free, pos, copy_size);
|
||||
|
||||
size -= copy_size;
|
||||
pos += copy_size;
|
||||
|
||||
if (size == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#undef NXT_WRITE
|
||||
#undef RC
|
||||
|
||||
return NXT_OK;
|
||||
|
||||
fail:
|
||||
|
||||
return NXT_ERROR;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 { \
|
||||
|
||||
3630
src/nxt_unit.c
Normal file
3630
src/nxt_unit.c
Normal file
File diff suppressed because it is too large
Load Diff
355
src/nxt_unit.h
Normal file
355
src/nxt_unit.h
Normal file
@@ -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_ */
|
||||
34
src/nxt_unit_field.h
Normal file
34
src/nxt_unit_field.h
Normal file
@@ -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_ */
|
||||
48
src/nxt_unit_request.h
Normal file
48
src/nxt_unit_request.h
Normal file
@@ -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_ */
|
||||
|
||||
27
src/nxt_unit_response.h
Normal file
27
src/nxt_unit_response.h
Normal file
@@ -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_ */
|
||||
38
src/nxt_unit_sptr.h
Normal file
38
src/nxt_unit_sptr.h
Normal file
@@ -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_ */
|
||||
25
src/nxt_unit_typedefs.h
Normal file
25
src/nxt_unit_typedefs.h
Normal file
@@ -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_ */
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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,42 +340,34 @@ 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,
|
||||
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;
|
||||
@@ -362,39 +375,40 @@ nxt_ruby_rack_app_run(VALUE arg)
|
||||
|
||||
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,
|
||||
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,
|
||||
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,175 +416,103 @@ 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);
|
||||
|
||||
for (i = 0; i < r->fields_count; i++) {
|
||||
f = r->fields + i;
|
||||
|
||||
nxt_ruby_add_sptr(hash_env, nxt_unit_sptr_get(&f->name), f->name_length,
|
||||
&f->value, f->value_length);
|
||||
}
|
||||
|
||||
rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_URI", &target);
|
||||
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;
|
||||
|
||||
nxt_ruby_add_sptr(hash_env, NL("CONTENT_LENGTH"),
|
||||
&f->value, f->value_length);
|
||||
}
|
||||
|
||||
rc = nxt_app_msg_read_str(task, rmsg, &path);
|
||||
if (nxt_slow_path(rc != NXT_OK)) {
|
||||
return NXT_ERROR;
|
||||
if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
|
||||
f = r->fields + r->content_type_field;
|
||||
|
||||
nxt_ruby_add_sptr(hash_env, NL("CONTENT_TYPE"),
|
||||
&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->host_field != NXT_UNIT_NONE_FIELD) {
|
||||
f = r->fields + r->host_field;
|
||||
|
||||
if (path.start == NULL || path.length == 0) {
|
||||
path = target;
|
||||
}
|
||||
|
||||
rb_hash_aset(hash_env, rb_str_new2("PATH_INFO"),
|
||||
rb_str_new((const char *) path.start, (long) path.length));
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_PROTOCOL", &str);
|
||||
if (nxt_slow_path(rc != NXT_OK)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
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;
|
||||
host_start = NULL;
|
||||
host_length = 0;
|
||||
}
|
||||
|
||||
rb_hash_aset(hash_env, rb_str_new2("SERVER_NAME"),
|
||||
rb_str_new((const char *) server_name.start,
|
||||
(long) server_name.length));
|
||||
nxt_unit_split_host(host_start, host_length, &host_start, &host_length,
|
||||
&port_start, &port_length);
|
||||
|
||||
rb_hash_aset(hash_env, rb_str_new2("SERVER_PORT"),
|
||||
rb_str_new((const char *) server_port.start,
|
||||
(long) server_port.length));
|
||||
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_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_TYPE", &str);
|
||||
if (nxt_slow_path(rc != NXT_OK)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
#undef NL
|
||||
|
||||
rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_LENGTH", &str);
|
||||
if (nxt_slow_path(rc != NXT_OK)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
rc = nxt_app_msg_read_str(task, rmsg, &str);
|
||||
if (nxt_slow_path(rc != NXT_OK)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (nxt_slow_path(str.length == 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
rb_hash_aset(hash_env, rb_str_new(name, name_len), rb_str_new(str, len));
|
||||
}
|
||||
|
||||
if (str->start == NULL) {
|
||||
rb_hash_aset(hash_env, rb_str_new2(name), Qnil);
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -578,119 +520,89 @@ 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];
|
||||
|
||||
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;
|
||||
}
|
||||
nxt_unit_req_error(nxt_ruby_run_ctx.req, "Ruby: Invalid response 'status' "
|
||||
"format from application");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_ruby_rack_result_headers(VALUE result)
|
||||
typedef struct {
|
||||
int rc;
|
||||
uint32_t fields;
|
||||
uint32_t size;
|
||||
} nxt_ruby_headers_info_t;
|
||||
|
||||
|
||||
static int
|
||||
nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status)
|
||||
{
|
||||
int rc;
|
||||
VALUE headers;
|
||||
nxt_int_t rc;
|
||||
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;
|
||||
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,
|
||||
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,
|
||||
nxt_unit_req_error(nxt_ruby_run_ctx.req,
|
||||
"Ruby: Wrong header entry 'value' from application");
|
||||
|
||||
goto fail;
|
||||
@@ -708,9 +620,55 @@ 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),
|
||||
headers_info->fields++;
|
||||
headers_info->size += RSTRING_LEN(r_key) + (pos - value);
|
||||
|
||||
pos++;
|
||||
value = pos;
|
||||
}
|
||||
|
||||
if (value <= value_end) {
|
||||
headers_info->fields++;
|
||||
headers_info->size += RSTRING_LEN(r_key) + (value_end - value);
|
||||
}
|
||||
|
||||
return ST_CONTINUE;
|
||||
|
||||
fail:
|
||||
|
||||
headers_info->rc = NXT_UNIT_ERROR;
|
||||
|
||||
return ST_STOP;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg)
|
||||
{
|
||||
int *rc;
|
||||
uint32_t key_len;
|
||||
const char *value, *value_end, *pos;
|
||||
|
||||
rc = (int *) (uintptr_t) arg;
|
||||
|
||||
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_OK)) {
|
||||
if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -719,59 +677,29 @@ nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg)
|
||||
}
|
||||
|
||||
if (value <= value_end) {
|
||||
rc = nxt_ruby_head_send_part(RSTRING_PTR(r_key), RSTRING_LEN(r_key),
|
||||
*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_OK)) {
|
||||
if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
*rc_p = NXT_OK;
|
||||
|
||||
return ST_CONTINUE;
|
||||
|
||||
fail:
|
||||
|
||||
*rc_p = NXT_ERROR;
|
||||
*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)
|
||||
{
|
||||
nxt_int_t rc;
|
||||
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
|
||||
(u_char *) "\r\n", nxt_length("\r\n"));
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
static int
|
||||
nxt_ruby_rack_result_body(VALUE result)
|
||||
{
|
||||
int rc;
|
||||
VALUE fn, body;
|
||||
nxt_int_t rc;
|
||||
|
||||
body = rb_ary_entry(result, 2);
|
||||
|
||||
@@ -779,119 +707,133 @@ 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
|
||||
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)
|
||||
{
|
||||
ssize_t res;
|
||||
nxt_ruby_rack_file_t *file;
|
||||
|
||||
file = read_info->data;
|
||||
|
||||
size = nxt_min(size, (size_t) file->rest);
|
||||
|
||||
res = pread(file->fd, dst, size, file->pos);
|
||||
|
||||
if (res >= 0) {
|
||||
file->pos += res;
|
||||
file->rest -= res;
|
||||
|
||||
if (size > (size_t) res) {
|
||||
file->rest = 0;
|
||||
}
|
||||
}
|
||||
|
||||
read_info->eof = file->rest == 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
nxt_ruby_rack_result_body_file_write(VALUE filepath)
|
||||
{
|
||||
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];
|
||||
int fd, rc;
|
||||
struct stat finfo;
|
||||
nxt_ruby_rack_file_t ruby_file;
|
||||
nxt_unit_read_info_t read_info;
|
||||
|
||||
nxt_memzero(&file, sizeof(nxt_file_t));
|
||||
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);
|
||||
|
||||
file.name = (nxt_file_name_t *) RSTRING_PTR(filepath);
|
||||
|
||||
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);
|
||||
|
||||
return NXT_ERROR;
|
||||
return NXT_UNIT_ERROR;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
goto fail;
|
||||
close(fd);
|
||||
|
||||
return NXT_UNIT_ERROR;
|
||||
}
|
||||
|
||||
rest = nxt_file_size(&finfo);
|
||||
ruby_file.fd = fd;
|
||||
ruby_file.pos = 0;
|
||||
ruby_file.rest = finfo.st_size;
|
||||
|
||||
while (rest != 0) {
|
||||
len = nxt_min(rest, (nxt_off_t) sizeof(buf));
|
||||
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;
|
||||
|
||||
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");
|
||||
|
||||
goto 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.");
|
||||
}
|
||||
|
||||
rest -= len;
|
||||
close(fd);
|
||||
|
||||
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");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
nxt_file_close(nxt_ruby_run_ctx.task, &file);
|
||||
|
||||
return NXT_OK;
|
||||
|
||||
fail:
|
||||
|
||||
nxt_file_close(nxt_ruby_run_ctx.task, &file);
|
||||
|
||||
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,
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
if (task != NULL) {
|
||||
nxt_log(task, level, "Ruby: %s", desc);
|
||||
|
||||
err = rb_errinfo();
|
||||
ary = rb_funcall(err, rb_intern("backtrace"), 0);
|
||||
} else {
|
||||
nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "Ruby: %s", desc);
|
||||
}
|
||||
|
||||
if (RARRAY_LEN(ary) == 0) {
|
||||
err = rb_errinfo();
|
||||
if (nxt_slow_path(err == Qnil)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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++) {
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -86,65 +86,49 @@ static VALUE
|
||||
nxt_ruby_stream_io_gets(VALUE obj, VALUE args)
|
||||
{
|
||||
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) {
|
||||
req = run_ctx->req;
|
||||
|
||||
if (req->content_length == 0) {
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
buf = rb_str_buf_new(1);
|
||||
size = 0;
|
||||
|
||||
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 - b->free;
|
||||
break;
|
||||
}
|
||||
|
||||
size += b_size;
|
||||
}
|
||||
|
||||
buf = rb_str_buf_new(size);
|
||||
|
||||
if (buf == Qnil) {
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
run_ctx->body_preread_size -= nxt_ruby_stream_io_read_line(run_ctx->rmsg,
|
||||
buf);
|
||||
size = nxt_unit_request_read(req, RSTRING_PTR(buf), size);
|
||||
|
||||
rb_str_set_len(buf, size);
|
||||
|
||||
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;
|
||||
|
||||
for (buf = rmsg->buf; buf != NULL; buf = buf->next) {
|
||||
|
||||
size = nxt_buf_mem_used_size(&buf->mem);
|
||||
p = memchr(buf->mem.pos, '\n', 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;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
rb_str_cat(str, (const char *) buf->mem.pos, size);
|
||||
|
||||
len += size;
|
||||
buf->mem.pos = buf->mem.free;
|
||||
}
|
||||
|
||||
rmsg->buf = buf;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static VALUE
|
||||
nxt_ruby_stream_io_each(VALUE obj, VALUE args)
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
191
src/test/nxt_unit_app_test.c
Normal file
191
src/test/nxt_unit_app_test.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -2,7 +2,8 @@ import atexit
|
||||
|
||||
def application(environ, start_response):
|
||||
def at_exit():
|
||||
environ['wsgi.errors'].write('At exit called.')
|
||||
environ['wsgi.errors'].write('At exit called.\n')
|
||||
environ['wsgi.errors'].flush()
|
||||
|
||||
atexit.register(at_exit)
|
||||
|
||||
|
||||
@@ -8,4 +8,5 @@ class application:
|
||||
yield b''
|
||||
|
||||
def close(self):
|
||||
self.environ['wsgi.errors'].write('Close called.')
|
||||
self.environ['wsgi.errors'].write('Close called.\n')
|
||||
self.environ['wsgi.errors'].flush()
|
||||
|
||||
@@ -8,4 +8,5 @@ class application:
|
||||
yield b''
|
||||
|
||||
def close(self):
|
||||
self.environ['wsgi.errors'].write('Close called.')
|
||||
self.environ['wsgi.errors'].write('Close called.\n')
|
||||
self.environ['wsgi.errors'].flush()
|
||||
|
||||
@@ -2,6 +2,7 @@ import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import mmap
|
||||
import time
|
||||
import shutil
|
||||
import socket
|
||||
@@ -363,9 +364,14 @@ class TestUnitApplicationProto(TestUnitControl):
|
||||
return time.mktime(time.strptime(date, '%a, %d %b %Y %H:%M:%S %Z'))
|
||||
|
||||
def search_in_log(self, pattern):
|
||||
with open(self.testdir + '/unit.log', 'r') as f:
|
||||
with open(self.testdir + '/unit.log', 'r', errors='ignore') as f:
|
||||
return re.search(pattern, f.read())
|
||||
|
||||
def find_in_log(self, pattern):
|
||||
with open(self.testdir + '/unit.log', 'rb', 0) as f, \
|
||||
mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as s:
|
||||
return s.find(pattern) != -1
|
||||
|
||||
class TestUnitApplicationPython(TestUnitApplicationProto):
|
||||
def load(self, script, name=None):
|
||||
if name is None:
|
||||
|
||||
Reference in New Issue
Block a user