github.com/cloudwego/hertz@v0.9.3/pkg/protocol/http1/resp/response_test.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * The MIT License (MIT) 17 * 18 * Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia, Kirill Danshin, Erik Dubbelboer, FastHTTP Authors 19 * 20 * Permission is hereby granted, free of charge, to any person obtaining a copy 21 * of this software and associated documentation files (the "Software"), to deal 22 * in the Software without restriction, including without limitation the rights 23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 * copies of the Software, and to permit persons to whom the Software is 25 * furnished to do so, subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be included in 28 * all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 36 * THE SOFTWARE. 37 * 38 * This file may have been modified by CloudWeGo authors. All CloudWeGo 39 * Modifications are Copyright 2022 CloudWeGo Authors. 40 */ 41 42 package resp 43 44 import ( 45 "bufio" 46 "bytes" 47 "errors" 48 "io" 49 "io/ioutil" 50 "strings" 51 "testing" 52 53 "github.com/cloudwego/hertz/internal/bytestr" 54 errs "github.com/cloudwego/hertz/pkg/common/errors" 55 "github.com/cloudwego/hertz/pkg/common/test/assert" 56 "github.com/cloudwego/hertz/pkg/common/test/mock" 57 "github.com/cloudwego/hertz/pkg/common/utils" 58 "github.com/cloudwego/hertz/pkg/protocol" 59 "github.com/cloudwego/hertz/pkg/protocol/consts" 60 "github.com/cloudwego/hertz/pkg/protocol/http1/ext" 61 "github.com/cloudwego/netpoll" 62 ) 63 64 var errBodyTooLarge = errs.New(errs.ErrBodyTooLarge, errs.ErrorTypePublic, "test") 65 66 type ErroneousBodyStream struct { 67 errOnRead bool 68 errOnClose bool 69 } 70 71 type testReader struct { 72 read chan (int) 73 cb chan (struct{}) 74 } 75 76 func (r *testReader) Read(b []byte) (int, error) { 77 read := <-r.read 78 79 if read == -1 { 80 return 0, io.EOF 81 } 82 83 r.cb <- struct{}{} 84 85 total := len(b) 86 if total > read { 87 total = read 88 } 89 90 for i := 0; i < total; i++ { 91 b[i] = 'x' 92 } 93 94 return total, nil 95 } 96 97 func (ebs *ErroneousBodyStream) Read(p []byte) (n int, err error) { 98 if ebs.errOnRead { 99 panic("reading erroneous body stream") 100 } 101 return 0, io.EOF 102 } 103 104 func (ebs *ErroneousBodyStream) Close() error { 105 if ebs.errOnClose { 106 panic("closing erroneous body stream") 107 } 108 return nil 109 } 110 111 func TestResponseBodyStreamErrorOnPanicDuringClose(t *testing.T) { 112 t.Parallel() 113 var resp protocol.Response 114 var w bytes.Buffer 115 zw := netpoll.NewWriter(&w) 116 117 ebs := &ErroneousBodyStream{errOnRead: false, errOnClose: true} 118 resp.SetBodyStream(ebs, 42) 119 err := Write(&resp, zw) 120 if err == nil { 121 t.Fatalf("expected error when writing response.") 122 } 123 e, ok := err.(*ErrBodyStreamWritePanic) 124 if !ok { 125 t.Fatalf("expected error struct to be *ErrBodyStreamWritePanic, got: %+v.", e) 126 } 127 if e.Error() != "panic while writing body stream: closing erroneous body stream" { 128 t.Fatalf("unexpected error value, got: %+v.", e.Error()) 129 } 130 } 131 132 func TestResponseBodyStreamErrorOnPanicDuringRead(t *testing.T) { 133 t.Parallel() 134 var resp protocol.Response 135 var w bytes.Buffer 136 zw := netpoll.NewWriter(&w) 137 138 ebs := &ErroneousBodyStream{errOnRead: true, errOnClose: false} 139 resp.SetBodyStream(ebs, 42) 140 err := Write(&resp, zw) 141 if err == nil { 142 t.Fatalf("expected error when writing response.") 143 } 144 e, ok := err.(*ErrBodyStreamWritePanic) 145 if !ok { 146 t.Fatalf("expected error struct to be *ErrBodyStreamWritePanic, got: %+v.", e) 147 } 148 if e.Error() != "panic while writing body stream: reading erroneous body stream" { 149 t.Fatalf("unexpected error value, got: %+v.", e.Error()) 150 } 151 } 152 153 func testResponseReadError(t *testing.T, resp *protocol.Response, response string) { 154 zr := mock.NewZeroCopyReader(response) 155 err := Read(resp, zr) 156 if err == nil { 157 t.Fatalf("Expecting error for response=%q", response) 158 } 159 160 testResponseReadSuccess(t, resp, "HTTP/1.1 303 Redisred sedfs sdf\r\nContent-Type: aaa\r\nContent-Length: 5\r\n\r\nHELLOaaa", 161 consts.StatusSeeOther, 5, "aaa", "HELLO", nil, consts.HTTP11) 162 } 163 164 func testResponseReadSuccess(t *testing.T, resp *protocol.Response, response string, expectedStatusCode, expectedContentLength int, 165 expectedContentType, expectedBody string, expectedTrailer map[string]string, expectedProtocol string, 166 ) { 167 zr := mock.NewZeroCopyReader(response) 168 err := Read(resp, zr) 169 if err != nil { 170 t.Fatalf("Unexpected error: %s", err) 171 } 172 173 verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, "", expectedProtocol) 174 if !bytes.Equal(resp.Body(), []byte(expectedBody)) { 175 t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), []byte(expectedBody)) 176 } 177 verifyResponseTrailer(t, &resp.Header, expectedTrailer) 178 } 179 180 func TestResponseReadSuccess(t *testing.T) { 181 t.Parallel() 182 183 resp := &protocol.Response{} 184 185 // usual response 186 testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: foo/bar\r\n\r\n0123456789", 187 consts.StatusOK, 10, "foo/bar", "0123456789", nil, consts.HTTP11) 188 189 // zero response 190 testResponseReadSuccess(t, resp, "HTTP/1.1 500 OK\r\nContent-Length: 0\r\nContent-Type: foo/bar\r\n\r\n", 191 consts.StatusInternalServerError, 0, "foo/bar", "", nil, consts.HTTP11) 192 193 // response with trailer 194 testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n", 195 consts.StatusMultipleChoices, 5, "bar", "56789", map[string]string{"Foo": "bar"}, consts.HTTP11) 196 197 // response with trailer disableNormalizing 198 resp.Header.DisableNormalizing() 199 resp.Header.Trailer().DisableNormalizing() 200 testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n", 201 consts.StatusMultipleChoices, 5, "bar", "56789", map[string]string{"foo": "bar"}, consts.HTTP11) 202 203 // no content-length ('identity' transfer-encoding) 204 testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foobar\r\n\r\nzxxxx", 205 consts.StatusOK, 5, "foobar", "zxxxx", nil, consts.HTTP11) 206 207 // explicitly stated 'Transfer-Encoding: identity' 208 testResponseReadSuccess(t, resp, "HTTP/1.1 234 ss\r\nContent-Type: xxx\r\n\r\nxag", 209 234, 3, "xxx", "xag", nil, consts.HTTP11) 210 211 // big 'identity' response 212 body := string(mock.CreateFixedBody(100500)) 213 testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\n\r\n"+body, 214 consts.StatusOK, 100500, "aa", body, nil, consts.HTTP11) 215 216 // chunked response 217 testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo2\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nFoo2: bar2\r\n\r\n", 218 200, 6, "text/html", "qwerty", map[string]string{"Foo2": "bar2"}, consts.HTTP11) 219 220 // chunked response with non-chunked Transfer-Encoding. 221 testResponseReadSuccess(t, resp, "HTTP/1.1 230 OK\r\nContent-Type: text\r\nTrailer: Foo3\r\nTransfer-Encoding: aaabbb\r\n\r\n2\r\ner\r\n2\r\nty\r\n0\r\nFoo3: bar3\r\n\r\n", 222 230, 4, "text", "erty", map[string]string{"Foo3": "bar3"}, consts.HTTP11) 223 224 // chunked response with empty body 225 testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo5\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo5: bar5\r\n\r\n", 226 consts.StatusOK, 0, "text/html", "", map[string]string{"Foo5": "bar5"}, consts.HTTP11) 227 } 228 229 func TestResponseReadError(t *testing.T) { 230 t.Parallel() 231 232 resp := &protocol.Response{} 233 234 // empty response 235 testResponseReadError(t, resp, "") 236 237 // invalid header 238 testResponseReadError(t, resp, "foobar") 239 240 // empty body 241 testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nContent-Length: 1234\r\n\r\n") 242 243 // short body 244 testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nContent-Length: 1234\r\n\r\nshort") 245 } 246 247 func TestResponseImmediateHeaderFlushChunked(t *testing.T) { 248 t.Parallel() 249 250 var r protocol.Response 251 252 r.ImmediateHeaderFlush = true 253 254 ch := make(chan int) 255 cb := make(chan struct{}) 256 257 buf := &testReader{read: ch, cb: cb} 258 259 r.SetBodyStream(buf, -1) 260 261 w := bytes.NewBuffer([]byte{}) 262 zw := netpoll.NewWriter(w) 263 264 waitForIt := make(chan struct{}) 265 266 go func() { 267 if err := Write(&r, zw); err != nil { 268 t.Errorf("unexpected error: %s", err) 269 } 270 271 waitForIt <- struct{}{} 272 }() 273 274 ch <- 3 275 276 if !strings.Contains(w.String(), "Transfer-Encoding: chunked") { 277 t.Fatalf("Expected headers to be flushed") 278 } 279 280 if strings.Contains(w.String(), "xxx") { 281 t.Fatalf("Did not expect body to be written yet") 282 } 283 284 <-cb 285 ch <- -1 286 287 <-waitForIt 288 } 289 290 func TestResponseImmediateHeaderFlushFixedLength(t *testing.T) { 291 t.Parallel() 292 293 var r protocol.Response 294 295 r.ImmediateHeaderFlush = true 296 297 ch := make(chan int) 298 cb := make(chan struct{}) 299 300 buf := &testReader{read: ch, cb: cb} 301 302 r.SetBodyStream(buf, 3) 303 304 w := bytes.NewBuffer([]byte{}) 305 zw := netpoll.NewWriter(w) 306 307 waitForIt := make(chan struct{}) 308 309 go func() { 310 if err := Write(&r, zw); err != nil { 311 t.Errorf("unexpected error: %s", err) 312 } 313 waitForIt <- struct{}{} 314 }() 315 316 // reader have more data than bodySize, but only the bodySize length of data will be send. 317 ch <- 10 318 319 if !strings.Contains(w.String(), "Content-Length: 3") { 320 t.Fatalf("Expected headers to be flushed") 321 } 322 323 if strings.Contains(w.String(), "xxx") { 324 t.Fatalf("Did not expect body to be written yet") 325 } 326 327 <-cb 328 // ch <- -1 329 330 <-waitForIt 331 } 332 333 func TestResponseImmediateHeaderFlushFixedLengthWithFewerData(t *testing.T) { 334 t.Parallel() 335 336 var r protocol.Response 337 338 r.ImmediateHeaderFlush = true 339 340 ch := make(chan int) 341 cb := make(chan struct{}) 342 343 buf := &testReader{read: ch, cb: cb} 344 345 r.SetBodyStream(buf, 3) 346 347 w := bytes.NewBuffer([]byte{}) 348 zw := netpoll.NewWriter(w) 349 350 waitForIt := make(chan struct{}) 351 352 go func() { 353 if err := Write(&r, zw); err != nil { 354 assert.NotNil(t, err) 355 } 356 waitForIt <- struct{}{} 357 }() 358 359 // reader have less data than bodySize, server should raise a error in this case 360 ch <- 2 361 362 <-cb 363 ch <- -1 364 365 <-waitForIt 366 } 367 368 func TestResponseSuccess(t *testing.T) { 369 t.Parallel() 370 371 // 200 response 372 testResponseSuccess(t, consts.StatusOK, "test/plain", "server", "foobar", 373 consts.StatusOK, "test/plain", "server") 374 375 // response with missing statusCode 376 testResponseSuccess(t, 0, "text/plain", "server", "foobar", 377 consts.StatusOK, "text/plain", "server") 378 379 // response with missing server 380 testResponseSuccess(t, consts.StatusInternalServerError, "aaa", "", "aaadfsd", 381 consts.StatusInternalServerError, "aaa", "") 382 383 // empty body 384 testResponseSuccess(t, consts.StatusOK, "bbb", "qwer", "", 385 consts.StatusOK, "bbb", "qwer") 386 387 // missing content-type 388 testResponseSuccess(t, consts.StatusOK, "", "asdfsd", "asdf", 389 consts.StatusOK, string(bytestr.DefaultContentType), "asdfsd") 390 } 391 392 func TestResponseReadLimitBody(t *testing.T) { 393 t.Parallel() 394 395 // response with content-length 396 testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 10) 397 testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 100) 398 testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 9) 399 // response with content-encoding 400 testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Encoding: gzip\r\n\r\n9876543210", 10) 401 // chunked response 402 testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 9) 403 testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nFoo: bar\r\n\r\n", 9) 404 testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 100) 405 testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nfoobar\r\n\r\n", 100) 406 testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 2) 407 408 // identity response 409 testResponseReadLimitBodySuccess(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 6) 410 testResponseReadLimitBodySuccess(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 106) 411 testResponseReadLimitBodyError(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 5) 412 } 413 414 func TestResponseReadWithoutBody(t *testing.T) { 415 var resp protocol.Response 416 417 testResponseReadWithoutBody(t, &resp, "HTTP/1.1 304 Not Modified\r\nContent-Type: aa\r\nContent-Encoding: gzip\r\nContent-Length: 1235\r\n\r\n", false, 418 consts.StatusNotModified, 1235, "aa", nil, "gzip", consts.HTTP11) 419 420 testResponseReadWithoutBody(t, &resp, "HTTP/1.1 200 Foo Bar\r\nContent-Type: aab\r\nTrailer: Foo\r\nContent-Encoding: deflate\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo: bar\r\n\r\nHTTP/1.2", false, 421 consts.StatusOK, 0, "aab", map[string]string{"Foo": "bar"}, "deflate", consts.HTTP11) 422 423 testResponseReadWithoutBody(t, &resp, "HTTP/1.1 204 Foo Bar\r\nContent-Type: aab\r\nTrailer: Foo\r\nContent-Encoding: deflate\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo: bar\r\n\r\nHTTP/1.2", true, 424 consts.StatusNoContent, -1, "aab", nil, "deflate", consts.HTTP11) 425 426 testResponseReadWithoutBody(t, &resp, "HTTP/1.1 123 AAA\r\nContent-Type: xxx\r\nContent-Encoding: gzip\r\nContent-Length: 3434\r\n\r\n", false, 427 123, 3434, "xxx", nil, "gzip", consts.HTTP11) 428 429 testResponseReadWithoutBody(t, &resp, "HTTP 200 OK\r\nContent-Type: text/xml\r\nContent-Encoding: deflate\r\nContent-Length: 123\r\n\r\nfoobar\r\n", true, 430 consts.StatusOK, 123, "text/xml", nil, "deflate", consts.HTTP10) 431 432 // '100 Continue' must be skipped. 433 testResponseReadWithoutBody(t, &resp, "HTTP/1.1 100 Continue\r\nFoo-bar: baz\r\n\r\nHTTP/1.1 329 aaa\r\nContent-Type: qwe\r\nContent-Encoding: gzip\r\nContent-Length: 894\r\n\r\n", true, 434 329, 894, "qwe", nil, "gzip", consts.HTTP11) 435 } 436 437 func verifyResponseHeader(t *testing.T, h *protocol.ResponseHeader, expectedStatusCode, expectedContentLength int, expectedContentType, expectedContentEncoding, expectedProtocol string) { 438 if h.StatusCode() != expectedStatusCode { 439 t.Fatalf("Unexpected status code %d. Expected %d", h.StatusCode(), expectedStatusCode) 440 } 441 if h.ContentLength() != expectedContentLength { 442 t.Fatalf("Unexpected content length %d. Expected %d", h.ContentLength(), expectedContentLength) 443 } 444 if string(h.ContentType()) != expectedContentType { 445 t.Fatalf("Unexpected content type %q. Expected %q", h.ContentType(), expectedContentType) 446 } 447 if string(h.ContentEncoding()) != expectedContentEncoding { 448 t.Fatalf("Unexpected content encoding %q. Expected %q", h.ContentEncoding(), expectedContentEncoding) 449 } 450 451 if h.GetProtocol() != expectedProtocol { 452 t.Fatalf("Unexpected protocol %q. Expected %q", h.GetProtocol(), expectedProtocol) 453 } 454 } 455 456 func testResponseSuccess(t *testing.T, statusCode int, contentType, serverName, body string, 457 expectedStatusCode int, expectedContentType, expectedServerName string, 458 ) { 459 var resp protocol.Response 460 resp.SetStatusCode(statusCode) 461 resp.Header.Set("Content-Type", contentType) 462 resp.Header.Set("Server", serverName) 463 resp.SetBody([]byte(body)) 464 465 w := &bytes.Buffer{} 466 // bw := bufio.NewWriter(w) 467 zw := netpoll.NewWriter(w) 468 err := Write(&resp, zw) 469 if err != nil { 470 t.Fatalf("Unexpected error when calling Response.Write(): %s", err) 471 } 472 473 if err = zw.Flush(); err != nil { 474 t.Fatalf("Unexpected error when flushing bufio.Writer: %s", err) 475 } 476 477 var resp1 protocol.Response 478 br := bufio.NewReader(w) 479 zr := netpoll.NewReader(br) 480 if err = Read(&resp1, zr); err != nil { 481 t.Fatalf("Unexpected error when calling Response.Read(): %s", err) 482 } 483 if resp1.StatusCode() != expectedStatusCode { 484 t.Fatalf("Unexpected status code: %d. Expected %d", resp1.StatusCode(), expectedStatusCode) 485 } 486 if resp1.Header.ContentLength() != len(body) { 487 t.Fatalf("Unexpected content-length: %d. Expected %d", resp1.Header.ContentLength(), len(body)) 488 } 489 if string(resp1.Header.Peek(consts.HeaderContentType)) != expectedContentType { 490 t.Fatalf("Unexpected content-type: %q. Expected %q", resp1.Header.Peek(consts.HeaderContentType), expectedContentType) 491 } 492 if string(resp1.Header.Peek(consts.HeaderServer)) != expectedServerName { 493 t.Fatalf("Unexpected server: %q. Expected %q", resp1.Header.Peek(consts.HeaderServer), expectedServerName) 494 } 495 if !bytes.Equal(resp1.Body(), []byte(body)) { 496 t.Fatalf("Unexpected body: %q. Expected %q", resp1.Body(), body) 497 } 498 } 499 500 func testResponseReadWithoutBody(t *testing.T, resp *protocol.Response, s string, skipBody bool, 501 expectedStatusCode, expectedContentLength int, expectedContentType string, expectedTrailer map[string]string, expectedContentEncoding, expectedProtocol string, 502 ) { 503 zr := mock.NewZeroCopyReader(s) 504 resp.SkipBody = skipBody 505 err := Read(resp, zr) 506 if err != nil { 507 t.Fatalf("Unexpected error when reading response without body: %s. response=%q", err, s) 508 } 509 if len(resp.Body()) != 0 { 510 t.Fatalf("Unexpected response body %q. Expected %q. response=%q", resp.Body(), "", s) 511 } 512 513 verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, expectedContentEncoding, expectedProtocol) 514 verifyResponseTrailer(t, &resp.Header, expectedTrailer) 515 516 // verify that ordinal response is read after null-body response 517 resp.SkipBody = false 518 testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nContent-Length: 5\r\nContent-Type: bar\r\n\r\n56789", 519 consts.StatusMultipleChoices, 5, "bar", "56789", nil, consts.HTTP11) 520 } 521 522 func verifyResponseTrailer(t *testing.T, h *protocol.ResponseHeader, expectedTrailers map[string]string) { 523 for k, v := range expectedTrailers { 524 got := h.Trailer().Peek(k) 525 if !bytes.Equal(got, []byte(v)) { 526 t.Fatalf("Unexpected trailer %q. Expected %q. Got %q", k, v, got) 527 } 528 } 529 530 h.Trailer().VisitAll(func(key, value []byte) { 531 if v := expectedTrailers[string(key)]; string(value) != v { 532 t.Fatalf("Unexpected trailer %q. Expected %q. Got %q", string(key), v, string(value)) 533 } 534 }) 535 } 536 537 func testResponseReadLimitBodyError(t *testing.T, s string, maxBodySize int) { 538 var resp protocol.Response 539 zr := netpoll.NewReader(bytes.NewBufferString(s)) 540 err := ReadHeaderAndLimitBody(&resp, zr, maxBodySize) 541 if err == nil { 542 t.Fatalf("expecting error. s=%q, maxBodySize=%d", s, maxBodySize) 543 } 544 if !errors.Is(err, errs.ErrBodyTooLarge) { 545 t.Fatalf("unexpected error: %s. Expecting %s. s=%q, maxBodySize=%d", err, errBodyTooLarge, s, maxBodySize) 546 } 547 } 548 549 func testResponseReadLimitBodySuccess(t *testing.T, s string, maxBodySize int) { 550 var resp protocol.Response 551 mr := mock.NewZeroCopyReader(s) 552 if err := ReadHeaderAndLimitBody(&resp, mr, maxBodySize); err != nil { 553 t.Fatalf("unexpected error: %s. s=%q, maxBodySize=%d", err, s, maxBodySize) 554 } 555 } 556 557 func TestResponseBodyStreamWithTrailer(t *testing.T) { 558 t.Parallel() 559 560 testResponseBodyStreamWithTrailer(t, nil, false) 561 562 body := mock.CreateFixedBody(1e5) 563 testResponseBodyStreamWithTrailer(t, body, false) 564 testResponseBodyStreamWithTrailer(t, body, true) 565 } 566 567 func testResponseBodyStreamWithTrailer(t *testing.T, body []byte, disableNormalizing bool) { 568 expectedTrailer := map[string]string{ 569 "foo": "testfoo", 570 "bar": "testbar", 571 } 572 var resp1 protocol.Response 573 if disableNormalizing { 574 resp1.Header.DisableNormalizing() 575 } 576 resp1.SetBodyStream(bytes.NewReader(body), -1) 577 for k, v := range expectedTrailer { 578 err := resp1.Header.Trailer().Add(k, v) 579 if err != nil { 580 t.Fatalf("unexpected error: %s", err) 581 } 582 } 583 584 var w bytes.Buffer 585 zw := netpoll.NewWriter(&w) 586 if err := Write(&resp1, zw); err != nil { 587 t.Fatalf("unexpected error: %s", err) 588 } 589 if err := zw.Flush(); err != nil { 590 t.Fatalf("unexpected error: %s", err) 591 } 592 593 var resp2 protocol.Response 594 if disableNormalizing { 595 resp2.Header.DisableNormalizing() 596 } 597 br := bufio.NewReader(&w) 598 zr := netpoll.NewReader(br) 599 if err := Read(&resp2, zr); err != nil { 600 t.Fatalf("unexpected error: %s", err) 601 } 602 603 respBody := resp2.Body() 604 if !bytes.Equal(respBody, body) { 605 t.Fatalf("unexpected body: %q. Expecting %q", respBody, body) 606 } 607 608 for k, v := range expectedTrailer { 609 kBytes := []byte(k) 610 utils.NormalizeHeaderKey(kBytes, disableNormalizing) 611 r := resp2.Header.Trailer().Peek(k) 612 if string(r) != v { 613 t.Fatalf("unexpected trailer header %q: %q. Expecting %s", kBytes, r, v) 614 } 615 } 616 } 617 618 func TestResponseReadBodyStreamBadReader(t *testing.T) { 619 t.Parallel() 620 621 resp := protocol.AcquireResponse() 622 623 errReader := mock.NewErrorReadConn(errors.New("test error")) 624 625 bodyBuf := resp.BodyBuffer() 626 bodyBuf.Reset() 627 628 bodyStream := ext.AcquireBodyStream(bodyBuf, errReader, resp.Header.Trailer(), 100) 629 resp.ConstructBodyStream(bodyBuf, convertClientRespStream(bodyStream, func(shouldClose bool) error { 630 assert.True(t, shouldClose) 631 return nil 632 })) 633 634 stBody := resp.BodyStream() 635 closer, _ := stBody.(io.Closer) 636 closer.Close() 637 } 638 639 func TestSetResponseBodyStreamFixedSize(t *testing.T) { 640 t.Parallel() 641 642 testSetResponseBodyStream(t, "a") 643 testSetResponseBodyStream(t, string(mock.CreateFixedBody(4097))) 644 testSetResponseBodyStream(t, string(mock.CreateFixedBody(100500))) 645 } 646 647 func TestSetResponseBodyStreamChunked(t *testing.T) { 648 t.Parallel() 649 650 testSetResponseBodyStreamChunked(t, "", map[string]string{"Foo": "bar"}) 651 652 body := "foobar baz aaa bbb ccc" 653 testSetResponseBodyStreamChunked(t, body, nil) 654 655 body = string(mock.CreateFixedBody(10001)) 656 testSetResponseBodyStreamChunked(t, body, map[string]string{"Foo": "test", "Bar": "test"}) 657 } 658 659 func testSetResponseBodyStream(t *testing.T, body string) { 660 var resp protocol.Response 661 bodySize := len(body) 662 if resp.IsBodyStream() { 663 t.Fatalf("IsBodyStream must return false") 664 } 665 resp.SetBodyStream(bytes.NewBufferString(body), bodySize) 666 if !resp.IsBodyStream() { 667 t.Fatalf("IsBodyStream must return true") 668 } 669 670 var w bytes.Buffer 671 zw := netpoll.NewWriter(&w) 672 if err := Write(&resp, zw); err != nil { 673 t.Fatalf("unexpected error when writing response: %s. body=%q", err, body) 674 } 675 if err := zw.Flush(); err != nil { 676 t.Fatalf("unexpected error when flushing response: %s. body=%q", err, body) 677 } 678 679 var resp1 protocol.Response 680 br := bufio.NewReader(&w) 681 zr := netpoll.NewReader(br) 682 if err := Read(&resp1, zr); err != nil { 683 t.Fatalf("unexpected error when reading response: %s. body=%q", err, body) 684 } 685 if string(resp1.Body()) != body { 686 t.Fatalf("unexpected body %q. Expecting %q", resp1.Body(), body) 687 } 688 } 689 690 func testSetResponseBodyStreamChunked(t *testing.T, body string, trailer map[string]string) { 691 var resp protocol.Response 692 if resp.IsBodyStream() { 693 t.Fatalf("IsBodyStream must return false") 694 } 695 resp.SetBodyStream(bytes.NewBufferString(body), -1) 696 if !resp.IsBodyStream() { 697 t.Fatalf("IsBodyStream must return true") 698 } 699 700 var w bytes.Buffer 701 zw := netpoll.NewWriter(&w) 702 for k, v := range trailer { 703 err := resp.Header.Trailer().Add(k, v) 704 if err != nil { 705 t.Fatalf("unexpected error: %s", err) 706 } 707 } 708 if err := Write(&resp, zw); err != nil { 709 t.Fatalf("unexpected error when writing response: %s. body=%q", err, body) 710 } 711 if err := zw.Flush(); err != nil { 712 t.Fatalf("unexpected error when flushing response: %s. body=%q", err, body) 713 } 714 var resp1 protocol.Response 715 br := bufio.NewReader(&w) 716 zr := netpoll.NewReader(br) 717 if err := Read(&resp1, zr); err != nil { 718 t.Fatalf("unexpected error when reading response: %s. body=%q", err, body) 719 } 720 if string(resp1.Body()) != body { 721 t.Fatalf("unexpected body %q. Expecting %q", resp1.Body(), body) 722 } 723 for k, v := range trailer { 724 r := resp.Header.Trailer().Peek(k) 725 if string(r) != v { 726 t.Fatalf("unexpected trailer %s. Expecting %s. Got %q", k, v, r) 727 } 728 } 729 } 730 731 func testResponseReadBodyStreamSuccess(t *testing.T, resp *protocol.Response, response string, expectedStatusCode, expectedContentLength int, 732 expectedContentType, expectedBody string, expectedTrailer map[string]string, expectedProtocol string, 733 ) { 734 zr := mock.NewZeroCopyReader(response) 735 err := ReadBodyStream(resp, zr, 0, nil) 736 if err != nil { 737 t.Fatalf("Unexpected error: %s", err) 738 } 739 assert.True(t, resp.IsBodyStream()) 740 741 body, err := ioutil.ReadAll(resp.BodyStream()) 742 if err != nil && err != io.EOF { 743 t.Fatalf("Unexpected error: %s", err) 744 } 745 verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, "", expectedProtocol) 746 if !bytes.Equal(body, []byte(expectedBody)) { 747 t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), []byte(expectedBody)) 748 } 749 verifyResponseTrailer(t, &resp.Header, expectedTrailer) 750 } 751 752 func testResponseReadBodyStreamBadTrailer(t *testing.T, resp *protocol.Response, response string) { 753 zr := mock.NewZeroCopyReader(response) 754 err := ReadBodyStream(resp, zr, 0, nil) 755 if err != nil { 756 t.Fatalf("Unexpected error: %s", err) 757 } 758 assert.True(t, resp.IsBodyStream()) 759 760 _, err = ioutil.ReadAll(resp.BodyStream()) 761 if err == nil || err == io.EOF { 762 t.Fatalf("expected error when reading response.") 763 } 764 } 765 766 func TestResponseReadBodyStream(t *testing.T) { 767 t.Parallel() 768 769 resp := &protocol.Response{} 770 771 // usual response 772 testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: foo/bar\r\n\r\n0123456789", 773 consts.StatusOK, 10, "foo/bar", "0123456789", nil, consts.HTTP11) 774 775 // zero response 776 testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 500 OK\r\nContent-Length: 0\r\nContent-Type: foo/bar\r\n\r\n", 777 consts.StatusInternalServerError, 0, "foo/bar", "", nil, consts.HTTP11) 778 779 // response with trailer 780 testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: Foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n", 781 consts.StatusMultipleChoices, -1, "bar", "56789", map[string]string{"Foo": "bar"}, consts.HTTP11) 782 783 // response with trailer disableNormalizing 784 resp.Header.DisableNormalizing() 785 resp.Header.Trailer().DisableNormalizing() 786 testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n", 787 consts.StatusMultipleChoices, -1, "bar", "56789", map[string]string{"foo": "bar"}, consts.HTTP11) 788 789 // no content-length ('identity' transfer-encoding) 790 testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foobar\r\n\r\nzxxxx", 791 consts.StatusOK, -2, "foobar", "zxxxx", nil, consts.HTTP11) 792 793 // explicitly stated 'Transfer-Encoding: identity' 794 testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 234 ss\r\nContent-Type: xxx\r\n\r\nxag", 795 234, -2, "xxx", "xag", nil, consts.HTTP11) 796 797 // big 'identity' response 798 body := string(mock.CreateFixedBody(100500)) 799 testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\n\r\n"+body, 800 consts.StatusOK, -2, "aa", body, nil, consts.HTTP11) 801 802 // chunked response 803 testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo2\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nFoo2: bar2\r\n\r\n", 804 200, -1, "text/html", "qwerty", map[string]string{"Foo2": "bar2"}, consts.HTTP11) 805 806 // chunked response with non-chunked Transfer-Encoding. 807 testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 230 OK\r\nContent-Type: text\r\nTrailer: Foo3\r\nTransfer-Encoding: aaabbb\r\n\r\n2\r\ner\r\n2\r\nty\r\n0\r\nFoo3: bar3\r\n\r\n", 808 230, -1, "text", "erty", map[string]string{"Foo3": "bar3"}, consts.HTTP11) 809 810 // chunked response with empty body 811 testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo5\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo5: bar5\r\n\r\n", 812 consts.StatusOK, -1, "text/html", "", map[string]string{"Foo5": "bar5"}, consts.HTTP11) 813 } 814 815 func TestResponseReadBodyStreamBadTrailer(t *testing.T) { 816 t.Parallel() 817 818 resp := &protocol.Response{} 819 820 testResponseReadBodyStreamBadTrailer(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\ncontent-type: bar\r\n\r\n") 821 testResponseReadBodyStreamBadTrailer(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nproxy-connection: bar2\r\n\r\n") 822 }