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:
Max Romanov
2018-08-06 17:27:33 +03:00
parent b6ce2da65c
commit 1bb22d1e92
54 changed files with 6685 additions and 4469 deletions

View File

@@ -54,6 +54,18 @@ done
$echo >> $NXT_MAKEFILE $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. # 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_STATIC_LINK $NXT_BUILD_DIR/$NXT_LIB_STATIC \\
\$(NXT_LIB_OBJS) \$(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 END
# Object files. # 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 do
nxt_obj=${nxt_src%.c}.o nxt_obj=${nxt_src%.c}.o
nxt_dep=${nxt_src%.c}.dep 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_BUILD_DIR/$NXT_LIB_STATIC \\
$NXT_LD_OPT $NXT_LIBM $NXT_LIBS $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 END
@@ -241,7 +265,8 @@ cat << END > Makefile
include $NXT_MAKEFILE include $NXT_MAKEFILE
.PHONY: tests .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 .PHONY: clean
clean: clean:

View File

@@ -106,6 +106,7 @@ ${NXT_GO}-install: ${NXT_GO}-install-build
${NXT_GO}-install-src: ${NXT_GO}-install-src:
install -d \$(DESTDIR)\$(NXT_GO_DST)/src/nginx/unit install -d \$(DESTDIR)\$(NXT_GO_DST)/src/nginx/unit
install -p -m644 ./src/*.h ./build/*.h ./src/go/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/ \$(DESTDIR)\$(NXT_GO_DST)/src/nginx/unit/
${NXT_GO}-install-build: ${NXT_GO}-install-src ${NXT_GO}-install-build: ${NXT_GO}-install-src

View File

@@ -155,7 +155,7 @@ NXT_PERL_MODULE_SRCS=" \
# The Perl module object files. # 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 for nxt_src in $NXT_PERL_MODULE_SRCS; do

View File

@@ -167,7 +167,7 @@ NXT_PHP_MODULE_SRCS=" \
# The php module object files. # 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 for nxt_src in $NXT_PHP_MODULE_SRCS; do

View File

@@ -137,7 +137,7 @@ NXT_PYTHON_MODULE_SRCS=" \
# The python module object files. # 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 for nxt_src in $NXT_PYTHON_MODULE_SRCS; do

View File

@@ -129,7 +129,7 @@ NXT_RUBY_MODULE_SRCS=" \
# The Ruby module object files. # 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 for nxt_src in $NXT_RUBY_MODULE_SRCS; do

View File

@@ -35,6 +35,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.so" NXT_LIB_SHARED="libnxt.so"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so" NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
NXT_LIB_UNIT_STATIC="libunit.a"
NXT_LIBM="-lm" NXT_LIBM="-lm"
NXT_LIBS="$NXT_LIBRT $NXT_LIBDL $NXT_PTHREAD" NXT_LIBS="$NXT_LIBRT $NXT_LIBDL $NXT_PTHREAD"
;; ;;
@@ -57,6 +59,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.so" NXT_LIB_SHARED="libnxt.so"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so" NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
NXT_LIB_UNIT_STATIC="libunit.a"
NXT_LIBM="-lm" NXT_LIBM="-lm"
NXT_LIBS="$NXT_LIBRT $NXT_PTHREAD" NXT_LIBS="$NXT_LIBRT $NXT_PTHREAD"
;; ;;
@@ -86,6 +90,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.so" NXT_LIB_SHARED="libnxt.so"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so" NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
NXT_LIB_UNIT_STATIC="libunit.a"
NXT_EXEC_LINK="\$(CC)" NXT_EXEC_LINK="\$(CC)"
NXT_SHARED_LOCAL_EXEC_LINK= NXT_SHARED_LOCAL_EXEC_LINK=
@@ -113,6 +119,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.dylib" NXT_LIB_SHARED="libnxt.dylib"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/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. # MacOSX libm.dylib is a symlink to libSystem.dylib.
NXT_LIBM= NXT_LIBM=
NXT_LIBS= NXT_LIBS=
@@ -135,6 +143,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.so" NXT_LIB_SHARED="libnxt.so"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so" NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
NXT_LIB_UNIT_STATIC="libunit.a"
NXT_LIBM="-lm" NXT_LIBM="-lm"
NXT_LIBS="$NXT_LIBRT $NXT_PTHREAD" NXT_LIBS="$NXT_LIBRT $NXT_PTHREAD"
;; ;;
@@ -155,6 +165,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.so" NXT_LIB_SHARED="libnxt.so"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so" NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
NXT_LIB_UNIT_STATIC="libunit.a"
NXT_LIBM="-lm" NXT_LIBM="-lm"
NXT_LIBS="$NXT_PTHREAD" NXT_LIBS="$NXT_PTHREAD"
;; ;;
@@ -175,6 +187,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.so" NXT_LIB_SHARED="libnxt.so"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so" NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
NXT_LIB_UNIT_STATIC="libunit.a"
NXT_LIBM="-lm" NXT_LIBM="-lm"
NXT_LIBS="$NXT_LIBRT $NXT_PTHREAD" NXT_LIBS="$NXT_LIBRT $NXT_PTHREAD"
;; ;;
@@ -194,6 +208,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.so" NXT_LIB_SHARED="libnxt.so"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so" NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
NXT_LIB_UNIT_STATIC="libunit.a"
NXT_LIBM="-lm" NXT_LIBM="-lm"
NXT_LIBS="$NXT_PTHREAD" NXT_LIBS="$NXT_PTHREAD"
;; ;;
@@ -213,6 +229,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.so" NXT_LIB_SHARED="libnxt.so"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so" NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
NXT_LIB_UNIT_STATIC="libunit.a"
NXT_LIBM="-lm" NXT_LIBM="-lm"
NXT_LIBS="$NXT_PTHREAD $NXT_LIBHG" NXT_LIBS="$NXT_PTHREAD $NXT_LIBHG"
;; ;;
@@ -232,6 +250,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.so" NXT_LIB_SHARED="libnxt.so"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so" NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
NXT_LIB_UNIT_STATIC="libunit.a"
NXT_LIBM="-lm" NXT_LIBM="-lm"
NXT_LIBS="$NXT_PTHREAD" NXT_LIBS="$NXT_PTHREAD"
;; ;;
@@ -250,6 +270,8 @@ case "$NXT_SYSTEM" in
NXT_LIB_SHARED="libnxt.so" NXT_LIB_SHARED="libnxt.so"
NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so" NXT_LIB_SHARED_LOCAL="$NXT_BUILD_DIR/libnxt.so"
NXT_LIB_UNIT_STATIC="libunit.a"
NXT_LIBM="-lm" NXT_LIBM="-lm"
NXT_LIBS="$NXT_LIBRT $NXT_LIBDL $NXT_PTHREAD" NXT_LIBS="$NXT_LIBRT $NXT_LIBDL $NXT_PTHREAD"
;; ;;

View File

@@ -102,6 +102,8 @@ NXT_LIB_SRC0=" \
src/nxt_mem_pool_cleanup.c \ 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_DEPS="src/nxt_ssltls.h"
NXT_LIB_SSLTLS_SRCS="src/nxt_ssltls.c" NXT_LIB_SSLTLS_SRCS="src/nxt_ssltls.c"

207
src/go/unit/nxt_cgo_lib.c Normal file
View 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
View 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_ */

View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -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);
}

View File

@@ -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_ */

