github.com/cloudwego/hertz@v0.9.3/pkg/protocol/http1/req/header_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 req
    43  
    44  import (
    45  	"bufio"
    46  	"bytes"
    47  	"errors"
    48  	"fmt"
    49  	"net/http"
    50  	"strings"
    51  	"testing"
    52  
    53  	errs "github.com/cloudwego/hertz/pkg/common/errors"
    54  	"github.com/cloudwego/hertz/pkg/common/test/assert"
    55  	"github.com/cloudwego/hertz/pkg/common/test/mock"
    56  	"github.com/cloudwego/hertz/pkg/protocol"
    57  	"github.com/cloudwego/hertz/pkg/protocol/consts"
    58  	"github.com/cloudwego/netpoll"
    59  )
    60  
    61  func TestRequestHeader_Read(t *testing.T) {
    62  	s := "PUT /foo/bar HTTP/1.1\r\nExpect: 100-continue\r\nUser-Agent: foo\r\nHost: 127.0.0.1\r\nConnection: Keep-Alive\r\nContent-Length: 5\r\nContent-Type: foo/bar\r\n\r\nabcdef4343"
    63  	zr := mock.NewZeroCopyReader(s)
    64  	rh := protocol.RequestHeader{}
    65  	ReadHeader(&rh, zr)
    66  
    67  	// firstline
    68  	assert.DeepEqual(t, []byte(consts.MethodPut), rh.Method())
    69  	assert.DeepEqual(t, []byte("/foo/bar"), rh.RequestURI())
    70  	assert.True(t, rh.IsHTTP11())
    71  
    72  	// headers
    73  	assert.DeepEqual(t, 5, rh.ContentLength())
    74  	assert.DeepEqual(t, []byte("foo/bar"), rh.ContentType())
    75  	count := 0
    76  	rh.VisitAll(func(key, value []byte) {
    77  		count += 1
    78  	})
    79  	assert.DeepEqual(t, 6, count)
    80  	assert.DeepEqual(t, []byte("foo"), rh.UserAgent())
    81  	assert.DeepEqual(t, []byte("127.0.0.1"), rh.Host())
    82  	assert.DeepEqual(t, []byte("100-continue"), rh.Peek("Expect"))
    83  }
    84  
    85  func TestRequestHeaderMultiLineValue(t *testing.T) {
    86  	s := "HTTP/1.1 200 OK\r\n" +
    87  		"EmptyValue1:\r\n" +
    88  		"Content-Type: foo/bar;\r\n\tnewline;\r\n another/newline\r\n" +
    89  		"Foo: Bar\r\n" +
    90  		"Multi-Line: one;\r\n two\r\n" +
    91  		"Values: v1;\r\n v2; v3;\r\n v4;\tv5\r\n" +
    92  		"\r\n"
    93  
    94  	header := new(protocol.RequestHeader)
    95  	if _, err := parse(header, []byte(s)); err != nil {
    96  		t.Fatalf("parse headers with multi-line values failed, %s", err)
    97  	}
    98  	response, err := http.ReadResponse(bufio.NewReader(strings.NewReader(s)), nil)
    99  	if err != nil {
   100  		t.Fatalf("parse response using net/http failed, %s", err)
   101  	}
   102  
   103  	for name, vals := range response.Header {
   104  		got := string(header.Peek(name))
   105  		want := vals[0]
   106  
   107  		if got != want {
   108  			t.Errorf("unexpected %s got: %q want: %q", name, got, want)
   109  		}
   110  	}
   111  }
   112  
   113  func TestRequestHeader_Peek(t *testing.T) {
   114  	s := "PUT /foo/bar HTTP/1.1\r\nExpect: 100-continue\r\nUser-Agent: foo\r\nHost: 127.0.0.1\r\nConnection: Keep-Alive\r\nContent-Length: 5\r\nTransfer-Encoding: foo\r\nContent-Type: foo/bar\r\n\r\nabcdef4343"
   115  	zr := mock.NewZeroCopyReader(s)
   116  	rh := protocol.RequestHeader{}
   117  	ReadHeader(&rh, zr)
   118  	assert.DeepEqual(t, []byte("100-continue"), rh.Peek("Expect"))
   119  	assert.DeepEqual(t, []byte("127.0.0.1"), rh.Peek("Host"))
   120  	assert.DeepEqual(t, []byte("foo"), rh.Peek("User-Agent"))
   121  	assert.DeepEqual(t, []byte("Keep-Alive"), rh.Peek("Connection"))
   122  	assert.DeepEqual(t, []byte(""), rh.Peek("Content-Length"))
   123  	assert.DeepEqual(t, []byte("foo/bar"), rh.Peek("Content-Type"))
   124  }
   125  
   126  func TestRequestHeaderSetGet(t *testing.T) {
   127  	t.Parallel()
   128  
   129  	h := &protocol.RequestHeader{}
   130  	h.SetRequestURI("/aa/bbb")
   131  	h.SetMethod(consts.MethodPost)
   132  	h.Set("foo", "bar")
   133  	h.Set("host", "12345")
   134  	h.Set("content-type", "aaa/bbb")
   135  	h.Set("content-length", "1234")
   136  	h.Set("user-agent", "aaabbb")
   137  	h.Set("referer", "axcv")
   138  	h.Set("baz", "xxxxx")
   139  	h.Set("transfer-encoding", "chunked")
   140  	h.Set("connection", "close")
   141  
   142  	expectRequestHeaderGet(t, h, "Foo", "bar")
   143  	expectRequestHeaderGet(t, h, consts.HeaderHost, "12345")
   144  	expectRequestHeaderGet(t, h, consts.HeaderContentType, "aaa/bbb")
   145  	expectRequestHeaderGet(t, h, consts.HeaderContentLength, "1234")
   146  	expectRequestHeaderGet(t, h, "USER-AGent", "aaabbb")
   147  	expectRequestHeaderGet(t, h, consts.HeaderReferer, "axcv")
   148  	expectRequestHeaderGet(t, h, "baz", "xxxxx")
   149  	expectRequestHeaderGet(t, h, consts.HeaderTransferEncoding, "")
   150  	expectRequestHeaderGet(t, h, "connecTION", "close")
   151  	if !h.ConnectionClose() {
   152  		t.Fatalf("unset connection: close")
   153  	}
   154  
   155  	if h.ContentLength() != 1234 {
   156  		t.Fatalf("Unexpected content-length %d. Expected %d", h.ContentLength(), 1234)
   157  	}
   158  
   159  	w := &bytes.Buffer{}
   160  	bw := bufio.NewWriter(w)
   161  	zw := netpoll.NewWriter(bw)
   162  	err := WriteHeader(h, zw)
   163  	if err != nil {
   164  		t.Fatalf("Unexpected error when writing request header: %s", err)
   165  	}
   166  	if err := bw.Flush(); err != nil {
   167  		t.Fatalf("Unexpected error when flushing request header: %s", err)
   168  	}
   169  	zw.Flush()
   170  	bw.Flush()
   171  
   172  	var h1 protocol.RequestHeader
   173  	br := bufio.NewReader(w)
   174  	zr := mock.ZeroCopyReader{Reader: br}
   175  	if err = ReadHeader(&h1, zr); err != nil {
   176  		t.Fatalf("Unexpected error when reading request header: %s", err)
   177  	}
   178  
   179  	if h1.ContentLength() != h.ContentLength() {
   180  		t.Fatalf("Unexpected Content-Length %d. Expected %d", h1.ContentLength(), h.ContentLength())
   181  	}
   182  
   183  	expectRequestHeaderGet(t, &h1, "Foo", "bar")
   184  	expectRequestHeaderGet(t, &h1, "HOST", "12345")
   185  	expectRequestHeaderGet(t, &h1, consts.HeaderContentType, "aaa/bbb")
   186  	expectRequestHeaderGet(t, &h1, consts.HeaderContentLength, "1234")
   187  	expectRequestHeaderGet(t, &h1, "USER-AGent", "aaabbb")
   188  	expectRequestHeaderGet(t, &h1, consts.HeaderReferer, "axcv")
   189  	expectRequestHeaderGet(t, &h1, "baz", "xxxxx")
   190  	expectRequestHeaderGet(t, &h1, consts.HeaderTransferEncoding, "")
   191  	expectRequestHeaderGet(t, &h1, consts.HeaderConnection, "close")
   192  	if !h1.ConnectionClose() {
   193  		t.Fatalf("unset connection: close")
   194  	}
   195  }
   196  
   197  func TestRequestHeaderCookie(t *testing.T) {
   198  	t.Parallel()
   199  
   200  	var h protocol.RequestHeader
   201  	h.SetRequestURI("/foobar")
   202  	h.Set(consts.HeaderHost, "foobar.com")
   203  
   204  	h.SetCookie("foo", "bar")
   205  	h.SetCookie("привет", "мир")
   206  
   207  	if string(h.Cookie("foo")) != "bar" {
   208  		t.Fatalf("Unexpected cookie value %q. Expected %q", h.Cookie("foo"), "bar")
   209  	}
   210  	if string(h.Cookie("привет")) != "мир" {
   211  		t.Fatalf("Unexpected cookie value %q. Expected %q", h.Cookie("привет"), "мир")
   212  	}
   213  
   214  	w := &bytes.Buffer{}
   215  	zw := netpoll.NewWriter(w)
   216  	if err := WriteHeader(&h, zw); err != nil {
   217  		t.Fatalf("Unexpected error: %s", err)
   218  	}
   219  	if err := zw.Flush(); err != nil {
   220  		t.Fatalf("Unexpected error: %s", err)
   221  	}
   222  
   223  	var h1 protocol.RequestHeader
   224  	br := bufio.NewReader(w)
   225  	zr := mock.ZeroCopyReader{Reader: br}
   226  	if err := ReadHeader(&h1, zr); err != nil {
   227  		t.Fatalf("Unexpected error: %s", err)
   228  	}
   229  
   230  	if !bytes.Equal(h1.Cookie("foo"), h.Cookie("foo")) {
   231  		t.Fatalf("Unexpected cookie value %q. Expected %q", h1.Cookie("foo"), h.Cookie("foo"))
   232  	}
   233  	h1.DelCookie("foo")
   234  	if len(h1.Cookie("foo")) > 0 {
   235  		t.Fatalf("Unexpected cookie found: %q", h1.Cookie("foo"))
   236  	}
   237  	if !bytes.Equal(h1.Cookie("привет"), h.Cookie("привет")) {
   238  		t.Fatalf("Unexpected cookie value %q. Expected %q", h1.Cookie("привет"), h.Cookie("привет"))
   239  	}
   240  	h1.DelCookie("привет")
   241  	if len(h1.Cookie("привет")) > 0 {
   242  		t.Fatalf("Unexpected cookie found: %q", h1.Cookie("привет"))
   243  	}
   244  
   245  	h.DelAllCookies()
   246  	if len(h.Cookie("foo")) > 0 {
   247  		t.Fatalf("Unexpected cookie found: %q", h.Cookie("foo"))
   248  	}
   249  	if len(h.Cookie("привет")) > 0 {
   250  		t.Fatalf("Unexpected cookie found: %q", h.Cookie("привет"))
   251  	}
   252  }
   253  
   254  func TestRequestRawHeaders(t *testing.T) {
   255  	t.Parallel()
   256  
   257  	kvs := "hOsT: foobar\r\n" +
   258  		"value:  b\r\n" +
   259  		"\r\n"
   260  	t.Run("normalized", func(t *testing.T) {
   261  		s := "GET / HTTP/1.1\r\n" + kvs
   262  		exp := kvs
   263  		var h protocol.RequestHeader
   264  		zr := mock.NewZeroCopyReader(s)
   265  		if err := ReadHeader(&h, zr); err != nil {
   266  			t.Fatalf("unexpected error: %s", err)
   267  		}
   268  		if string(h.Host()) != "foobar" {
   269  			t.Fatalf("unexpected host: %q. Expecting %q", h.Host(), "foobar")
   270  		}
   271  		v2 := h.Peek("Value")
   272  		if !bytes.Equal(v2, []byte{'b'}) {
   273  			t.Fatalf("expecting non empty value. Got %q", v2)
   274  		}
   275  		if raw := h.RawHeaders(); string(raw) != exp {
   276  			t.Fatalf("expected header %q, got %q", exp, raw)
   277  		}
   278  	})
   279  	for _, n := range []int{0, 1, 4, 8} {
   280  		t.Run(fmt.Sprintf("post-%dk", n), func(t *testing.T) {
   281  			l := 1024 * n
   282  			body := make([]byte, l)
   283  			for i := range body {
   284  				body[i] = 'a'
   285  			}
   286  			cl := fmt.Sprintf("Content-Length: %d\r\n", l)
   287  			s := "POST / HTTP/1.1\r\n" + cl + kvs + string(body)
   288  			exp := cl + kvs
   289  			var h protocol.RequestHeader
   290  			zr := mock.NewZeroCopyReader(s)
   291  			if err := ReadHeader(&h, zr); err != nil {
   292  				t.Fatalf("unexpected error: %s", err)
   293  			}
   294  			if string(h.Host()) != "foobar" {
   295  				t.Fatalf("unexpected host: %q. Expecting %q", h.Host(), "foobar")
   296  			}
   297  			v2 := h.Peek("Value")
   298  			if !bytes.Equal(v2, []byte{'b'}) {
   299  				t.Fatalf("expecting non empty value. Got %q", v2)
   300  			}
   301  			if raw := h.RawHeaders(); string(raw) != exp {
   302  				t.Fatalf("expected header %q, got %q", exp, raw)
   303  			}
   304  		})
   305  	}
   306  	t.Run("http10", func(t *testing.T) {
   307  		s := "GET / HTTP/1.0\r\n" + kvs
   308  		exp := kvs
   309  		var h protocol.RequestHeader
   310  		zr := mock.NewZeroCopyReader(s)
   311  		if err := ReadHeader(&h, zr); err != nil {
   312  			t.Fatalf("unexpected error: %s", err)
   313  		}
   314  		if string(h.Host()) != "foobar" {
   315  			t.Fatalf("unexpected host: %q. Expecting %q", h.Host(), "foobar")
   316  		}
   317  		v2 := h.Peek("Value")
   318  		if !bytes.Equal(v2, []byte{'b'}) {
   319  			t.Fatalf("expecting non empty value. Got %q", v2)
   320  		}
   321  		if raw := h.RawHeaders(); string(raw) != exp {
   322  			t.Fatalf("expected header %q, got %q", exp, raw)
   323  		}
   324  	})
   325  	t.Run("no-kvs", func(t *testing.T) {
   326  		s := "GET / HTTP/1.1\r\n\r\n"
   327  		exp := ""
   328  		var h protocol.RequestHeader
   329  		h.DisableNormalizing()
   330  		zr := mock.NewZeroCopyReader(s)
   331  		if err := ReadHeader(&h, zr); err != nil {
   332  			t.Fatalf("unexpected error: %s", err)
   333  		}
   334  		if string(h.Host()) != "" {
   335  			t.Fatalf("unexpected host: %q. Expecting %q", h.Host(), "")
   336  		}
   337  		v1 := h.Peek("NoKey")
   338  		if len(v1) > 0 {
   339  			t.Fatalf("expecting empty value. Got %q", v1)
   340  		}
   341  		if raw := h.RawHeaders(); string(raw) != exp {
   342  			t.Fatalf("expected header %q, got %q", exp, raw)
   343  		}
   344  	})
   345  }
   346  
   347  func TestRequestHeaderEmptyValueFromHeader(t *testing.T) {
   348  	t.Parallel()
   349  
   350  	var h1 protocol.RequestHeader
   351  	h1.SetRequestURI("/foo/bar")
   352  	h1.SetHost("foobar")
   353  	h1.Set("EmptyValue1", "")
   354  	h1.Set("EmptyValue2", " ")
   355  	s := h1.String()
   356  
   357  	var h protocol.RequestHeader
   358  	zr := mock.NewZeroCopyReader(s)
   359  	if err := ReadHeader(&h, zr); err != nil {
   360  		t.Fatalf("unexpected error: %s", err)
   361  	}
   362  	if string(h.Host()) != string(h1.Host()) {
   363  		t.Fatalf("unexpected host: %q. Expecting %q", h.Host(), h1.Host())
   364  	}
   365  	v1 := h.Peek("EmptyValue1")
   366  	if len(v1) > 0 {
   367  		t.Fatalf("expecting empty value. Got %q", v1)
   368  	}
   369  	v2 := h.Peek("EmptyValue2")
   370  	if len(v2) > 0 {
   371  		t.Fatalf("expecting empty value. Got %q", v2)
   372  	}
   373  }
   374  
   375  func TestRequestHeaderEmptyValueFromString(t *testing.T) {
   376  	t.Parallel()
   377  
   378  	s := "GET / HTTP/1.1\r\n" +
   379  		"EmptyValue1:\r\n" +
   380  		"Host: foobar\r\n" +
   381  		"EmptyValue2: \r\n" +
   382  		"\r\n"
   383  	var h protocol.RequestHeader
   384  	zr := mock.NewZeroCopyReader(s)
   385  	if err := ReadHeader(&h, zr); err != nil {
   386  		t.Fatalf("unexpected error: %s", err)
   387  	}
   388  	if string(h.Host()) != "foobar" {
   389  		t.Fatalf("unexpected host: %q. Expecting %q", h.Host(), "foobar")
   390  	}
   391  	v1 := h.Peek("EmptyValue1")
   392  	if len(v1) > 0 {
   393  		t.Fatalf("expecting empty value. Got %q", v1)
   394  	}
   395  	v2 := h.Peek("EmptyValue2")
   396  	if len(v2) > 0 {
   397  		t.Fatalf("expecting empty value. Got %q", v2)
   398  	}
   399  }
   400  
   401  func expectRequestHeaderGet(t *testing.T, h *protocol.RequestHeader, key, expectedValue string) {
   402  	if string(h.Peek(key)) != expectedValue {
   403  		t.Fatalf("Unexpected value for key %q: %q. Expected %q", key, h.Peek(key), expectedValue)
   404  	}
   405  }
   406  
   407  func TestRequestHeader_PeekIfExists(t *testing.T) {
   408  	s := "PUT /foo/bar HTTP/1.1\r\nExpect: 100-continue\r\nexists: \r\nContent-Type: foo/bar\r\n\r\nabcdef4343"
   409  	rh := protocol.RequestHeader{}
   410  	err := ReadHeader(&rh, mock.NewZeroCopyReader(s))
   411  	if err != nil {
   412  		t.Fatal(err)
   413  	}
   414  	assert.DeepEqual(t, []byte{}, rh.Peek("exists"))
   415  	assert.DeepEqual(t, []byte(nil), rh.Peek("non-exists"))
   416  }
   417  
   418  func TestRequestHeaderError(t *testing.T) {
   419  	er := mock.EOFReader{}
   420  	rh := protocol.RequestHeader{}
   421  	err := ReadHeader(&rh, &er)
   422  	assert.True(t, errors.Is(err, errs.ErrNothingRead))
   423  }
   424  
   425  func TestReadHeader(t *testing.T) {
   426  	s := "P"
   427  	zr := mock.NewZeroCopyReader(s)
   428  	rh := protocol.RequestHeader{}
   429  	err := ReadHeader(&rh, zr)
   430  	assert.NotNil(t, err)
   431  }
   432  
   433  func TestParseHeaders(t *testing.T) {
   434  	rh := protocol.RequestHeader{}
   435  	_, err := parseHeaders(&rh, []byte{' '})
   436  	assert.NotNil(t, err)
   437  }
   438  
   439  func TestTryRead(t *testing.T) {
   440  	rh := protocol.RequestHeader{}
   441  	s := "P"
   442  	zr := mock.NewZeroCopyReader(s)
   443  	err := tryRead(&rh, zr, 0)
   444  	assert.Nil(t, err)
   445  }
   446  
   447  func TestParseFirstLine(t *testing.T) {
   448  	tests := []struct {
   449  		input    []byte
   450  		method   string
   451  		uri      string
   452  		protocol string
   453  		err      error
   454  	}{
   455  		// Test case 1: n < 0
   456  		{
   457  			input:    []byte("GET /path/to/resource HTTP/1.0\r\n"),
   458  			method:   "GET",
   459  			uri:      "/path/to/resource",
   460  			protocol: "HTTP/1.0",
   461  			err:      nil,
   462  		},
   463  		// Test case 2: n == 0
   464  		{
   465  			input:    []byte(" /path/to/resource HTTP/1.1\r\n"),
   466  			method:   "",
   467  			uri:      "",
   468  			protocol: "",
   469  			err:      fmt.Errorf("requestURI cannot be empty in"),
   470  		},
   471  		// Test case 3: !bytes.Equal(b[n+1:], bytestr.StrHTTP11)
   472  		{
   473  			input:    []byte("POST /path/to/resource HTTP/1.2\r\n"),
   474  			method:   "POST",
   475  			uri:      "/path/to/resource",
   476  			protocol: "HTTP/1.0",
   477  			err:      nil,
   478  		},
   479  	}
   480  
   481  	for _, tc := range tests {
   482  		header := &protocol.RequestHeader{}
   483  		_, err := parseFirstLine(header, tc.input)
   484  		if tc.err != nil {
   485  			assert.NotNil(t, err)
   486  		} else {
   487  			assert.Nil(t, err)
   488  		}
   489  	}
   490  }
   491  
   492  func TestParse(t *testing.T) {
   493  	tests := []struct {
   494  		name     string
   495  		input    []byte
   496  		expected int
   497  		wantErr  bool
   498  	}{
   499  		// normal test
   500  		{
   501  			name:     "normal",
   502  			input:    []byte("GET /path/to/resource HTTP/1.1\r\nHost: example.com\r\n\r\n"),
   503  			expected: len([]byte("GET /path/to/resource HTTP/1.1\r\nHost: example.com\r\n\r\n")),
   504  			wantErr:  false,
   505  		},
   506  		// parseFirstLine error
   507  		{
   508  			name:     "parseFirstLine error",
   509  			input:    []byte("INVALID_LINE\r\nHost: example.com\r\n\r\n"),
   510  			expected: 0,
   511  			wantErr:  true,
   512  		},
   513  		// ext.ReadRawHeaders error
   514  		{
   515  			name:     "ext.ReadRawHeaders error",
   516  			input:    []byte("GET /path/to/resource HTTP/1.1\r\nINVALID_HEADER\r\n\r\n"),
   517  			expected: 0,
   518  			wantErr:  true,
   519  		},
   520  		// parseHeaders error
   521  		{
   522  			name:     "parseHeaders error",
   523  			input:    []byte("GET /path/to/resource HTTP/1.1\r\nHost: example.com\r\nINVALID_HEADER\r\n"),
   524  			expected: 0,
   525  			wantErr:  true,
   526  		},
   527  	}
   528  
   529  	for _, tc := range tests {
   530  		t.Run(tc.name, func(t *testing.T) {
   531  			header := &protocol.RequestHeader{}
   532  			bytesRead, err := parse(header, tc.input)
   533  			if (err != nil) != tc.wantErr {
   534  				t.Errorf("Expected error: %v, but got: %v", tc.wantErr, err)
   535  			}
   536  			if bytesRead != tc.expected {
   537  				t.Errorf("Expected bytes read: %d, but got: %d", tc.expected, bytesRead)
   538  			}
   539  		})
   540  	}
   541  }