aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZhidao HONG <z.hong@f5.com>2025-04-22 01:04:31 +0800
committerhongzhidao <hongzhidao@gmail.com>2025-05-09 01:14:21 +0800
commit401d622aa5fb9c757fde3f6e5cef0731b2b7726a (patch)
tree15e89a187f283a5fc299c6a8b3dd9ce883aac663
parent37b4c07719e12363f33de8a591a7a61815122c91 (diff)
downloadnjs-401d622aa5fb9c757fde3f6e5cef0731b2b7726a.tar.gz
njs-401d622aa5fb9c757fde3f6e5cef0731b2b7726a.zip
Fetch: separated ngx_js_http_* from ngx_js_fetch_*.
-rw-r--r--nginx/config2
-rw-r--r--nginx/ngx_js_fetch.c1748
-rw-r--r--nginx/ngx_js_http.c1531
-rw-r--r--nginx/ngx_js_http.h160
4 files changed, 1779 insertions, 1662 deletions
diff --git a/nginx/config b/nginx/config
index 03ec03d0..b994f97f 100644
--- a/nginx/config
+++ b/nginx/config
@@ -6,9 +6,11 @@ NJS_ZLIB=${NJS_ZLIB:-YES}
NJS_QUICKJS=${NJS_QUICKJS:-YES}
NJS_DEPS="$ngx_addon_dir/ngx_js.h \
+ $ngx_addon_dir/ngx_js_http.h \
$ngx_addon_dir/ngx_js_fetch.h \
$ngx_addon_dir/ngx_js_shared_dict.h"
NJS_SRCS="$ngx_addon_dir/ngx_js.c \
+ $ngx_addon_dir/ngx_js_http.c \
$ngx_addon_dir/ngx_js_fetch.c \
$ngx_addon_dir/ngx_js_regex.c \
$ngx_addon_dir/ngx_js_shared_dict.c"
diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c
index 4902fe4f..802353d6 100644
--- a/nginx/ngx_js_fetch.c
+++ b/nginx/ngx_js_fetch.c
@@ -12,9 +12,7 @@
#include <ngx_event.h>
#include <ngx_event_connect.h>
#include "ngx_js.h"
-
-
-typedef struct ngx_js_http_s ngx_js_http_t;
+#include "ngx_js_http.h"
typedef struct {
@@ -24,136 +22,16 @@ typedef struct {
typedef struct {
- ngx_uint_t state;
- ngx_uint_t code;
- u_char *status_text;
- u_char *status_text_end;
- ngx_uint_t count;
- ngx_flag_t chunked;
- off_t content_length_n;
-
- u_char *header_name_start;
- u_char *header_name_end;
- u_char *header_start;
- u_char *header_end;
-} ngx_js_http_parse_t;
-
-
-typedef struct {
- u_char *pos;
- uint64_t chunk_size;
- uint8_t state;
- uint8_t last;
-} ngx_js_http_chunk_parse_t;
-
-
-typedef struct ngx_js_tb_elt_s ngx_js_tb_elt_t;
-
-struct ngx_js_tb_elt_s {
- ngx_uint_t hash;
- ngx_str_t key;
- ngx_str_t value;
- ngx_js_tb_elt_t *next;
-};
-
-
-typedef struct {
- enum {
- GUARD_NONE = 0,
- GUARD_REQUEST,
- GUARD_IMMUTABLE,
- GUARD_RESPONSE,
- } guard;
- ngx_list_t header_list;
- ngx_js_tb_elt_t *content_type;
-} ngx_js_headers_t;
-
-
-typedef struct {
- enum {
- CACHE_MODE_DEFAULT = 0,
- CACHE_MODE_NO_STORE,
- CACHE_MODE_RELOAD,
- CACHE_MODE_NO_CACHE,
- CACHE_MODE_FORCE_CACHE,
- CACHE_MODE_ONLY_IF_CACHED,
- } cache_mode;
- enum {
- CREDENTIALS_SAME_ORIGIN = 0,
- CREDENTIALS_INCLUDE,
- CREDENTIALS_OMIT,
- } credentials;
- enum {
- MODE_NO_CORS = 0,
- MODE_SAME_ORIGIN,
- MODE_CORS,
- MODE_NAVIGATE,
- MODE_WEBSOCKET,
- } mode;
- njs_str_t url;
- njs_str_t method;
- u_char m[8];
- uint8_t body_used;
- njs_str_t body;
- ngx_js_headers_t headers;
- njs_opaque_value_t header_value;
-} ngx_js_request_t;
-
-
-typedef struct {
- njs_str_t url;
- ngx_int_t code;
- njs_str_t status_text;
- uint8_t body_used;
- njs_chb_t chain;
- ngx_js_headers_t headers;
- njs_opaque_value_t header_value;
-} ngx_js_response_t;
-
-
-struct ngx_js_http_s {
- ngx_log_t *log;
- ngx_pool_t *pool;
+ ngx_js_http_t http;
njs_vm_t *vm;
ngx_js_event_t *event;
- ngx_resolver_ctx_t *ctx;
- ngx_addr_t addr;
- ngx_addr_t *addrs;
- ngx_uint_t naddrs;
- ngx_uint_t naddr;
- in_port_t port;
-
- ngx_peer_connection_t peer;
- ngx_msec_t timeout;
-
- ngx_int_t buffer_size;
- ngx_int_t max_response_body_size;
-
- unsigned header_only;
-
-#if (NGX_SSL)
- ngx_str_t tls_name;
- ngx_ssl_t *ssl;
- njs_bool_t ssl_verify;
-#endif
-
- ngx_buf_t *buffer;
- ngx_buf_t *chunk;
- njs_chb_t chain;
-
- ngx_js_response_t response;
njs_opaque_value_t response_value;
njs_opaque_value_t promise;
njs_opaque_value_t promise_callbacks[2];
-
- uint8_t done;
- ngx_js_http_parse_t http_parse;
- ngx_js_http_chunk_parse_t http_chunk_parse;
- ngx_int_t (*process)(ngx_js_http_t *http);
-};
+} ngx_js_fetch_t;
static njs_int_t ngx_js_method_process(njs_vm_t *vm, ngx_js_request_t *r);
@@ -161,44 +39,29 @@ static njs_int_t ngx_js_headers_inherit(njs_vm_t *vm, ngx_js_headers_t *headers,
ngx_js_headers_t *orig);
static njs_int_t ngx_js_headers_fill(njs_vm_t *vm, ngx_js_headers_t *headers,
njs_value_t *init);
-static ngx_js_http_t *ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool,
+static ngx_js_fetch_t *ngx_js_fetch_alloc(njs_vm_t *vm, ngx_pool_t *pool,
ngx_log_t *log);
-static void ngx_js_http_resolve_done(ngx_js_http_t *http);
-static void ngx_js_http_close_peer(ngx_js_http_t *http);
-static void ngx_js_http_destructor(ngx_js_event_t *event);
-static ngx_resolver_ctx_t *ngx_js_http_resolve(ngx_js_http_t *http,
- ngx_resolver_t *r, ngx_str_t *host, in_port_t port, ngx_msec_t timeout);
-static void ngx_js_http_resolve_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_js_fetch_error(ngx_js_http_t *http, const char *err);
+static void ngx_js_fetch_destructor(ngx_js_event_t *event);
static njs_int_t ngx_js_fetch_promissified_result(njs_vm_t *vm,
njs_value_t *result, njs_int_t rc, njs_value_t *retval);
-static void ngx_js_http_fetch_done(ngx_js_http_t *http,
- njs_opaque_value_t *retval, njs_int_t rc);
+static void ngx_js_fetch_done(ngx_js_fetch_t *fetch, njs_opaque_value_t *retval,
+ njs_int_t rc);
static njs_int_t ngx_js_http_promise_trampoline(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t unused,
njs_value_t *retval);
-static void ngx_js_http_connect(ngx_js_http_t *http);
-static void ngx_js_http_next(ngx_js_http_t *http);
-static void ngx_js_http_write_handler(ngx_event_t *wev);
-static void ngx_js_http_read_handler(ngx_event_t *rev);
static njs_int_t ngx_js_request_constructor(njs_vm_t *vm,
ngx_js_request_t *request, ngx_url_t *u, njs_external_ptr_t external,
njs_value_t *args, njs_uint_t nargs);
+static ngx_int_t ngx_js_fetch_append_headers(ngx_js_http_t *http,
+ ngx_js_headers_t *headers, u_char *name, size_t len, u_char *value,
+ size_t vlen);
+static void ngx_js_fetch_process_done(ngx_js_http_t *http);
static njs_int_t ngx_js_headers_append(njs_vm_t *vm, ngx_js_headers_t *headers,
u_char *name, size_t len, u_char *value, size_t vlen);
-static ngx_int_t ngx_js_http_process_status_line(ngx_js_http_t *http);
-static ngx_int_t ngx_js_http_process_headers(ngx_js_http_t *http);
-static ngx_int_t ngx_js_http_process_body(ngx_js_http_t *http);
-static ngx_int_t ngx_js_http_parse_status_line(ngx_js_http_parse_t *hp,
- ngx_buf_t *b);
-static ngx_int_t ngx_js_http_parse_header_line(ngx_js_http_parse_t *hp,
- ngx_buf_t *b);
-static ngx_int_t ngx_js_http_parse_chunked(ngx_js_http_chunk_parse_t *hcp,
- ngx_buf_t *b, njs_chb_t *chain);
-static void ngx_js_http_dummy_handler(ngx_event_t *ev);
-
static njs_int_t ngx_headers_js_ext_append(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
static njs_int_t ngx_headers_js_ext_delete(njs_vm_t *vm, njs_value_t *args,
@@ -254,15 +117,6 @@ static njs_int_t ngx_response_js_ext_type(njs_vm_t *vm,
static njs_int_t ngx_response_js_ext_body(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
-#if (NGX_SSL)
-static void ngx_js_http_ssl_init_connection(ngx_js_http_t *http);
-static void ngx_js_http_ssl_handshake_handler(ngx_connection_t *c);
-static void ngx_js_http_ssl_handshake(ngx_js_http_t *http);
-static njs_int_t ngx_js_http_ssl_name(ngx_js_http_t *http);
-#endif
-
-static void ngx_js_http_trim(u_char **value, size_t *len,
- njs_bool_t trim_c0_control_or_space);
static njs_int_t ngx_fetch_flag(njs_vm_t *vm, const ngx_js_entry_t *entries,
njs_int_t value, njs_value_t *retval);
static njs_int_t ngx_fetch_flag_set(njs_vm_t *vm, const ngx_js_entry_t *entries,
@@ -663,6 +517,7 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
ngx_pool_t *pool;
njs_value_t *init, *value;
ngx_js_http_t *http;
+ ngx_js_fetch_t *fetch;
ngx_list_part_t *part;
ngx_js_tb_elt_t *h;
ngx_js_request_t request;
@@ -681,11 +536,13 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
c = ngx_external_connection(vm, external);
pool = ngx_external_pool(vm, external);
- http = ngx_js_http_alloc(vm, pool, c->log);
- if (http == NULL) {
+ fetch = ngx_js_fetch_alloc(vm, pool, c->log);
+ if (fetch == NULL) {
return NJS_ERROR;
}
+ http = &fetch->http;
+
ret = ngx_js_request_constructor(vm, &request, &u, external, args, nargs);
if (ret != NJS_OK) {
goto fail;
@@ -734,6 +591,7 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
http->header_only = njs_strstr_eq(&request.method, &njs_str_value("HEAD"));
NJS_CHB_MP_INIT(&http->chain, njs_vm_memory_pool(vm));
+ NJS_CHB_MP_INIT(&http->response.chain, njs_vm_memory_pool(vm));
njs_chb_append(&http->chain, request.method.start, request.method.length);
njs_chb_append_literal(&http->chain, " ");
@@ -848,7 +706,7 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
goto fail;
}
- njs_value_assign(retval, njs_value_arg(&http->promise));
+ njs_value_assign(retval, njs_value_arg(&fetch->promise));
return NJS_OK;
}
@@ -859,18 +717,17 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
ngx_js_http_connect(http);
- njs_value_assign(retval, njs_value_arg(&http->promise));
+ njs_value_assign(retval, njs_value_arg(&fetch->promise));
return NJS_OK;
fail:
-
njs_vm_exception_get(vm, njs_value_arg(&lvalue));
- ngx_js_http_fetch_done(http, &lvalue, NJS_ERROR);
+ ngx_js_fetch_done(fetch, &lvalue, NJS_ERROR);
- njs_value_assign(retval, njs_value_arg(&http->promise));
+ njs_value_assign(retval, njs_value_arg(&fetch->promise));
return NJS_OK;
}
@@ -1249,30 +1106,36 @@ ngx_js_headers_fill(njs_vm_t *vm, ngx_js_headers_t *headers, njs_value_t *init)
}
-static ngx_js_http_t *
-ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log)
+static ngx_js_fetch_t *
+ngx_js_fetch_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log)
{
njs_int_t ret;
ngx_js_ctx_t *ctx;
ngx_js_http_t *http;
+ ngx_js_fetch_t *fetch;
ngx_js_event_t *event;
njs_function_t *callback;
- http = ngx_pcalloc(pool, sizeof(ngx_js_http_t));
- if (http == NULL) {
+ fetch = ngx_pcalloc(pool, sizeof(ngx_js_fetch_t));
+ if (fetch == NULL) {
goto failed;
}
+ http = &fetch->http;
+
http->pool = pool;
http->log = log;
- http->vm = vm;
http->timeout = 10000;
http->http_parse.content_length_n = -1;
- ret = njs_vm_promise_create(vm, njs_value_arg(&http->promise),
- njs_value_arg(&http->promise_callbacks));
+ http->append_headers = ngx_js_fetch_append_headers;
+ http->ready_handler = ngx_js_fetch_process_done;
+ http->error_handler = ngx_js_fetch_error;
+
+ ret = njs_vm_promise_create(vm, njs_value_arg(&fetch->promise),
+ njs_value_arg(&fetch->promise_callbacks));
if (ret != NJS_OK) {
goto failed;
}
@@ -1291,17 +1154,18 @@ ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log)
event->ctx = vm;
njs_value_function_set(njs_value_arg(&event->function), callback);
- event->destructor = ngx_js_http_destructor;
+ event->destructor = ngx_js_fetch_destructor;
event->fd = ctx->event_id++;
- event->data = http;
+ event->data = fetch;
ngx_js_add_event(ctx, event);
- http->event = event;
+ fetch->vm = vm;
+ fetch->event = event;
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, "js fetch alloc:%p", http);
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, "js fetch alloc:%p", fetch);
- return http;
+ return fetch;
failed:
@@ -1312,194 +1176,31 @@ failed:
static void
-ngx_js_http_error(ngx_js_http_t *http, const char *fmt, ...)
-{
- u_char *p, *end;
- va_list args;
- u_char err[NGX_MAX_ERROR_STR];
-
- end = err + NGX_MAX_ERROR_STR - 1;
-
- va_start(args, fmt);
- p = njs_vsprintf(err, end, fmt, args);
- *p = '\0';
- va_end(args);
-
- njs_vm_error(http->vm, (const char *) err);
- njs_vm_exception_get(http->vm, njs_value_arg(&http->response_value));
- ngx_js_http_fetch_done(http, &http->response_value, NJS_ERROR);
-}
-
-
-static ngx_resolver_ctx_t *
-ngx_js_http_resolve(ngx_js_http_t *http, ngx_resolver_t *r, ngx_str_t *host,
- in_port_t port, ngx_msec_t timeout)
-{
- ngx_int_t ret;
- ngx_resolver_ctx_t *ctx;
-
- ctx = ngx_resolve_start(r, NULL);
- if (ctx == NULL) {
- return NULL;
- }
-
- if (ctx == NGX_NO_RESOLVER) {
- return ctx;
- }
-
- http->ctx = ctx;
- http->port = port;
-
- ctx->name = *host;
- ctx->handler = ngx_js_http_resolve_handler;
- ctx->data = http;
- ctx->timeout = timeout;
-
- ret = ngx_resolve_name(ctx);
- if (ret != NGX_OK) {
- http->ctx = NULL;
- return NULL;
- }
-
- return ctx;
-}
-
-
-static void
-ngx_js_http_resolve_handler(ngx_resolver_ctx_t *ctx)
-{
- u_char *p;
- size_t len;
- socklen_t socklen;
- ngx_uint_t i;
- ngx_js_http_t *http;
- struct sockaddr *sockaddr;
-
- http = ctx->data;
-
- if (ctx->state) {
- ngx_js_http_error(http, "\"%V\" could not be resolved (%i: %s)",
- &ctx->name, ctx->state,
- ngx_resolver_strerror(ctx->state));
- return;
- }
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0,
- "http fetch resolved: \"%V\"", &ctx->name);
-
-#if (NGX_DEBUG)
- {
- u_char text[NGX_SOCKADDR_STRLEN];
- ngx_str_t addr;
- ngx_uint_t i;
-
- addr.data = text;
-
- for (i = 0; i < ctx->naddrs; i++) {
- addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,
- text, NGX_SOCKADDR_STRLEN, 0);
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0,
- "name was resolved to \"%V\"", &addr);
- }
- }
-#endif
-
- http->naddrs = ctx->naddrs;
- http->addrs = ngx_pcalloc(http->pool, http->naddrs * sizeof(ngx_addr_t));
-
- if (http->addrs == NULL) {
- goto failed;
- }
-
- for (i = 0; i < ctx->naddrs; i++) {
- socklen = ctx->addrs[i].socklen;
-
- sockaddr = ngx_palloc(http->pool, socklen);
- if (sockaddr == NULL) {
- goto failed;
- }
-
- ngx_memcpy(sockaddr, ctx->addrs[i].sockaddr, socklen);
- ngx_inet_set_port(sockaddr, http->port);
-
- http->addrs[i].sockaddr = sockaddr;
- http->addrs[i].socklen = socklen;
-
- p = ngx_pnalloc(http->pool, NGX_SOCKADDR_STRLEN);
- if (p == NULL) {
- goto failed;
- }
-
- len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
- http->addrs[i].name.len = len;
- http->addrs[i].name.data = p;
- }
-
- ngx_js_http_resolve_done(http);
-
- ngx_js_http_connect(http);
-
- return;
-
-failed:
-
- ngx_js_http_error(http, "memory error");
-}
-
-
-static void
-ngx_js_http_close_connection(ngx_connection_t *c)
+ngx_js_fetch_error(ngx_js_http_t *http, const char *err)
{
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "js fetch close connection: %d", c->fd);
-
-#if (NGX_SSL)
- if (c->ssl) {
- c->ssl->no_wait_shutdown = 1;
-
- if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
- c->ssl->handler = ngx_js_http_close_connection;
- return;
- }
- }
-#endif
-
- c->destroyed = 1;
-
- ngx_close_connection(c);
-}
+ ngx_js_fetch_t *fetch;
+ fetch = (ngx_js_fetch_t *) http;
-static void
-ngx_js_http_resolve_done(ngx_js_http_t *http)
-{
- if (http->ctx != NULL) {
- ngx_resolve_name_done(http->ctx);
- http->ctx = NULL;
- }
-}
+ njs_vm_error(fetch->vm, err);
+ njs_vm_exception_get(fetch->vm, njs_value_arg(&fetch->response_value));
-static void
-ngx_js_http_close_peer(ngx_js_http_t *http)
-{
- if (http->peer.connection != NULL) {
- ngx_js_http_close_connection(http->peer.connection);
- http->peer.connection = NULL;
- }
+ ngx_js_fetch_done(fetch, &fetch->response_value, NJS_ERROR);
}
static void
-ngx_js_http_destructor(ngx_js_event_t *event)
+ngx_js_fetch_destructor(ngx_js_event_t *event)
{
- ngx_js_http_t *http;
+ ngx_js_http_t *http;
+ ngx_js_fetch_t *fetch;
- http = event->data;
+ fetch = event->data;
+ http = &fetch->http;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch destructor:%p",
- http);
+ fetch);
ngx_js_http_resolve_done(http);
ngx_js_http_close_peer(http);
@@ -1552,26 +1253,29 @@ error:
static void
-ngx_js_http_fetch_done(ngx_js_http_t *http, njs_opaque_value_t *retval,
+ngx_js_fetch_done(ngx_js_fetch_t *fetch, njs_opaque_value_t *retval,
njs_int_t rc)
{
njs_vm_t *vm;
ngx_js_ctx_t *ctx;
+ ngx_js_http_t *http;
ngx_js_event_t *event;
njs_opaque_value_t arguments[2], *action;
+ http = &fetch->http;
+
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0,
- "js fetch done http:%p rc:%i", http, (ngx_int_t) rc);
+ "js fetch done fetch:%p rc:%i", fetch, (ngx_int_t) rc);
ngx_js_http_close_peer(http);
- if (http->event != NULL) {
- action = &http->promise_callbacks[(rc != NJS_OK)];
+ if (fetch->event != NULL) {
+ action = &fetch->promise_callbacks[(rc != NJS_OK)];
njs_value_assign(&arguments[0], action);
njs_value_assign(&arguments[1], retval);
- vm = http->vm;
- event = http->event;
+ vm = fetch->vm;
+ event = fetch->event;
rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)),
&arguments[0], 2);
@@ -1600,394 +1304,6 @@ ngx_js_http_promise_trampoline(njs_vm_t *vm, njs_value_t *args,
}
-static void
-ngx_js_http_connect(ngx_js_http_t *http)
-{
- ngx_int_t rc;
- ngx_addr_t *addr;
-
- addr = &http->addrs[http->naddr];
-
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0,
- "js fetch connect %ui/%ui", http->naddr, http->naddrs);
-
- http->peer.sockaddr = addr->sockaddr;
- http->peer.socklen = addr->socklen;
- http->peer.name = &addr->name;
- http->peer.get = ngx_event_get_peer;
- http->peer.log = http->log;
- http->peer.log_error = NGX_ERROR_ERR;
-
- rc = ngx_event_connect_peer(&http->peer);
-
- if (rc == NGX_ERROR) {
- ngx_js_http_error(http, "connect failed");
- return;
- }
-
- if (rc == NGX_BUSY || rc == NGX_DECLINED) {
- ngx_js_http_next(http);
- return;
- }
-
- http->peer.connection->data = http;
- http->peer.connection->pool = http->pool;
-
- http->peer.connection->write->handler = ngx_js_http_write_handler;
- http->peer.connection->read->handler = ngx_js_http_read_handler;
-
- http->process = ngx_js_http_process_status_line;
-
- ngx_add_timer(http->peer.connection->read, http->timeout);
- ngx_add_timer(http->peer.connection->write, http->timeout);
-
-#if (NGX_SSL)
- if (http->ssl != NULL && http->peer.connection->ssl == NULL) {
- ngx_js_http_ssl_init_connection(http);
- return;
- }
-#endif
-
- if (rc == NGX_OK) {
- ngx_js_http_write_handler(http->peer.connection->write);
- }
-}
-
-
-#if (NGX_SSL)
-
-static void
-ngx_js_http_ssl_init_connection(ngx_js_http_t *http)
-{
- ngx_int_t rc;
- ngx_connection_t *c;
-
- c = http->peer.connection;
-
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0,
- "js fetch secure connect %ui/%ui", http->naddr,
- http->naddrs);
-
- if (ngx_ssl_create_connection(http->ssl, c, NGX_SSL_BUFFER|NGX_SSL_CLIENT)
- != NGX_OK)
- {
- ngx_js_http_error(http, "failed to create ssl connection");
- return;
- }
-
- c->sendfile = 0;
-
- if (ngx_js_http_ssl_name(http) != NGX_OK) {
- ngx_js_http_error(http, "failed to create ssl connection");
- return;
- }
-
- c->log->action = "SSL handshaking to fetch target";
-
- rc = ngx_ssl_handshake(c);
-
- if (rc == NGX_AGAIN) {
- c->data = http;
- c->ssl->handler = ngx_js_http_ssl_handshake_handler;
- return;
- }
-
- ngx_js_http_ssl_handshake(http);
-}
-
-
-static void
-ngx_js_http_ssl_handshake_handler(ngx_connection_t *c)
-{
- ngx_js_http_t *http;
-
- http = c->data;
-
- http->peer.connection->write->handler = ngx_js_http_write_handler;
- http->peer.connection->read->handler = ngx_js_http_read_handler;
-
- ngx_js_http_ssl_handshake(http);
-}
-
-
-static void
-ngx_js_http_ssl_handshake(ngx_js_http_t *http)
-{
- long rc;
- ngx_connection_t *c;
-
- c = http->peer.connection;
-
- if (c->ssl->handshaked) {
- if (http->ssl_verify) {
- rc = SSL_get_verify_result(c->ssl->connection);
-
- if (rc != X509_V_OK) {
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "js fetch SSL certificate verify error: (%l:%s)",
- rc, X509_verify_cert_error_string(rc));
- goto failed;
- }
-
- if (ngx_ssl_check_host(c, &http->tls_name) != NGX_OK) {
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "js fetch SSL certificate does not match \"%V\"",
- &http->tls_name);
- goto failed;
- }
- }
-
- c->write->handler = ngx_js_http_write_handler;
- c->read->handler = ngx_js_http_read_handler;
-
- if (c->read->ready) {
- ngx_post_event(c->read, &ngx_posted_events);
- }
-
- http->process = ngx_js_http_process_status_line;
- ngx_js_http_write_handler(c->write);
-
- return;
- }
-
-failed:
-
- ngx_js_http_next(http);
- }
-
-
-static njs_int_t
-ngx_js_http_ssl_name(ngx_js_http_t *http)
-{
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
- u_char *p;
-
- /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */
- ngx_str_t *name = &http->tls_name;
-
- if (name->len == 0 || *name->data == '[') {
- goto done;
- }
-
- if (ngx_inet_addr(name->data, name->len) != INADDR_NONE) {
- goto done;
- }
-
- /*
- * SSL_set_tlsext_host_name() needs a null-terminated string,
- * hence we explicitly null-terminate name here
- */
-
- p = ngx_pnalloc(http->pool, name->len + 1);
- if (p == NULL) {
- return NGX_ERROR;
- }
-
- (void) ngx_cpystrn(p, name->data, name->len + 1);
-
- name->data = p;
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0,
- "js fetch SSL server name: \"%s\"", name->data);
-
- if (SSL_set_tlsext_host_name(http->peer.connection->ssl->connection,
- (char *) name->data)
- == 0)
- {
- ngx_ssl_error(NGX_LOG_ERR, http->log, 0,
- "SSL_set_tlsext_host_name(\"%s\") failed", name->data);
- return NGX_ERROR;
- }
-
-#endif
-done:
-
- return NJS_OK;
-}
-
-#endif
-
-
-static void
-ngx_js_http_next(ngx_js_http_t *http)
-{
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch next addr");
-
- if (++http->naddr >= http->naddrs) {
- ngx_js_http_error(http, "connect failed");
- return;
- }
-
- if (http->peer.connection != NULL) {
- ngx_js_http_close_connection(http->peer.connection);
- http->peer.connection = NULL;
- }
-
- http->buffer = NULL;
-
- ngx_js_http_connect(http);
-}
-
-
-static void
-ngx_js_http_write_handler(ngx_event_t *wev)
-{
- ssize_t n, size;
- ngx_buf_t *b;
- ngx_js_http_t *http;
- ngx_connection_t *c;
-
- c = wev->data;
- http = c->data;
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, "js fetch write handler");
-
- if (wev->timedout) {
- ngx_js_http_error(http, "write timed out");
- return;
- }
-
-#if (NGX_SSL)
- if (http->ssl != NULL && http->peer.connection->ssl == NULL) {
- ngx_js_http_ssl_init_connection(http);
- return;
- }
-#endif
-
- b = http->buffer;
-
- if (b == NULL) {
- size = njs_chb_size(&http->chain);
- if (size < 0) {
- ngx_js_http_error(http, "memory error");
- return;
- }
-
- b = ngx_create_temp_buf(http->pool, size);
- if (b == NULL) {
- ngx_js_http_error(http, "memory error");
- return;
- }
-
- njs_chb_join_to(&http->chain, b->last);
- b->last += size;
-
- http->buffer = b;
- }
-
- size = b->last - b->pos;
-
- n = c->send(c, b->pos, size);
-
- if (n == NGX_ERROR) {
- ngx_js_http_next(http);
- return;
- }
-
- if (n > 0) {
- b->pos += n;
-
- if (n == size) {
- wev->handler = ngx_js_http_dummy_handler;
-
- http->buffer = NULL;
-
- if (wev->timer_set) {
- ngx_del_timer(wev);
- }
-
- if (ngx_handle_write_event(wev, 0) != NGX_OK) {
- ngx_js_http_error(http, "write failed");
- }
-
- return;
- }
- }
-
- if (!wev->timer_set) {
- ngx_add_timer(wev, http->timeout);
- }
-}
-
-
-static void
-ngx_js_http_read_handler(ngx_event_t *rev)
-{
- ssize_t n, size;
- ngx_int_t rc;
- ngx_buf_t *b;
- ngx_js_http_t *http;
- ngx_connection_t *c;
-
- c = rev->data;
- http = c->data;
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, "js fetch read handler");
-
- if (rev->timedout) {
- ngx_js_http_error(http, "read timed out");
- return;
- }
-
- if (http->buffer == NULL) {
- b = ngx_create_temp_buf(http->pool, http->buffer_size);
- if (b == NULL) {
- ngx_js_http_error(http, "memory error");
- return;
- }
-
- http->buffer = b;
- }
-
- for ( ;; ) {
- b = http->buffer;
- size = b->end - b->last;
-
- n = c->recv(c, b->last, size);
-
- if (n > 0) {
- b->last += n;
-
- rc = http->process(http);
-
- if (rc == NGX_ERROR) {
- return;
- }
-
- continue;
- }
-
- if (n == NGX_AGAIN) {
- if (ngx_handle_read_event(rev, 0) != NGX_OK) {
- ngx_js_http_error(http, "read failed");
- }
-
- return;
- }
-
- if (n == NGX_ERROR) {
- ngx_js_http_next(http);
- return;
- }
-
- break;
- }
-
- http->done = 1;
-
- rc = http->process(http);
-
- if (rc == NGX_DONE) {
- /* handler was called */
- return;
- }
-
- if (rc == NGX_AGAIN) {
- ngx_js_http_error(http, "prematurely closed connection");
- }
-}
-
-
static njs_int_t
ngx_js_request_constructor(njs_vm_t *vm, ngx_js_request_t *request,
ngx_url_t *u, njs_external_ptr_t external, njs_value_t *args,
@@ -2205,91 +1521,36 @@ ngx_js_request_constructor(njs_vm_t *vm, ngx_js_request_t *request,
}
-njs_inline njs_int_t
-ngx_js_http_whitespace(u_char c)
+static ngx_int_t
+ngx_js_fetch_append_headers(ngx_js_http_t *http, ngx_js_headers_t *headers,
+ u_char *name, size_t len, u_char *value, size_t vlen)
{
- switch (c) {
- case 0x09: /* <TAB> */
- case 0x0A: /* <LF> */
- case 0x0D: /* <CR> */
- case 0x20: /* <SP> */
- return 1;
+ ngx_js_fetch_t *fetch;
- default:
- return 0;
- }
+ fetch = (ngx_js_fetch_t *) http;
+
+ return ngx_js_headers_append(fetch->vm, headers, name, len, value, vlen);
}
static void
-ngx_js_http_trim(u_char **value, size_t *len,
- njs_bool_t trim_c0_control_or_space)
+ngx_js_fetch_process_done(ngx_js_http_t *http)
{
- u_char *start, *end;
+ njs_int_t ret;
+ ngx_js_fetch_t *fetch;
- start = *value;
- end = start + *len;
-
- for ( ;; ) {
- if (start == end) {
- break;
- }
-
- if (ngx_js_http_whitespace(*start)
- || (trim_c0_control_or_space && *start <= ' '))
- {
- start++;
- continue;
- }
-
- break;
- }
-
- for ( ;; ) {
- if (start == end) {
- break;
- }
-
- end--;
-
- if (ngx_js_http_whitespace(*end)
- || (trim_c0_control_or_space && *end <= ' '))
- {
- continue;
- }
+ fetch = (ngx_js_fetch_t *) http;
- end++;
- break;
+ ret = njs_vm_external_create(fetch->vm,
+ njs_value_arg(&fetch->response_value),
+ ngx_http_js_fetch_response_proto_id,
+ &fetch->http.response, 0);
+ if (ret != NJS_OK) {
+ ngx_js_fetch_error(http, "fetch response creation failed");
+ return;
}
- *value = start;
- *len = end - start;
-}
-
-
-static const uint32_t token_map[] = {
- 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
-
- /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
- 0x03ff6cfa, /* 0000 0011 1111 1111 0110 1100 1111 1010 */
-
- /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
- 0xc7fffffe, /* 1100 0111 1111 1111 1111 1111 1111 1110 */
-
- /* ~}| {zyx wvut srqp onml kjih gfed cba` */
- 0x57ffffff, /* 0101 0111 1111 1111 1111 1111 1111 1111 */
-
- 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
- 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
- 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
- 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
-};
-
-
-njs_inline njs_bool_t
-njs_is_token(uint32_t byte)
-{
- return ((token_map[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0);
+ ngx_js_fetch_done(fetch, &fetch->response_value, NJS_OK);
}
@@ -2298,22 +1559,17 @@ ngx_js_headers_append(njs_vm_t *vm, ngx_js_headers_t *headers,
u_char *name, size_t len, u_char *value, size_t vlen)
{
u_char *p, *end;
+ ngx_int_t ret;
ngx_uint_t i;
ngx_js_tb_elt_t *h, **ph;
ngx_list_part_t *part;
ngx_js_http_trim(&value, &vlen, 0);
- p = name;
- end = p + len;
-
- while (p < end) {
- if (!njs_is_token(*p)) {
- njs_vm_error(vm, "invalid header name");
- return NJS_ERROR;
- }
-
- p++;
+ ret = ngx_js_check_header_name(name, len);
+ if (ret != NGX_OK) {
+ njs_vm_error(vm, "invalid header name");
+ return NJS_ERROR;
}
p = value;
@@ -2389,838 +1645,6 @@ ngx_js_headers_append(njs_vm_t *vm, ngx_js_headers_t *headers,
}
-static ngx_int_t
-ngx_js_http_process_status_line(ngx_js_http_t *http)
-{
- ngx_int_t rc;
- ngx_js_http_parse_t *hp;
-
- hp = &http->http_parse;
-
- rc = ngx_js_http_parse_status_line(hp, http->buffer);
-
- if (rc == NGX_OK) {
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch status %ui",
- hp->code);
-
- http->response.code = hp->code;
- http->response.status_text.start = hp->status_text;
- http->response.status_text.length = hp->status_text_end
- - hp->status_text;
- http->process = ngx_js_http_process_headers;
-
- return http->process(http);
- }
-
- if (rc == NGX_AGAIN) {
- return NGX_AGAIN;
- }
-
- /* rc == NGX_ERROR */
-
- ngx_js_http_error(http, "invalid fetch status line");
-
- return NGX_ERROR;
-}
-
-
-static ngx_int_t
-ngx_js_http_process_headers(ngx_js_http_t *http)
-{
- size_t len, vlen;
- ngx_int_t rc;
- njs_int_t ret;
- ngx_js_http_parse_t *hp;
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, http->log, 0,
- "js fetch process headers");
-
- hp = &http->http_parse;
-
- if (http->response.headers.header_list.size == 0) {
- rc = ngx_list_init(&http->response.headers.header_list, http->pool, 4,
- sizeof(ngx_js_tb_elt_t));
- if (rc != NGX_OK) {
- ngx_js_http_error(http, "alloc failed");
- return NGX_ERROR;
- }
- }
-
- for ( ;; ) {
- rc = ngx_js_http_parse_header_line(hp, http->buffer);
-
- if (rc == NGX_OK) {
- len = hp->header_name_end - hp->header_name_start;
- vlen = hp->header_end - hp->header_start;
-
- ret = ngx_js_headers_append(http->vm, &http->response.headers,
- hp->header_name_start, len,
- hp->header_start, vlen);
-
- if (ret == NJS_ERROR) {
- ngx_js_http_error(http, "cannot add respose header");
- return NGX_ERROR;
- }
-
- ngx_log_debug4(NGX_LOG_DEBUG_EVENT, http->log, 0,
- "js fetch header \"%*s: %*s\"",
- len, hp->header_name_start, vlen, hp->header_start);
-
- if (len == njs_strlen("Transfer-Encoding")
- && vlen == njs_strlen("chunked")
- && ngx_strncasecmp(hp->header_name_start,
- (u_char *) "Transfer-Encoding", len) == 0
- && ngx_strncasecmp(hp->header_start, (u_char *) "chunked",
- vlen) == 0)
- {
- hp->chunked = 1;
- }
-
- if (len == njs_strlen("Content-Length")
- && ngx_strncasecmp(hp->header_name_start,
- (u_char *) "Content-Length", len) == 0)
- {
- hp->content_length_n = ngx_atoof(hp->header_start, vlen);
- if (hp->content_length_n == NGX_ERROR) {
- ngx_js_http_error(http, "invalid fetch content length");
- return NGX_ERROR;
- }
-
- if (!http->header_only
- && hp->content_length_n
- > (off_t) http->max_response_body_size)
- {
- ngx_js_http_error(http,
- "fetch content length is too large");
- return NGX_ERROR;
- }
- }
-
- continue;
- }
-
- if (rc == NGX_DONE) {
- http->response.headers.guard = GUARD_IMMUTABLE;
- break;
- }
-
- if (rc == NGX_AGAIN) {
- return NGX_AGAIN;
- }
-
- /* rc == NGX_ERROR */
-
- ngx_js_http_error(http, "invalid fetch header");
-
- return NGX_ERROR;
- }
-
- njs_chb_destroy(&http->chain);
-
- NJS_CHB_MP_INIT(&http->response.chain, njs_vm_memory_pool(http->vm));
-
- http->process = ngx_js_http_process_body;
-
- return http->process(http);
-}
-
-
-static ngx_int_t
-ngx_js_http_process_body(ngx_js_http_t *http)
-{
- ssize_t size, chsize, need;
- ngx_int_t rc;
- njs_int_t ret;
- ngx_buf_t *b;
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0,
- "js fetch process body done:%ui", (ngx_uint_t) http->done);
-
- if (http->done) {
- size = njs_chb_size(&http->response.chain);
- if (size < 0) {
- ngx_js_http_error(http, "memory error");
- return NGX_ERROR;
- }
-
- if (!http->header_only
- && http->http_parse.chunked
- && http->http_parse.content_length_n == -1)
- {
- ngx_js_http_error(http, "invalid fetch chunked response");
- return NGX_ERROR;
- }
-
- if (http->header_only
- || http->http_parse.content_length_n == -1
- || size == http->http_parse.content_length_n)
- {
- ret = njs_vm_external_create(http->vm,
- njs_value_arg(&http->response_value),
- ngx_http_js_fetch_response_proto_id,
- &http->response, 0);
- if (ret != NJS_OK) {
- ngx_js_http_error(http, "fetch response creation failed");
- return NGX_ERROR;
- }
-
- ngx_js_http_fetch_done(http, &http->response_value, NJS_OK);
- return NGX_DONE;
- }
-
- if (size < http->http_parse.content_length_n) {
- return NGX_AGAIN;
- }
-
- ngx_js_http_error(http, "fetch trailing data");
- return NGX_ERROR;
- }
-
- b = http->buffer;
-
- if (http->http_parse.chunked) {
- rc = ngx_js_http_parse_chunked(&http->http_chunk_parse, b,
- &http->response.chain);
- if (rc == NGX_ERROR) {
- ngx_js_http_error(http, "invalid fetch chunked response");
- return NGX_ERROR;
- }
-
- size = njs_chb_size(&http->response.chain);
-
- if (rc == NGX_OK) {
- http->http_parse.content_length_n = size;
- }
-
- if (size > http->max_response_body_size * 10) {
- ngx_js_http_error(http, "very large fetch chunked response");
- return NGX_ERROR;
- }
-
- b->pos = http->http_chunk_parse.pos;
-
- } else {
- size = njs_chb_size(&http->response.chain);
-
- if (http->header_only) {
- need = 0;
-
- } else if (http->http_parse.content_length_n == -1) {
- need = http->max_response_body_size - size;
-
- } else {
- need = http->http_parse.content_length_n - size;
- }
-
- chsize = ngx_min(need, b->last - b->pos);
-
- if (size + chsize > http->max_response_body_size) {
- ngx_js_http_error(http, "fetch response body is too large");
- return NGX_ERROR;
- }
-
- if (chsize > 0) {
- njs_chb_append(&http->response.chain, b->pos, chsize);
- b->pos += chsize;
- }
-
- rc = (need > chsize) ? NGX_AGAIN : NGX_DONE;
- }
-
- if (b->pos == b->end) {
- if (http->chunk == NULL) {
- b = ngx_create_temp_buf(http->pool, http->buffer_size);
- if (b == NULL) {
- ngx_js_http_error(http, "memory error");
- return NGX_ERROR;
- }
-
- http->buffer = b;
- http->chunk = b;
-
- } else {
- b->last = b->start;
- b->pos = b->start;
- }
- }
-
- return rc;
-}
-
-
-static ngx_int_t
-ngx_js_http_parse_status_line(ngx_js_http_parse_t *hp, ngx_buf_t *b)
-{
- u_char ch;
- u_char *p;
- enum {
- sw_start = 0,
- sw_H,
- sw_HT,
- sw_HTT,
- sw_HTTP,
- sw_first_major_digit,
- sw_major_digit,
- sw_first_minor_digit,
- sw_minor_digit,
- sw_status,
- sw_space_after_status,
- sw_status_text,
- sw_almost_done
- } state;
-
- state = hp->state;
-
- for (p = b->pos; p < b->last; p++) {
- ch = *p;
-
- switch (state) {
-
- /* "HTTP/" */
- case sw_start:
- switch (ch) {
- case 'H':
- state = sw_H;
- break;
- default:
- return NGX_ERROR;
- }
- break;
-
- case sw_H:
- switch (ch) {
- case 'T':
- state = sw_HT;
- break;
- default:
- return NGX_ERROR;
- }
- break;
-
- case sw_HT:
- switch (ch) {
- case 'T':
- state = sw_HTT;
- break;
- default:
- return NGX_ERROR;
- }
- break;
-
- case sw_HTT:
- switch (ch) {
- case 'P':
- state = sw_HTTP;
- break;
- default:
- return NGX_ERROR;
- }
- break;
-
- case sw_HTTP:
- switch (ch) {
- case '/':
- state = sw_first_major_digit;
- break;
- default:
- return NGX_ERROR;
- }
- break;
-
- /* the first digit of major HTTP version */
- case sw_first_major_digit:
- if (ch < '1' || ch > '9') {
- return NGX_ERROR;
- }
-
- state = sw_major_digit;
- break;
-
- /* the major HTTP version or dot */
- case sw_major_digit:
- if (ch == '.') {
- state = sw_first_minor_digit;
- break;
- }
-
- if (ch < '0' || ch > '9') {
- return NGX_ERROR;
- }
-
- break;
-
- /* the first digit of minor HTTP version */
- case sw_first_minor_digit:
- if (ch < '0' || ch > '9') {
- return NGX_ERROR;
- }
-
- state = sw_minor_digit;
- break;
-
- /* the minor HTTP version or the end of the request line */
- case sw_minor_digit:
- if (ch == ' ') {
- state = sw_status;
- break;
- }
-
- if (ch < '0' || ch > '9') {
- return NGX_ERROR;
- }
-
- break;
-
- /* HTTP status code */
- case sw_status:
- if (ch == ' ') {
- break;
- }
-
- if (ch < '0' || ch > '9') {
- return NGX_ERROR;
- }
-
- hp->code = hp->code * 10 + (ch - '0');
-
- if (++hp->count == 3) {
- state = sw_space_after_status;
- }
-
- break;
-
- /* space or end of line */
- case sw_space_after_status:
- switch (ch) {
- case ' ':
- state = sw_status_text;
- break;
- case '.': /* IIS may send 403.1, 403.2, etc */
- state = sw_status_text;
- break;
- case CR:
- break;
- case LF:
- goto done;
- default:
- return NGX_ERROR;
- }
- break;
-
- /* any text until end of line */
- case sw_status_text:
- switch (ch) {
- case CR:
- hp->status_text_end = p;
- state = sw_almost_done;
- break;
- case LF:
- hp->status_text_end = p;
- goto done;
- }
-
- if (hp->status_text == NULL) {
- hp->status_text = p;
- }
-
- break;
-
- /* end of status line */
- case sw_almost_done:
- switch (ch) {
- case LF:
- goto done;
- default:
- return NGX_ERROR;
- }
- }
- }
-
- b->pos = p;
- hp->state = state;
-
- return NGX_AGAIN;
-
-done:
-
- b->pos = p + 1;
- hp->state = sw_start;
-
- return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_js_http_parse_header_line(ngx_js_http_parse_t *hp, ngx_buf_t *b)
-{
- u_char c, ch, *p;
- enum {
- sw_start = 0,
- sw_name,
- sw_space_before_value,
- sw_value,
- sw_space_after_value,
- sw_almost_done,
- sw_header_almost_done
- } state;
-
- state = hp->state;
-
- for (p = b->pos; p < b->last; p++) {
- ch = *p;
-
- switch (state) {
-
- /* first char */
- case sw_start:
-
- switch (ch) {
- case CR:
- hp->header_end = p;
- state = sw_header_almost_done;
- break;
- case LF:
- hp->header_end = p;
- goto header_done;
- default:
- state = sw_name;
- hp->header_name_start = p;
-
- c = (u_char) (ch | 0x20);
- if (c >= 'a' && c <= 'z') {
- break;
- }
-
- if (ch >= '0' && ch <= '9') {
- break;
- }
-
- return NGX_ERROR;
- }
- break;
-
- /* header name */
- case sw_name:
- c = (u_char) (ch | 0x20);
- if (c >= 'a' && c <= 'z') {
- break;
- }
-
- if (ch == ':') {
- hp->header_name_end = p;
- state = sw_space_before_value;
- break;
- }
-
- if (ch == '-' || ch == '_') {
- break;
- }
-
- if (ch >= '0' && ch <= '9') {
- break;
- }
-
- if (ch == CR) {
- hp->header_name_end = p;
- hp->header_start = p;
- hp->header_end = p;
- state = sw_almost_done;
- break;
- }
-
- if (ch == LF) {
- hp->header_name_end = p;
- hp->header_start = p;
- hp->header_end = p;
- goto done;
- }
-
- return NGX_ERROR;
-
- /* space* before header value */
- case sw_space_before_value:
- switch (ch) {
- case ' ':
- break;
- case CR:
- hp->header_start = p;
- hp->header_end = p;
- state = sw_almost_done;
- break;
- case LF:
- hp->header_start = p;
- hp->header_end = p;
- goto done;
- default:
- hp->header_start = p;
- state = sw_value;
- break;
- }
- break;
-
- /* header value */
- case sw_value:
- switch (ch) {
- case ' ':
- hp->header_end = p;
- state = sw_space_after_value;
- break;
- case CR:
- hp->header_end = p;
- state = sw_almost_done;
- break;
- case LF:
- hp->header_end = p;
- goto done;
- }
- break;
-
- /* space* before end of header line */
- case sw_space_after_value:
- switch (ch) {
- case ' ':
- break;
- case CR:
- state = sw_almost_done;
- break;
- case LF:
- goto done;
- default:
- state = sw_value;
- break;
- }
- break;
-
- /* end of header line */
- case sw_almost_done:
- switch (ch) {
- case LF:
- goto done;
- default:
- return NGX_ERROR;
- }
-
- /* end of header */
- case sw_header_almost_done:
- switch (ch) {
- case LF:
- goto header_done;
- default:
- return NGX_ERROR;
- }
- }
- }
-
- b->pos = p;
- hp->state = state;
-
- return NGX_AGAIN;
-
-done:
-
- b->pos = p + 1;
- hp->state = sw_start;
-
- return NGX_OK;
-
-header_done:
-
- b->pos = p + 1;
- hp->state = sw_start;
-
- return NGX_DONE;
-}
-
-
-#define \
-ngx_size_is_sufficient(cs) \
- (cs < ((__typeof__(cs)) 1 << (sizeof(cs) * 8 - 4)))
-
-
-#define NGX_JS_HTTP_CHUNK_MIDDLE 0
-#define NGX_JS_HTTP_CHUNK_ON_BORDER 1
-#define NGX_JS_HTTP_CHUNK_END 2
-
-
-static ngx_int_t
-ngx_js_http_chunk_buffer(ngx_js_http_chunk_parse_t *hcp, ngx_buf_t *b,
- njs_chb_t *chain)
-{
- size_t size;
-
- size = b->last - hcp->pos;
-
- if (hcp->chunk_size < size) {
- njs_chb_append(chain, hcp->pos, hcp->chunk_size);
- hcp->pos += hcp->chunk_size;
-
- return NGX_JS_HTTP_CHUNK_END;
- }
-
- njs_chb_append(chain, hcp->pos, size);
- hcp->pos += size;
-
- hcp->chunk_size -= size;
-
- if (hcp->chunk_size == 0) {
- return NGX_JS_HTTP_CHUNK_ON_BORDER;
- }
-
- return NGX_JS_HTTP_CHUNK_MIDDLE;
-}
-
-
-static ngx_int_t
-ngx_js_http_parse_chunked(ngx_js_http_chunk_parse_t *hcp,
- ngx_buf_t *b, njs_chb_t *chain)
-{
- u_char c, ch;
- ngx_int_t rc;
-
- enum {
- sw_start = 0,
- sw_chunk_size,
- sw_chunk_size_linefeed,
- sw_chunk_end_newline,
- sw_chunk_end_linefeed,
- sw_chunk,
- } state;
-
- state = hcp->state;
-
- hcp->pos = b->pos;
-
- while (hcp->pos < b->last) {
- /*
- * The sw_chunk state is tested outside the switch
- * to preserve hcp->pos and to not touch memory.
- */
- if (state == sw_chunk) {
- rc = ngx_js_http_chunk_buffer(hcp, b, chain);
- if (rc == NGX_ERROR) {
- return rc;
- }
-
- if (rc == NGX_JS_HTTP_CHUNK_MIDDLE) {
- break;
- }
-
- state = sw_chunk_end_newline;
-
- if (rc == NGX_JS_HTTP_CHUNK_ON_BORDER) {
- break;
- }
-
- /* rc == NGX_JS_HTTP_CHUNK_END */
- }
-
- ch = *hcp->pos++;
-
- switch (state) {
-
- case sw_start:
- state = sw_chunk_size;
-
- c = ch - '0';
-
- if (c <= 9) {
- hcp->chunk_size = c;
- continue;
- }
-
- c = (ch | 0x20) - 'a';
-
- if (c <= 5) {
- hcp->chunk_size = 0x0A + c;
- continue;
- }
-
- return NGX_ERROR;
-
- case sw_chunk_size:
-
- c = ch - '0';
-
- if (c > 9) {
- c = (ch | 0x20) - 'a';
-
- if (c <= 5) {
- c += 0x0A;
-
- } else if (ch == '\r') {
- state = sw_chunk_size_linefeed;
- continue;
-
- } else {
- return NGX_ERROR;
- }
- }
-
- if (ngx_size_is_sufficient(hcp->chunk_size)) {
- hcp->chunk_size = (hcp->chunk_size << 4) + c;
- continue;
- }
-
- return NGX_ERROR;
-
- case sw_chunk_size_linefeed:
- if (ch == '\n') {
-
- if (hcp->chunk_size != 0) {
- state = sw_chunk;
- continue;
- }
-
- hcp->last = 1;
- state = sw_chunk_end_newline;
- continue;
- }
-
- return NGX_ERROR;
-
- case sw_chunk_end_newline:
- if (ch == '\r') {
- state = sw_chunk_end_linefeed;
- continue;
- }
-
- return NGX_ERROR;
-
- case sw_chunk_end_linefeed:
- if (ch == '\n') {
-
- if (!hcp->last) {
- state = sw_start;
- continue;
- }
-
- return NGX_OK;
- }
-
- return NGX_ERROR;
-
- case sw_chunk:
- /*
- * This state is processed before the switch.
- * It added here just to suppress a warning.
- */
- continue;
- }
- }
-
- hcp->state = state;
-
- return NGX_AGAIN;
-}
-
-
-static void
-ngx_js_http_dummy_handler(ngx_event_t *ev)
-{
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "js fetch dummy handler");
-}
-
-
static njs_int_t
ngx_headers_js_get(njs_vm_t *vm, njs_value_t *value, njs_str_t *name,
njs_value_t *retval, njs_bool_t as_array)
diff --git a/nginx/ngx_js_http.c b/nginx/ngx_js_http.c
new file mode 100644
index 00000000..c958b0c8
--- /dev/null
+++ b/nginx/ngx_js_http.c
@@ -0,0 +1,1531 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) hongzhidao
+ * Copyright (C) Antoine Bonavita
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include "ngx_js.h"
+#include "ngx_js_http.h"
+
+
+static void ngx_js_http_resolve_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_js_http_next(ngx_js_http_t *http);
+static void ngx_js_http_write_handler(ngx_event_t *wev);
+static void ngx_js_http_read_handler(ngx_event_t *rev);
+static void ngx_js_http_dummy_handler(ngx_event_t *ev);
+
+static ngx_int_t ngx_js_http_process_status_line(ngx_js_http_t *http);
+static ngx_int_t ngx_js_http_process_headers(ngx_js_http_t *http);
+static ngx_int_t ngx_js_http_process_body(ngx_js_http_t *http);
+static ngx_int_t ngx_js_http_parse_status_line(ngx_js_http_parse_t *hp,
+ ngx_buf_t *b);
+static ngx_int_t ngx_js_http_parse_header_line(ngx_js_http_parse_t *hp,
+ ngx_buf_t *b);
+static ngx_int_t ngx_js_http_parse_chunked(ngx_js_http_chunk_parse_t *hcp,
+ ngx_buf_t *b, njs_chb_t *chain);
+
+#if (NGX_SSL)
+static void ngx_js_http_ssl_init_connection(ngx_js_http_t *http);
+static void ngx_js_http_ssl_handshake_handler(ngx_connection_t *c);
+static void ngx_js_http_ssl_handshake(ngx_js_http_t *http);
+static ngx_int_t ngx_js_http_ssl_name(ngx_js_http_t *http);
+#endif
+
+
+static void
+ngx_js_http_error(ngx_js_http_t *http, const char *fmt, ...)
+{
+ u_char *p, *end;
+ va_list args;
+ u_char err[NGX_MAX_ERROR_STR];
+
+ end = err + NGX_MAX_ERROR_STR - 1;
+
+ va_start(args, fmt);
+ p = njs_vsprintf(err, end, fmt, args);
+ *p = '\0';
+ va_end(args);
+
+ http->error_handler(http, (const char *) err);
+}
+
+
+ngx_resolver_ctx_t *
+ngx_js_http_resolve(ngx_js_http_t *http, ngx_resolver_t *r, ngx_str_t *host,
+ in_port_t port, ngx_msec_t timeout)
+{
+ ngx_int_t ret;
+ ngx_resolver_ctx_t *ctx;
+
+ ctx = ngx_resolve_start(r, NULL);
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ if (ctx == NGX_NO_RESOLVER) {
+ return ctx;
+ }
+
+ http->ctx = ctx;
+ http->port = port;
+
+ ctx->name = *host;
+ ctx->handler = ngx_js_http_resolve_handler;
+ ctx->data = http;
+ ctx->timeout = timeout;
+
+ ret = ngx_resolve_name(ctx);
+ if (ret != NGX_OK) {
+ http->ctx = NULL;
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+static void
+ngx_js_http_resolve_handler(ngx_resolver_ctx_t *ctx)
+{
+ u_char *p;
+ size_t len;
+ socklen_t socklen;
+ ngx_uint_t i;
+ ngx_js_http_t *http;
+ struct sockaddr *sockaddr;
+
+ http = ctx->data;
+
+ if (ctx->state) {
+ ngx_js_http_error(http, "\"%V\" could not be resolved (%i: %s)",
+ &ctx->name, ctx->state,
+ ngx_resolver_strerror(ctx->state));
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "http resolved: \"%V\"", &ctx->name);
+
+#if (NGX_DEBUG)
+ {
+ u_char text[NGX_SOCKADDR_STRLEN];
+ ngx_str_t addr;
+ ngx_uint_t i;
+
+ addr.data = text;
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,
+ text, NGX_SOCKADDR_STRLEN, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "name was resolved to \"%V\"", &addr);
+ }
+ }
+#endif
+
+ http->naddrs = ctx->naddrs;
+ http->addrs = ngx_pcalloc(http->pool, http->naddrs * sizeof(ngx_addr_t));
+
+ if (http->addrs == NULL) {
+ goto failed;
+ }
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ socklen = ctx->addrs[i].socklen;
+
+ sockaddr = ngx_palloc(http->pool, socklen);
+ if (sockaddr == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(sockaddr, ctx->addrs[i].sockaddr, socklen);
+ ngx_inet_set_port(sockaddr, http->port);
+
+ http->addrs[i].sockaddr = sockaddr;
+ http->addrs[i].socklen = socklen;
+
+ p = ngx_pnalloc(http->pool, NGX_SOCKADDR_STRLEN);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
+ http->addrs[i].name.len = len;
+ http->addrs[i].name.data = p;
+ }
+
+ ngx_js_http_resolve_done(http);
+
+ ngx_js_http_connect(http);
+
+ return;
+
+failed:
+
+ ngx_js_http_error(http, "memory error");
+}
+
+
+static void
+ngx_js_http_close_connection(ngx_connection_t *c)
+{
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "js http close connection: %d", c->fd);
+
+#if (NGX_SSL)
+ if (c->ssl) {
+ c->ssl->no_wait_shutdown = 1;
+
+ if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+ c->ssl->handler = ngx_js_http_close_connection;
+ return;
+ }
+ }
+#endif
+
+ c->destroyed = 1;
+
+ ngx_close_connection(c);
+}
+
+
+void
+ngx_js_http_resolve_done(ngx_js_http_t *http)
+{
+ if (http->ctx != NULL) {
+ ngx_resolve_name_done(http->ctx);
+ http->ctx = NULL;
+ }
+}
+
+
+void
+ngx_js_http_close_peer(ngx_js_http_t *http)
+{
+ if (http->peer.connection != NULL) {
+ ngx_js_http_close_connection(http->peer.connection);
+ http->peer.connection = NULL;
+ }
+}
+
+
+void
+ngx_js_http_connect(ngx_js_http_t *http)
+{
+ ngx_int_t rc;
+ ngx_addr_t *addr;
+
+ addr = &http->addrs[http->naddr];
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "js http connect %ui/%ui", http->naddr, http->naddrs);
+
+ http->peer.sockaddr = addr->sockaddr;
+ http->peer.socklen = addr->socklen;
+ http->peer.name = &addr->name;
+ http->peer.get = ngx_event_get_peer;
+ http->peer.log = http->log;
+ http->peer.log_error = NGX_ERROR_ERR;
+
+ rc = ngx_event_connect_peer(&http->peer);
+
+ if (rc == NGX_ERROR) {
+ ngx_js_http_error(http, "connect failed");
+ return;
+ }
+
+ if (rc == NGX_BUSY || rc == NGX_DECLINED) {
+ ngx_js_http_next(http);
+ return;
+ }
+
+ http->peer.connection->data = http;
+ http->peer.connection->pool = http->pool;
+
+ http->peer.connection->write->handler = ngx_js_http_write_handler;
+ http->peer.connection->read->handler = ngx_js_http_read_handler;
+
+ http->process = ngx_js_http_process_status_line;
+
+ ngx_add_timer(http->peer.connection->read, http->timeout);
+ ngx_add_timer(http->peer.connection->write, http->timeout);
+
+#if (NGX_SSL)
+ if (http->ssl != NULL && http->peer.connection->ssl == NULL) {
+ ngx_js_http_ssl_init_connection(http);
+ return;
+ }
+#endif
+
+ if (rc == NGX_OK) {
+ ngx_js_http_write_handler(http->peer.connection->write);
+ }
+}
+
+
+#if (NGX_SSL)
+
+static void
+ngx_js_http_ssl_init_connection(ngx_js_http_t *http)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+
+ c = http->peer.connection;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "js http secure connect %ui/%ui", http->naddr,
+ http->naddrs);
+
+ if (ngx_ssl_create_connection(http->ssl, c, NGX_SSL_BUFFER|NGX_SSL_CLIENT)
+ != NGX_OK)
+ {
+ ngx_js_http_error(http, "failed to create ssl connection");
+ return;
+ }
+
+ c->sendfile = 0;
+
+ if (ngx_js_http_ssl_name(http) != NGX_OK) {
+ ngx_js_http_error(http, "failed to create ssl connection");
+ return;
+ }
+
+ c->log->action = "SSL handshaking to http target";
+
+ rc = ngx_ssl_handshake(c);
+
+ if (rc == NGX_AGAIN) {
+ c->data = http;
+ c->ssl->handler = ngx_js_http_ssl_handshake_handler;
+ return;
+ }
+
+ ngx_js_http_ssl_handshake(http);
+}
+
+
+static void
+ngx_js_http_ssl_handshake_handler(ngx_connection_t *c)
+{
+ ngx_js_http_t *http;
+
+ http = c->data;
+
+ http->peer.connection->write->handler = ngx_js_http_write_handler;
+ http->peer.connection->read->handler = ngx_js_http_read_handler;
+
+ ngx_js_http_ssl_handshake(http);
+}
+
+
+static void
+ngx_js_http_ssl_handshake(ngx_js_http_t *http)
+{
+ long rc;
+ ngx_connection_t *c;
+
+ c = http->peer.connection;
+
+ if (c->ssl->handshaked) {
+ if (http->ssl_verify) {
+ rc = SSL_get_verify_result(c->ssl->connection);
+
+ if (rc != X509_V_OK) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "js http SSL certificate verify error: (%l:%s)",
+ rc, X509_verify_cert_error_string(rc));
+ goto failed;
+ }
+
+ if (ngx_ssl_check_host(c, &http->tls_name) != NGX_OK) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "js http SSL certificate does not match \"%V\"",
+ &http->tls_name);
+ goto failed;
+ }
+ }
+
+ c->write->handler = ngx_js_http_write_handler;
+ c->read->handler = ngx_js_http_read_handler;
+
+ if (c->read->ready) {
+ ngx_post_event(c->read, &ngx_posted_events);
+ }
+
+ http->process = ngx_js_http_process_status_line;
+ ngx_js_http_write_handler(c->write);
+
+ return;
+ }
+
+failed:
+
+ ngx_js_http_next(http);
+}
+
+
+static ngx_int_t
+ngx_js_http_ssl_name(ngx_js_http_t *http)
+{
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ u_char *p;
+
+ /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */
+ ngx_str_t *name = &http->tls_name;
+
+ if (name->len == 0 || *name->data == '[') {
+ goto done;
+ }
+
+ if (ngx_inet_addr(name->data, name->len) != INADDR_NONE) {
+ goto done;
+ }
+
+ /*
+ * SSL_set_tlsext_host_name() needs a null-terminated string,
+ * hence we explicitly null-terminate name here
+ */
+
+ p = ngx_pnalloc(http->pool, name->len + 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_cpystrn(p, name->data, name->len + 1);
+
+ name->data = p;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "js http SSL server name: \"%s\"", name->data);
+
+ if (SSL_set_tlsext_host_name(http->peer.connection->ssl->connection,
+ (char *) name->data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_ERR, http->log, 0,
+ "SSL_set_tlsext_host_name(\"%s\") failed", name->data);
+ return NGX_ERROR;
+ }
+
+#endif
+done:
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static void
+ngx_js_http_next(ngx_js_http_t *http)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, http->log, 0, "js http next addr");
+
+ if (++http->naddr >= http->naddrs) {
+ ngx_js_http_error(http, "connect failed");
+ return;
+ }
+
+ if (http->peer.connection != NULL) {
+ ngx_js_http_close_connection(http->peer.connection);
+ http->peer.connection = NULL;
+ }
+
+ http->buffer = NULL;
+
+ ngx_js_http_connect(http);
+}
+
+
+static void
+ngx_js_http_write_handler(ngx_event_t *wev)
+{
+ ssize_t n, size;
+ ngx_buf_t *b;
+ ngx_js_http_t *http;
+ ngx_connection_t *c;
+
+ c = wev->data;
+ http = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, "js http write handler");
+
+ if (wev->timedout) {
+ ngx_js_http_error(http, "write timed out");
+ return;
+ }
+
+#if (NGX_SSL)
+ if (http->ssl != NULL && http->peer.connection->ssl == NULL) {
+ ngx_js_http_ssl_init_connection(http);
+ return;
+ }
+#endif
+
+ b = http->buffer;
+
+ if (b == NULL) {
+ size = njs_chb_size(&http->chain);
+ if (size < 0) {
+ ngx_js_http_error(http, "memory error");
+ return;
+ }
+
+ b = ngx_create_temp_buf(http->pool, size);
+ if (b == NULL) {
+ ngx_js_http_error(http, "memory error");
+ return;
+ }
+
+ njs_chb_join_to(&http->chain, b->last);
+ b->last += size;
+
+ http->buffer = b;
+ }
+
+ size = b->last - b->pos;
+
+ n = c->send(c, b->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_js_http_next(http);
+ return;
+ }
+
+ if (n > 0) {
+ b->pos += n;
+
+ if (n == size) {
+ wev->handler = ngx_js_http_dummy_handler;
+
+ http->buffer = NULL;
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+ ngx_js_http_error(http, "write failed");
+ }
+
+ return;
+ }
+ }
+
+ if (!wev->timer_set) {
+ ngx_add_timer(wev, http->timeout);
+ }
+}
+
+
+static void
+ngx_js_http_read_handler(ngx_event_t *rev)
+{
+ ssize_t n, size;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_js_http_t *http;
+ ngx_connection_t *c;
+
+ c = rev->data;
+ http = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, "js http read handler");
+
+ if (rev->timedout) {
+ ngx_js_http_error(http, "read timed out");
+ return;
+ }
+
+ if (http->buffer == NULL) {
+ b = ngx_create_temp_buf(http->pool, http->buffer_size);
+ if (b == NULL) {
+ ngx_js_http_error(http, "memory error");
+ return;
+ }
+
+ http->buffer = b;
+ }
+
+ for ( ;; ) {
+ b = http->buffer;
+ size = b->end - b->last;
+
+ n = c->recv(c, b->last, size);
+
+ if (n > 0) {
+ b->last += n;
+
+ rc = http->process(http);
+
+ if (rc == NGX_ERROR) {
+ return;
+ }
+
+ continue;
+ }
+
+ if (n == NGX_AGAIN) {
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_js_http_error(http, "read failed");
+ }
+
+ return;
+ }
+
+ if (n == NGX_ERROR) {
+ ngx_js_http_next(http);
+ return;
+ }
+
+ break;
+ }
+
+ http->done = 1;
+
+ rc = http->process(http);
+
+ if (rc == NGX_DONE) {
+ /* handler was called */
+ return;
+ }
+
+ if (rc == NGX_AGAIN) {
+ ngx_js_http_error(http, "prematurely closed connection");
+ }
+}
+
+
+static void
+ngx_js_http_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "js http dummy handler");
+}
+
+
+static ngx_int_t
+ngx_js_http_process_status_line(ngx_js_http_t *http)
+{
+ ngx_int_t rc;
+ ngx_js_http_parse_t *hp;
+
+ hp = &http->http_parse;
+
+ rc = ngx_js_http_parse_status_line(hp, http->buffer);
+
+ if (rc == NGX_OK) {
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "js http status %ui",
+ hp->code);
+
+ http->response.code = hp->code;
+ http->response.status_text.start = hp->status_text;
+ http->response.status_text.length = hp->status_text_end
+ - hp->status_text;
+ http->process = ngx_js_http_process_headers;
+
+ return http->process(http);
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_js_http_error(http, "invalid http status line");
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_js_http_process_headers(ngx_js_http_t *http)
+{
+ size_t len, vlen;
+ ngx_int_t rc;
+ ngx_js_http_parse_t *hp;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "js http process headers");
+
+ hp = &http->http_parse;
+
+ if (http->response.headers.header_list.size == 0) {
+ rc = ngx_list_init(&http->response.headers.header_list, http->pool, 4,
+ sizeof(ngx_js_tb_elt_t));
+ if (rc != NGX_OK) {
+ ngx_js_http_error(http, "alloc failed");
+ return NGX_ERROR;
+ }
+ }
+
+ for ( ;; ) {
+ rc = ngx_js_http_parse_header_line(hp, http->buffer);
+
+ if (rc == NGX_OK) {
+ len = hp->header_name_end - hp->header_name_start;
+ vlen = hp->header_end - hp->header_start;
+
+ rc = http->append_headers(http, &http->response.headers,
+ hp->header_name_start, len,
+ hp->header_start, vlen);
+
+ if (rc == NGX_ERROR) {
+ ngx_js_http_error(http, "cannot add respose header");
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "js http header \"%*s: %*s\"",
+ len, hp->header_name_start, vlen, hp->header_start);
+
+ if (len == (sizeof("Transfer-Encoding") -1)
+ && vlen == (sizeof("chunked") - 1)
+ && ngx_strncasecmp(hp->header_name_start,
+ (u_char *) "Transfer-Encoding", len) == 0
+ && ngx_strncasecmp(hp->header_start, (u_char *) "chunked",
+ vlen) == 0)
+ {
+ hp->chunked = 1;
+ }
+
+ if (len == (sizeof("Content-Length") - 1)
+ && ngx_strncasecmp(hp->header_name_start,
+ (u_char *) "Content-Length", len) == 0)
+ {
+ hp->content_length_n = ngx_atoof(hp->header_start, vlen);
+ if (hp->content_length_n == NGX_ERROR) {
+ ngx_js_http_error(http, "invalid http content length");
+ return NGX_ERROR;
+ }
+
+ if (!http->header_only
+ && hp->content_length_n
+ > (off_t) http->max_response_body_size)
+ {
+ ngx_js_http_error(http,
+ "http content length is too large");
+ return NGX_ERROR;
+ }
+ }
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+ http->response.headers.guard = GUARD_IMMUTABLE;
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_js_http_error(http, "invalid http header");
+
+ return NGX_ERROR;
+ }
+
+ njs_chb_destroy(&http->chain);
+
+ http->process = ngx_js_http_process_body;
+
+ return http->process(http);
+}
+
+
+static ngx_int_t
+ngx_js_http_process_body(ngx_js_http_t *http)
+{
+ ssize_t size, chsize, need;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0,
+ "js http process body done:%ui", (ngx_uint_t) http->done);
+
+ if (http->done) {
+ size = njs_chb_size(&http->response.chain);
+ if (size < 0) {
+ ngx_js_http_error(http, "memory error");
+ return NGX_ERROR;
+ }
+
+ if (!http->header_only
+ && http->http_parse.chunked
+ && http->http_parse.content_length_n == -1)
+ {
+ ngx_js_http_error(http, "invalid http chunked response");
+ return NGX_ERROR;
+ }
+
+ if (http->header_only
+ || http->http_parse.content_length_n == -1
+ || size == http->http_parse.content_length_n)
+ {
+ http->ready_handler(http);
+ return NGX_DONE;
+ }
+
+ if (size < http->http_parse.content_length_n) {
+ return NGX_AGAIN;
+ }
+
+ ngx_js_http_error(http, "http trailing data");
+ return NGX_ERROR;
+ }
+
+ b = http->buffer;
+
+ if (http->http_parse.chunked) {
+ rc = ngx_js_http_parse_chunked(&http->http_chunk_parse, b,
+ &http->response.chain);
+ if (rc == NGX_ERROR) {
+ ngx_js_http_error(http, "invalid http chunked response");
+ return NGX_ERROR;
+ }
+
+ size = njs_chb_size(&http->response.chain);
+
+ if (rc == NGX_OK) {
+ http->http_parse.content_length_n = size;
+ }
+
+ if (size > http->max_response_body_size * 10) {
+ ngx_js_http_error(http, "very large http chunked response");
+ return NGX_ERROR;
+ }
+
+ b->pos = http->http_chunk_parse.pos;
+
+ } else {
+ size = njs_chb_size(&http->response.chain);
+
+ if (http->header_only) {
+ need = 0;
+
+ } else if (http->http_parse.content_length_n == -1) {
+ need = http->max_response_body_size - size;
+
+ } else {
+ need = http->http_parse.content_length_n - size;
+ }
+
+ chsize = ngx_min(need, b->last - b->pos);
+
+ if (size + chsize > http->max_response_body_size) {
+ ngx_js_http_error(http, "http response body is too large");
+ return NGX_ERROR;
+ }
+
+ if (chsize > 0) {
+ njs_chb_append(&http->response.chain, b->pos, chsize);
+ b->pos += chsize;
+ }
+
+ rc = (need > chsize) ? NGX_AGAIN : NGX_DONE;
+ }
+
+ if (b->pos == b->end) {
+ if (http->chunk == NULL) {
+ b = ngx_create_temp_buf(http->pool, http->buffer_size);
+ if (b == NULL) {
+ ngx_js_http_error(http, "memory error");
+ return NGX_ERROR;
+ }
+
+ http->buffer = b;
+ http->chunk = b;
+
+ } else {
+ b->last = b->start;
+ b->pos = b->start;
+ }
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_js_http_parse_status_line(ngx_js_http_parse_t *hp, ngx_buf_t *b)
+{
+ u_char ch;
+ u_char *p;
+ enum {
+ sw_start = 0,
+ sw_H,
+ sw_HT,
+ sw_HTT,
+ sw_HTTP,
+ sw_first_major_digit,
+ sw_major_digit,
+ sw_first_minor_digit,
+ sw_minor_digit,
+ sw_status,
+ sw_space_after_status,
+ sw_status_text,
+ sw_almost_done
+ } state;
+
+ state = hp->state;
+
+ for (p = b->pos; p < b->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* "HTTP/" */
+ case sw_start:
+ switch (ch) {
+ case 'H':
+ state = sw_H;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_H:
+ switch (ch) {
+ case 'T':
+ state = sw_HT;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HT:
+ switch (ch) {
+ case 'T':
+ state = sw_HTT;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HTT:
+ switch (ch) {
+ case 'P':
+ state = sw_HTTP;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HTTP:
+ switch (ch) {
+ case '/':
+ state = sw_first_major_digit;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ /* the first digit of major HTTP version */
+ case sw_first_major_digit:
+ if (ch < '1' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ state = sw_major_digit;
+ break;
+
+ /* the major HTTP version or dot */
+ case sw_major_digit:
+ if (ch == '.') {
+ state = sw_first_minor_digit;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ break;
+
+ /* the first digit of minor HTTP version */
+ case sw_first_minor_digit:
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ state = sw_minor_digit;
+ break;
+
+ /* the minor HTTP version or the end of the request line */
+ case sw_minor_digit:
+ if (ch == ' ') {
+ state = sw_status;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ break;
+
+ /* HTTP status code */
+ case sw_status:
+ if (ch == ' ') {
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ hp->code = hp->code * 10 + (ch - '0');
+
+ if (++hp->count == 3) {
+ state = sw_space_after_status;
+ }
+
+ break;
+
+ /* space or end of line */
+ case sw_space_after_status:
+ switch (ch) {
+ case ' ':
+ state = sw_status_text;
+ break;
+ case '.': /* IIS may send 403.1, 403.2, etc */
+ state = sw_status_text;
+ break;
+ case CR:
+ break;
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ /* any text until end of line */
+ case sw_status_text:
+ switch (ch) {
+ case CR:
+ hp->status_text_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ hp->status_text_end = p;
+ goto done;
+ }
+
+ if (hp->status_text == NULL) {
+ hp->status_text = p;
+ }
+
+ break;
+
+ /* end of status line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ b->pos = p;
+ hp->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ b->pos = p + 1;
+ hp->state = sw_start;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_js_http_parse_header_line(ngx_js_http_parse_t *hp, ngx_buf_t *b)
+{
+ u_char c, ch, *p;
+ enum {
+ sw_start = 0,
+ sw_name,
+ sw_space_before_value,
+ sw_value,
+ sw_space_after_value,
+ sw_almost_done,
+ sw_header_almost_done
+ } state;
+
+ state = hp->state;
+
+ for (p = b->pos; p < b->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* first char */
+ case sw_start:
+
+ switch (ch) {
+ case CR:
+ hp->header_end = p;
+ state = sw_header_almost_done;
+ break;
+ case LF:
+ hp->header_end = p;
+ goto header_done;
+ default:
+ state = sw_name;
+ hp->header_name_start = p;
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ return NGX_ERROR;
+ }
+ break;
+
+ /* header name */
+ case sw_name:
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch == ':') {
+ hp->header_name_end = p;
+ state = sw_space_before_value;
+ break;
+ }
+
+ if (ch == '-' || ch == '_') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ if (ch == CR) {
+ hp->header_name_end = p;
+ hp->header_start = p;
+ hp->header_end = p;
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ hp->header_name_end = p;
+ hp->header_start = p;
+ hp->header_end = p;
+ goto done;
+ }
+
+ return NGX_ERROR;
+
+ /* space* before header value */
+ case sw_space_before_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ hp->header_start = p;
+ hp->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ hp->header_start = p;
+ hp->header_end = p;
+ goto done;
+ default:
+ hp->header_start = p;
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* header value */
+ case sw_value:
+ switch (ch) {
+ case ' ':
+ hp->header_end = p;
+ state = sw_space_after_value;
+ break;
+ case CR:
+ hp->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ hp->header_end = p;
+ goto done;
+ }
+ break;
+
+ /* space* before end of header line */
+ case sw_space_after_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* end of header line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+
+ /* end of header */
+ case sw_header_almost_done:
+ switch (ch) {
+ case LF:
+ goto header_done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ b->pos = p;
+ hp->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ b->pos = p + 1;
+ hp->state = sw_start;
+
+ return NGX_OK;
+
+header_done:
+
+ b->pos = p + 1;
+ hp->state = sw_start;
+
+ return NGX_DONE;
+}
+
+
+#define \
+ngx_size_is_sufficient(cs) \
+ (cs < ((__typeof__(cs)) 1 << (sizeof(cs) * 8 - 4)))
+
+
+#define NGX_JS_HTTP_CHUNK_MIDDLE 0
+#define NGX_JS_HTTP_CHUNK_ON_BORDER 1
+#define NGX_JS_HTTP_CHUNK_END 2
+
+
+static ngx_int_t
+ngx_js_http_chunk_buffer(ngx_js_http_chunk_parse_t *hcp, ngx_buf_t *b,
+ njs_chb_t *chain)
+{
+ size_t size;
+
+ size = b->last - hcp->pos;
+
+ if (hcp->chunk_size < size) {
+ njs_chb_append(chain, hcp->pos, hcp->chunk_size);
+ hcp->pos += hcp->chunk_size;
+
+ return NGX_JS_HTTP_CHUNK_END;
+ }
+
+ njs_chb_append(chain, hcp->pos, size);
+ hcp->pos += size;
+
+ hcp->chunk_size -= size;
+
+ if (hcp->chunk_size == 0) {
+ return NGX_JS_HTTP_CHUNK_ON_BORDER;
+ }
+
+ return NGX_JS_HTTP_CHUNK_MIDDLE;
+}
+
+
+static ngx_int_t
+ngx_js_http_parse_chunked(ngx_js_http_chunk_parse_t *hcp,
+ ngx_buf_t *b, njs_chb_t *chain)
+{
+ u_char c, ch;
+ ngx_int_t rc;
+
+ enum {
+ sw_start = 0,
+ sw_chunk_size,
+ sw_chunk_size_linefeed,
+ sw_chunk_end_newline,
+ sw_chunk_end_linefeed,
+ sw_chunk,
+ } state;
+
+ state = hcp->state;
+
+ hcp->pos = b->pos;
+
+ while (hcp->pos < b->last) {
+ /*
+ * The sw_chunk state is tested outside the switch
+ * to preserve hcp->pos and to not touch memory.
+ */
+ if (state == sw_chunk) {
+ rc = ngx_js_http_chunk_buffer(hcp, b, chain);
+ if (rc == NGX_ERROR) {
+ return rc;
+ }
+
+ if (rc == NGX_JS_HTTP_CHUNK_MIDDLE) {
+ break;
+ }
+
+ state = sw_chunk_end_newline;
+
+ if (rc == NGX_JS_HTTP_CHUNK_ON_BORDER) {
+ break;
+ }
+
+ /* rc == NGX_JS_HTTP_CHUNK_END */
+ }
+
+ ch = *hcp->pos++;
+
+ switch (state) {
+
+ case sw_start:
+ state = sw_chunk_size;
+
+ c = ch - '0';
+
+ if (c <= 9) {
+ hcp->chunk_size = c;
+ continue;
+ }
+
+ c = (ch | 0x20) - 'a';
+
+ if (c <= 5) {
+ hcp->chunk_size = 0x0A + c;
+ continue;
+ }
+
+ return NGX_ERROR;
+
+ case sw_chunk_size:
+
+ c = ch - '0';
+
+ if (c > 9) {
+ c = (ch | 0x20) - 'a';
+
+ if (c <= 5) {
+ c += 0x0A;
+
+ } else if (ch == '\r') {
+ state = sw_chunk_size_linefeed;
+ continue;
+
+ } else {
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_size_is_sufficient(hcp->chunk_size)) {
+ hcp->chunk_size = (hcp->chunk_size << 4) + c;
+ continue;
+ }
+
+ return NGX_ERROR;
+
+ case sw_chunk_size_linefeed:
+ if (ch == '\n') {
+
+ if (hcp->chunk_size != 0) {
+ state = sw_chunk;
+ continue;
+ }
+
+ hcp->last = 1;
+ state = sw_chunk_end_newline;
+ continue;
+ }
+
+ return NGX_ERROR;
+
+ case sw_chunk_end_newline:
+ if (ch == '\r') {
+ state = sw_chunk_end_linefeed;
+ continue;
+ }
+
+ return NGX_ERROR;
+
+ case sw_chunk_end_linefeed:
+ if (ch == '\n') {
+
+ if (!hcp->last) {
+ state = sw_start;
+ continue;
+ }
+
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+
+ case sw_chunk:
+ /*
+ * This state is processed before the switch.
+ * It added here just to suppress a warning.
+ */
+ continue;
+ }
+ }
+
+ hcp->state = state;
+
+ return NGX_AGAIN;
+}
+
+
+static inline int
+ngx_js_http_whitespace(u_char c)
+{
+ switch (c) {
+ case 0x09: /* <TAB> */
+ case 0x0A: /* <LF> */
+ case 0x0D: /* <CR> */
+ case 0x20: /* <SP> */
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+void
+ngx_js_http_trim(u_char **value, size_t *len, int trim_c0_control_or_space)
+{
+ u_char *start, *end;
+
+ start = *value;
+ end = start + *len;
+
+ for ( ;; ) {
+ if (start == end) {
+ break;
+ }
+
+ if (ngx_js_http_whitespace(*start)
+ || (trim_c0_control_or_space && *start <= ' '))
+ {
+ start++;
+ continue;
+ }
+
+ break;
+ }
+
+ for ( ;; ) {
+ if (start == end) {
+ break;
+ }
+
+ end--;
+
+ if (ngx_js_http_whitespace(*end)
+ || (trim_c0_control_or_space && *end <= ' '))
+ {
+ continue;
+ }
+
+ end++;
+ break;
+ }
+
+ *value = start;
+ *len = end - start;
+}
+
+
+static const uint32_t token_map[] = {
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x03ff6cfa, /* 0000 0011 1111 1111 0110 1100 1111 1010 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0xc7fffffe, /* 1100 0111 1111 1111 1111 1111 1111 1110 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x57ffffff, /* 0101 0111 1111 1111 1111 1111 1111 1111 */
+
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+};
+
+
+static inline int
+ngx_is_token(uint32_t byte)
+{
+ return ((token_map[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0);
+}
+
+
+ngx_int_t
+ngx_js_check_header_name(u_char *name, size_t len)
+{
+ u_char *p, *end;
+
+ p = name;
+ end = p + len;
+
+ while (p < end) {
+ if (!ngx_is_token(*p)) {
+ return NGX_ERROR;
+ }
+
+ p++;
+ }
+
+ return NGX_OK;
+}
diff --git a/nginx/ngx_js_http.h b/nginx/ngx_js_http.h
new file mode 100644
index 00000000..f5b171c2
--- /dev/null
+++ b/nginx/ngx_js_http.h
@@ -0,0 +1,160 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) hongzhidao
+ * Copyright (C) Antoine Bonavita
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#ifndef _NGX_JS_HTTP_H_INCLUDED_
+#define _NGX_JS_HTTP_H_INCLUDED_
+
+
+typedef struct ngx_js_http_s ngx_js_http_t;
+
+
+typedef struct {
+ ngx_uint_t state;
+ ngx_uint_t code;
+ u_char *status_text;
+ u_char *status_text_end;
+ ngx_uint_t count;
+ ngx_flag_t chunked;
+ off_t content_length_n;
+
+ u_char *header_name_start;
+ u_char *header_name_end;
+ u_char *header_start;
+ u_char *header_end;
+} ngx_js_http_parse_t;
+
+
+typedef struct {
+ u_char *pos;
+ uint64_t chunk_size;
+ uint8_t state;
+ uint8_t last;
+} ngx_js_http_chunk_parse_t;
+
+
+typedef struct ngx_js_tb_elt_s ngx_js_tb_elt_t;
+
+struct ngx_js_tb_elt_s {
+ ngx_uint_t hash;
+ ngx_str_t key;
+ ngx_str_t value;
+ ngx_js_tb_elt_t *next;
+};
+
+
+typedef struct {
+ enum {
+ GUARD_NONE = 0,
+ GUARD_REQUEST,
+ GUARD_IMMUTABLE,
+ GUARD_RESPONSE,
+ } guard;
+ ngx_list_t header_list;
+ ngx_js_tb_elt_t *content_type;
+} ngx_js_headers_t;
+
+
+typedef struct {
+ enum {
+ CACHE_MODE_DEFAULT = 0,
+ CACHE_MODE_NO_STORE,
+ CACHE_MODE_RELOAD,
+ CACHE_MODE_NO_CACHE,
+ CACHE_MODE_FORCE_CACHE,
+ CACHE_MODE_ONLY_IF_CACHED,
+ } cache_mode;
+ enum {
+ CREDENTIALS_SAME_ORIGIN = 0,
+ CREDENTIALS_INCLUDE,
+ CREDENTIALS_OMIT,
+ } credentials;
+ enum {
+ MODE_NO_CORS = 0,
+ MODE_SAME_ORIGIN,
+ MODE_CORS,
+ MODE_NAVIGATE,
+ MODE_WEBSOCKET,
+ } mode;
+ njs_str_t url;
+ njs_str_t method;
+ u_char m[8];
+ uint8_t body_used;
+ njs_str_t body;
+ ngx_js_headers_t headers;
+ njs_opaque_value_t header_value;
+} ngx_js_request_t;
+
+
+typedef struct {
+ njs_str_t url;
+ ngx_int_t code;
+ njs_str_t status_text;
+ uint8_t body_used;
+ njs_chb_t chain;
+ ngx_js_headers_t headers;
+ njs_opaque_value_t header_value;
+} ngx_js_response_t;
+
+
+struct ngx_js_http_s {
+ ngx_log_t *log;
+ ngx_pool_t *pool;
+
+ ngx_resolver_ctx_t *ctx;
+ ngx_addr_t addr;
+ ngx_addr_t *addrs;
+ ngx_uint_t naddrs;
+ ngx_uint_t naddr;
+ in_port_t port;
+
+ ngx_peer_connection_t peer;
+ ngx_msec_t timeout;
+
+ ngx_int_t buffer_size;
+ ngx_int_t max_response_body_size;
+
+ unsigned header_only;
+
+#if (NGX_SSL)
+ ngx_str_t tls_name;
+ ngx_ssl_t *ssl;
+ njs_bool_t ssl_verify;
+#endif
+
+ ngx_buf_t *buffer;
+ ngx_buf_t *chunk;
+ njs_chb_t chain;
+
+ ngx_js_response_t response;
+
+ uint8_t done;
+ ngx_js_http_parse_t http_parse;
+ ngx_js_http_chunk_parse_t http_chunk_parse;
+ ngx_int_t (*process)(ngx_js_http_t *http);
+ ngx_int_t (*append_headers)(ngx_js_http_t *http,
+ ngx_js_headers_t *headers,
+ u_char *name, size_t len,
+ u_char *value, size_t vlen);
+ void (*ready_handler)(ngx_js_http_t *http);
+ void (*error_handler)(ngx_js_http_t *http,
+ const char *err);
+};
+
+
+ngx_resolver_ctx_t *ngx_js_http_resolve(ngx_js_http_t *http, ngx_resolver_t *r,
+ ngx_str_t *host, in_port_t port, ngx_msec_t timeout);
+void ngx_js_http_connect(ngx_js_http_t *http);
+void ngx_js_http_resolve_done(ngx_js_http_t *http);
+void ngx_js_http_close_peer(ngx_js_http_t *http);
+void ngx_js_http_trim(u_char **value, size_t *len,
+ int trim_c0_control_or_space);
+ngx_int_t ngx_js_check_header_name(u_char *name, size_t len);
+
+
+#endif /* _NGX_JS_HTTP_H_INCLUDED_ */