@@ -9,6 +9,10 @@ import (
99 "errors"
1010 "io"
1111 "net/http"
12+ "net/http/httptrace"
13+ "net/textproto"
14+ "reflect"
15+ "slices"
1216 "strings"
1317 "testing"
1418 "testing/synctest"
@@ -354,13 +358,27 @@ func TestRoundTripRequestBodyErrorAfterHeaders(t *testing.T) {
354358
355359func TestRoundTripExpect100Continue (t * testing.T ) {
356360 synctest .Test (t , func (t * testing.T ) {
361+ var callCount1xx , callCount100 , callCount100Wait int
362+ trace := & httptrace.ClientTrace {
363+ Got1xxResponse : func (code int , header textproto.MIMEHeader ) error {
364+ callCount1xx ++
365+ return nil
366+ },
367+ Got100Continue : func () {
368+ callCount100 ++
369+ },
370+ Wait100Continue : func () {
371+ callCount100Wait ++
372+ },
373+ }
374+
357375 tc := newTestClientConn (t )
358376 tc .greet ()
359377 clientBody := []byte ("client's body that will be sent later" )
360378 serverBody := []byte ("server's body" )
361379
362380 // Client sends an Expect: 100-continue request.
363- req , _ := http .NewRequest ( "PUT " , "https://example.tld/" , bytes .NewBuffer (clientBody ))
381+ req , _ := http .NewRequestWithContext ( httptrace . WithClientTrace ( t . Context (), trace ), "GET " , "https://example.tld/" , bytes .NewBuffer (clientBody ))
364382 req .Header = http.Header {"Expect" : {"100-continue" }}
365383 rt := tc .roundTrip (req )
366384 st := tc .wantStream (streamTypeRequest )
@@ -387,16 +405,35 @@ func TestRoundTripExpect100Continue(t *testing.T) {
387405 // Client receives the response from server.
388406 rt .wantStatus (200 )
389407 rt .wantBody (serverBody )
408+
409+ gotCount := []int {callCount1xx , callCount100 , callCount100Wait }
410+ if ! slices .Equal (gotCount , []int {1 , 1 , 1 }) {
411+ t .Errorf ("Got1xxResponse, Got100Continue, and Wait100Continue was called %v times respectively, want [1 1 1]" , gotCount )
412+ }
390413 })
391414}
392415
393416func TestRoundTripExpect100ContinueRejected (t * testing.T ) {
394417 synctest .Test (t , func (t * testing.T ) {
418+ var callCount1xx , callCount100 , callCount100Wait int
419+ trace := & httptrace.ClientTrace {
420+ Got1xxResponse : func (code int , header textproto.MIMEHeader ) error {
421+ callCount1xx ++
422+ return nil
423+ },
424+ Got100Continue : func () {
425+ callCount100 ++
426+ },
427+ Wait100Continue : func () {
428+ callCount100Wait ++
429+ },
430+ }
431+
395432 tc := newTestClientConn (t )
396433 tc .greet ()
397434
398435 // Client sends an Expect: 100-continue request.
399- req , _ := http .NewRequest ( "PUT " , "https://example.tld/" , bytes .NewBufferString ("client's body" ))
436+ req , _ := http .NewRequestWithContext ( httptrace . WithClientTrace ( t . Context (), trace ), "GET " , "https://example.tld/" , bytes .NewBufferString ("client's body" ))
400437 req .Header = http.Header {"Expect" : {"100-continue" }}
401438 rt := tc .roundTrip (req )
402439 st := tc .wantStream (streamTypeRequest )
@@ -416,6 +453,11 @@ func TestRoundTripExpect100ContinueRejected(t *testing.T) {
416453
417454 rt .wantStatus (200 )
418455 rt .wantBody (serverBody )
456+
457+ gotCount := []int {callCount1xx , callCount100 , callCount100Wait }
458+ if ! slices .Equal (gotCount , []int {0 , 0 , 1 }) {
459+ t .Errorf ("Got1xxResponse, Got100Continue, and Wait100Continue was called %v times respectively, want [0 0 1]" , gotCount )
460+ }
419461 })
420462}
421463
@@ -633,3 +675,58 @@ func TestRoundTripReadTrailerNoBody(t *testing.T) {
633675 st .wantClosed ("request is complete" )
634676 })
635677}
678+
679+ func TestRoundTrip103EarlyHints (t * testing.T ) {
680+ synctest .Test (t , func (t * testing.T ) {
681+ firstHeader := http.Header {
682+ ":status" : {"103" },
683+ "Link" : {"</style.css>; rel=preload; as=style" },
684+ }
685+ secondHeader := http.Header {
686+ ":status" : {"103" },
687+ "Link" : {"</style.css>; rel=preload; as=style" , "</script.js>; rel=preload; as=script" },
688+ }
689+
690+ var respCounter int
691+ trace := & httptrace.ClientTrace {
692+ Got1xxResponse : func (code int , header textproto.MIMEHeader ) error {
693+ var wantHeader textproto.MIMEHeader
694+ switch respCounter {
695+ case 0 :
696+ wantHeader = textproto .MIMEHeader (firstHeader )
697+ case 1 :
698+ wantHeader = textproto .MIMEHeader (secondHeader )
699+ default :
700+ t .Error ("Unexpected 1xx response" )
701+ }
702+ wantHeader .Del (":status" )
703+ if ! reflect .DeepEqual (header , wantHeader ) {
704+ t .Errorf ("got %v early hints header, want %v" , header , wantHeader )
705+ }
706+ respCounter ++
707+ return nil
708+ },
709+ }
710+ req , _ := http .NewRequestWithContext (httptrace .WithClientTrace (t .Context (), trace ), "GET" , "https://example.tld/" , nil )
711+
712+ tc := newTestClientConn (t )
713+ tc .greet ()
714+ rt := tc .roundTrip (req )
715+ st := tc .wantStream (streamTypeRequest )
716+
717+ st .wantHeaders (nil )
718+ st .writeHeaders (firstHeader )
719+ st .writeHeaders (secondHeader )
720+
721+ st .writeHeaders (http.Header {
722+ ":status" : {"200" },
723+ })
724+ body := []byte ("some body" )
725+ st .writeData (body )
726+ st .stream .stream .CloseWrite ()
727+
728+ rt .wantStatus (200 )
729+ rt .wantBody (body )
730+ st .wantClosed ("request is complete" )
731+ })
732+ }
0 commit comments