@@ -138,6 +138,8 @@ typedef struct {
138
138
ngx_chain_t * free ;
139
139
ngx_chain_t * busy ;
140
140
141
+ ngx_buf_t * trailers ;
142
+
141
143
unsigned head :1 ;
142
144
unsigned internal_chunked :1 ;
143
145
unsigned header_sent :1 ;
@@ -163,6 +165,8 @@ static ngx_int_t ngx_http_proxy_non_buffered_copy_filter(void *data,
163
165
ssize_t bytes );
164
166
static ngx_int_t ngx_http_proxy_non_buffered_chunked_filter (void * data ,
165
167
ssize_t bytes );
168
+ static ngx_int_t ngx_http_proxy_process_trailer (ngx_http_request_t * r ,
169
+ ngx_buf_t * buf );
166
170
static void ngx_http_proxy_abort_request (ngx_http_request_t * r );
167
171
static void ngx_http_proxy_finalize_request (ngx_http_request_t * r ,
168
172
ngx_int_t rc );
@@ -457,6 +461,13 @@ static ngx_command_t ngx_http_proxy_commands[] = {
457
461
offsetof(ngx_http_proxy_loc_conf_t , upstream .pass_request_body ),
458
462
NULL },
459
463
464
+ { ngx_string ("proxy_pass_trailers" ),
465
+ NGX_HTTP_MAIN_CONF |NGX_HTTP_SRV_CONF |NGX_HTTP_LOC_CONF |NGX_CONF_FLAG ,
466
+ ngx_conf_set_flag_slot ,
467
+ NGX_HTTP_LOC_CONF_OFFSET ,
468
+ offsetof(ngx_http_proxy_loc_conf_t , upstream .pass_trailers ),
469
+ NULL },
470
+
460
471
{ ngx_string ("proxy_buffer_size" ),
461
472
NGX_HTTP_MAIN_CONF |NGX_HTTP_SRV_CONF |NGX_HTTP_LOC_CONF |NGX_CONF_TAKE1 ,
462
473
ngx_conf_set_size_slot ,
@@ -2181,11 +2192,12 @@ ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
2181
2192
static ngx_int_t
2182
2193
ngx_http_proxy_chunked_filter (ngx_event_pipe_t * p , ngx_buf_t * buf )
2183
2194
{
2184
- ngx_int_t rc ;
2185
- ngx_buf_t * b , * * prev ;
2186
- ngx_chain_t * cl ;
2187
- ngx_http_request_t * r ;
2188
- ngx_http_proxy_ctx_t * ctx ;
2195
+ ngx_int_t rc ;
2196
+ ngx_buf_t * b , * * prev ;
2197
+ ngx_chain_t * cl ;
2198
+ ngx_http_request_t * r ;
2199
+ ngx_http_proxy_ctx_t * ctx ;
2200
+ ngx_http_proxy_loc_conf_t * plcf ;
2189
2201
2190
2202
if (buf -> pos == buf -> last ) {
2191
2203
return NGX_OK ;
@@ -2216,11 +2228,39 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
2216
2228
}
2217
2229
2218
2230
b = NULL ;
2231
+
2232
+ if (ctx -> trailers ) {
2233
+ rc = ngx_http_proxy_process_trailer (r , buf );
2234
+
2235
+ if (rc == NGX_ERROR ) {
2236
+ return NGX_ERROR ;
2237
+ }
2238
+
2239
+ if (rc == NGX_OK ) {
2240
+
2241
+ /* a whole response has been parsed successfully */
2242
+
2243
+ p -> length = 0 ;
2244
+ r -> upstream -> keepalive = !r -> upstream -> headers_in .connection_close ;
2245
+
2246
+ if (buf -> pos != buf -> last ) {
2247
+ ngx_log_error (NGX_LOG_WARN , p -> log , 0 ,
2248
+ "upstream sent data after trailers" );
2249
+ r -> upstream -> keepalive = 0 ;
2250
+ }
2251
+ }
2252
+
2253
+ goto free_buf ;
2254
+ }
2255
+
2256
+ plcf = ngx_http_get_module_loc_conf (r , ngx_http_proxy_module );
2257
+
2219
2258
prev = & buf -> shadow ;
2220
2259
2221
2260
for ( ;; ) {
2222
2261
2223
- rc = ngx_http_parse_chunked (r , buf , & ctx -> chunked );
2262
+ rc = ngx_http_parse_chunked (r , buf , & ctx -> chunked ,
2263
+ plcf -> upstream .pass_trailers );
2224
2264
2225
2265
if (rc == NGX_OK ) {
2226
2266
@@ -2275,6 +2315,19 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
2275
2315
2276
2316
if (rc == NGX_DONE ) {
2277
2317
2318
+ if (plcf -> upstream .pass_trailers ) {
2319
+ rc = ngx_http_proxy_process_trailer (r , buf );
2320
+
2321
+ if (rc == NGX_ERROR ) {
2322
+ return NGX_ERROR ;
2323
+ }
2324
+
2325
+ if (rc == NGX_AGAIN ) {
2326
+ p -> length = 1 ;
2327
+ break ;
2328
+ }
2329
+ }
2330
+
2278
2331
/* a whole response has been parsed successfully */
2279
2332
2280
2333
p -> length = 0 ;
@@ -2306,6 +2359,8 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
2306
2359
return NGX_ERROR ;
2307
2360
}
2308
2361
2362
+ free_buf :
2363
+
2309
2364
ngx_log_debug2 (NGX_LOG_DEBUG_HTTP , p -> log , 0 ,
2310
2365
"http proxy chunked state %ui, length %O" ,
2311
2366
ctx -> chunked .state , p -> length );
@@ -2401,11 +2456,14 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
2401
2456
{
2402
2457
ngx_http_request_t * r = data ;
2403
2458
2404
- ngx_int_t rc ;
2405
- ngx_buf_t * b , * buf ;
2406
- ngx_chain_t * cl , * * ll ;
2407
- ngx_http_upstream_t * u ;
2408
- ngx_http_proxy_ctx_t * ctx ;
2459
+ ngx_int_t rc ;
2460
+ ngx_buf_t * b , * buf ;
2461
+ ngx_chain_t * cl , * * ll ;
2462
+ ngx_http_upstream_t * u ;
2463
+ ngx_http_proxy_ctx_t * ctx ;
2464
+ ngx_http_proxy_loc_conf_t * plcf ;
2465
+
2466
+ plcf = ngx_http_get_module_loc_conf (r , ngx_http_proxy_module );
2409
2467
2410
2468
ctx = ngx_http_get_module_ctx (r , ngx_http_proxy_module );
2411
2469
@@ -2419,13 +2477,38 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
2419
2477
buf -> pos = buf -> last ;
2420
2478
buf -> last += bytes ;
2421
2479
2480
+ if (ctx -> trailers ) {
2481
+ rc = ngx_http_proxy_process_trailer (r , buf );
2482
+
2483
+ if (rc == NGX_ERROR ) {
2484
+ return NGX_ERROR ;
2485
+ }
2486
+
2487
+ if (rc == NGX_OK ) {
2488
+
2489
+ /* a whole response has been parsed successfully */
2490
+
2491
+ r -> upstream -> keepalive = !u -> headers_in .connection_close ;
2492
+ u -> length = 0 ;
2493
+
2494
+ if (buf -> pos != buf -> last ) {
2495
+ ngx_log_error (NGX_LOG_WARN , r -> connection -> log , 0 ,
2496
+ "upstream sent data after trailers" );
2497
+ u -> keepalive = 0 ;
2498
+ }
2499
+ }
2500
+
2501
+ return NGX_OK ;
2502
+ }
2503
+
2422
2504
for (cl = u -> out_bufs , ll = & u -> out_bufs ; cl ; cl = cl -> next ) {
2423
2505
ll = & cl -> next ;
2424
2506
}
2425
2507
2426
2508
for ( ;; ) {
2427
2509
2428
- rc = ngx_http_parse_chunked (r , buf , & ctx -> chunked );
2510
+ rc = ngx_http_parse_chunked (r , buf , & ctx -> chunked ,
2511
+ plcf -> upstream .pass_trailers );
2429
2512
2430
2513
if (rc == NGX_OK ) {
2431
2514
@@ -2467,6 +2550,19 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
2467
2550
2468
2551
if (rc == NGX_DONE ) {
2469
2552
2553
+ if (plcf -> upstream .pass_trailers ) {
2554
+ rc = ngx_http_proxy_process_trailer (r , buf );
2555
+
2556
+ if (rc == NGX_ERROR ) {
2557
+ return NGX_ERROR ;
2558
+ }
2559
+
2560
+ if (rc == NGX_AGAIN ) {
2561
+ u -> length = 1 ;
2562
+ break ;
2563
+ }
2564
+ }
2565
+
2470
2566
/* a whole response has been parsed successfully */
2471
2567
2472
2568
u -> keepalive = !u -> headers_in .connection_close ;
@@ -2497,6 +2593,115 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
2497
2593
}
2498
2594
2499
2595
2596
+ static ngx_int_t
2597
+ ngx_http_proxy_process_trailer (ngx_http_request_t * r , ngx_buf_t * buf )
2598
+ {
2599
+ size_t len ;
2600
+ ngx_int_t rc ;
2601
+ ngx_buf_t * b ;
2602
+ ngx_table_elt_t * h ;
2603
+ ngx_http_proxy_ctx_t * ctx ;
2604
+ ngx_http_proxy_loc_conf_t * plcf ;
2605
+
2606
+ plcf = ngx_http_get_module_loc_conf (r , ngx_http_proxy_module );
2607
+
2608
+ ctx = ngx_http_get_module_ctx (r , ngx_http_proxy_module );
2609
+
2610
+ if (ctx -> trailers == NULL ) {
2611
+ ctx -> trailers = ngx_create_temp_buf (r -> pool ,
2612
+ plcf -> upstream .buffer_size );
2613
+ if (ctx -> trailers == NULL ) {
2614
+ return NGX_ERROR ;
2615
+ }
2616
+ }
2617
+
2618
+ b = ctx -> trailers ;
2619
+ len = ngx_min (buf -> last - buf -> pos , b -> end - b -> last );
2620
+
2621
+ b -> last = ngx_cpymem (b -> last , buf -> pos , len );
2622
+
2623
+ for ( ;; ) {
2624
+
2625
+ rc = ngx_http_parse_header_line (r , b , 1 );
2626
+
2627
+ if (rc == NGX_OK ) {
2628
+
2629
+ /* a header line has been parsed successfully */
2630
+
2631
+ h = ngx_list_push (& r -> upstream -> headers_in .trailers );
2632
+ if (h == NULL ) {
2633
+ return NGX_ERROR ;
2634
+ }
2635
+
2636
+ h -> hash = r -> header_hash ;
2637
+
2638
+ h -> key .len = r -> header_name_end - r -> header_name_start ;
2639
+ h -> value .len = r -> header_end - r -> header_start ;
2640
+
2641
+ h -> key .data = ngx_pnalloc (r -> pool ,
2642
+ h -> key .len + 1 + h -> value .len + 1 + h -> key .len );
2643
+ if (h -> key .data == NULL ) {
2644
+ h -> hash = 0 ;
2645
+ return NGX_ERROR ;
2646
+ }
2647
+
2648
+ h -> value .data = h -> key .data + h -> key .len + 1 ;
2649
+ h -> lowcase_key = h -> key .data + h -> key .len + 1 + h -> value .len + 1 ;
2650
+
2651
+ ngx_memcpy (h -> key .data , r -> header_name_start , h -> key .len );
2652
+ h -> key .data [h -> key .len ] = '\0' ;
2653
+ ngx_memcpy (h -> value .data , r -> header_start , h -> value .len );
2654
+ h -> value .data [h -> value .len ] = '\0' ;
2655
+
2656
+ if (h -> key .len == r -> lowcase_index ) {
2657
+ ngx_memcpy (h -> lowcase_key , r -> lowcase_header , h -> key .len );
2658
+
2659
+ } else {
2660
+ ngx_strlow (h -> lowcase_key , h -> key .data , h -> key .len );
2661
+ }
2662
+
2663
+ ngx_log_debug2 (NGX_LOG_DEBUG_HTTP , r -> connection -> log , 0 ,
2664
+ "http proxy trailer: \"%V: %V\"" ,
2665
+ & h -> key , & h -> value );
2666
+ continue ;
2667
+ }
2668
+
2669
+ if (rc == NGX_HTTP_PARSE_HEADER_DONE ) {
2670
+
2671
+ /* a whole header has been parsed successfully */
2672
+
2673
+ buf -> pos += len - (b -> last - b -> pos );
2674
+
2675
+ ngx_log_debug0 (NGX_LOG_DEBUG_HTTP , r -> connection -> log , 0 ,
2676
+ "http proxy trailer done" );
2677
+
2678
+ return NGX_OK ;
2679
+ }
2680
+
2681
+ if (rc == NGX_AGAIN ) {
2682
+ buf -> pos += len ;
2683
+
2684
+ if (b -> last == b -> end ) {
2685
+ ngx_log_error (NGX_LOG_ERR , r -> connection -> log , 0 ,
2686
+ "upstream sent too big trailers" );
2687
+ return NGX_ERROR ;
2688
+ }
2689
+
2690
+ return NGX_AGAIN ;
2691
+ }
2692
+
2693
+ /* rc == NGX_HTTP_PARSE_INVALID_HEADER */
2694
+
2695
+ ngx_log_error (NGX_LOG_ERR , r -> connection -> log , 0 ,
2696
+ "upstream sent invalid trailer: \"%*s\\x%02xd...\"" ,
2697
+ r -> header_end - r -> header_name_start ,
2698
+ r -> header_name_start , * r -> header_end );
2699
+
2700
+ return NGX_ERROR ;
2701
+ }
2702
+ }
2703
+
2704
+
2500
2705
static void
2501
2706
ngx_http_proxy_abort_request (ngx_http_request_t * r )
2502
2707
{
@@ -3379,6 +3584,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
3379
3584
3380
3585
conf -> upstream .pass_request_headers = NGX_CONF_UNSET ;
3381
3586
conf -> upstream .pass_request_body = NGX_CONF_UNSET ;
3587
+ conf -> upstream .pass_trailers = NGX_CONF_UNSET ;
3382
3588
3383
3589
#if (NGX_HTTP_CACHE )
3384
3590
conf -> upstream .cache = NGX_CONF_UNSET ;
@@ -3721,6 +3927,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
3721
3927
ngx_conf_merge_value (conf -> upstream .pass_request_body ,
3722
3928
prev -> upstream .pass_request_body , 1 );
3723
3929
3930
+ ngx_conf_merge_value (conf -> upstream .pass_trailers ,
3931
+ prev -> upstream .pass_trailers , 0 );
3932
+
3724
3933
ngx_conf_merge_value (conf -> upstream .intercept_errors ,
3725
3934
prev -> upstream .intercept_errors , 0 );
3726
3935
0 commit comments