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  }