18
18
#include "util_fcgi.h"
19
19
#include "util_script.h"
20
20
#include "ap_expr.h"
21
+ #include <ctype.h>
21
22
22
23
module AP_MODULE_DECLARE_DATA proxy_fcgi_module ;
23
24
@@ -593,6 +594,72 @@ static int handle_headers(request_rec *r, int *state,
593
594
return 0 ;
594
595
}
595
596
597
+ /**
598
+ * @struct ap_varbuf
599
+ * @brief Structure representing a dynamically resizable buffer.
600
+ *
601
+ * This structure holds a character buffer along with its current length and the total
602
+ * allocated size. The buffer is allocated from the provided APR pool.
603
+ */
604
+ typedef struct {
605
+ char * buf ;
606
+ apr_size_t cur_len ;
607
+ apr_size_t size ;
608
+ apr_pool_t * pool ;
609
+ } ap_varbuf ;
610
+
611
+ /**
612
+ * @brief Allocate and initialize a new dynamic variable buffer.
613
+ *
614
+ * This function creates a new ap_varbuf structure, allocates an initial buffer
615
+ * of the specified size from the given APR pool, and initializes its members.
616
+ *
617
+ * @param vb A pointer to the pointer that will hold the allocated ap_varbuf.
618
+ * @param p The APR memory pool from which to allocate the buffer.
619
+ * @param initial_size The initial size in bytes for the buffer.
620
+ * @return APR_SUCCESS on success.
621
+ */
622
+ static apr_status_t ap_varbuf_make (ap_varbuf * * vb , apr_pool_t * p , apr_size_t initial_size )
623
+ {
624
+ * vb = apr_pcalloc (p , sizeof (ap_varbuf ));
625
+ (* vb )-> pool = p ;
626
+ (* vb )-> buf = apr_palloc (p , initial_size );
627
+ (* vb )-> size = initial_size ;
628
+ (* vb )-> cur_len = 0 ;
629
+ (* vb )-> buf [0 ] = '\0' ;
630
+ return APR_SUCCESS ;
631
+ }
632
+
633
+
634
+ /**
635
+ * @brief Append a specified number of characters to a dynamic variable buffer.
636
+ *
637
+ * This function appends up to @c len characters from the string @c str to the
638
+ * dynamic buffer represented by @c vb. If the buffer is not large enough to hold
639
+ * the additional characters plus a null terminator, the function reallocates the
640
+ * buffer with a larger size. The buffer is always null-terminated after the operation.
641
+ *
642
+ * @param vb The dynamic variable buffer to which the string will be appended.
643
+ * @param str The string containing the characters to append.
644
+ * @param len The number of characters from @c str to append.
645
+ * @return APR_SUCCESS on success.
646
+ */
647
+ static apr_status_t ap_varbuf_strncat (ap_varbuf * vb , const char * str , apr_size_t len )
648
+ {
649
+ if (vb -> cur_len + len + 1 > vb -> size ) {
650
+ apr_size_t new_size = vb -> cur_len + len + 1 ;
651
+ char * newbuf = apr_palloc (vb -> pool , new_size );
652
+ memcpy (newbuf , vb -> buf , vb -> cur_len );
653
+ vb -> buf = newbuf ;
654
+ vb -> size = new_size ;
655
+ }
656
+ memcpy (vb -> buf + vb -> cur_len , str , len );
657
+ vb -> cur_len += len ;
658
+ vb -> buf [vb -> cur_len ] = '\0' ;
659
+ return APR_SUCCESS ;
660
+ }
661
+
662
+
596
663
static apr_status_t dispatch (proxy_conn_rec * conn , proxy_dir_conf * conf ,
597
664
request_rec * r , apr_pool_t * setaside_pool ,
598
665
apr_uint16_t request_id , const char * * err ,
@@ -614,6 +681,9 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
614
681
char stack_iobuf [AP_IOBUFSIZE ];
615
682
apr_size_t iobuf_size = AP_IOBUFSIZE ;
616
683
char * iobuf = stack_iobuf ;
684
+ /* Create our dynamic header buffer for large headers */
685
+ ap_varbuf * header_vb = NULL ;
686
+ ap_varbuf_make (& header_vb , r -> pool , 1024 );
617
687
618
688
* err = NULL ;
619
689
if (conn -> worker -> s -> io_buffer_size_set ) {
@@ -823,61 +893,78 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
823
893
APR_BRIGADE_INSERT_TAIL (ob , b );
824
894
825
895
if (! seen_end_of_headers ) {
826
- int st = handle_headers (r , & header_state ,
827
- iobuf , readbuflen );
828
-
829
- if (st == 1 ) {
830
- int status ;
896
+ /* Try our dynamic header buffer approach first */
897
+ ap_varbuf_strncat (header_vb , iobuf , readbuflen );
898
+ if (strstr (header_vb -> buf , "\r\n\r\n" ) != NULL ) {
899
+ while (header_vb -> cur_len > 0 &&
900
+ isspace ((unsigned char )header_vb -> buf [0 ])) {
901
+ memmove (header_vb -> buf , header_vb -> buf + 1 , header_vb -> cur_len - 1 );
902
+ header_vb -> cur_len -- ;
903
+ header_vb -> buf [header_vb -> cur_len ] = '\0' ;
904
+ }
831
905
seen_end_of_headers = 1 ;
832
-
833
- status = ap_scan_script_header_err_brigade_ex (r , ob ,
834
- NULL , APLOG_MODULE_INDEX );
835
-
836
- /* FCGI has its own body framing mechanism which we don't
837
- * match against any provided Content-Length, so let the
838
- * core determine C-L vs T-E based on what's actually sent.
839
- */
840
- if (!apr_table_get (r -> subprocess_env , AP_TRUST_CGILIKE_CL_ENVVAR ))
841
- apr_table_unset (r -> headers_out , "Content-Length" );
842
- apr_table_unset (r -> headers_out , "Transfer-Encoding" );
843
-
844
- /* suck in all the rest */
845
- if (status != OK ) {
846
- apr_bucket * tmp_b ;
847
- apr_brigade_cleanup (ob );
848
- tmp_b = apr_bucket_eos_create (c -> bucket_alloc );
849
- APR_BRIGADE_INSERT_TAIL (ob , tmp_b );
850
-
851
- * has_responded = 1 ;
852
- r -> status = status ;
853
- rv = ap_pass_brigade (r -> output_filters , ob );
854
- if (rv != APR_SUCCESS ) {
855
- * err = "passing headers brigade to output filters" ;
856
- break ;
906
+ {
907
+ int status_hdr ;
908
+ apr_bucket_brigade * tmp_bb = apr_brigade_create (r -> pool , c -> bucket_alloc );
909
+ apr_bucket * hdr_bucket = apr_bucket_heap_create (header_vb -> buf ,
910
+ header_vb -> cur_len , NULL , c -> bucket_alloc );
911
+ APR_BRIGADE_INSERT_TAIL (tmp_bb , hdr_bucket );
912
+ hdr_bucket = apr_bucket_eos_create (c -> bucket_alloc );
913
+ APR_BRIGADE_INSERT_TAIL (tmp_bb , hdr_bucket );
914
+ status_hdr = ap_scan_script_header_err_brigade_ex (r , tmp_bb ,
915
+ NULL , APLOG_MODULE_INDEX );
916
+ apr_brigade_cleanup (tmp_bb );
917
+ if (status_hdr != OK ) {
918
+ * has_responded = 1 ;
919
+ return APR_EGENERAL ;
857
920
}
858
- else if (status == HTTP_NOT_MODIFIED
859
- || status == HTTP_PRECONDITION_FAILED ) {
860
- /* Special 'status' cases handled:
861
- * 1) HTTP 304 response MUST NOT contain
862
- * a message-body, ignore it.
863
- * 2) HTTP 412 response.
864
- * The break is not added since there might
865
- * be more bytes to read from the FCGI
866
- * connection. Even if the message-body is
867
- * ignored (and the EOS bucket has already
868
- * been sent) we want to avoid subsequent
869
- * bogus reads. */
870
- ignore_body = 1 ;
921
+ }
922
+ {
923
+ char * hdr_end = strstr (header_vb -> buf , "\r\n\r\n" ) + 4 ;
924
+ apr_size_t hdr_total = header_vb -> cur_len ;
925
+ apr_size_t remaining = hdr_total - (hdr_end - header_vb -> buf );
926
+ if (remaining > 0 ) {
927
+ apr_bucket * rem_bucket = apr_bucket_heap_create (hdr_end ,
928
+ remaining , NULL , c -> bucket_alloc );
929
+ APR_BRIGADE_INSERT_TAIL (ob , rem_bucket );
871
930
}
872
- else {
873
- ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , APLOGNO (01070 )
931
+ }
932
+ }
933
+ else {
934
+ int st = handle_headers (r , & header_state ,
935
+ iobuf , readbuflen );
936
+ if (st == 1 ) {
937
+ int status ;
938
+ seen_end_of_headers = 1 ;
939
+ status = ap_scan_script_header_err_brigade_ex (r , ob ,
940
+ NULL , APLOG_MODULE_INDEX );
941
+ if (!apr_table_get (r -> subprocess_env , AP_TRUST_CGILIKE_CL_ENVVAR ))
942
+ apr_table_unset (r -> headers_out , "Content-Length" );
943
+ apr_table_unset (r -> headers_out , "Transfer-Encoding" );
944
+ if (status != OK ) {
945
+ apr_bucket * tmp_b ;
946
+ apr_brigade_cleanup (ob );
947
+ tmp_b = apr_bucket_eos_create (c -> bucket_alloc );
948
+ APR_BRIGADE_INSERT_TAIL (ob , tmp_b );
949
+ * has_responded = 1 ;
950
+ r -> status = status ;
951
+ rv = ap_pass_brigade (r -> output_filters , ob );
952
+ if (rv != APR_SUCCESS ) {
953
+ * err = "passing headers brigade to output filters" ;
954
+ break ;
955
+ }
956
+ else if (status == HTTP_NOT_MODIFIED
957
+ || status == HTTP_PRECONDITION_FAILED ) {
958
+ ignore_body = 1 ;
959
+ }
960
+ else {
961
+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , APLOGNO (01070 )
874
962
"Error parsing script headers" );
875
- rv = APR_EINVAL ;
876
- break ;
963
+ rv = APR_EINVAL ;
964
+ break ;
965
+ }
877
966
}
878
- }
879
-
880
- if (ap_proxy_should_override (conf , r -> status ) && ap_is_initial_req (r )) {
967
+ if (ap_proxy_should_override (conf , r -> status ) && ap_is_initial_req (r )) {
881
968
/*
882
969
* set script_error_status to discard
883
970
* everything after the headers
@@ -912,6 +999,7 @@ static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
912
999
* headers, so this part of the data will need
913
1000
* to persist. */
914
1001
apr_bucket_setaside (b , setaside_pool );
1002
+ }
915
1003
}
916
1004
} else {
917
1005
/* we've already passed along the headers, so now pass
0 commit comments