aboutsummaryrefslogtreecommitdiff
path: root/src/http/ngx_http_upstream.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/http/ngx_http_upstream.c')
-rw-r--r--src/http/ngx_http_upstream.c159
1 files changed, 158 insertions, 1 deletions
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index d4cf1b7fe..de0f92a4f 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -48,6 +48,9 @@ static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
static void ngx_http_upstream_read_request_handler(ngx_http_request_t *r);
static void ngx_http_upstream_process_header(ngx_http_request_t *r,
ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_process_early_hints(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_early_hints_writer(ngx_http_request_t *r);
static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
@@ -1120,7 +1123,7 @@ ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)
return NGX_ERROR;
}
- if (rc == NGX_AGAIN) {
+ if (rc == NGX_AGAIN || rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) {
rc = NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
@@ -2530,6 +2533,18 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
continue;
}
+ if (rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) {
+ rc = ngx_http_upstream_process_early_hints(r, u);
+
+ if (rc == NGX_OK) {
+ rc = u->process_header(r);
+
+ if (rc == NGX_AGAIN) {
+ continue;
+ }
+ }
+ }
+
break;
}
@@ -2568,6 +2583,148 @@ ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
static ngx_int_t
+ngx_http_upstream_process_early_hints(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ u_char *p;
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *h, *ho;
+ ngx_connection_t *c;
+
+ c = r->connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream early hints");
+
+ if (u->conf->pass_early_hints) {
+
+ u->early_hints_length += u->buffer.pos - u->buffer.start;
+
+ if (u->early_hints_length <= (off_t) u->conf->buffer_size) {
+
+ part = &u->headers_in.headers.part;
+ h = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ h = part->elts;
+ i = 0;
+ }
+
+ if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
+ h[i].lowcase_key, h[i].key.len))
+ {
+ continue;
+ }
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = h[i];
+ }
+
+ if (ngx_http_send_early_hints(r) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (c->buffered) {
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ r->write_event_handler = ngx_http_upstream_early_hints_writer;
+ }
+
+ } else {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "upstream sent too big early hints");
+ }
+ }
+
+ ngx_http_clean_header(r);
+
+ ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
+ u->headers_in.content_length_n = -1;
+ u->headers_in.last_modified_time = -1;
+
+ if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ p = u->buffer.pos;
+
+ u->buffer.pos = u->buffer.start;
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+ u->buffer.pos += r->cache->header_start;
+ }
+
+#endif
+
+ u->buffer.last = ngx_movemem(u->buffer.pos, p, u->buffer.last - p);
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_early_hints_writer(ngx_http_request_t *r)
+{
+ ngx_connection_t *c;
+ ngx_http_upstream_t *u;
+
+ c = r->connection;
+ u = r->upstream;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream early hints writer");
+
+ c->log->action = "sending early hints to client";
+
+ if (ngx_http_write_filter(r, NULL) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (!c->buffered) {
+ if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
+ r->write_event_handler =
+ ngx_http_upstream_wr_check_broken_connection;
+
+ } else {
+ r->write_event_handler = ngx_http_request_empty_handler;
+ }
+ }
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ }
+}
+
+
+static ngx_int_t
ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ngx_msec_t timeout;