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  }