View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -6,14 +6,12 @@
package unit package unit
/* /*
#include "nxt_go_lib.h" #include "nxt_cgo_lib.h"
#include "nxt_process_type.h"
*/ */
import "C" import "C"
import ( import (
"net" "net"
"net/http"
"os" "os"
"sync" "sync"
"unsafe" "unsafe"
@@ -26,7 +24,6 @@ type port_key struct {
type port struct { type port struct {
key port_key key port_key
t int
rcv *net.UnixConn rcv *net.UnixConn
snd *net.UnixConn snd *net.UnixConn
} }
@@ -34,7 +31,6 @@ type port struct {
type port_registry struct { type port_registry struct {
sync.RWMutex sync.RWMutex
m map[port_key]*port m map[port_key]*port
t [C.NXT_PROCESS_MAX]*port
} }
var port_registry_ port_registry var port_registry_ port_registry
@@ -47,42 +43,14 @@ func find_port(key port_key) *port {
return res 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) { func add_port(p *port) {
nxt_go_debug("add_port: %d:%d", p.key.pid, p.key.id);
port_registry_.Lock() port_registry_.Lock()
if port_registry_.m == nil { if port_registry_.m == nil {
port_registry_.m = make(map[port_key]*port) port_registry_.m = make(map[port_key]*port)
} }
port_registry_.m[p.key] = p port_registry_.m[p.key] = p
port_registry_.t[p.t] = p
port_registry_.Unlock() port_registry_.Unlock()
} }
@@ -120,14 +88,38 @@ func getUnixConn(fd int) *net.UnixConn {
return uc return uc
} }
//export nxt_go_new_port //export nxt_go_add_port
func nxt_go_new_port(pid C.int, id C.int, t C.int, rcv C.int, snd C.int) { func nxt_go_add_port(pid C.int, id C.int, rcv C.int, snd C.int) {
new_port(int(pid), int(id), int(t), int(rcv), int(snd)) 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 //export nxt_go_port_send
func nxt_go_port_send(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int, 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{ key := port_key{
pid: int(pid), 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 return 0
} }
n, oobn, err := p.snd.WriteMsgUnix(C.GoBytes(buf, buf_size), n, oobn, err := p.snd.WriteMsgUnix(GoBytes(buf, buf_size),
C.GoBytes(oob, oob_size), nil) GoBytes(oob, oob_size), nil)
if err != nil { if err != nil {
nxt_go_warn("write result %d (%d), %s", n, oobn, err) 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 //export nxt_go_port_recv
func nxt_go_main_send(buf unsafe.Pointer, buf_size C.int, oob unsafe.Pointer, func nxt_go_port_recv(pid C.int, id C.int, buf unsafe.Pointer, buf_size C.int,
oob_size C.int) 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 { if p == nil {
nxt_go_warn("port %d:%d not found", pid, id)
return 0 return 0
} }
n, oobn, err := p.snd.WriteMsgUnix(C.GoBytes(buf, buf_size), n, oobn, _, _, err := p.rcv.ReadMsgUnix(GoBytes(buf, buf_size),
C.GoBytes(oob, oob_size), nil) GoBytes(oob, oob_size))
if err != nil { if err != nil {
nxt_go_warn("write result %d (%d), %s", n, oobn, err) nxt_go_warn("write result %d (%d), %s", n, oobn, err)
} }
return C.int(n) return C.ssize_t(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
} }

View File

@@ -6,7 +6,7 @@
package unit package unit
/* /*
#include "nxt_go_lib.h" #include "nxt_cgo_lib.h"
*/ */
import "C" import "C"
@@ -20,15 +20,11 @@ import (
type request struct { type request struct {
req http.Request req http.Request
resp *response resp *response
c_req C.nxt_go_request_t c_req C.uintptr_t
id C.uint32_t
} }
func (r *request) Read(p []byte) (n int, err error) { func (r *request) Read(p []byte) (n int, err error) {
c := C.size_t(len(p)) res := C.nxt_cgo_request_read(r.c_req, buf_ref(p), C.uint32_t(len(p)))
b := C.uintptr_t(uintptr(unsafe.Pointer(&p[0])))
res := C.nxt_go_request_read(r.c_req, b, c)
if res == 0 && len(p) > 0 { if res == 0 && len(p) > 0 {
return 0, io.EOF return 0, io.EOF
@@ -38,7 +34,7 @@ func (r *request) Read(p []byte) (n int, err error) {
} }
func (r *request) Close() error { func (r *request) Close() error {
C.nxt_go_request_close(r.c_req) C.nxt_cgo_request_close(r.c_req)
return nil return nil
} }
@@ -55,16 +51,16 @@ func (r *request) done() {
if !resp.headerSent { if !resp.headerSent {
resp.WriteHeader(http.StatusOK) 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 { func get_request(go_req uintptr) *request {
return (*request)(unsafe.Pointer(uintptr(go_req))) return (*request)(unsafe.Pointer(go_req))
} }
//export nxt_go_new_request //export nxt_go_request_create
func nxt_go_new_request(c_req C.nxt_go_request_t, id C.uint32_t, func nxt_go_request_create(c_req C.uintptr_t,
c_method *C.nxt_go_str_t, c_uri *C.nxt_go_str_t) uintptr { c_method *C.nxt_cgo_str_t, c_uri *C.nxt_cgo_str_t) uintptr {
uri := C.GoStringN(c_uri.start, c_uri.length) 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, RequestURI: uri,
}, },
c_req: c_req, c_req: c_req,
id: id,
} }
r.req.Body = r 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 //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) { maj C.int, min C.int) {
r := get_request(go_req) 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 //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, func nxt_go_request_add_header(go_req uintptr, name *C.nxt_cgo_str_t,
value *C.nxt_go_str_t) { value *C.nxt_cgo_str_t) {
r := get_request(go_req) r := get_request(go_req)
r.req.Header.Add(C.GoStringN(name.start, name.length), 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 //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) get_request(go_req).req.ContentLength = int64(l)
} }
//export nxt_go_request_set_host //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) get_request(go_req).req.Host = C.GoStringN(host.start, host.length)
} }
//export nxt_go_request_set_url //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) get_request(go_req).req.URL.Scheme = C.GoString(scheme)
} }
//export nxt_go_request_set_remote_addr //export nxt_go_request_set_remote_addr
func nxt_go_request_set_remote_addr(go_req C.nxt_go_request_t, func nxt_go_request_set_remote_addr(go_req uintptr, addr *C.nxt_cgo_str_t) {
addr *C.nxt_go_str_t) {
get_request(go_req).req.RemoteAddr = C.GoStringN(addr.start, addr.length) 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)
}

View File

@@ -6,12 +6,11 @@
package unit package unit
/* /*
#include "nxt_go_lib.h" #include "nxt_cgo_lib.h"
*/ */
import "C" import "C"
import ( import (
"fmt"
"net/http" "net/http"
) )
@@ -19,10 +18,10 @@ type response struct {
header http.Header header http.Header
headerSent bool headerSent bool
req *http.Request 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{ resp := &response{
header: http.Header{}, header: http.Header{},
req: req, req: req,
@@ -41,9 +40,7 @@ func (r *response) Write(p []byte) (n int, err error) {
r.WriteHeader(http.StatusOK) r.WriteHeader(http.StatusOK)
} }
l := C.size_t(len(p)) res := C.nxt_cgo_response_write(r.c_req, buf_ref(p), C.uint32_t(len(p)))
b := buf_ref(p)
res := C.nxt_go_response_write(r.c_req, b, l)
return int(res), nil return int(res), nil
} }
@@ -54,22 +51,37 @@ func (r *response) WriteHeader(code int) {
return return
} }
r.headerSent = true r.headerSent = true
fmt.Fprintf(r, "Status: %d\r\n", code)
// Set a default Content-Type // Set a default Content-Type
if _, hasType := r.header["Content-Type"]; !hasType { if _, hasType := r.header["Content-Type"]; !hasType {
r.header.Add("Content-Type", "text/html; charset=utf-8") 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() { func (r *response) Flush() {
if !r.headerSent { if !r.headerSent {
r.WriteHeader(http.StatusOK) r.WriteHeader(http.StatusOK)
} }
C.nxt_go_response_flush(r.c_req)
} }

View File

@@ -6,17 +6,13 @@
package unit package unit
/* /*
#include "nxt_go_lib.h" #include "nxt_cgo_lib.h"
*/ */
import "C" import "C"
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"os"
"strconv"
"strings"
"unsafe" "unsafe"
) )
@@ -33,105 +29,74 @@ func buf_ref(buf []byte) C.uintptr_t {
return C.uintptr_t(uintptr(unsafe.Pointer(&buf[0]))) 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.b = buf_ref(b)
buf.s = C.size_t(len(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 { func (buf *cbuf) GoBytes() []byte {
if buf == nil { if buf == nil {
var b [0]byte var b [0]byte
return b[:0] 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),
}
return *(*[]byte)(unsafe.Pointer(bytesHeader))
} }
var nxt_go_quit bool = false func GoBytes(buf unsafe.Pointer, size C.int) []byte {
bytesHeader := &SliceHeader{
Data: buf,
Len: int(size),
Cap: int(size),
}
//export nxt_go_set_quit return *(*[]byte)(unsafe.Pointer(bytesHeader))
func nxt_go_set_quit() {
nxt_go_quit = true
} }
func nxt_go_warn(format string, args ...interface{}) { 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{}) { C.nxt_cgo_warn(str_ref(str), C.uint32_t(len(str)))
// fmt.Fprintf(os.Stderr, "[go debug] " + format + "\n", args...)
} }
func ListenAndServe(addr string, handler http.Handler) error { 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") rc := C.nxt_cgo_run(C.uintptr_t(uintptr(unsafe.Pointer(&handler))))
if go_ports_env == "" {
if rc != 0 {
return http.ListenAndServe(addr, handler) 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 return nil
} }

View File

@@ -12,6 +12,7 @@
#include <nxt_router.h> #include <nxt_router.h>
#include <nxt_http.h> #include <nxt_http.h>
#include <nxt_application.h> #include <nxt_application.h>
#include <nxt_unit.h>
#include <nxt_port_memory_int.h> #include <nxt_port_memory_int.h>
#include <glob.h> #include <glob.h>
@@ -46,10 +47,7 @@ static uint32_t compat[] = {
nxt_str_t nxt_server = nxt_string(NXT_SERVER); nxt_str_t nxt_server = nxt_string(NXT_SERVER);
static nxt_thread_mutex_t nxt_app_mutex; static nxt_app_module_t *nxt_app;
static nxt_thread_cond_t nxt_app_cond;
static nxt_application_module_t *nxt_app;
nxt_int_t 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_uint_t i, n;
nxt_module_t *module; nxt_module_t *module;
nxt_app_type_t type; nxt_app_type_t type;
nxt_application_module_t *app; nxt_app_module_t *app;
/* /*
* Only memory allocation failure should return NXT_ERROR. * Only memory allocation failure should return NXT_ERROR.
@@ -353,14 +351,6 @@ nxt_app_start(nxt_task_t *task, void *data)
return NXT_ERROR; 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); ret = nxt_app->init(task, data);
if (nxt_slow_path(ret != NXT_OK)) { 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_int_t
nxt_app_http_req_done(nxt_task_t *task, nxt_app_parse_ctx_t *ar) 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_t *
nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name) 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; 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;
}

View File

@@ -11,6 +11,8 @@
#include <nxt_conf.h> #include <nxt_conf.h>
#include <nxt_unit_typedefs.h>
typedef enum { typedef enum {
NXT_APP_PYTHON, NXT_APP_PYTHON,
@@ -23,7 +25,6 @@ typedef enum {
} nxt_app_type_t; } nxt_app_type_t;
typedef struct nxt_app_module_s nxt_application_module_t;
typedef struct nxt_app_module_s nxt_app_module_t; typedef struct nxt_app_module_s nxt_app_module_t;
@@ -31,7 +32,7 @@ typedef struct {
nxt_app_type_t type; nxt_app_type_t type;
u_char *version; u_char *version;
char *file; char *file;
nxt_application_module_t *module; nxt_app_module_t *module;
} nxt_app_lang_module_t; } nxt_app_lang_module_t;
@@ -129,6 +130,7 @@ typedef struct {
typedef struct nxt_app_parse_ctx_s nxt_app_parse_ctx_t; typedef struct nxt_app_parse_ctx_s nxt_app_parse_ctx_t;
struct nxt_app_parse_ctx_s { struct nxt_app_parse_ctx_s {
nxt_app_request_t r; nxt_app_request_t r;
nxt_http_request_t *request; 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); 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 { struct nxt_app_module_s {
size_t compat_length; size_t compat_length;
uint32_t *compat; uint32_t *compat;
@@ -221,94 +154,17 @@ struct nxt_app_module_s {
nxt_int_t (*init)(nxt_task_t *task, nxt_int_t (*init)(nxt_task_t *task,
nxt_common_app_conf_t *conf); 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_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_app_type_t nxt_app_parse_type(u_char *p, size_t length);
NXT_EXPORT extern nxt_str_t nxt_server; 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_ */ #endif /* _NXT_APPLICATION_H_INCLIDED_ */

View File

@@ -6,21 +6,17 @@
#include <nxt_main.h> #include <nxt_main.h>
#include <nxt_router.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_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
static nxt_int_t nxt_go_run(nxt_task_t *task, nxt_app_module_t nxt_go_module = {
nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *msg);
nxt_application_module_t nxt_go_module = {
0, 0,
NULL, NULL,
nxt_string("go"), nxt_string("go"),
"*", "*",
nxt_go_init, 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; return NXT_ERROR;
} }
nxt_fd_blocking(task, fd);
return NXT_OK; 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, p = nxt_sprintf(buf, end,
"%s;%uD;" "%s;%uD;"
"%PI,%ud,%d,%d,%d;" "%PI,%ud,%d;"
"%PI,%ud,%d,%d,%d%Z", "%PI,%ud,%d;"
"%d,%Z",
NXT_VERSION, my_port->process->init->stream, NXT_VERSION, my_port->process->init->stream,
main_port->pid, main_port->id, (int) main_port->type, main_port->pid, main_port->id, main_port->pair[1],
-1, main_port->pair[1], my_port->pid, my_port->id, my_port->pair[0],
my_port->pid, my_port->id, (int) my_port->type, 2);
my_port->pair[0], -1);
if (nxt_slow_path(p == end)) { 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; 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)) { 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; return NXT_ERROR;
} }
@@ -174,11 +173,3 @@ nxt_go_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
return NXT_ERROR; 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;
}

View File

@@ -1,4 +1,3 @@
/* /*
* Copyright (C) Max Romanov * Copyright (C) Max Romanov
* Copyright (C) Valentin V. Bartenev * Copyright (C) Valentin V. Bartenev
@@ -12,12 +11,19 @@
#include <nxt_main.h> #include <nxt_main.h>
#include <nxt_router.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_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
static nxt_int_t nxt_php_run(nxt_task_t *task, static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg); 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 #if PHP_MAJOR_VERSION >= 7
# define NXT_PHP7 1 # 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); int type);
static int nxt_php_send_headers(sapi_headers_struct *sapi_headers); static int nxt_php_send_headers(sapi_headers_struct *sapi_headers);
static char *nxt_php_read_cookies(void); 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); static void nxt_php_register_variables(zval *track_vars_array);
#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE #ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
static void nxt_php_log_message(char *message, int syslog_type_int); 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); static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC);
#endif #endif
static void nxt_php_flush(void *server_context);
static sapi_module_struct nxt_php_sapi_module = static sapi_module_struct nxt_php_sapi_module =
{ {
@@ -72,7 +82,7 @@ static sapi_module_struct nxt_php_sapi_module =
NULL, /* deactivate */ NULL, /* deactivate */
nxt_php_unbuffered_write, /* unbuffered write */ nxt_php_unbuffered_write, /* unbuffered write */
nxt_php_flush, /* flush */ NULL, /* flush */
NULL, /* get uid */ NULL, /* get uid */
NULL, /* getenv */ NULL, /* getenv */
@@ -120,19 +130,12 @@ static sapi_module_struct nxt_php_sapi_module =
NULL /* input_filter_init */ NULL /* input_filter_init */
}; };
typedef struct {
nxt_task_t *task; struct nxt_php_run_ctx_s {
nxt_app_rmsg_t *rmsg; char *cookie;
nxt_app_request_t r;
nxt_str_t script; nxt_str_t script;
nxt_app_wmsg_t *wmsg; nxt_unit_request_info_t *req;
};
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);
static nxt_str_t nxt_php_path; 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 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[] = { static uint32_t compat[] = {
NXT_VERNUM, NXT_DEBUG, NXT_VERNUM, NXT_DEBUG,
}; };
NXT_EXPORT nxt_application_module_t nxt_app_module = { NXT_EXPORT nxt_app_module_t nxt_app_module = {
sizeof(compat), sizeof(compat),
compat, compat,
nxt_string("php"), nxt_string("php"),
PHP_VERSION, PHP_VERSION,
nxt_php_init, nxt_php_init,
nxt_php_run,
NULL,
}; };
static nxt_task_t *nxt_php_task; 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 static nxt_int_t
nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
{ {
u_char *p; u_char *p;
nxt_str_t rpath, ini_path; nxt_str_t rpath, ini_path;
nxt_str_t *root, *path, *script, *index; 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_conf_value_t *value;
nxt_php_app_conf_t *c; 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_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; return NXT_OK;
} }
@@ -430,51 +450,57 @@ nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
#endif #endif
static nxt_int_t static void
nxt_php_read_request(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_php_str_trim_trail(nxt_str_t *str, u_char t)
nxt_php_run_ctx_t *ctx)
{ {
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; u_char *p;
size_t s; nxt_str_t path, script_name;
nxt_int_t rc; nxt_unit_field_t *f;
nxt_str_t script_name; zend_file_handle file_handle;
nxt_app_request_header_t *h; 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) \ ctx = &run_ctx;
do { \ ctx->req = req;
rc = (S); \
if (nxt_slow_path(rc != NXT_OK)) { \
goto fail; \
} \
} while(0)
#define NXT_READ(dst) \ r = req->request;
RC(nxt_app_msg_read_str(task, rmsg, (dst)))
NXT_READ(&h->method); path.length = r->path_length;
NXT_READ(&h->target); path.start = nxt_unit_sptr_get(&r->path);
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;
}
if (nxt_php_path.start == NULL) { 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; script_name = nxt_php_index;
} else { } else {
@@ -482,15 +508,17 @@ nxt_php_read_request(nxt_task_t *task, nxt_app_rmsg_t *rmsg,
script_name.start = NULL; 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; + script_name.length;
p = ctx->script.start = nxt_malloc(ctx->script.length + 1); p = ctx->script.start = nxt_malloc(ctx->script.length + 1);
if (nxt_slow_path(p == NULL)) { 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, 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) { if (script_name.length > 0) {
p = nxt_cpymem(p, script_name.start, script_name.length); 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; ctx->script = nxt_php_path;
} }
NXT_READ(&h->version); SG(server_context) = ctx;
SG(request_info).request_uri = nxt_unit_sptr_get(&r->target);
NXT_READ(&ctx->r.remote); SG(request_info).request_method = nxt_unit_sptr_get(&r->method);
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(request_info).proto_num = 1001; SG(request_info).proto_num = 1001;
SG(request_info).query_string = (char *) h->query.start; SG(request_info).query_string = r->query.offset
SG(request_info).content_length = h->parsed_content_length; ? nxt_unit_sptr_get(&r->query) : NULL;
SG(request_info).content_length = r->content_length;
if (h->content_type.start != NULL) { if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
SG(request_info).content_type = (char *) h->content_type.start; 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; SG(sapi_headers).http_response_code = 200;
@@ -570,60 +557,41 @@ nxt_php_run(nxt_task_t *task,
SG(request_info).path_translated = NULL; SG(request_info).path_translated = NULL;
file_handle.type = ZEND_HANDLE_FILENAME; 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.free_filename = 0;
file_handle.opened_path = NULL; 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) { 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 { } 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)) { if (nxt_slow_path(php_request_startup() == FAILURE)) {
nxt_debug(task, "php_request_startup() failed"); nxt_unit_req_debug(req, "php_request_startup() failed");
rc = NXT_ERROR; rc = NXT_UNIT_ERROR;
goto fail; goto fail;
} }
rc = NXT_UNIT_OK;
php_execute_script(&file_handle TSRMLS_CC); php_execute_script(&file_handle TSRMLS_CC);
php_request_shutdown(NULL); php_request_shutdown(NULL);
nxt_app_msg_flush(task, wmsg, 1);
rc = NXT_OK;
fail: fail:
if (run_ctx.script.start != nxt_php_path.start) { nxt_unit_request_done(req, rc);
nxt_free(run_ctx.script.start);
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) nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC)
#endif #endif
{ {
nxt_int_t rc; int rc;
nxt_php_run_ctx_t *ctx; nxt_php_run_ctx_t *ctx;
ctx = SG(server_context); ctx = SG(server_context);
rc = nxt_php_write(ctx, (u_char *) str, str_length, 1, 0); rc = nxt_unit_response_write(ctx->req, str, str_length);
if (nxt_fast_path(rc == NXT_UNIT_OK)) {
if (nxt_fast_path(rc == NXT_OK)) {
return str_length; return str_length;
} }
// TODO handle NXT_AGAIN
php_handle_aborted_connection(); php_handle_aborted_connection();
return 0; 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 static int
nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
{ {
size_t len; int rc, fields_count;
u_char *status, buf[64]; char *colon, *status_line, *value;
nxt_int_t rc; uint16_t status;
uint32_t resp_size;
nxt_php_run_ctx_t *ctx; nxt_php_run_ctx_t *ctx;
sapi_header_struct *h; sapi_header_struct *h;
zend_llist_position zpos; zend_llist_position zpos;
nxt_unit_request_info_t *req;
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";
ctx = SG(server_context); ctx = SG(server_context);
req = ctx->req;
#define RC(S) \ nxt_unit_req_debug(req, "nxt_php_send_headers");
do { \
rc = (S); \
if (nxt_slow_path(rc != NXT_OK)) { \
goto fail; \
} \
} while(0)
if (SG(request_info).no_headers == 1) { if (SG(request_info).no_headers == 1) {
RC(nxt_php_write(ctx, default_repsonse, nxt_length(default_repsonse), rc = nxt_unit_response_init(req, 200, 0, 0);
1, 0)); if (nxt_slow_path(rc != NXT_UNIT_OK)) {
return SAPI_HEADER_SEND_FAILED;
}
return SAPI_HEADER_SENT_SUCCESSFULLY; 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) { if (SG(sapi_headers).http_status_line) {
status = (u_char *) SG(sapi_headers).http_status_line; status_line = SG(sapi_headers).http_status_line;
len = nxt_strlen(status);
if (len < 12) { status = nxt_int_parse((u_char *) status_line + 9, 3);
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));
} else if (SG(sapi_headers).http_response_code) { } else if (SG(sapi_headers).http_response_code) {
status = nxt_sprintf(buf, buf + sizeof(buf), "%03d", status = SG(sapi_headers).http_response_code;
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));
} else { } 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)); rc = nxt_unit_response_init(req, status, fields_count, resp_size);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos); return SAPI_HEADER_SEND_FAILED;
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_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; 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) nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC)
#endif #endif
{ {
size_t size, rest;
nxt_php_run_ctx_t *ctx; nxt_php_run_ctx_t *ctx;
nxt_app_request_header_t *h;
ctx = SG(server_context); 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); return nxt_unit_request_read(ctx->req, buffer, count_bytes);
if (rest == 0) {
return 0;
}
rest = nxt_min(ctx->body_preread_size, (size_t) count_bytes);
size = nxt_app_msg_read_raw(ctx->task, ctx->rmsg, buffer, rest);
ctx->body_preread_size -= size;
return size;
} }
@@ -789,44 +737,36 @@ nxt_php_read_cookies(TSRMLS_D)
ctx = SG(server_context); 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 static void
nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) nxt_php_register_variables(zval *track_vars_array TSRMLS_DC)
{ {
u_char *colon; char *host_start, *port_start;
size_t rest, size; uint32_t host_length, port_length;
nxt_str_t n, v; const char *name;
nxt_int_t rc; nxt_unit_field_t *f, *f_end;
nxt_str_t host, server_name, server_port;
nxt_buf_t *b, buf;
nxt_task_t *task;
nxt_app_rmsg_t *rmsg, rmsg_tmp;
nxt_php_run_ctx_t *ctx; nxt_php_run_ctx_t *ctx;
nxt_app_request_header_t *h; nxt_unit_request_t *r;
nxt_unit_request_info_t *req;
static nxt_str_t def_host = nxt_string("localhost");
static nxt_str_t def_port = nxt_string("80");
ctx = SG(server_context); ctx = SG(server_context);
h = &ctx->r.header; req = ctx->req;
task = ctx->task; r = req->request;
nxt_debug(task, "php register variables"); nxt_unit_req_debug(req, "nxt_php_register_variables");
#define NXT_PHP_SET(n, v) \ php_register_variable_safe((char *) "SERVER_SOFTWARE",
nxt_debug(task, "php: register %s='%V'", n, &v); \ (char *) nxt_server.start,
php_register_variable_safe((char *) (n), (char *) (v).start, \ nxt_server.length, track_vars_array TSRMLS_CC);
(v).length, track_vars_array TSRMLS_CC) \
NXT_PHP_SET("SERVER_SOFTWARE", nxt_server); nxt_php_set_sptr(req, "SERVER_PROTOCOL", &r->version, r->version_length,
track_vars_array TSRMLS_CC);
NXT_PHP_SET("SERVER_PROTOCOL", h->version);
/* /*
* 'SCRIPT_NAME' * '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 * 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. * script name since PHP 4.3.0. Previously it was not available.
*/ */
NXT_PHP_SET("PHP_SELF", nxt_php_script); nxt_php_set_str(req, "PHP_SELF", &nxt_php_script,
NXT_PHP_SET("SCRIPT_NAME", nxt_php_script); track_vars_array TSRMLS_CC);
nxt_php_set_str(req, "SCRIPT_NAME", &nxt_php_script,
track_vars_array TSRMLS_CC);
} else { } else {
NXT_PHP_SET("PHP_SELF", h->path); nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length,
NXT_PHP_SET("SCRIPT_NAME", h->path); 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_str(req, "SCRIPT_FILENAME", &ctx->script,
NXT_PHP_SET("DOCUMENT_ROOT", nxt_php_root); 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_sptr(req, "REQUEST_METHOD", &r->method, r->method_length,
NXT_PHP_SET("REQUEST_URI", h->target); track_vars_array TSRMLS_CC);
nxt_php_set_sptr(req, "REQUEST_URI", &r->target, r->target_length,
if (h->query.start != NULL) { track_vars_array TSRMLS_CC);
NXT_PHP_SET("QUERY_STRING", h->query); 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_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length,
NXT_PHP_SET("CONTENT_TYPE", h->content_type); 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) { if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
NXT_PHP_SET("CONTENT_LENGTH", h->content_length); 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 (r->content_type_field != NXT_UNIT_NONE_FIELD) {
if (host.length == 0) { f = r->fields + r->content_type_field;
host = def_host;
nxt_php_set_sptr(req, "CONTENT_TYPE", &f->value, f->value_length,
track_vars_array TSRMLS_CC);
} }
server_name = host; if (r->host_field != NXT_UNIT_NONE_FIELD) {
colon = nxt_memchr(host.start, ':', host.length); f = r->fields + r->host_field;
if (colon != NULL) { host_start = nxt_unit_sptr_get(&f->value);
server_name.length = colon - host.start; host_length = f->value_length;
server_port.start = colon + 1;
server_port.length = host.length - server_name.length - 1;
} else { } else {
server_port = def_port; host_start = NULL;
host_length = 0;
} }
NXT_PHP_SET("SERVER_NAME", server_name); nxt_unit_split_host(host_start, host_length, &host_start, &host_length,
NXT_PHP_SET("SERVER_PORT", server_port); &port_start, &port_length);
NXT_PHP_SET("REMOTE_ADDR", ctx->r.remote); nxt_php_set_cstr(req, "SERVER_NAME", host_start, host_length,
NXT_PHP_SET("SERVER_ADDR", ctx->r.local); 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) { static void
/* Skipping request body. */ 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 { nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, str);
if (nxt_slow_path(b == NULL)) {
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; 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) { php_register_variable_safe((char *) name, cstr, len,
nxt_memcpy(&buf, b, NXT_BUF_MEM_SIZE); track_vars_array TSRMLS_CC);
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
} }

View File

@@ -255,7 +255,7 @@ fail:
static nxt_port_mmap_handler_t * static nxt_port_mmap_handler_t *
nxt_port_new_port_mmap(nxt_task_t *task, nxt_process_t *process, 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; void *mem;
u_char *p, name[64]; 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. */ /* TODO introduce port_mmap limit and release wait. */
*c = 0; *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: unlock_return:

View File

@@ -14,6 +14,10 @@
#include <nxt_main.h> #include <nxt_main.h>
#include <nxt_runtime.h> #include <nxt_runtime.h>
#include <nxt_router.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" * According to "PEP 3333 / A Note On String Types"
@@ -49,30 +53,33 @@
#define PyBytes_AS_STRING PyString_AS_STRING #define PyBytes_AS_STRING PyString_AS_STRING
#endif #endif
typedef struct nxt_python_run_ctx_s nxt_python_run_ctx_t;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
//nxt_app_request_t *request;
} nxt_py_input_t; } nxt_py_input_t;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
//nxt_app_request_t *request;
} nxt_py_error_t; } 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_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
static nxt_int_t nxt_python_run(nxt_task_t *task, static void nxt_python_request_handler(nxt_unit_request_info_t *req);
nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *msg); static void nxt_python_atexit(void);
static void nxt_python_atexit(nxt_task_t *task);
static PyObject *nxt_python_create_environ(nxt_task_t *task); static PyObject *nxt_python_create_environ(nxt_task_t *task);
static PyObject *nxt_python_get_environ(nxt_task_t *task, static PyObject *nxt_python_get_environ(nxt_python_run_ctx_t *ctx);
nxt_app_rmsg_t *rmsg, 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 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 PyObject *nxt_py_write(PyObject *self, PyObject *args);
static void nxt_py_input_dealloc(nxt_py_input_t *self); 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_readline(nxt_py_input_t *self, PyObject *args);
static PyObject *nxt_py_input_readlines(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 { struct nxt_python_run_ctx_s {
nxt_task_t *task; uint64_t content_length;
nxt_app_rmsg_t *rmsg; uint64_t bytes_sent;
nxt_app_wmsg_t *wmsg; PyObject *environ;
nxt_unit_request_info_t *req;
size_t body_preread_size;
}; };
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[] = { static uint32_t compat[] = {
NXT_VERNUM, NXT_DEBUG, NXT_VERNUM, NXT_DEBUG,
}; };
NXT_EXPORT nxt_application_module_t nxt_app_module = { NXT_EXPORT nxt_app_module_t nxt_app_module = {
sizeof(compat), sizeof(compat),
compat, compat,
nxt_string("python"), nxt_string("python"),
PY_VERSION, PY_VERSION,
nxt_python_init, 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 static nxt_int_t
nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
{ {
int rc;
char *nxt_py_module; char *nxt_py_module;
size_t len; size_t len;
PyObject *obj, *pypath, *module; PyObject *obj, *pypath, *module;
nxt_unit_ctx_t *unit_ctx;
nxt_unit_init_t python_init;
nxt_python_app_conf_t *c; nxt_python_app_conf_t *c;
#if PY_MAJOR_VERSION == 3 #if PY_MAJOR_VERSION == 3
char *path; char *path;
@@ -378,6 +379,23 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
nxt_py_application = obj; 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; return NXT_OK;
fail: fail:
@@ -393,26 +411,26 @@ fail:
} }
static nxt_int_t static void
nxt_python_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) nxt_python_request_handler(nxt_unit_request_info_t *req)
{ {
u_char *buf; int rc;
size_t size;
PyObject *result, *iterator, *item, *args, *environ; PyObject *result, *iterator, *item, *args, *environ;
nxt_python_run_ctx_t run_ctx = {task, rmsg, wmsg, 0}; nxt_python_run_ctx_t run_ctx = {-1, 0, NULL, req};
environ = nxt_python_get_environ(task, rmsg, &run_ctx);
environ = nxt_python_get_environ(&run_ctx);
if (nxt_slow_path(environ == NULL)) { if (nxt_slow_path(environ == NULL)) {
return NXT_ERROR; nxt_unit_request_done(req, NXT_UNIT_ERROR);
return;
} }
args = PyTuple_New(2); args = PyTuple_New(2);
if (nxt_slow_path(args == NULL)) { if (nxt_slow_path(args == NULL)) {
nxt_log_error(NXT_LOG_ERR, task->log, nxt_unit_req_error(req, "Python failed to create arguments tuple");
"Python failed to create arguments tuple");
return NXT_ERROR; nxt_unit_request_done(req, NXT_UNIT_ERROR);
return;
} }
PyTuple_SET_ITEM(args, 0, environ); 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); Py_DECREF(args);
if (nxt_slow_path(result == NULL)) { if (nxt_slow_path(result == NULL)) {
nxt_log_error(NXT_LOG_ERR, task->log, nxt_unit_req_error(req, "Python failed to call the application");
"Python failed to call the application");
PyErr_Print(); PyErr_Print();
return NXT_ERROR;
nxt_unit_request_done(req, NXT_UNIT_ERROR);
nxt_python_run_ctx = NULL;
return;
} }
item = NULL; item = NULL;
iterator = NULL; iterator = NULL;
/* Shortcut: avoid iterate over result string symbols. */ /* Shortcut: avoid iterate over result string symbols. */
if (PyBytes_Check(result) != 0) { if (PyBytes_Check(result)) {
size = PyBytes_GET_SIZE(result); rc = nxt_python_write(&run_ctx, result);
buf = (u_char *) PyBytes_AS_STRING(result); if (nxt_slow_path(rc != NXT_UNIT_OK)) {
goto fail;
nxt_python_write(&run_ctx, buf, size, 1, 1); }
} else { } else {
iterator = PyObject_GetIter(result); iterator = PyObject_GetIter(result);
if (nxt_slow_path(iterator == NULL)) { if (nxt_slow_path(iterator == NULL)) {
nxt_log_error(NXT_LOG_ERR, task->log, nxt_unit_req_error(req, "the application returned "
"the application returned not an iterable object"); "not an iterable object");
goto fail; goto fail;
} }
while((item = PyIter_Next(iterator))) { while (run_ctx.bytes_sent < run_ctx.content_length
&& (item = PyIter_Next(iterator)))
if (nxt_slow_path(PyBytes_Check(item) == 0)) { {
nxt_log_error(NXT_LOG_ERR, task->log, if (nxt_slow_path(!PyBytes_Check(item))) {
"the application returned not a bytestring object"); nxt_unit_req_error(req, "the application returned "
"not a bytestring object");
goto fail; goto fail;
} }
size = PyBytes_GET_SIZE(item); rc = nxt_python_write(&run_ctx, item);
buf = (u_char *) PyBytes_AS_STRING(item); if (nxt_slow_path(rc != NXT_UNIT_OK)) {
goto fail;
nxt_debug(task, "nxt_app_write(fake): %uz", size); }
nxt_python_write(&run_ctx, buf, size, 1, 0);
Py_DECREF(item); Py_DECREF(item);
} }
Py_DECREF(iterator); Py_DECREF(iterator);
nxt_python_write(&run_ctx, NULL, 0, 1, 1);
if (PyObject_HasAttrString(result, "close")) { if (PyObject_HasAttrString(result, "close")) {
PyObject_CallMethod(result, (char *) "close", NULL); PyObject_CallMethod(result, (char *) "close", NULL);
} }
} }
if (nxt_slow_path(PyErr_Occurred() != 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(); PyErr_Print();
} }
nxt_unit_request_done(req, NXT_UNIT_OK);
Py_DECREF(result); Py_DECREF(result);
nxt_python_run_ctx = NULL; nxt_python_run_ctx = NULL;
return NXT_OK; return;
fail: fail:
@@ -509,12 +530,12 @@ fail:
Py_DECREF(result); Py_DECREF(result);
nxt_python_run_ctx = NULL; nxt_python_run_ctx = NULL;
return NXT_ERROR; nxt_unit_request_done(req, NXT_UNIT_ERROR);
} }
static void static void
nxt_python_atexit(nxt_task_t *task) nxt_python_atexit(void)
{ {
Py_DECREF(nxt_py_application); Py_DECREF(nxt_py_application);
Py_DECREF(nxt_py_start_resp_obj); Py_DECREF(nxt_py_start_resp_obj);
@@ -673,162 +694,91 @@ fail:
return NULL; 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 * static PyObject *
nxt_python_get_environ(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_python_get_environ(nxt_python_run_ctx_t *ctx)
nxt_python_run_ctx_t *ctx)
{ {
size_t s; int rc;
u_char *colon; char *name, *host_start, *port_start;
uint32_t i, host_length, port_length;
PyObject *environ; PyObject *environ;
nxt_int_t rc; nxt_unit_field_t *f;
nxt_str_t n, v, target, path, query; nxt_unit_request_t *r;
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");
environ = PyDict_Copy(nxt_py_environ_ptyp); environ = PyDict_Copy(nxt_py_environ_ptyp);
if (nxt_slow_path(environ == NULL)) { if (nxt_slow_path(environ == NULL)) {
nxt_log_error(NXT_LOG_ERR, task->log, nxt_unit_req_error(ctx->req,
"Python failed to create the \"environ\" dictionary"); "Python failed to copy the \"environ\" dictionary");
return NULL; return NULL;
} }
ctx->environ = environ;
r = ctx->req->request;
#define RC(S) \ #define RC(S) \
do { \ do { \
rc = (S); \ rc = (S); \
if (nxt_slow_path(rc != NXT_OK)) { \ if (nxt_slow_path(rc != NXT_UNIT_OK)) { \
goto fail; \ goto fail; \
} \ } \
} while(0) } while(0)
#define NXT_READ(N) \ RC(nxt_python_add_sptr(ctx, "REQUEST_METHOD", &r->method,
RC(nxt_python_read_add_env(task, rmsg, environ, N, &v)) r->method_length));
RC(nxt_python_add_sptr(ctx, "REQUEST_URI", &r->target, r->target_length));
NXT_READ("REQUEST_METHOD"); if (r->query.offset) {
NXT_READ("REQUEST_URI"); RC(nxt_python_add_sptr(ctx, "QUERY_STRING", &r->query,
r->query_length));
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;
} }
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) { if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
path = target; 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"); RC(nxt_python_add_sptr(ctx, "CONTENT_TYPE", &f->value,
f->value_length));
NXT_READ("REMOTE_ADDR");
NXT_READ("SERVER_ADDR");
RC(nxt_app_msg_read_str(task, rmsg, &host));
if (host.length == 0) {
host = def_host;
} }
server_name = host; if (r->host_field != NXT_UNIT_NONE_FIELD) {
colon = nxt_memchr(host.start, ':', host.length); f = r->fields + r->host_field;
if (colon != NULL) { host_start = nxt_unit_sptr_get(&f->value);
server_name.length = colon - host.start; host_length = f->value_length;
server_port.start = colon + 1;
server_port.length = host.length - server_name.length - 1;
} else { } else {
server_port = def_port; host_start = NULL;
host_length = 0;
} }
RC(nxt_python_add_env(task, environ, "SERVER_NAME", &server_name)); nxt_unit_split_host(host_start, host_length, &host_start, &host_length,
RC(nxt_python_add_env(task, environ, "SERVER_PORT", &server_port)); &port_start, &port_length);
NXT_READ("CONTENT_TYPE"); RC(nxt_python_add_str(ctx, "SERVER_NAME", host_start, host_length));
NXT_READ("CONTENT_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 #undef RC
return environ; 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 * static PyObject *
nxt_py_start_resp(PyObject *self, PyObject *args) nxt_py_start_resp(PyObject *self, PyObject *args)
{ {
PyObject *headers, *tuple, *string; int rc, status;
nxt_int_t rc; char *status_str, *space_ptr;
nxt_uint_t i, n; 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; 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; ctx = nxt_python_run_ctx;
if (nxt_slow_path(ctx == NULL)) { if (nxt_slow_path(ctx == NULL)) {
return PyErr_Format(PyExc_RuntimeError, return PyErr_Format(PyExc_RuntimeError,
"start_response() is called " "start_response() is called "
@@ -869,25 +883,21 @@ nxt_py_start_resp(PyObject *self, PyObject *args)
} }
string = PyTuple_GET_ITEM(args, 0); string = PyTuple_GET_ITEM(args, 0);
if (!PyBytes_Check(string) && !PyUnicode_Check(string)) {
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)) {
return PyErr_Format(PyExc_TypeError, return PyErr_Format(PyExc_TypeError,
"failed to write first argument (not a string?)"); "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); headers = PyTuple_GET_ITEM(args, 1);
if (!PyList_Check(headers)) { if (!PyList_Check(headers)) {
return PyErr_Format(PyExc_TypeError, return PyErr_Format(PyExc_TypeError,
"the second argument is not a response headers list"); "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); tuple = PyList_GET_ITEM(headers, i);
if (!PyTuple_Check(tuple)) { if (!PyTuple_Check(tuple)) {
@@ -901,52 +911,182 @@ nxt_py_start_resp(PyObject *self, PyObject *args)
} }
string = PyTuple_GET_ITEM(tuple, 0); 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); } else if (PyUnicode_Check(string)) {
if (nxt_slow_path(rc != NXT_OK)) { fields_size += PyUnicode_GET_SIZE(string);
} else {
return PyErr_Format(PyExc_TypeError, return PyErr_Format(PyExc_TypeError,
"failed to write response header name" "header #%d name is not a string", (int) i);
" (not a string?)");
} }
nxt_python_write(ctx, sc_sp, nxt_length(sc_sp), 0, 0);
string = PyTuple_GET_ITEM(tuple, 1); 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); } else if (PyUnicode_Check(string)) {
if (nxt_slow_path(rc != NXT_OK)) { fields_size += PyUnicode_GET_SIZE(string);
} else {
return PyErr_Format(PyExc_TypeError, return PyErr_Format(PyExc_TypeError,
"failed to write response header value" "header #%d value is not a string", (int) i);
" (not a string?)"); }
} }
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 */ space_ptr = memchr(status_str, ' ', status_len);
nxt_python_write(ctx, cr_lf, nxt_length(cr_lf), 1, 0); 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); Py_INCREF(nxt_py_write_obj);
return 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 * static PyObject *
nxt_py_write(PyObject *self, PyObject *str) nxt_py_write(PyObject *self, PyObject *str)
{ {
nxt_int_t rc; int rc;
if (nxt_fast_path(!PyBytes_Check(str))) { if (nxt_fast_path(!PyBytes_Check(str))) {
return PyErr_Format(PyExc_TypeError, "the argument is not a %s", return PyErr_Format(PyExc_TypeError, "the argument is not a %s",
NXT_PYTHON_BYTES_TYPE); NXT_PYTHON_BYTES_TYPE);
} }
rc = nxt_app_msg_write_raw(nxt_python_run_ctx->task, rc = nxt_python_write(nxt_python_run_ctx, str);
nxt_python_run_ctx->wmsg, if (nxt_slow_path(rc != NXT_UNIT_OK)) {
(const u_char *) PyBytes_AS_STRING(str),
PyBytes_GET_SIZE(str));
if (nxt_slow_path(rc != NXT_OK)) {
return PyErr_Format(PyExc_RuntimeError, return PyErr_Format(PyExc_RuntimeError,
"failed to write response value"); "failed to write response value");
} }
@@ -965,22 +1105,19 @@ nxt_py_input_dealloc(nxt_py_input_t *self)
static PyObject * static PyObject *
nxt_py_input_read(nxt_py_input_t *self, PyObject *args) nxt_py_input_read(nxt_py_input_t *self, PyObject *args)
{ {
u_char *buf; char *buf;
size_t copy_size; PyObject *content, *obj;
PyObject *body, *obj; Py_ssize_t size, n;
Py_ssize_t size;
nxt_uint_t n;
nxt_python_run_ctx_t *ctx; nxt_python_run_ctx_t *ctx;
ctx = nxt_python_run_ctx; ctx = nxt_python_run_ctx;
if (nxt_slow_path(ctx == NULL)) { if (nxt_slow_path(ctx == NULL)) {
return PyErr_Format(PyExc_RuntimeError, return PyErr_Format(PyExc_RuntimeError,
"wsgi.input.read() is called " "wsgi.input.read() is called "
"outside of WSGI request processing"); "outside of WSGI request processing");
} }
size = ctx->body_preread_size; size = ctx->req->content_length;
n = PyTuple_GET_SIZE(args); 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"); "the read body size cannot be zero or less");
} }
if (size == 0 || size > (Py_ssize_t) ctx->body_preread_size) { if (size == 0 || size > (Py_ssize_t) ctx->req->content_length) {
size = ctx->body_preread_size; size = ctx->req->content_length;
} }
} }
body = PyBytes_FromStringAndSize(NULL, size); content = PyBytes_FromStringAndSize(NULL, size);
if (nxt_slow_path(content == NULL)) {
if (nxt_slow_path(body == NULL)) {
return 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); size = nxt_unit_request_read(ctx->req, buf, size);
copy_size = nxt_app_msg_read_raw(ctx->task, ctx->rmsg, buf, copy_size);
ctx->body_preread_size -= copy_size; return content;
return body;
} }
@@ -1038,49 +1171,38 @@ nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args)
} }
nxt_inline nxt_int_t static int
nxt_python_write(nxt_python_run_ctx_t *ctx, const u_char *data, size_t len, nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes)
nxt_bool_t flush, nxt_bool_t last)
{ {
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) { if (nxt_slow_path(str_length == 0)) {
rc = nxt_app_msg_flush(ctx->task, ctx->wmsg, last); return NXT_UNIT_OK;
} }
return rc; /*
} * PEP 3333:
*
* If the application supplies a Content-Length header, the server should
nxt_inline nxt_int_t * not transmit more bytes to the client than the header allows, and should
nxt_python_write_py_str(nxt_python_run_ctx_t *ctx, PyObject *str, * stop iterating over the response when enough data has been sent, or raise
nxt_bool_t flush, nxt_bool_t last) * an error if the application tries to write() past that point.
{ */
PyObject *bytes; if (nxt_slow_path(str_length > ctx->content_length - ctx->bytes_sent)) {
nxt_int_t rc; nxt_unit_req_error(ctx->req, "content length %"PRIu64" exceeded",
ctx->content_length);
rc = NXT_OK;
return NXT_UNIT_ERROR;
if (PyBytes_Check(str)) { }
rc = nxt_python_write(ctx, (u_char *) PyBytes_AS_STRING(str),
PyBytes_GET_SIZE(str), flush, last); rc = nxt_unit_response_write(ctx->req, str_buf, str_length);
if (nxt_fast_path(rc == NXT_UNIT_OK)) {
} else { ctx->bytes_sent += str_length;
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);
} }
return rc; return rc;

View File

@@ -8,6 +8,9 @@
#include <nxt_router.h> #include <nxt_router.h>
#include <nxt_conf.h> #include <nxt_conf.h>
#include <nxt_http.h> #include <nxt_http.h>
#include <nxt_port_memory_int.h>
#include <nxt_unit_request.h>
#include <nxt_unit_response.h>
typedef struct { 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, static void nxt_router_app_prepare_request(nxt_task_t *task,
nxt_req_app_link_t *ra); nxt_req_app_link_t *ra);
static nxt_int_t nxt_python_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, static nxt_buf_t *nxt_router_prepare_msg(nxt_task_t *task, nxt_app_request_t *r,
nxt_app_wmsg_t *wmsg); nxt_port_t *port, const nxt_str_t *prefix);
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 void nxt_router_app_timeout(nxt_task_t *task, void *obj, void *data); 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, 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 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[] = { static const nxt_str_t *nxt_app_msg_prefix[] = {
nxt_python_prepare_msg, &http_prefix,
nxt_php_prepare_msg, &http_prefix,
nxt_go_prepare_msg, &empty_prefix,
nxt_perl_prepare_msg, &http_prefix,
nxt_ruby_prepare_msg, &http_prefix,
}; };
@@ -1459,7 +1456,6 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
app->live = 1; app->live = 1;
app->max_pending_responses = 2; app->max_pending_responses = 2;
app->max_requests = apcf.requests; app->max_requests = apcf.requests;
app->prepare_msg = nxt_app_prepare_msg[lang->type];
engine = task->thread->engine; 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_http_request_t *r;
nxt_req_conn_link_t *rc; nxt_req_conn_link_t *rc;
nxt_app_parse_ctx_t *ar; nxt_app_parse_ctx_t *ar;
nxt_unit_response_t *resp;
b = msg->buf; b = msg->buf;
rc = data; 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); nxt_http_request_send_body(task, r, NULL);
} else { } 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); ret = nxt_http_parse_fields(&ar->resp_parser, &b->mem);
if (nxt_slow_path(ret != NXT_DONE)) { if (nxt_slow_path(ret != NXT_DONE)) {
goto fail; goto fail;
} }
*/
r->resp.fields = ar->resp_parser.fields; r->resp.fields = ar->resp_parser.fields;
ret = nxt_http_fields_process(r->resp.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; 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) { if (nxt_buf_mem_used_size(&b->mem) == 0) {
nxt_work_queue_add(&task->thread->engine->fast_work_queue, nxt_work_queue_add(&task->thread->engine->fast_work_queue,
b->completion_handler, task, b, b->parent); 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) nxt_router_app_prepare_request(nxt_task_t *task, nxt_req_app_link_t *ra)
{ {
uint32_t request_failed; uint32_t request_failed;
nxt_buf_t *b; nxt_buf_t *buf;
nxt_int_t res; nxt_int_t res;
nxt_port_t *port, *c_port, *reply_port; nxt_port_t *port, *c_port, *reply_port;
nxt_app_wmsg_t wmsg;
nxt_app_parse_ctx_t *ap; nxt_app_parse_ctx_t *ap;
nxt_assert(ra->app_port != NULL); 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); nxt_process_connected_port_add(port->process, reply_port);
} }
wmsg.port = port; buf = nxt_router_prepare_msg(task, &ap->r, port,
wmsg.write = NULL; nxt_app_msg_prefix[port->app->type]);
wmsg.buf = &wmsg.write;
wmsg.stream = ra->stream;
res = port->app->prepare_msg(task, &ap->r, &wmsg); if (nxt_slow_path(buf == NULL)) {
if (nxt_slow_path(res != NXT_OK)) {
nxt_router_ra_error(ra, 500, nxt_router_ra_error(ra, 500,
"Failed to prepare message for application"); "Failed to prepare message for application");
goto release_port; goto release_port;
} }
nxt_debug(task, "about to send %O bytes buffer to app process port %d", nxt_debug(task, "about to send %O bytes buffer to app process port %d",
nxt_buf_used_size(wmsg.write), nxt_buf_used_size(buf),
wmsg.port->socket.fd); port->socket.fd);
request_failed = 0; request_failed = 0;
ra->msg_info.buf = wmsg.write; ra->msg_info.buf = buf;
ra->msg_info.completion_handler = wmsg.write->completion_handler; ra->msg_info.completion_handler = buf->completion_handler;
for (b = wmsg.write; b != NULL; b = b->next) { for (; buf; buf = buf->next) {
b->completion_handler = nxt_router_dummy_buf_completion; 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, res = nxt_port_mmap_get_tracking(task, port, &ra->msg_info.tracking,
ra->stream); ra->stream);
if (nxt_slow_path(res != NXT_OK)) { 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; goto release_port;
} }
res = nxt_port_socket_twrite(task, wmsg.port, NXT_PORT_MSG_DATA, res = nxt_port_socket_twrite(task, port, NXT_PORT_MSG_DATA,
-1, ra->stream, reply_port->id, wmsg.write, -1, ra->stream, reply_port->id, buf,
&ra->msg_info.tracking); &ra->msg_info.tracking);
if (nxt_slow_path(res != NXT_OK)) { if (nxt_slow_path(res != NXT_OK)) {
@@ -4288,421 +4327,317 @@ release_port:
} }
static nxt_int_t struct nxt_fields_iter_s {
nxt_python_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, nxt_list_part_t *part;
nxt_app_wmsg_t *wmsg)
{
nxt_int_t rc;
nxt_buf_t *b;
nxt_http_field_t *field; nxt_http_field_t *field;
nxt_app_request_header_t *h; };
static const nxt_str_t prefix = nxt_string("HTTP_"); typedef struct nxt_fields_iter_s nxt_fields_iter_t;
static const nxt_str_t eof = nxt_null_string;
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; h = &r->header;
#define RC(S) \ req_size = sizeof(nxt_unit_request_t)
do { \ + h->method.length + 1
rc = (S); \ + h->version.length + 1
if (nxt_slow_path(rc != NXT_OK)) { \ + r->remote.length + 1
goto fail; \ + r->local.length + 1
} \ + h->target.length + 1
} while(0) + (h->path.start != h->target.start ? h->path.length + 1 : 0);
#define NXT_WRITE(N) \ fields_count = 0;
RC(nxt_app_msg_write_str(task, wmsg, N))
/* TODO error handle, async mmap buffer assignment */ nxt_list_each(field, h->fields) {
fields_count++;
NXT_WRITE(&h->method); req_size += field->name_length + prefix->length + 1
NXT_WRITE(&h->target); + 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) { if (h->path.start == h->target.start) {
NXT_WRITE(&eof); nxt_unit_sptr_set(&req->path, target_pos);
} else { } 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) { if (h->query.start != NULL) {
RC(nxt_app_msg_write_size(task, wmsg, query_pos = nxt_pointer_to(target_pos,
h->query.start - h->target.start + 1)); h->query.start - h->target.start);
nxt_unit_sptr_set(&req->query, query_pos);
} else { } 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); dst_field = req->fields;
NXT_WRITE(&r->local);
NXT_WRITE(&h->host); for (field = nxt_fields_first(h->fields, &iter);
NXT_WRITE(&h->content_type); field != NULL;
NXT_WRITE(&h->content_length); field = nxt_fields_next(&iter))
{
if (field->skip) {
continue;
}
nxt_list_each(field, h->fields) { dst_field->hash = field->hash;
RC(nxt_app_msg_write_prefixed_upcase(task, wmsg, &prefix, field->name, dst_field->skip = 0;
field->name_length)); dst_field->name_length = field->name_length + prefix->length;
RC(nxt_app_msg_write(task, wmsg, field->value, field->value_length)); dst_field->value_length = field->value_length;
} nxt_list_loop; if (field->value == h->host.start) {
req->host_field = dst_field - req->fields;
/* end-of-headers mark */ } else if (field->value == h->content_length.start) {
NXT_WRITE(&eof); req->content_length_field = dst_field - req->fields;
RC(nxt_app_msg_write_size(task, wmsg, r->body.preread_size)); } else if (field->value == h->content_type.start) {
req->content_type_field = dst_field - req->fields;
} else if (field->value == h->cookie.start) {
req->cookie_field = dst_field - req->fields;
}
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 (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_unit_sptr_set(&dst_field->name, p);
p = nxt_cpymem(p, field->name, field->name_length);
}
*p++ = '\0';
nxt_unit_sptr_set(&dst_field->value, p);
p = nxt_cpymem(p, field->value, field->value_length);
if (prefix->length != 0) {
dup_iter = iter;
for (dup = nxt_fields_next(&dup_iter);
dup != NULL;
dup = nxt_fields_next(&dup_iter))
{
if (dup->name_length != field->name_length
|| dup->skip
|| dup->hash != field->hash
|| nxt_memcasecmp(dup->name, field->name, dup->name_length))
{
continue;
}
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;
}
}
*p++ = '\0';
dst_field++;
}
req->fields_count = dst_field - req->fields;
nxt_unit_sptr_set(&req->preread_content, out->mem.free);
buf = out;
tail = &buf->next;
for (b = r->body.buf; b != NULL; b = b->next) { for (b = r->body.buf; b != NULL; b = b->next) {
RC(nxt_app_msg_write_raw(task, wmsg, b->mem.pos, size = nxt_buf_mem_used_size(&b->mem);
nxt_buf_mem_used_size(&b->mem))); pos = b->mem.pos;
while (size > 0) {
if (buf == NULL) {
free_size = nxt_min(size, PORT_MMAP_DATA_SIZE);
buf = nxt_port_mmap_get_buf(task, port, free_size);
if (nxt_slow_path(buf == NULL)) {
while (out != NULL) {
buf = out->next;
out->completion_handler(task, out, out->parent);
out = buf;
}
return NULL;
} }
#undef NXT_WRITE *tail = buf;
#undef RC tail = &buf->next;
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)
{
nxt_int_t rc;
nxt_buf_t *b;
nxt_http_field_t *field;
nxt_app_request_header_t *h;
static const nxt_str_t prefix = nxt_string("HTTP_");
static const nxt_str_t eof = nxt_null_string;
h = &r->header;
#define RC(S) \
do { \
rc = (S); \
if (nxt_slow_path(rc != NXT_OK)) { \
goto fail; \
} \
} while(0)
#define NXT_WRITE(N) \
RC(nxt_app_msg_write_str(task, wmsg, N))
/* TODO error handle, async mmap buffer assignment */
NXT_WRITE(&h->method);
NXT_WRITE(&h->target);
if (h->path.start == h->target.start) {
NXT_WRITE(&eof);
} else { } else {
NXT_WRITE(&h->path); free_size = nxt_buf_mem_free_size(&buf->mem);
if (free_size < size
&& nxt_port_mmap_increase_buf(task, buf, size, 1)
== NXT_OK)
{
free_size = nxt_buf_mem_free_size(&buf->mem);
}
} }
if (h->query.start != NULL) { if (free_size > 0) {
RC(nxt_app_msg_write_size(task, wmsg, copy_size = nxt_min(free_size, size);
h->query.start - h->target.start + 1));
} else { buf->mem.free = nxt_cpymem(buf->mem.free, pos, copy_size);
RC(nxt_app_msg_write_size(task, wmsg, 0));
size -= copy_size;
pos += copy_size;
if (size == 0) {
break;
}
} }
NXT_WRITE(&h->version); buf = NULL;
}
// PHP_SELF
// SCRIPT_NAME
// SCRIPT_FILENAME
// DOCUMENT_ROOT
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) { return out;
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)
{
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)
{
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);
}
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(&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)));
}
#undef NXT_WRITE
#undef RC
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)
{
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);
}
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(&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)));
}
#undef NXT_WRITE
#undef RC
return NXT_OK;
fail:
return NXT_ERROR;
} }

