@@ -155,6 +155,7 @@ bool allow_empty_body_content_type;
155
155
#define S_HTTP "http://"
156
156
#define S_HTTPS "https://"
157
157
158
+ #define S_100 "HTTP/1.1 100 Continue"
158
159
#define S_200 "HTTP/1.1 200 OK"
159
160
#define S_301 "HTTP/1.1 301 Moved Permanently"
160
161
#define S_302 "HTTP/1.1 302 Found"
@@ -204,6 +205,30 @@ bool allow_empty_body_content_type;
204
205
* Array with predefined response data
205
206
*/
206
207
static TfwStr http_predef_resps [RESP_NUM ] = {
208
+ [RESP_100 ] = {
209
+ .chunks = (TfwStr []){
210
+ { .data = S_100 , .len = SLEN (S_100 ) },
211
+ { .data = S_CRLF S_F_DATE ,
212
+ .len = SLEN (S_CRLF S_F_DATE ), .hpack_idx = 33 },
213
+ { .data = NULL , .len = SLEN (S_V_DATE ) },
214
+ { .data = S_CRLF S_F_CONTENT_LENGTH ,
215
+ .len = SLEN (S_CRLF S_F_CONTENT_LENGTH ),
216
+ .hpack_idx = 28 },
217
+ { .data = "0" , .len = SLEN ("0" ) },
218
+ { .data = S_CRLF S_F_SERVER ,
219
+ .len = SLEN (S_CRLF S_F_SERVER ), .hpack_idx = 54 },
220
+ { .data = TFW_NAME "/" TFW_VERSION ,
221
+ .len = SLEN (TFW_NAME "/" TFW_VERSION ) },
222
+ { .data = S_CRLF S_CRLF , .len = SLEN (S_CRLF S_CRLF ) },
223
+ { .data = NULL , .len = 0 }, /* Reserved for Connection */
224
+ { .data = NULL , .len = 0 }, /* Reserved for CRLFCRLF */
225
+ { .data = NULL , .len = 0 }, /* Body */
226
+ },
227
+ .len = SLEN (S_200 S_CRLF S_F_DATE S_V_DATE S_CRLF
228
+ S_F_CONTENT_LENGTH "0" S_CRLF S_F_SERVER TFW_NAME
229
+ "/" TFW_VERSION S_CRLF S_CRLF ),
230
+ .nchunks = 11
231
+ },
207
232
[RESP_200 ] = {
208
233
.chunks = (TfwStr []){
209
234
{ .data = S_200 , .len = SLEN (S_200 ) },
@@ -688,6 +713,33 @@ tfw_h2_prep_resp(TfwHttpResp *resp, unsigned short status, TfwStr *msg)
688
713
return r ;
689
714
}
690
715
716
+ static int
717
+ tfw_h1_write_resp (TfwHttpResp * resp , unsigned short status , TfwStr * msg )
718
+ {
719
+ TfwMsgIter it ;
720
+ TfwStr * body = NULL ;
721
+ int r = 0 ;
722
+ TfwStr * c , * end , * field_c , * field_end ;
723
+
724
+ if ((r = tfw_http_msg_setup ((TfwHttpMsg * )resp , & it , msg -> len , 0 )))
725
+ return r ;
726
+
727
+ body = TFW_STR_BODY_CH (msg );
728
+ resp -> status = status ;
729
+ resp -> content_length = body -> len ;
730
+
731
+ TFW_STR_FOR_EACH_CHUNK (c , msg , end ) {
732
+ if (c -> data ) {
733
+ TFW_STR_FOR_EACH_CHUNK (field_c , c , field_end ) {
734
+ if ((r = tfw_msg_write (& it , field_c )))
735
+ return r ;
736
+ }
737
+ }
738
+ }
739
+
740
+ return r ;
741
+ }
742
+
691
743
/*
692
744
* Preparing custom HTTP1 response to a client.
693
745
* Set the "Connection:" header field if it was present in the request.
@@ -696,10 +748,6 @@ static int
696
748
tfw_h1_prep_resp (TfwHttpResp * resp , unsigned short status , TfwStr * msg )
697
749
{
698
750
TfwHttpReq * req = resp -> req ;
699
- TfwMsgIter it ;
700
- TfwStr * body = NULL ;
701
- int r = 0 ;
702
- TfwStr * c , * end , * field_c , * field_end ;
703
751
704
752
/* Set "Connection:" header field if needed. */
705
753
if (test_bit (TFW_HTTP_B_CONN_CLOSE , req -> flags )) {
@@ -720,23 +768,7 @@ tfw_h1_prep_resp(TfwHttpResp *resp, unsigned short status, TfwStr *msg)
720
768
msg -> len += SLEN (S_CRLF S_F_CONNECTION ) + SLEN (S_V_CONN_KA );
721
769
}
722
770
723
- if ((r = tfw_http_msg_setup ((TfwHttpMsg * )resp , & it , msg -> len , 0 )))
724
- return r ;
725
-
726
- body = TFW_STR_BODY_CH (msg );
727
- resp -> status = status ;
728
- resp -> content_length = body -> len ;
729
-
730
- TFW_STR_FOR_EACH_CHUNK (c , msg , end ) {
731
- if (c -> data ) {
732
- TFW_STR_FOR_EACH_CHUNK (field_c , c , field_end ) {
733
- if ((r = tfw_msg_write (& it , field_c )))
734
- return r ;
735
- }
736
- }
737
- }
738
-
739
- return r ;
771
+ return tfw_h1_write_resp (resp , status , msg );
740
772
}
741
773
742
774
/**
@@ -1033,6 +1065,8 @@ static inline resp_code_t
1033
1065
tfw_http_enum_resp_code (int status )
1034
1066
{
1035
1067
switch (status ) {
1068
+ case 100 :
1069
+ return RESP_100 ;
1036
1070
case 200 :
1037
1071
return RESP_200 ;
1038
1072
case 400 :
@@ -2072,6 +2106,7 @@ tfw_http_conn_fwd_unsent(TfwSrvConn *srv_conn, struct list_head *eq)
2072
2106
2073
2107
list_for_each_entry_safe_from (req , tmp , fwd_queue , fwd_list ) {
2074
2108
int ret = tfw_http_req_fwd_single (srv_conn , srv , req , eq );
2109
+
2075
2110
/*
2076
2111
* In case of busy work queue and absence of forwarded but
2077
2112
* unanswered request(s) in connection, the forwarding procedure
@@ -3605,6 +3640,18 @@ tfw_h1_rewrite_head_to_get(struct sk_buff **head_p)
3605
3640
return tfw_h1_rewrite_method_to_get (head_p , 1 );
3606
3641
}
3607
3642
3643
+ static int
3644
+ tfw_h1_req_del_expect_hdr (TfwHttpMsg * hm )
3645
+ {
3646
+ static TfwStr val = {};
3647
+
3648
+ if (test_bit (TFW_HTTP_B_EXPECT_CONTINUE , hm -> flags ))
3649
+ return tfw_http_msg_hdr_xfrm_str (hm , & val , TFW_HTTP_HDR_EXPECT ,
3650
+ false);
3651
+
3652
+ return 0 ;
3653
+ }
3654
+
3608
3655
/**
3609
3656
* Adjust the request before proxying it to real server.
3610
3657
*/
@@ -3634,6 +3681,10 @@ tfw_h1_adjust_req(TfwHttpReq *req)
3634
3681
return r ;
3635
3682
}
3636
3683
3684
+ r = tfw_h1_req_del_expect_hdr (hm );
3685
+ if (r )
3686
+ return r ;
3687
+
3637
3688
r = tfw_http_add_x_forwarded_for (hm );
3638
3689
if (r )
3639
3690
return r ;
@@ -4346,16 +4397,25 @@ __tfw_http_resp_fwd(TfwCliConn *cli_conn, struct list_head *ret_queue)
4346
4397
TfwHttpReq * req , * tmp ;
4347
4398
4348
4399
list_for_each_entry_safe (req , tmp , ret_queue , msg .seq_list ) {
4400
+ bool send_cont ;
4401
+
4349
4402
BUG_ON (!req -> resp );
4350
- tfw_http_resp_init_ss_flags (req -> resp );
4403
+ send_cont = test_bit (TFW_HTTP_B_CONTINUE_RESP ,
4404
+ req -> resp -> flags );
4405
+ if (!send_cont )
4406
+ tfw_http_resp_init_ss_flags (req -> resp );
4351
4407
if (tfw_cli_conn_send (cli_conn , (TfwMsg * )req -> resp )) {
4408
+ TFW_INC_STAT_BH (serv .msgs_otherr );
4352
4409
tfw_connection_close ((TfwConn * )cli_conn , true);
4353
4410
return ;
4354
4411
}
4355
4412
TFW_INC_STAT_BH (serv .msgs_forwarded );
4356
4413
tfw_inc_global_hm_stats (req -> resp -> status );
4357
4414
list_del_init (& req -> msg .seq_list );
4358
- tfw_http_resp_pair_free (req );
4415
+ if (!send_cont )
4416
+ tfw_http_resp_pair_free (req );
4417
+ else
4418
+ tfw_http_msg_free (req -> pair );
4359
4419
}
4360
4420
}
4361
4421
@@ -4462,9 +4522,6 @@ tfw_http_resp_fwd(TfwHttpResp *resp)
4462
4522
4463
4523
__tfw_http_resp_fwd (cli_conn , & ret_queue );
4464
4524
4465
- spin_unlock_bh (& cli_conn -> ret_qlock );
4466
- tfw_connection_put ((TfwConn * )(cli_conn ));
4467
-
4468
4525
/* Zap request/responses that were not sent due to an error. */
4469
4526
if (!list_empty (& ret_queue )) {
4470
4527
TfwHttpReq * tmp ;
@@ -4473,10 +4530,17 @@ tfw_http_resp_fwd(TfwHttpResp *resp)
4473
4530
__func__ , cli_conn , req -> resp );
4474
4531
BUG_ON (!req -> resp );
4475
4532
list_del_init (& req -> msg .seq_list );
4476
- tfw_http_resp_pair_free (req );
4533
+ if (!test_bit (TFW_HTTP_B_CONTINUE_RESP ,
4534
+ req -> resp -> flags ))
4535
+ tfw_http_resp_pair_free (req );
4536
+ else
4537
+ tfw_http_msg_free (req -> pair );
4477
4538
TFW_INC_STAT_BH (serv .msgs_otherr );
4478
4539
}
4479
4540
}
4541
+
4542
+ spin_unlock_bh (& cli_conn -> ret_qlock );
4543
+ tfw_connection_put ((TfwConn * )(cli_conn ));
4480
4544
}
4481
4545
4482
4546
int
@@ -6009,6 +6073,159 @@ tfw_http_check_ja5h_req_limit(TfwHttpReq *req)
6009
6073
return rate > limit ;
6010
6074
}
6011
6075
6076
+ /*
6077
+ * Whether we should delete request with ready 100-continue response from
6078
+ * @seq_queue. Delete request when the body or its part received, but request
6079
+ * still in @seq_queue with ready 100-continue response that not sent to client.
6080
+ *
6081
+ * RFC 9110 10.1.1:
6082
+ * A server MAY omit sending a 100 (Continue) response if it has already
6083
+ * received some or all of the content for the corresponding request, or
6084
+ * if the framing indicates that there is no content.
6085
+ */
6086
+ static bool
6087
+ tfw_http_should_del_continuation_seq_queue (TfwHttpReq * req )
6088
+ {
6089
+ return test_bit (TFW_HTTP_B_CONTINUE_QUEUED , req -> flags );
6090
+ }
6091
+
6092
+ /*
6093
+ * Remove request with ready 100-continue response from @seq_queue and free
6094
+ * the response.
6095
+ */
6096
+ static void
6097
+ tfw_http_del_continuation_seq_queue (TfwCliConn * cli_conn , TfwHttpReq * req )
6098
+ {
6099
+ struct list_head * seq_queue = & cli_conn -> seq_queue ;
6100
+ TfwHttpReq * queued_req = NULL ;
6101
+
6102
+ clear_bit (TFW_HTTP_B_CONTINUE_QUEUED , req -> flags );
6103
+
6104
+ /* Remove request from @seq_queue only if we ensure that it's there.
6105
+ * Otherwise request might be in @ret_queue, therefore we can't do
6106
+ * that under @seq_qlock.
6107
+ */
6108
+ spin_lock_bh (& cli_conn -> seq_qlock );
6109
+ list_for_each_entry (queued_req , seq_queue , msg .seq_list ) {
6110
+ if (queued_req != req )
6111
+ continue ;
6112
+
6113
+ list_del_init (& req -> msg .seq_list );
6114
+ tfw_http_msg_free ((TfwHttpMsg * )req -> resp );
6115
+ spin_unlock_bh (& cli_conn -> seq_qlock );
6116
+ return ;
6117
+ }
6118
+ spin_unlock_bh (& cli_conn -> seq_qlock );
6119
+
6120
+ spin_lock_bh (& cli_conn -> ret_qlock );
6121
+ /*
6122
+ * Need this section to ensure that request sent or removed from
6123
+ * @ret_queue due to error. We can't move forward if request still in
6124
+ * @ret_queue. In this case we just spin until @ret_queue drained.
6125
+ */
6126
+ BUG_ON (!list_empty (& req -> msg .seq_list ));
6127
+ spin_unlock_bh (& cli_conn -> ret_qlock );
6128
+ }
6129
+
6130
+ /**
6131
+ * Send 100-continue response to the client.
6132
+ *
6133
+ * When request is the first in the sequence (no pipelined requests), then
6134
+ * immediately send 100-continue response to the client, otherwise place
6135
+ * request into @seq_queue, the response will be sent later when one of
6136
+ * the queued responses will be forwarded by @tfw_http_resp_fwd.
6137
+ */
6138
+ static int
6139
+ tfw_http_send_continuation (TfwCliConn * cli_conn , TfwHttpReq * req )
6140
+ {
6141
+ TfwHttpResp * resp ;
6142
+ struct list_head * seq_queue = & cli_conn -> seq_queue ;
6143
+ TfwStr msg = MAX_PREDEF_RESP ;
6144
+
6145
+ tfw_http_prep_err_resp (req , 100 , & msg );
6146
+
6147
+ if (!(resp = tfw_http_msg_alloc_resp_light (req )))
6148
+ goto err ;
6149
+
6150
+ if (tfw_h1_write_resp (resp , 100 , & msg )) {
6151
+ tfw_http_msg_free ((TfwHttpMsg * )resp );
6152
+ goto err ;
6153
+ }
6154
+
6155
+ spin_lock_bh (& cli_conn -> seq_qlock );
6156
+ if (list_empty (seq_queue )) {
6157
+ /*
6158
+ * A queue is empty, don't hold a lock. Next request can be
6159
+ * added to the queue only on the current CPU when this
6160
+ * request will be processed.
6161
+ */
6162
+ spin_unlock_bh (& cli_conn -> seq_qlock );
6163
+ tfw_connection_get ((TfwConn * )(cli_conn ));
6164
+ if (tfw_cli_conn_send (cli_conn , (TfwMsg * )resp )) {
6165
+ tfw_http_msg_free ((TfwHttpMsg * )resp );
6166
+ tfw_connection_put ((TfwConn * )(cli_conn ));
6167
+ goto err ;
6168
+ }
6169
+ tfw_inc_global_hm_stats (resp -> status );
6170
+ tfw_http_msg_free ((TfwHttpMsg * )resp );
6171
+ tfw_connection_put ((TfwConn * )(cli_conn ));
6172
+ } else {
6173
+ set_bit (TFW_HTTP_B_CONTINUE_QUEUED , req -> flags );
6174
+ set_bit (TFW_HTTP_B_CONTINUE_RESP , resp -> flags );
6175
+ set_bit (TFW_HTTP_B_RESP_READY , resp -> flags );
6176
+ list_add_tail (& req -> msg .seq_list , seq_queue );
6177
+ spin_unlock_bh (& cli_conn -> seq_qlock );
6178
+ }
6179
+
6180
+ return 0 ;
6181
+
6182
+ err :
6183
+ TFW_INC_STAT_BH (serv .msgs_otherr );
6184
+ return T_BAD ;
6185
+ }
6186
+
6187
+ /**
6188
+ * Whether we should send 100-continue response.
6189
+ *
6190
+ * Circumstances in which Tempesta must respond with 100-continue code:
6191
+ * 1. Headers are fully parsed.
6192
+ * 2. "Expect" header is present in request.
6193
+ * 3. Vesrion is HTTP/1.1.
6194
+ *
6195
+ * RFC 9110 10.1.1:
6196
+ * - A server that receives a 100-continue expectation in an HTTP/1.0 request
6197
+ * MUST ignore that expectation.
6198
+ * - A server MAY omit sending a 100 (Continue) response if it has already
6199
+ * received some or all of the content for the corresponding request, or if the
6200
+ * framing indicates that there is no content.
6201
+ */
6202
+ static bool
6203
+ tfw_http_should_handle_expect (TfwHttpReq * req )
6204
+ {
6205
+ return test_bit (TFW_HTTP_B_HEADERS_PARSED , req -> flags ) &&
6206
+ test_bit (TFW_HTTP_B_EXPECT_CONTINUE , req -> flags ) &&
6207
+ req -> version == TFW_HTTP_VER_11 ;
6208
+ }
6209
+
6210
+ /*
6211
+ * Handle `Expect: 100-continue` in the request.
6212
+ */
6213
+ static int
6214
+ tfw_http_handle_expect_request (TfwCliConn * conn , TfwHttpReq * req )
6215
+ {
6216
+ if (!req -> body .len )
6217
+ return tfw_http_send_continuation (conn , req );
6218
+ else if (tfw_http_should_del_continuation_seq_queue (req ))
6219
+ /**
6220
+ * Part of the body received, but 100-continue didn't send,
6221
+ * however handled. It implies it was queued, try to remove it
6222
+ * from queue.
6223
+ */
6224
+ tfw_http_del_continuation_seq_queue (conn , req );
6225
+
6226
+ return T_OK ;
6227
+ }
6228
+
6012
6229
/**
6013
6230
* @return zero on success and negative value otherwise.
6014
6231
* TODO enter the function depending on current GFSM state.
@@ -6147,6 +6364,14 @@ tfw_http_req_process(TfwConn *conn, TfwStream *stream, struct sk_buff *skb,
6147
6364
"postponed request has been filtered out" ,
6148
6365
HTTP2_ECODE_PROTO );
6149
6366
}
6367
+
6368
+ if (tfw_http_should_handle_expect (req )) {
6369
+ r = tfw_http_handle_expect_request ((TfwCliConn * )conn ,
6370
+ req );
6371
+ if (unlikely (r ))
6372
+ return r ;
6373
+ }
6374
+
6150
6375
/*
6151
6376
* T_POSTPONE status means that parsing succeeded
6152
6377
* but more data is needed to complete it. Lower layers
@@ -6168,6 +6393,10 @@ tfw_http_req_process(TfwConn *conn, TfwStream *stream, struct sk_buff *skb,
6168
6393
}
6169
6394
}
6170
6395
6396
+ /* The body received, remove 100-continue from queue. */
6397
+ if (unlikely (tfw_http_should_del_continuation_seq_queue (req )))
6398
+ tfw_http_del_continuation_seq_queue ((TfwCliConn * )conn , req );
6399
+
6171
6400
req -> ja5h .method = req -> method ;
6172
6401
6173
6402
if (tfw_http_check_ja5h_req_limit (req )) {
0 commit comments