Source file src/testing/synctest/http_example_test.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build goexperiment.synctest
     6  
     7  package synctest_test
     8  
     9  import (
    10  	"bufio"
    11  	"bytes"
    12  	"context"
    13  	"fmt"
    14  	"io"
    15  	"net"
    16  	"net/http"
    17  	"strings"
    18  	"testing/synctest"
    19  	"time"
    20  )
    21  
    22  // This example demonstrates testing [http.Transport]'s 100 Continue handling.
    23  //
    24  // An HTTP client sending a request can include an "Expect: 100-continue" header
    25  // to tell the server that the client has additional data to send.
    26  // The server may then respond with an 100 Continue information response
    27  // to request the data, or some other status to tell the client the data is not needed.
    28  // For example, a client uploading a large file might use this feature to confirm
    29  // that the server is willing to accept the file before sending it.
    30  //
    31  // This test confirms that when sending an "Expect: 100-continue" header
    32  // the HTTP client does not send a request's content before the server requests it,
    33  // and that it does send the content after receiving a 100 Continue response.
    34  func Example_httpTransport100Continue() {
    35  	synctest.Run(func() {
    36  		// Create an in-process fake network connection.
    37  		// We cannot use a loopback network connection for this test,
    38  		// because goroutines blocked on network I/O prevent a synctest
    39  		// bubble from becoming idle.
    40  		srvConn, cliConn := net.Pipe()
    41  		defer cliConn.Close()
    42  		defer srvConn.Close()
    43  
    44  		tr := &http.Transport{
    45  			// Use the fake network connection created above.
    46  			DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
    47  				return cliConn, nil
    48  			},
    49  			// Enable "Expect: 100-continue" handling.
    50  			ExpectContinueTimeout: 5 * time.Second,
    51  		}
    52  
    53  		// Send a request with the "Expect: 100-continue" header set.
    54  		// Send it in a new goroutine, since it won't complete until the end of the test.
    55  		body := "request body"
    56  		go func() {
    57  			req, err := http.NewRequest("PUT", "http://test.tld/", strings.NewReader(body))
    58  			if err != nil {
    59  				panic(err)
    60  			}
    61  			req.Header.Set("Expect", "100-continue")
    62  			resp, err := tr.RoundTrip(req)
    63  			if err != nil {
    64  				fmt.Printf("RoundTrip: unexpected error %v\n", err)
    65  			} else {
    66  				resp.Body.Close()
    67  			}
    68  		}()
    69  
    70  		// Read the request headers sent by the client.
    71  		req, err := http.ReadRequest(bufio.NewReader(srvConn))
    72  		if err != nil {
    73  			fmt.Printf("ReadRequest: %v\n", err)
    74  			return
    75  		}
    76  
    77  		// Start a new goroutine copying the body sent by the client into a buffer.
    78  		// Wait for all goroutines in the bubble to block and verify that we haven't
    79  		// read anything from the client yet.
    80  		var gotBody bytes.Buffer
    81  		go io.Copy(&gotBody, req.Body)
    82  		synctest.Wait()
    83  		fmt.Printf("before sending 100 Continue, read body: %q\n", gotBody.String())
    84  
    85  		// Write a "100 Continue" response to the client and verify that
    86  		// it sends the request body.
    87  		srvConn.Write([]byte("HTTP/1.1 100 Continue\r\n\r\n"))
    88  		synctest.Wait()
    89  		fmt.Printf("after sending 100 Continue, read body: %q\n", gotBody.String())
    90  
    91  		// Finish up by sending the "200 OK" response to conclude the request.
    92  		srvConn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))
    93  
    94  		// We started several goroutines during the test.
    95  		// The synctest.Run call will wait for all of them to exit before returning.
    96  	})
    97  
    98  	// Output:
    99  	// before sending 100 Continue, read body: ""
   100  	// after sending 100 Continue, read body: "request body"
   101  }
   102  

View as plain text