github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/internal/requesttesting/headers/clte_test.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 headers 16 17 import ( 18 "context" 19 "io/ioutil" 20 "net/http" 21 "testing" 22 23 "github.com/google/go-safeweb/internal/requesttesting" 24 25 "github.com/google/go-cmp/cmp" 26 ) 27 28 func TestContentLengthTransferEncoding(t *testing.T) { 29 type testWant struct { 30 headers map[string][]string 31 contentLength int64 32 transferEncoding []string 33 body string 34 } 35 36 var tests = []struct { 37 name string 38 request []byte 39 want testWant 40 }{ 41 { 42 name: "ContentLength", 43 request: []byte("GET / HTTP/1.1\r\n" + 44 "Host: localhost:8080\r\n" + 45 "Content-Length: 5\r\n" + 46 "\r\n" + 47 "ABCDE\r\n" + 48 "\r\n"), 49 want: testWant{ 50 headers: map[string][]string{"Content-Length": {"5"}}, 51 contentLength: 5, 52 transferEncoding: nil, 53 body: "ABCDE", 54 }, 55 }, 56 { 57 name: "ContentButNoContentLength", 58 request: []byte("GET / HTTP/1.1\r\n" + 59 "Host: localhost:8080\r\n" + 60 "\r\n" + 61 "ABCDE\r\n" + 62 "\r\n"), 63 want: testWant{ 64 headers: map[string][]string{}, 65 contentLength: 0, 66 transferEncoding: nil, 67 body: "", 68 }, 69 }, 70 { 71 name: "TransferEncodingChunked", 72 request: []byte("GET / HTTP/1.1\r\n" + 73 "Host: localhost:8080\r\n" + 74 "Transfer-Encoding: chunked\r\n" + 75 "\r\n" + 76 "5\r\n" + 77 "ABCDE\r\n" + 78 "0\r\n\r\n"), 79 want: testWant{ 80 headers: map[string][]string{}, 81 contentLength: -1, 82 transferEncoding: []string{"chunked"}, 83 body: "ABCDE", 84 }, 85 }, 86 { 87 name: "ContentLengthAndTransferEncoding", 88 request: []byte("GET / HTTP/1.1\r\n" + 89 "Host: localhost:8080\r\n" + 90 "Content-Length: 11\r\n" + 91 "Transfer-Encoding: chunked\r\n" + 92 "\r\n" + 93 "5\r\n" + 94 "ABCDE\r\n" + 95 "0\r\n" + 96 "\r\n"), 97 want: testWant{ 98 headers: map[string][]string{}, 99 contentLength: -1, 100 transferEncoding: []string{"chunked"}, 101 body: "ABCDE", 102 }, 103 }, 104 } 105 106 for _, tt := range tests { 107 t.Run(tt.name, func(t *testing.T) { 108 resp, err := requesttesting.MakeRequest(context.Background(), tt.request, func(r *http.Request) { 109 if diff := cmp.Diff(tt.want.headers, map[string][]string(r.Header)); diff != "" { 110 t.Errorf("r.Header mismatch (-want +got):\n%s", diff) 111 } 112 113 if diff := cmp.Diff(tt.want.transferEncoding, r.TransferEncoding); diff != "" { 114 t.Errorf("r.TransferEncoding mismatch (-want +got):\n%s", diff) 115 } 116 117 if r.ContentLength != tt.want.contentLength { 118 t.Errorf("r.ContentLength got: %v want: %v", r.ContentLength, tt.want.contentLength) 119 } 120 121 body, err := ioutil.ReadAll(r.Body) 122 if err != nil { 123 t.Fatalf("ioutil.ReadAll(r.Body) got err: %v", err) 124 } 125 126 if got := string(body); got != tt.want.body { 127 t.Errorf("r.Body got: %q want: %q", got, tt.want.body) 128 } 129 }) 130 if err != nil { 131 t.Fatalf("MakeRequest() got err: %v", err) 132 } 133 134 if got, want := extractStatus(resp), statusOK; !matchStatus(got, want) { 135 t.Errorf("status code got: %q want: %q", got, want) 136 } 137 }) 138 } 139 } 140 141 func TestContentLengthTransferEncodingStatusMessages(t *testing.T) { 142 var tests = []struct { 143 name string 144 request []byte 145 want string 146 }{ 147 { 148 name: "MultipleContentLength", 149 request: []byte("GET / HTTP/1.1\r\n" + 150 "Host: localhost:8080\r\n" + 151 "Content-Length: 5\r\n" + 152 "Content-Length: 14\r\n" + 153 "\r\n" + 154 "ABCDE\r\n" + 155 "\r\n" + 156 "ABCDE\r\n" + 157 "\r\n"), 158 want: statusBadRequestPrefix, 159 }, 160 { 161 name: "MultipleTransferEncodingChunkedFirst", 162 request: []byte("GET / HTTP/1.1\r\n" + 163 "Host: localhost:8080\r\n" + 164 "Content-Length: 11\r\n" + 165 "Transfer-Encoding: chunked\r\n" + 166 "Transfer-Encoding: asdf\r\n" + 167 "\r\n" + 168 "5\r\n" + 169 "ABCDE\r\n" + 170 "0\r\n" + 171 "\r\n"), 172 want: statusNotImplemented, 173 }, 174 { 175 name: "MultipleTransferEncodingChunkedSecond", 176 request: []byte("GET / HTTP/1.1\r\n" + 177 "Host: localhost:8080\r\n" + 178 "Content-Length: 11\r\n" + 179 "Transfer-Encoding: asdf\r\n" + 180 "Transfer-Encoding: chunked\r\n" + 181 "\r\n" + 182 "5\r\n" + 183 "ABCDE\r\n" + 184 "0\r\n" + 185 "\r\n"), 186 want: statusNotImplemented, 187 }, 188 { 189 name: "TransferEncodingListChunkedFirst", 190 request: []byte("GET / HTTP/1.1\r\n" + 191 "Host: localhost:8080\r\n" + 192 "Content-Length: 11\r\n" + 193 "Transfer-Encoding: chunked, xyz, asdf\r\n" + 194 "\r\n" + 195 "5\r\n" + 196 "ABCDE\r\n" + 197 "0\r\n" + 198 "\r\n"), 199 want: statusNotImplemented, 200 }, 201 { 202 name: "TransferEncodingListChunkedChunked", 203 request: []byte("GET / HTTP/1.1\r\n" + 204 "Host: localhost:8080\r\n" + 205 "Content-Length: 11\r\n" + 206 "Transfer-Encoding: chunked, chunked\r\n" + 207 "\r\n" + 208 "5\r\n" + 209 "ABCDE\r\n" + 210 "0\r\n" + 211 "\r\n"), 212 want: statusNotImplemented, 213 }, 214 { 215 name: "Transfer-Encoding Identity", 216 request: []byte("GET / HTTP/1.1\r\n" + 217 "Host: localhost:8080\r\n" + 218 "Content-Length: 11\r\n" + 219 "Transfer-Encoding: identity\r\n" + 220 "\r\n" + 221 "5\r\n" + 222 "ABCDE\r\n" + 223 "0\r\n" + 224 "\r\n"), 225 want: statusNotImplemented, 226 }, 227 { 228 name: "TransferEncodingListIdentityFirst", 229 request: []byte("GET / HTTP/1.1\r\n" + 230 "Host: localhost:8080\r\n" + 231 "Content-Length: 11\r\n" + 232 "Transfer-Encoding: identity, xyz, asdf\r\n" + 233 "\r\n" + 234 "5\r\n" + 235 "ABCDE\r\n" + 236 "0\r\n" + 237 "\r\n"), 238 want: statusNotImplemented, 239 }, 240 { 241 name: "TransferEncodingListChunkedIdentity", 242 request: []byte("GET / HTTP/1.1\r\n" + 243 "Host: localhost:8080\r\n" + 244 "Content-Length: 11\r\n" + 245 "Transfer-Encoding: chunked, identity\r\n" + 246 "\r\n" + 247 "5\r\n" + 248 "ABCDE\r\n" + 249 "0\r\n" + 250 "\r\n"), 251 want: statusNotImplemented, 252 }, 253 } 254 255 for _, tt := range tests { 256 t.Run(tt.name, func(t *testing.T) { 257 resp, err := requesttesting.MakeRequest(context.Background(), tt.request, nil) 258 if err != nil { 259 t.Fatalf("MakeRequest() got err: %v", err) 260 } 261 262 if got := extractStatus(resp); !matchStatus(got, tt.want) { 263 t.Errorf("status code got: %q want: %q", got, tt.want) 264 } 265 }) 266 } 267 } 268 269 func TestTransferEncodingChunkSizeLength(t *testing.T) { 270 request := []byte("GET / HTTP/1.1\r\n" + 271 "Host:localhost\r\n" + 272 "Transfer-Encoding: chunked\r\n" + 273 "\r\n" + 274 "000000000000000000007\r\n" + 275 "\r\n" + 276 "ABCDE\r\n" + 277 "0\r\n" + 278 "\r\n") 279 280 _, err := requesttesting.MakeRequest(context.Background(), request, func(r *http.Request) { 281 if diff := cmp.Diff(map[string][]string{}, map[string][]string(r.Header)); diff != "" { 282 t.Errorf("r.Header mismatch (-want +got):\n%s", diff) 283 } 284 285 if diff := cmp.Diff([]string{"chunked"}, r.TransferEncoding); diff != "" { 286 t.Errorf("r.TransferEncoding mismatch (-want +got):\n%s", diff) 287 } 288 289 if r.ContentLength != -1 { 290 t.Errorf("r.ContentLength got: %v want: -1", r.ContentLength) 291 } 292 293 _, err := ioutil.ReadAll(r.Body) 294 if err == nil { 295 t.Fatalf("ioutil.ReadAll(r.Body) got: %v want: http chunk length too large", err) 296 } 297 }) 298 if err != nil { 299 t.Fatalf("MakeRequest() got err: %v", err) 300 } 301 }