@@ -532,8 +532,7 @@ pub const Request = struct {
532532 ctx : * anyopaque = undefined ,
533533
534534 start_callback : ? * const fn (transfer : * Transfer ) anyerror ! void = null ,
535- header_callback : ? * const fn (transfer : * Transfer , header : Http.Header ) anyerror ! void = null ,
536- header_done_callback : * const fn (transfer : * Transfer ) anyerror ! void ,
535+ header_callback : * const fn (transfer : * Transfer ) anyerror ! void ,
537536 data_callback : * const fn (transfer : * Transfer , data : []const u8 ) anyerror ! void ,
538537 done_callback : * const fn (ctx : * anyopaque ) anyerror ! void ,
539538 error_callback : * const fn (ctx : * anyopaque , err : anyerror ) void ,
@@ -739,8 +738,8 @@ pub const Transfer = struct {
739738 if (i >= ct .? .amount ) break ;
740739 }
741740
742- transfer .req .header_done_callback (transfer ) catch | err | {
743- log .err (.http , "header_done_callback " , .{ .err = err , .req = transfer });
741+ transfer .req .header_callback (transfer ) catch | err | {
742+ log .err (.http , "header_callback " , .{ .err = err , .req = transfer });
744743 // returning < buf_len terminates the request
745744 return 0 ;
746745 };
@@ -750,15 +749,6 @@ pub const Transfer = struct {
750749 .transfer = transfer ,
751750 });
752751 }
753- } else {
754- if (transfer .req .header_callback ) | cb | {
755- if (Http .Headers .parseHeader (header )) | hdr | {
756- cb (transfer , hdr ) catch | err | {
757- log .err (.http , "header_callback" , .{ .err = err , .req = transfer });
758- return 0 ;
759- };
760- }
761- }
762752 }
763753 return buf_len ;
764754 }
@@ -784,10 +774,17 @@ pub const Transfer = struct {
784774 return chunk_len ;
785775 }
786776
787- // we assume that the caller is smart and only calling this after being
788- // told that the header was ready.
789777 pub fn responseHeaderIterator (self : * Transfer ) HeaderIterator {
790- return .{ .easy = self ._handle .? .conn .easy };
778+ if (self ._handle ) | handle | {
779+ // If we have a handle, than this is a real curl request and we
780+ // iterate through the header that curl maintains.
781+ return .{ .curl = .{ .easy = handle .conn .easy } };
782+ }
783+ // If there's no handle, it either means this is being called before
784+ // the request is even being made (which would be a bug in the code)
785+ // or when a response was injected via transfer.fulfill. The injected
786+ // header should be iterated, since there is no handle/easy.
787+ return .{ .list = .{ .list = self .response_header .? ._injected_headers } };
791788 }
792789
793790 // pub because Page.printWaitAnalysis uses it
@@ -817,15 +814,10 @@ pub const Transfer = struct {
817814 try cb (transfer );
818815 }
819816
820- if (req .header_callback ) | cb | {
821- for (headers ) | hdr | {
822- try cb (transfer , hdr );
823- }
824- }
825-
826817 transfer .response_header = .{
827818 .status = status ,
828819 .url = req .url ,
820+ ._injected_headers = headers ,
829821 };
830822 for (headers ) | hdr | {
831823 if (std .ascii .eqlIgnoreCase (hdr .name , "content-type" )) {
@@ -835,7 +827,7 @@ pub const Transfer = struct {
835827 }
836828 }
837829
838- try req .header_done_callback (transfer );
830+ try req .header_callback (transfer );
839831
840832 if (body ) | b | {
841833 try req .data_callback (transfer , b );
@@ -852,6 +844,11 @@ pub const ResponseHeader = struct {
852844 url : [* c ]const u8 ,
853845 _content_type_len : usize = 0 ,
854846 _content_type : [MAX_CONTENT_TYPE_LEN ]u8 = undefined ,
847+ // this is normally an empty list, but if the response is being injected
848+ // than it'll be populated. It isn't meant to be used directly, but should
849+ // be used through the transfer.responseHeaderIterator() which abstracts
850+ // whether the headers are from a live curl easy handle, or injected.
851+ _injected_headers : []const Http.Header = &.{},
855852
856853 pub fn contentType (self : * ResponseHeader ) ? []u8 {
857854 if (self ._content_type_len == 0 ) {
@@ -861,20 +858,49 @@ pub const ResponseHeader = struct {
861858 }
862859};
863860
864- const HeaderIterator = struct {
865- easy : * c.CURL ,
866- prev : ? * c.curl_header = null ,
861+ // In normal cases, the header iterator comes from the curl linked list.
862+ // But it's also possible to inject a response, via `transfer.fulfill`. In that
863+ // case, the resposne headers are a list, []const Http.Header.
864+ // This union, is an iterator that exposes the same API for either case.
865+ const HeaderIterator = union (enum ) {
866+ curl : CurlHeaderIterator ,
867+ list : ListHeaderIterator ,
867868
868869 pub fn next (self : * HeaderIterator ) ? Http.Header {
869- const h = c .curl_easy_nextheader (self .easy , c .CURLH_HEADER , -1 , self .prev ) orelse return null ;
870- self .prev = h ;
871-
872- const header = h .* ;
873- return .{
874- .name = std .mem .span (header .name ),
875- .value = std .mem .span (header .value ),
876- };
870+ switch (self .* ) {
871+ inline else = > | * it | return it .next (),
872+ }
877873 }
874+
875+ const CurlHeaderIterator = struct {
876+ easy : * c.CURL ,
877+ prev : ? * c.curl_header = null ,
878+
879+ pub fn next (self : * CurlHeaderIterator ) ? Http.Header {
880+ const h = c .curl_easy_nextheader (self .easy , c .CURLH_HEADER , -1 , self .prev ) orelse return null ;
881+ self .prev = h ;
882+
883+ const header = h .* ;
884+ return .{
885+ .name = std .mem .span (header .name ),
886+ .value = std .mem .span (header .value ),
887+ };
888+ }
889+ };
890+
891+ const ListHeaderIterator = struct {
892+ index : usize = 0 ,
893+ list : []const Http.Header ,
894+
895+ pub fn next (self : * ListHeaderIterator ) ? Http.Header {
896+ const index = self .index ;
897+ if (index == self .list .len ) {
898+ return null ;
899+ }
900+ self .index = index + 1 ;
901+ return self .list [index ];
902+ }
903+ };
878904};
879905
880906const CurlHeaderValue = struct {
0 commit comments