github.com/useflyent/fhttp@v0.0.0-20211004035111-333f430cfbbf/http2/push_consume_test.go (about) 1 // Copyright 2018 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 package http2 5 6 import ( 7 "errors" 8 "net/url" 9 "reflect" 10 "testing" 11 12 http "github.com/useflyent/fhttp" 13 "github.com/useflyent/fhttp/http2/hpack" 14 ) 15 16 func TestPushPromiseHeadersToHTTPRequest(t *testing.T) { 17 headers := http.Header{} 18 headers.Add("X", "y") 19 getUrl := func(path, authority, scheme string) *url.URL { 20 reqUrl, err := url.ParseRequestURI(path) 21 if err != nil { 22 t.Error(err) 23 return nil 24 } 25 reqUrl.Host = authority 26 reqUrl.Scheme = scheme 27 return reqUrl 28 } 29 30 requiredHeaders := []hpack.HeaderField{ 31 {Name: ":method", Value: "GET"}, 32 {Name: ":scheme", Value: "https"}, 33 {Name: ":authority", Value: "foo.org"}, 34 {Name: ":path", Value: "/hello"}, 35 } 36 37 tests := []struct { 38 name string 39 headers []hpack.HeaderField 40 expectedReq *http.Request 41 expectedErr error 42 }{ 43 { 44 "NoErrors_IncludeNonRequiredHeaders", 45 append(requiredHeaders, 46 hpack.HeaderField{Name: "X", Value: "y"}, 47 ), 48 &http.Request{ 49 Method: "GET", 50 Proto: "HTTP/2.0", 51 ProtoMajor: 2, 52 URL: getUrl("/hello", "foo.org", "https"), 53 Header: headers, 54 }, 55 nil, 56 }, 57 { 58 "NoErrors_OnlyRequiredHeaders", 59 requiredHeaders, 60 &http.Request{ 61 Method: "GET", 62 Proto: "HTTP/2.0", 63 ProtoMajor: 2, 64 URL: getUrl("/hello", "foo.org", "https"), 65 }, 66 nil, 67 }, 68 { 69 "Missing_Method", 70 []hpack.HeaderField{ 71 {Name: ":scheme", Value: "https"}, 72 {Name: ":authority", Value: "foo.org"}, 73 {Name: ":path", Value: "/hello"}, 74 }, 75 nil, 76 errMissingHeaderMethod, 77 }, 78 { 79 "Missing_Scheme", 80 []hpack.HeaderField{ 81 {Name: ":method", Value: "GET"}, 82 {Name: ":authority", Value: "foo.org"}, 83 {Name: ":path", Value: "/hello"}, 84 }, 85 nil, 86 errMissingHeaderScheme, 87 }, 88 { 89 "Missing_Authority", 90 []hpack.HeaderField{ 91 {Name: ":scheme", Value: "https"}, 92 {Name: ":method", Value: "GET"}, 93 {Name: ":path", Value: "/hello"}, 94 }, 95 nil, 96 errMissingHeaderAuthority, 97 }, 98 { 99 "Missing_Path", 100 []hpack.HeaderField{ 101 {Name: ":scheme", Value: "https"}, 102 {Name: ":method", Value: "GET"}, 103 {Name: ":authority", Value: "foo.org"}, 104 }, 105 nil, 106 errMissingHeaderPath, 107 }, 108 { 109 "Invalid_Method", 110 []hpack.HeaderField{ 111 {Name: ":method", Value: "POST"}, 112 {Name: ":scheme", Value: "https"}, 113 {Name: ":authority", Value: "foo.org"}, 114 {Name: ":path", Value: "/hello"}, 115 }, 116 nil, 117 errInvalidMethod, 118 }, 119 { 120 "Invalid_Scheme", 121 []hpack.HeaderField{ 122 {Name: ":method", Value: "GET"}, 123 {Name: ":scheme", Value: "ftp"}, 124 {Name: ":authority", Value: "foo.org"}, 125 {Name: ":path", Value: "/hello"}, 126 }, 127 nil, 128 errInvalidScheme, 129 }, 130 { 131 "Cannot_Have_Body", 132 append(requiredHeaders, 133 hpack.HeaderField{Name: "Content-Length", Value: "100"}, 134 ), 135 nil, 136 errors.New(`promised request cannot include body related header "Content-Length"`), 137 }, 138 { 139 "Invalid_HTTP2_Header", 140 append(requiredHeaders, 141 hpack.HeaderField{Name: "Connection", Value: "close"}, 142 ), 143 nil, 144 errors.New(`request header "Connection" is not valid in HTTP/2`), 145 }, 146 } 147 148 for _, tt := range tests { 149 t.Run(tt.name, func(t *testing.T) { 150 mpp := &MetaPushPromiseFrame{nil, tt.headers, false} 151 req, err := pushedRequestToHTTPRequest(mpp) 152 if !reflect.DeepEqual(err, tt.expectedErr) { 153 t.Fatalf("expected error %q but got error %q", tt.expectedErr, err) 154 } 155 if !reflect.DeepEqual(req, tt.expectedReq) { 156 t.Fatalf("expected %v, but got %v", tt.expectedReq, req) 157 } 158 }) 159 } 160 } 161 162 type testPushHandlerRecordHandled struct { 163 messageDone bool 164 requestHandled bool 165 } 166 167 func (ph *testPushHandlerRecordHandled) HandlePush(r *PushedRequest) { 168 ph.requestHandled = true 169 if ph.messageDone { 170 r.pushedStream.done <- struct{}{} 171 } 172 } 173 174 func TestHandlePushNoActionCancel(t *testing.T) { 175 tests := []struct { 176 name string 177 returnBeforeComplete bool 178 expectCancel bool 179 }{ 180 { 181 "ReturnBeforeComplete", 182 true, 183 true, 184 }, 185 { 186 "ReturnAfterComplete", 187 false, 188 false, 189 }, 190 } 191 192 for _, tt := range tests { 193 t.Run(tt.name, func(t *testing.T) { 194 st := newServerTester(t, nil) 195 defer st.Close() 196 tr := &Transport{TLSClientConfig: tlsConfigInsecure} 197 defer tr.CloseIdleConnections() 198 cc, err := tr.dialClientConn(st.ts.Listener.Addr().String(), false) 199 if err != nil { 200 t.Fatal(err) 201 } 202 cs := cc.newStreamWithID(2, false) 203 pr := &PushedRequest{pushedStream: cs} 204 ph := &testPushHandlerRecordHandled{messageDone: !tt.returnBeforeComplete} 205 handlePushEarlyReturnCancel(ph, pr) 206 if cs.didReset && !tt.expectCancel { 207 t.Error("expected pushed stream to be cancelled but it was not") 208 } else if !cs.didReset && tt.expectCancel { 209 t.Error("expected pushed stream to not be cancelled but it was") 210 } 211 }) 212 } 213 }