View File

@@ -81,11 +81,6 @@ typedef struct {
} nxt_joint_job_t; } 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 { struct nxt_app_s {
nxt_thread_mutex_t mutex; /* Protects ports queue. */ nxt_thread_mutex_t mutex; /* Protects ports queue. */
nxt_queue_t ports; /* of nxt_port_t.app_link */ nxt_queue_t ports; /* of nxt_port_t.app_link */
@@ -120,7 +115,6 @@ struct nxt_app_s {
nxt_queue_link_t link; nxt_queue_link_t link;
nxt_str_t conf; nxt_str_t conf;
nxt_app_prepare_msg_t prepare_msg;
nxt_atomic_t use_count; nxt_atomic_t use_count;
}; };

View File

@@ -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); 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); 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_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) \ #define nxt_runtime_process_each(rt, process) \
do { \ do { \

3630
src/nxt_unit.c Normal file

File diff suppressed because it is too large Load Diff

355
src/nxt_unit.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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_ */

View File

@@ -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, static void nxt_worker_process_sigquit_handler(nxt_task_t *task, void *obj,
void *data); void *data);
nxt_port_handlers_t nxt_app_process_port_handlers = { nxt_port_handlers_t nxt_app_process_port_handlers = {
.quit = nxt_app_quit_handler,
.new_port = nxt_port_new_port_handler, .new_port = nxt_port_new_port_handler,
.change_file = nxt_port_change_log_file_handler, .change_file = nxt_port_change_log_file_handler,
.mmap = nxt_port_mmap_handler, .mmap = nxt_port_mmap_handler,
.data = nxt_app_data_handler,
.remove_pid = nxt_port_remove_pid_handler, .remove_pid = nxt_port_remove_pid_handler,
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,9 @@
#include <ruby/nxt_ruby.h> #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_MAJOR 1
#define NXT_RUBY_RACK_API_VERSION_MINOR 3 #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_require_rack(VALUE arg);
static VALUE nxt_ruby_rack_parse_script(VALUE ctx); static VALUE nxt_ruby_rack_parse_script(VALUE ctx);
static VALUE nxt_ruby_rack_env_create(VALUE arg); 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, static void nxt_ruby_request_handler(nxt_unit_request_info_t *req);
nxt_app_wmsg_t *wmsg);
static VALUE nxt_ruby_rack_app_run(VALUE arg); static VALUE nxt_ruby_rack_app_run(VALUE arg);
static nxt_int_t nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx, static int nxt_ruby_read_request(VALUE hash_env);
VALUE hash_env); nxt_inline void nxt_ruby_add_sptr(VALUE hash_env,
nxt_inline nxt_int_t nxt_ruby_read_add_env(nxt_task_t *task, const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len);
nxt_app_rmsg_t *rmsg, VALUE hash_env, const char *name, nxt_str_t *str); 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); 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, static int nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status);
const u_char *data, size_t len, nxt_bool_t flush, nxt_bool_t last); static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg);
static nxt_int_t nxt_ruby_rack_result_headers(VALUE result); static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg);
static int nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg); static int nxt_ruby_rack_result_body(VALUE result);
static nxt_int_t nxt_ruby_head_send_part(const char *key, size_t key_size, static int nxt_ruby_rack_result_body_file_write(VALUE filepath);
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 VALUE nxt_ruby_rack_result_body_each(VALUE body); static VALUE nxt_ruby_rack_result_body_each(VALUE body);
static void nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, static void nxt_ruby_exception_log(nxt_task_t *task, uint32_t level,
const char *desc); const char *desc);
static void nxt_ruby_atexit(nxt_task_t *task); static void nxt_ruby_atexit(void);
static uint32_t compat[] = { static uint32_t compat[] = {
@@ -71,22 +71,22 @@ static VALUE nxt_ruby_io_input;
static VALUE nxt_ruby_io_error; static VALUE nxt_ruby_io_error;
static nxt_ruby_run_ctx_t nxt_ruby_run_ctx; 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), sizeof(compat),
compat, compat,
nxt_string("ruby"), nxt_string("ruby"),
ruby_version, ruby_version,
nxt_ruby_init, nxt_ruby_init,
nxt_ruby_run,
nxt_ruby_atexit,
}; };
static nxt_int_t static nxt_int_t
nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
{ {
int state; int state, rc;
VALUE dummy, res; VALUE dummy, res;
nxt_unit_ctx_t *unit_ctx;
nxt_unit_init_t ruby_unit_init;
nxt_ruby_rack_init_t rack_init; nxt_ruby_rack_init_t rack_init;
ruby_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_call);
rb_gc_register_address(&nxt_ruby_env); 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; return NXT_OK;
} }
@@ -319,42 +340,34 @@ nxt_ruby_rack_env_create(VALUE arg)
} }
static nxt_int_t static void
nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) nxt_ruby_request_handler(nxt_unit_request_info_t *req)
{ {
int state; int state;
VALUE res; VALUE res;
nxt_ruby_run_ctx.task = task; nxt_ruby_run_ctx.req = req;
nxt_ruby_run_ctx.rmsg = rmsg;
nxt_ruby_run_ctx.wmsg = wmsg;
res = rb_protect(nxt_ruby_rack_app_run, Qnil, &state); res = rb_protect(nxt_ruby_rack_app_run, Qnil, &state);
if (nxt_slow_path(state != 0)) { if (nxt_slow_path(res == Qnil || state != 0)) {
nxt_ruby_exception_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
"Failed to run ruby script"); "Failed to run ruby script");
return NXT_ERROR;
} }
if (nxt_slow_path(res == Qnil)) {
return NXT_ERROR;
}
return NXT_OK;
} }
static VALUE static VALUE
nxt_ruby_rack_app_run(VALUE arg) nxt_ruby_rack_app_run(VALUE arg)
{ {
int rc;
VALUE env, result; VALUE env, result;
nxt_int_t rc; nxt_int_t status;
env = rb_hash_dup(nxt_ruby_env); env = rb_hash_dup(nxt_ruby_env);
rc = nxt_ruby_read_request(&nxt_ruby_run_ctx, env); rc = nxt_ruby_read_request(env);
if (nxt_slow_path(rc != NXT_OK)) { if (nxt_slow_path(rc != NXT_UNIT_OK)) {
nxt_alert(nxt_ruby_run_ctx.task, nxt_unit_req_alert(nxt_ruby_run_ctx.req,
"Ruby: Failed to process incoming request"); "Ruby: Failed to process incoming request");
goto fail; goto fail;
@@ -362,39 +375,40 @@ nxt_ruby_rack_app_run(VALUE arg)
result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env); result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env);
if (nxt_slow_path(TYPE(result) != T_ARRAY)) { 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"); "Ruby: Invalid response format from application");
goto fail; goto fail;
} }
if (nxt_slow_path(RARRAY_LEN(result) != 3)) { 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. " "Ruby: Invalid response format from application. "
"Need 3 entries [Status, Headers, Body]"); "Need 3 entries [Status, Headers, Body]");
goto fail; goto fail;
} }
rc = nxt_ruby_rack_result_status(result); status = nxt_ruby_rack_result_status(result);
if (nxt_slow_path(rc != NXT_OK)) { if (nxt_slow_path(status < 0)) {
nxt_unit_req_error(nxt_ruby_run_ctx.req,
"Ruby: Invalid response status from application.");
goto fail; goto fail;
} }
rc = nxt_ruby_rack_result_headers(result); rc = nxt_ruby_rack_result_headers(result, status);
if (nxt_slow_path(rc != NXT_OK)) { if (nxt_slow_path(rc != NXT_UNIT_OK)) {
goto fail; goto fail;
} }
rc = nxt_ruby_rack_result_body(result); rc = nxt_ruby_rack_result_body(result);
if (nxt_slow_path(rc != NXT_OK)) { if (nxt_slow_path(rc != NXT_UNIT_OK)) {
goto fail; goto fail;
} }
rc = nxt_app_msg_flush(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, 1); nxt_unit_request_done(nxt_ruby_run_ctx.req, rc);
if (nxt_slow_path(rc != NXT_OK)) { nxt_ruby_run_ctx.req = NULL;
goto fail;
}
rb_hash_delete(env, rb_obj_id(env)); rb_hash_delete(env, rb_obj_id(env));
@@ -402,175 +416,103 @@ nxt_ruby_rack_app_run(VALUE arg)
fail: 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)); rb_hash_delete(env, rb_obj_id(env));
return Qnil; return Qnil;
} }
static nxt_int_t static int
nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx, VALUE hash_env) nxt_ruby_read_request(VALUE hash_env)
{ {
u_char *colon; char *host_start, *port_start;
size_t query_size; uint32_t i, host_length, port_length;
nxt_int_t rc; nxt_unit_field_t *f;
nxt_str_t str, value, path, target; nxt_unit_request_t *r;
nxt_str_t host, server_name, server_port;
nxt_task_t *task;
nxt_app_rmsg_t *rmsg;
static nxt_str_t def_host = nxt_string("localhost"); r = nxt_ruby_run_ctx.req->request;
static nxt_str_t def_port = nxt_string("80");
task = run_ctx->task; #define NL(S) (S), sizeof(S)-1
rmsg = run_ctx->rmsg;
rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_METHOD", &str); nxt_ruby_add_sptr(hash_env, NL("REQUEST_METHOD"), &r->method,
if (nxt_slow_path(rc != NXT_OK)) { r->method_length);
return NXT_ERROR; 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 (r->content_length_field != NXT_UNIT_NONE_FIELD) {
if (nxt_slow_path(rc != NXT_OK)) { f = r->fields + r->content_length_field;
return NXT_ERROR;
nxt_ruby_add_sptr(hash_env, NL("CONTENT_LENGTH"),
&f->value, f->value_length);
} }
rc = nxt_app_msg_read_str(task, rmsg, &path); if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
if (nxt_slow_path(rc != NXT_OK)) { f = r->fields + r->content_type_field;
return NXT_ERROR;
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 (r->host_field != NXT_UNIT_NONE_FIELD) {
if (nxt_slow_path(rc != NXT_OK)) { f = r->fields + r->host_field;
return NXT_ERROR;
}
if (path.start == NULL || path.length == 0) { host_start = nxt_unit_sptr_get(&f->value);
path = target; host_length = f->value_length;
}
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;
} else { } else {
server_port = def_port; host_start = NULL;
host_length = 0;
} }
rb_hash_aset(hash_env, rb_str_new2("SERVER_NAME"), nxt_unit_split_host(host_start, host_length, &host_start, &host_length,
rb_str_new((const char *) server_name.start, &port_start, &port_length);
(long) server_name.length));
rb_hash_aset(hash_env, rb_str_new2("SERVER_PORT"), nxt_ruby_add_str(hash_env, NL("SERVER_NAME"), host_start, host_length);
rb_str_new((const char *) server_port.start, nxt_ruby_add_str(hash_env, NL("SERVER_PORT"), port_start, port_length);
(long) server_port.length));
rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_TYPE", &str); #undef NL
if (nxt_slow_path(rc != NXT_OK)) {
return NXT_ERROR;
}
rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_LENGTH", &str); return NXT_UNIT_OK;
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;
} }
nxt_inline nxt_int_t nxt_inline void
nxt_ruby_read_add_env(nxt_task_t *task, nxt_app_rmsg_t *rmsg, VALUE hash_env, nxt_ruby_add_sptr(VALUE hash_env,
const char *name, nxt_str_t *str) 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); str = nxt_unit_sptr_get(sptr);
if (nxt_slow_path(rc != NXT_OK)) {
return rc;
}
if (str->start == NULL) { rb_hash_aset(hash_env, rb_str_new(name, name_len), rb_str_new(str, len));
rb_hash_aset(hash_env, rb_str_new2(name), 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) nxt_ruby_rack_result_status(VALUE result)
{ {
VALUE status; VALUE status;
u_char *p;
size_t len;
nxt_int_t rc;
u_char buf[3];
status = rb_ary_entry(result, 0); status = rb_ary_entry(result, 0);
if (TYPE(status) == T_FIXNUM) { if (TYPE(status) == T_FIXNUM) {
nxt_sprintf(buf, buf + 3, "%03d", FIX2INT(status)); return 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;
} }
rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, if (TYPE(status) == T_STRING) {
(u_char *) "Status: ", nxt_length("Status: "), 0, 0); return nxt_int_parse((u_char *) RSTRING_PTR(status),
if (nxt_slow_path(rc != NXT_OK)) { RSTRING_LEN(status));
return NXT_ERROR;
} }
rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, nxt_unit_req_error(nxt_ruby_run_ctx.req, "Ruby: Invalid response 'status' "
p, len, 0, 0); "format from application");
if (nxt_slow_path(rc != NXT_OK)) {
return NXT_ERROR;
}
rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, return -2;
(u_char *) "\r\n", nxt_length("\r\n"), 0, 0);
if (nxt_slow_path(rc != NXT_OK)) {
return NXT_ERROR;
}
return NXT_OK;
} }
nxt_inline nxt_int_t typedef struct {
nxt_ruby_write(nxt_task_t *task, nxt_app_wmsg_t *wmsg, int rc;
const u_char *data, size_t len, nxt_bool_t flush, nxt_bool_t last) uint32_t fields;
{ uint32_t size;
nxt_int_t rc; } nxt_ruby_headers_info_t;
rc = nxt_app_msg_write_raw(task, wmsg, data, len);
if (nxt_slow_path(rc != NXT_OK)) { static int
return rc; nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status)
}
if (flush || last) {
rc = nxt_app_msg_flush(task, wmsg, last);
}
return rc;
}
static nxt_int_t
nxt_ruby_rack_result_headers(VALUE result)
{ {
int rc;
VALUE headers; VALUE headers;
nxt_int_t rc; nxt_ruby_headers_info_t headers_info;
headers = rb_ary_entry(result, 1); headers = rb_ary_entry(result, 1);
if (nxt_slow_path(TYPE(headers) != T_HASH)) { if (nxt_slow_path(TYPE(headers) != T_HASH)) {
nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, nxt_unit_req_error(nxt_ruby_run_ctx.req,
"Ruby: Invalid response 'headers' format from application"); "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); headers_info.rc = NXT_UNIT_OK;
if (nxt_slow_path(rc != NXT_OK)) { headers_info.fields = 0;
return NXT_ERROR; 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, rc = nxt_unit_response_init(nxt_ruby_run_ctx.req, status,
(u_char *) "\r\n", nxt_length("\r\n"), 0, 0); headers_info.fields, headers_info.size);
if (nxt_slow_path(rc != NXT_OK)) { if (nxt_slow_path(rc != NXT_UNIT_OK)) {
return NXT_ERROR; return rc;
} }
return NXT_OK; rb_hash_foreach(headers, nxt_ruby_hash_add, (VALUE) (uintptr_t) &rc);
return rc;
} }
static int static int
nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg) nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg)
{ {
nxt_int_t rc, *rc_p;
const char *value, *value_end, *pos; const char *value, *value_end, *pos;
nxt_ruby_headers_info_t *headers_info;
rc_p = (nxt_int_t *) (uintptr_t) arg; headers_info = (void *) (uintptr_t) arg;
if (nxt_slow_path(TYPE(r_key) != T_STRING)) { 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"); "Ruby: Wrong header entry 'key' from application");
goto fail; goto fail;
} }
if (nxt_slow_path(TYPE(r_value) != T_STRING)) { 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"); "Ruby: Wrong header entry 'value' from application");
goto fail; goto fail;
@@ -708,9 +620,55 @@ nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg)
break; 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); value, pos - value);
if (nxt_slow_path(rc != NXT_OK)) { if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
goto fail; goto fail;
} }
@@ -719,59 +677,29 @@ nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg)
} }
if (value <= value_end) { 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); value, value_end - value);
if (nxt_slow_path(rc != NXT_OK)) { if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
goto fail; goto fail;
} }
} }
*rc_p = NXT_OK;
return ST_CONTINUE; return ST_CONTINUE;
fail: fail:
*rc_p = NXT_ERROR; *rc = NXT_UNIT_ERROR;
return ST_STOP; return ST_STOP;
} }
static nxt_int_t static int
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
nxt_ruby_rack_result_body(VALUE result) nxt_ruby_rack_result_body(VALUE result)
{ {
int rc;
VALUE fn, body; VALUE fn, body;
nxt_int_t rc;
body = rb_ary_entry(result, 2); 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); fn = rb_funcall(body, rb_intern("to_path"), 0);
if (nxt_slow_path(TYPE(fn) != T_STRING)) { if (nxt_slow_path(TYPE(fn) != T_STRING)) {
nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, nxt_unit_req_error(nxt_ruby_run_ctx.req,
"Ruby: Failed to get 'body' file path from application"); "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); rc = nxt_ruby_rack_result_body_file_write(fn);
if (nxt_slow_path(rc != NXT_OK)) { if (nxt_slow_path(rc != NXT_UNIT_OK)) {
return NXT_ERROR; return rc;
} }
} else if (rb_respond_to(body, rb_intern("each"))) { } else if (rb_respond_to(body, rb_intern("each"))) {
rb_iterate(rb_each, body, nxt_ruby_rack_result_body_each, 0); rb_iterate(rb_each, body, nxt_ruby_rack_result_body_each, 0);
} else { } else {
nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, nxt_unit_req_error(nxt_ruby_run_ctx.req,
"Ruby: Invalid response 'body' format from application"); "Ruby: Invalid response 'body' format "
"from application");
return NXT_ERROR; return NXT_UNIT_ERROR;
} }
if (rb_respond_to(body, rb_intern("close"))) { if (rb_respond_to(body, rb_intern("close"))) {
rb_funcall(body, rb_intern("close"), 0); 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) nxt_ruby_rack_result_body_file_write(VALUE filepath)
{ {
size_t len; int fd, rc;
ssize_t n; struct stat finfo;
nxt_off_t rest; nxt_ruby_rack_file_t ruby_file;
nxt_int_t rc; nxt_unit_read_info_t read_info;
nxt_file_t file;
nxt_file_info_t finfo;
u_char buf[8192];
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); return NXT_UNIT_ERROR;
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;
} }
rc = nxt_file_info(&file, &finfo); rc = fstat(fd, &finfo);
if (nxt_slow_path(rc != NXT_OK)) { if (nxt_slow_path(rc == -1)) {
nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, nxt_unit_req_error(nxt_ruby_run_ctx.req,
"Ruby: Failed to get 'body' file information: %s", "Ruby: Content file fstat(\"%s\") failed: %s (%d)",
(const char *) file.name); 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) { read_info.read = nxt_ruby_rack_file_read;
len = nxt_min(rest, (nxt_off_t) sizeof(buf)); 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); rc = nxt_unit_response_write_cb(nxt_ruby_run_ctx.req, &read_info);
if (nxt_slow_path(n != (ssize_t) len)) { if (nxt_slow_path(rc != NXT_UNIT_OK)) {
nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, nxt_unit_req_error(nxt_ruby_run_ctx.req,
"Ruby: Failed to read 'body' file"); "Ruby: Failed to write content file.");
goto fail;
} }
rest -= len; close(fd);
rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, return rc;
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;
} }
static VALUE static VALUE
nxt_ruby_rack_result_body_each(VALUE body) nxt_ruby_rack_result_body_each(VALUE body)
{ {
nxt_int_t rc; int rc;
if (TYPE(body) != T_STRING) { if (TYPE(body) != T_STRING) {
return Qnil; return Qnil;
} }
rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, rc = nxt_unit_response_write(nxt_ruby_run_ctx.req, RSTRING_PTR(body),
(u_char *) RSTRING_PTR(body), RSTRING_LEN(body)); RSTRING_LEN(body));
if (nxt_slow_path(rc != NXT_OK)) { if (nxt_slow_path(rc != NXT_UNIT_OK)) {
nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, nxt_unit_req_error(nxt_ruby_run_ctx.req,
"Ruby: Failed to write 'body' from application"); "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; int i;
VALUE err, ary, eclass, msg; VALUE err, ary, eclass, msg;
if (task != NULL) {
nxt_log(task, level, "Ruby: %s", desc); nxt_log(task, level, "Ruby: %s", desc);
err = rb_errinfo(); } else {
ary = rb_funcall(err, rb_intern("backtrace"), 0); 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; return;
} }
eclass = rb_class_name(rb_class_of(err)); eclass = rb_class_name(rb_class_of(err));
msg = rb_funcall(err, rb_intern("message"), 0); msg = rb_funcall(err, rb_intern("message"), 0);
if (task != NULL) {
nxt_log(task, level, "Ruby: %s: %s (%s)", nxt_log(task, level, "Ruby: %s: %s (%s)",
RSTRING_PTR(RARRAY_PTR(ary)[0]), RSTRING_PTR(RARRAY_PTR(ary)[0]),
RSTRING_PTR(msg), RSTRING_PTR(eclass)); 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++) { for (i = 1; i < RARRAY_LEN(ary); i++) {
if (task != NULL) {
nxt_log(task, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i])); 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 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_input);
rb_gc_unregister_address(&nxt_ruby_io_error); rb_gc_unregister_address(&nxt_ruby_io_error);

View File

@@ -17,14 +17,12 @@
#include <nxt_router.h> #include <nxt_router.h>
#include <nxt_runtime.h> #include <nxt_runtime.h>
#include <nxt_application.h> #include <nxt_application.h>
#include <nxt_unit_typedefs.h>
typedef struct { typedef struct {
nxt_task_t *task; nxt_unit_ctx_t *unit_ctx;
nxt_app_rmsg_t *rmsg; nxt_unit_request_info_t *req;
nxt_app_wmsg_t *wmsg;
size_t body_preread_size;
} nxt_ruby_run_ctx_t; } nxt_ruby_run_ctx_t;

View File

@@ -5,12 +5,12 @@
*/ */
#include <ruby/nxt_ruby.h> #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_new(VALUE class, VALUE wrap);
static VALUE nxt_ruby_stream_io_initialize(int argc, VALUE *argv, VALUE self); 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 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_each(VALUE obj, VALUE args);
static VALUE nxt_ruby_stream_io_read(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); 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) nxt_ruby_stream_io_gets(VALUE obj, VALUE args)
{ {
VALUE buf; VALUE buf;
char *p;
size_t size, b_size;
nxt_unit_buf_t *b;
nxt_ruby_run_ctx_t *run_ctx; nxt_ruby_run_ctx_t *run_ctx;
nxt_unit_request_info_t *req;
Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx); 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; 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) { if (buf == Qnil) {
return Qnil; return Qnil;
} }
run_ctx->body_preread_size -= nxt_ruby_stream_io_read_line(run_ctx->rmsg, size = nxt_unit_request_read(req, RSTRING_PTR(buf), size);
buf);
rb_str_set_len(buf, size);
return buf; return buf;
} }
static size_t
nxt_ruby_stream_io_read_line(nxt_app_rmsg_t *rmsg, VALUE str)
{
size_t len, size;
u_char *p;
nxt_buf_t *buf;
len = 0;
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 static VALUE
nxt_ruby_stream_io_each(VALUE obj, VALUE args) nxt_ruby_stream_io_each(VALUE obj, VALUE args)
{ {
@@ -173,12 +157,11 @@ nxt_ruby_stream_io_read(VALUE obj, VALUE args)
{ {
VALUE buf; VALUE buf;
long copy_size, u_size; long copy_size, u_size;
size_t len;
nxt_ruby_run_ctx_t *run_ctx; nxt_ruby_run_ctx_t *run_ctx;
Data_Get_Struct(obj, 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) { if (RARRAY_LEN(args) > 0 && TYPE(RARRAY_PTR(args)[0]) == T_FIXNUM) {
u_size = NUM2LONG(RARRAY_PTR(args)[0]); u_size = NUM2LONG(RARRAY_PTR(args)[0]);
@@ -202,8 +185,8 @@ nxt_ruby_stream_io_read(VALUE obj, VALUE args)
return Qnil; return Qnil;
} }
len = nxt_app_msg_read_raw(run_ctx->task, run_ctx->rmsg, copy_size = nxt_unit_request_read(run_ctx->req, RSTRING_PTR(buf),
RSTRING_PTR(buf), (size_t) copy_size); copy_size);
if (RARRAY_LEN(args) > 1 && TYPE(RARRAY_PTR(args)[1]) == T_STRING) { 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_cat(RARRAY_PTR(args)[1], RSTRING_PTR(buf), copy_size);
} }
rb_str_set_len(buf, (long) len); rb_str_set_len(buf, copy_size);
run_ctx->body_preread_size -= len;
return buf; 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", nxt_unit_req_error(run_ctx->req, "Ruby: %s", RSTRING_PTR(val));
RSTRING_PTR(val));
return RSTRING_LEN(val); return RSTRING_LEN(val);
} }

View 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;
}

