@@ -257,6 +257,12 @@ static int on_body(http_parser *parser, const char *buf, size_t len)
257257 http_parser_context * ctx = (http_parser_context * ) parser -> data ;
258258 size_t max_len ;
259259
260+ /* Saw data when we expected not to (eg, in consume_response_body) */
261+ if (ctx -> output_buf == NULL && ctx -> output_size == 0 ) {
262+ ctx -> parse_status = PARSE_STATUS_NO_OUTPUT ;
263+ return 0 ;
264+ }
265+
260266 assert (ctx -> output_size >= ctx -> output_written );
261267
262268 max_len = min (ctx -> output_size - ctx -> output_written , len );
@@ -556,117 +562,6 @@ static int http_client_connect(git_http_client *client)
556562 return error ;
557563}
558564
559- int git_http_client_send_request (
560- git_http_client * client ,
561- git_http_request * request )
562- {
563- int error = -1 ;
564-
565- assert (client && request );
566-
567- http_parser_init (& client -> parser , HTTP_RESPONSE );
568- git_buf_clear (& client -> read_buf );
569-
570- if (git_trace_level () >= GIT_TRACE_DEBUG ) {
571- git_buf url = GIT_BUF_INIT ;
572- git_net_url_fmt (& url , request -> url );
573- git_trace (GIT_TRACE_DEBUG , "Sending %s request to %s" ,
574- name_for_method (request -> method ),
575- url .ptr ? url .ptr : "<invalid>" );
576- git_buf_dispose (& url );
577- }
578-
579- if ((error = http_client_setup_hosts (client , request )) < 0 ||
580- (error = http_client_connect (client )) < 0 ||
581- (error = generate_request (client , request )) < 0 ||
582- (error = stream_write (& client -> server ,
583- client -> request_msg .ptr ,
584- client -> request_msg .size )) < 0 )
585- goto done ;
586-
587- if (request -> content_length || request -> chunked ) {
588- client -> state = SENDING_BODY ;
589- client -> request_body_len = request -> content_length ;
590- client -> request_body_remain = request -> content_length ;
591- client -> request_chunked = request -> chunked ;
592- } else {
593- client -> state = SENT_REQUEST ;
594- }
595-
596- done :
597- return error ;
598- }
599-
600- int git_http_client_send_body (
601- git_http_client * client ,
602- const char * buffer ,
603- size_t buffer_len )
604- {
605- git_http_server * server ;
606- git_buf hdr = GIT_BUF_INIT ;
607- int error ;
608-
609- assert (client && client -> state == SENDING_BODY );
610-
611- if (!buffer_len )
612- return 0 ;
613-
614- server = & client -> server ;
615-
616- if (client -> request_body_len ) {
617- assert (buffer_len <= client -> request_body_remain );
618-
619- if ((error = stream_write (server , buffer , buffer_len )) < 0 )
620- goto done ;
621-
622- client -> request_body_remain -= buffer_len ;
623- } else {
624- if ((error = git_buf_printf (& hdr , "%" PRIxZ "\r\n" , buffer_len )) < 0 ||
625- (error = stream_write (server , hdr .ptr , hdr .size )) < 0 ||
626- (error = stream_write (server , buffer , buffer_len )) < 0 ||
627- (error = stream_write (server , "\r\n" , 2 )) < 0 )
628- goto done ;
629- }
630-
631- done :
632- git_buf_dispose (& hdr );
633- return error ;
634- }
635-
636- static int complete_request (git_http_client * client )
637- {
638- int error = 0 ;
639-
640- assert (client && client -> state == SENDING_BODY );
641-
642- if (client -> request_body_len && client -> request_body_remain ) {
643- git_error_set (GIT_ERROR_NET , "truncated write" );
644- error = -1 ;
645- } else if (client -> request_chunked ) {
646- error = stream_write (& client -> server , "0\r\n\r\n" , 5 );
647- }
648-
649- return error ;
650- }
651-
652- static bool parser_settings_initialized ;
653- static http_parser_settings parser_settings ;
654-
655- GIT_INLINE (http_parser_settings * ) http_client_parser_settings (void )
656- {
657- if (! parser_settings_initialized ) {
658- parser_settings .on_header_field = on_header_field ;
659- parser_settings .on_header_value = on_header_value ;
660- parser_settings .on_headers_complete = on_headers_complete ;
661- parser_settings .on_body = on_body ;
662- parser_settings .on_message_complete = on_message_complete ;
663-
664- parser_settings_initialized = true;
665- }
666-
667- return & parser_settings ;
668- }
669-
670565GIT_INLINE (int ) client_read (git_http_client * client )
671566{
672567 char * buf = client -> read_buf .ptr + client -> read_buf .size ;
@@ -698,6 +593,25 @@ GIT_INLINE(int) client_read(git_http_client *client)
698593 return (int )read_len ;
699594}
700595
596+ static bool parser_settings_initialized ;
597+ static http_parser_settings parser_settings ;
598+
599+ GIT_INLINE (http_parser_settings * ) http_client_parser_settings (void )
600+ {
601+ if (!parser_settings_initialized ) {
602+ parser_settings .on_header_field = on_header_field ;
603+ parser_settings .on_header_value = on_header_value ;
604+ parser_settings .on_headers_complete = on_headers_complete ;
605+ parser_settings .on_body = on_body ;
606+ parser_settings .on_message_complete = on_message_complete ;
607+
608+ parser_settings_initialized = true;
609+ }
610+
611+ return & parser_settings ;
612+ }
613+
614+
701615GIT_INLINE (int ) client_read_and_parse (git_http_client * client )
702616{
703617 http_parser * parser = & client -> parser ;
@@ -784,6 +698,134 @@ GIT_INLINE(int) client_read_and_parse(git_http_client *client)
784698 return (int )parsed_len ;
785699}
786700
701+ /*
702+ * See if we've consumed the entire response body. If the client was
703+ * reading the body but did not consume it entirely, it's possible that
704+ * they knew that the stream had finished (in a git response, seeing a final
705+ * flush) and stopped reading. But if the response was chunked, we may have
706+ * not consumed the final chunk marker. Consume it to ensure that we don't
707+ * have it waiting in our socket. If there's more than just a chunk marker,
708+ * close the connection.
709+ */
710+ static void complete_response_body (git_http_client * client )
711+ {
712+ http_parser_context parser_context = {0 };
713+
714+ /* If we're not keeping alive, don't bother. */
715+ if (!client -> keepalive ) {
716+ client -> connected = 0 ;
717+ return ;
718+ }
719+
720+ parser_context .client = client ;
721+ client -> parser .data = & parser_context ;
722+
723+ /* If there was an error, just close the connection. */
724+ if (client_read_and_parse (client ) < 0 ||
725+ parser_context .error != HPE_OK ||
726+ (parser_context .parse_status != PARSE_STATUS_OK &&
727+ parser_context .parse_status != PARSE_STATUS_NO_OUTPUT )) {
728+ git_error_clear ();
729+ client -> connected = 0 ;
730+ }
731+ }
732+
733+ int git_http_client_send_request (
734+ git_http_client * client ,
735+ git_http_request * request )
736+ {
737+ int error = -1 ;
738+
739+ assert (client && request );
740+
741+ /* If the client did not finish reading, clean up the stream. */
742+ if (client -> state == READING_BODY )
743+ complete_response_body (client );
744+
745+ http_parser_init (& client -> parser , HTTP_RESPONSE );
746+
747+ if (git_trace_level () >= GIT_TRACE_DEBUG ) {
748+ git_buf url = GIT_BUF_INIT ;
749+ git_net_url_fmt (& url , request -> url );
750+ git_trace (GIT_TRACE_DEBUG , "Sending %s request to %s" ,
751+ name_for_method (request -> method ),
752+ url .ptr ? url .ptr : "<invalid>" );
753+ git_buf_dispose (& url );
754+ }
755+
756+ if ((error = http_client_setup_hosts (client , request )) < 0 ||
757+ (error = http_client_connect (client )) < 0 ||
758+ (error = generate_request (client , request )) < 0 ||
759+ (error = stream_write (& client -> server ,
760+ client -> request_msg .ptr ,
761+ client -> request_msg .size )) < 0 )
762+ goto done ;
763+
764+ if (request -> content_length || request -> chunked ) {
765+ client -> state = SENDING_BODY ;
766+ client -> request_body_len = request -> content_length ;
767+ client -> request_body_remain = request -> content_length ;
768+ client -> request_chunked = request -> chunked ;
769+ } else {
770+ client -> state = SENT_REQUEST ;
771+ }
772+
773+ done :
774+ return error ;
775+ }
776+
777+ int git_http_client_send_body (
778+ git_http_client * client ,
779+ const char * buffer ,
780+ size_t buffer_len )
781+ {
782+ git_http_server * server ;
783+ git_buf hdr = GIT_BUF_INIT ;
784+ int error ;
785+
786+ assert (client && client -> state == SENDING_BODY );
787+
788+ if (!buffer_len )
789+ return 0 ;
790+
791+ server = & client -> server ;
792+
793+ if (client -> request_body_len ) {
794+ assert (buffer_len <= client -> request_body_remain );
795+
796+ if ((error = stream_write (server , buffer , buffer_len )) < 0 )
797+ goto done ;
798+
799+ client -> request_body_remain -= buffer_len ;
800+ } else {
801+ if ((error = git_buf_printf (& hdr , "%" PRIxZ "\r\n" , buffer_len )) < 0 ||
802+ (error = stream_write (server , hdr .ptr , hdr .size )) < 0 ||
803+ (error = stream_write (server , buffer , buffer_len )) < 0 ||
804+ (error = stream_write (server , "\r\n" , 2 )) < 0 )
805+ goto done ;
806+ }
807+
808+ done :
809+ git_buf_dispose (& hdr );
810+ return error ;
811+ }
812+
813+ static int complete_request (git_http_client * client )
814+ {
815+ int error = 0 ;
816+
817+ assert (client && client -> state == SENDING_BODY );
818+
819+ if (client -> request_body_len && client -> request_body_remain ) {
820+ git_error_set (GIT_ERROR_NET , "truncated write" );
821+ error = -1 ;
822+ } else if (client -> request_chunked ) {
823+ error = stream_write (& client -> server , "0\r\n\r\n" , 5 );
824+ }
825+
826+ return error ;
827+ }
828+
787829int git_http_client_read_response (
788830 git_http_response * response ,
789831 git_http_client * client )
0 commit comments