github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/internal/requesttesting/test_harness.go (about) 1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package requesttesting provides a harness and other test utilities for 16 // verifying the behaviour of the net/http package in Go's standard library. 17 package requesttesting 18 19 import ( 20 "context" 21 "errors" 22 "io" 23 "net" 24 "net/http" 25 "sync" 26 ) 27 28 // AssertHandler is used to assert properties about the http.Request that it receives in using a callback function. 29 type AssertHandler struct { 30 callback func(*http.Request) 31 } 32 33 func (h *AssertHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 34 h.callback(r) 35 if _, err := io.WriteString(w, "Hello world!"); err != nil { 36 panic(err) 37 } 38 } 39 40 // FakeListener creates a custom listener that avoids opening a socket in order 41 // to establish communication between an HTTP client and server 42 type FakeListener struct { 43 closeOnce sync.Once 44 channel chan net.Conn 45 serverEndpoint io.Closer 46 clientEndpoint net.Conn 47 } 48 49 // NewFakeListener creates an instance of fakeListener. 50 func NewFakeListener() *FakeListener { 51 s2c, c2s := net.Pipe() 52 c := make(chan net.Conn, 1) 53 c <- s2c 54 return &FakeListener{ 55 channel: c, 56 serverEndpoint: s2c, 57 clientEndpoint: c2s, 58 } 59 } 60 61 // Accept passes a network connection to the HTTP server to enable bidirectional communication with the client. 62 // It will return an error if Accept is called after the listener was closed. 63 func (l *FakeListener) Accept() (net.Conn, error) { 64 ch, ok := <-l.channel 65 if !ok { 66 return nil, errors.New("Listener closed") 67 } 68 return ch, nil 69 } 70 71 // Close will close the two network connections and the listener. 72 func (l *FakeListener) Close() error { 73 l.closeOnce.Do(func() { 74 close(l.channel) 75 }) 76 err := l.serverEndpoint.Close() 77 err2 := l.clientEndpoint.Close() 78 if err2 != nil { 79 return err2 80 } 81 return err 82 } 83 84 // Addr returns the network address of the client endpoint. 85 func (l *FakeListener) Addr() net.Addr { 86 return l.clientEndpoint.LocalAddr() 87 } 88 89 // SendRequest writes a request to the client endpoint connection. This will be passed to the server through the listener. 90 // The function blocks until the server has finished reading the message. 91 func (l *FakeListener) SendRequest(request []byte) error { 92 n, err := l.clientEndpoint.Write(request) 93 94 if err != nil { 95 return err 96 } 97 if n != len(request) { 98 return errors.New("client connection failed to write the entire request") 99 } 100 return nil 101 } 102 103 // ReadResponse reads the response from the clientEndpoint connection, sent by the listening server. 104 // It will block until the server has sent a response. 105 func (l *FakeListener) ReadResponse(bytes []byte) (int, error) { 106 return l.clientEndpoint.Read(bytes) 107 } 108 109 // MakeRequest instantiates a new http.Server, sends the request provided as 110 // argument and returns the response. callback will be called in the 111 // http.Handler with the http.Request that the handler receives. The size of the 112 // response is limited to 4096 bytes. If the response received is larger, an 113 // error will be returned. 114 func MakeRequest(ctx context.Context, req []byte, callback func(*http.Request)) ([]byte, error) { 115 listener := NewFakeListener() 116 defer listener.Close() 117 118 // WARNING: We cannot depend on httptest.Server here. The reason is that we 119 // want to send a request as a slice of bytes. Requests sent to 120 // httptest.Server can only be sent using http.Client, which already uses 121 // the http.Request type. Using this type will make us obvlivious to any 122 // kind of request parsing problems in the Go standard library - and we want 123 // to test for these. 124 handler := &AssertHandler{callback: callback} 125 server := &http.Server{Handler: handler} 126 go server.Serve(listener) 127 defer server.Close() 128 129 if err := listener.SendRequest(req); err != nil { 130 return nil, err 131 } 132 133 resp := make([]byte, 4096) 134 n, err := listener.ReadResponse(resp) 135 if err != nil { 136 return nil, err 137 } 138 if n == 4096 { 139 return nil, errors.New("response larger than or equal to 4096 bytes") 140 } 141 return resp[:n], server.Close() /* Forceful shutdown. We don't want to delay anything. */ 142 }