View File

@@ -2,7 +2,8 @@ import atexit
def application(environ, start_response): def application(environ, start_response):
def at_exit(): 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) atexit.register(at_exit)

View File

@@ -8,4 +8,5 @@ class application:
yield b'' yield b''
def close(self): def close(self):
self.environ['wsgi.errors'].write('Close called.') self.environ['wsgi.errors'].write('Close called.\n')
self.environ['wsgi.errors'].flush()

View File

@@ -8,4 +8,5 @@ class application:
yield b'' yield b''
def close(self): def close(self):
self.environ['wsgi.errors'].write('Close called.') self.environ['wsgi.errors'].write('Close called.\n')
self.environ['wsgi.errors'].flush()

View File

@@ -2,6 +2,7 @@ import os
import re import re
import sys import sys
import json import json
import mmap
import time import time
import shutil import shutil
import socket import socket
@@ -363,9 +364,14 @@ class TestUnitApplicationProto(TestUnitControl):
return time.mktime(time.strptime(date, '%a, %d %b %Y %H:%M:%S %Z')) return time.mktime(time.strptime(date, '%a, %d %b %Y %H:%M:%S %Z'))
def search_in_log(self, pattern): 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()) 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): class TestUnitApplicationPython(TestUnitApplicationProto):
def load(self, script, name=None): def load(self, script, name=None):
if name is None: if name is None: