github.com/ethereum/go-ethereum@v1.16.1/rpc/http_test.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package rpc
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  	"net/http/httptest"
    24  	"strings"
    25  	"testing"
    26  )
    27  
    28  func confirmStatusCode(t *testing.T, got, want int) {
    29  	t.Helper()
    30  	if got == want {
    31  		return
    32  	}
    33  	if gotName := http.StatusText(got); len(gotName) > 0 {
    34  		if wantName := http.StatusText(want); len(wantName) > 0 {
    35  			t.Fatalf("response status code: got %d (%s), want %d (%s)", got, gotName, want, wantName)
    36  		}
    37  	}
    38  	t.Fatalf("response status code: got %d, want %d", got, want)
    39  }
    40  
    41  func confirmRequestValidationCode(t *testing.T, method, contentType, body string, expectedStatusCode int) {
    42  	t.Helper()
    43  
    44  	s := NewServer()
    45  	request := httptest.NewRequest(method, "http://url.com", strings.NewReader(body))
    46  	if len(contentType) > 0 {
    47  		request.Header.Set("Content-Type", contentType)
    48  	}
    49  	code, err := s.validateRequest(request)
    50  	if code == 0 {
    51  		if err != nil {
    52  			t.Errorf("validation: got error %v, expected nil", err)
    53  		}
    54  	} else if err == nil {
    55  		t.Errorf("validation: code %d: got nil, expected error", code)
    56  	}
    57  	confirmStatusCode(t, code, expectedStatusCode)
    58  }
    59  
    60  func TestHTTPErrorResponseWithDelete(t *testing.T) {
    61  	t.Parallel()
    62  
    63  	confirmRequestValidationCode(t, http.MethodDelete, contentType, "", http.StatusMethodNotAllowed)
    64  }
    65  
    66  func TestHTTPErrorResponseWithPut(t *testing.T) {
    67  	t.Parallel()
    68  
    69  	confirmRequestValidationCode(t, http.MethodPut, contentType, "", http.StatusMethodNotAllowed)
    70  }
    71  
    72  func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) {
    73  	t.Parallel()
    74  
    75  	body := make([]rune, defaultBodyLimit+1)
    76  	confirmRequestValidationCode(t,
    77  		http.MethodPost, contentType, string(body), http.StatusRequestEntityTooLarge)
    78  }
    79  
    80  func TestHTTPErrorResponseWithEmptyContentType(t *testing.T) {
    81  	t.Parallel()
    82  
    83  	confirmRequestValidationCode(t, http.MethodPost, "", "", http.StatusUnsupportedMediaType)
    84  }
    85  
    86  func TestHTTPErrorResponseWithValidRequest(t *testing.T) {
    87  	t.Parallel()
    88  
    89  	confirmRequestValidationCode(t, http.MethodPost, contentType, "", 0)
    90  }
    91  
    92  func confirmHTTPRequestYieldsStatusCode(t *testing.T, method, contentType, body string, expectedStatusCode int) {
    93  	t.Helper()
    94  	s := Server{}
    95  	ts := httptest.NewServer(&s)
    96  	defer ts.Close()
    97  
    98  	request, err := http.NewRequest(method, ts.URL, strings.NewReader(body))
    99  	if err != nil {
   100  		t.Fatalf("failed to create a valid HTTP request: %v", err)
   101  	}
   102  	if len(contentType) > 0 {
   103  		request.Header.Set("Content-Type", contentType)
   104  	}
   105  	resp, err := http.DefaultClient.Do(request)
   106  	if err != nil {
   107  		t.Fatalf("request failed: %v", err)
   108  	}
   109  	resp.Body.Close()
   110  	confirmStatusCode(t, resp.StatusCode, expectedStatusCode)
   111  }
   112  
   113  func TestHTTPResponseWithEmptyGet(t *testing.T) {
   114  	t.Parallel()
   115  
   116  	confirmHTTPRequestYieldsStatusCode(t, http.MethodGet, "", "", http.StatusOK)
   117  }
   118  
   119  // This checks that maxRequestContentLength is not applied to the response of a request.
   120  func TestHTTPRespBodyUnlimited(t *testing.T) {
   121  	t.Parallel()
   122  
   123  	const respLength = defaultBodyLimit * 3
   124  
   125  	s := NewServer()
   126  	defer s.Stop()
   127  	s.RegisterName("test", largeRespService{respLength})
   128  	ts := httptest.NewServer(s)
   129  	defer ts.Close()
   130  
   131  	c, err := DialHTTP(ts.URL)
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  	defer c.Close()
   136  
   137  	var r string
   138  	if err := c.Call(&r, "test_largeResp"); err != nil {
   139  		t.Fatal(err)
   140  	}
   141  	if len(r) != respLength {
   142  		t.Fatalf("response has wrong length %d, want %d", len(r), respLength)
   143  	}
   144  }
   145  
   146  // Tests that an HTTP error results in an HTTPError instance
   147  // being returned with the expected attributes.
   148  func TestHTTPErrorResponse(t *testing.T) {
   149  	t.Parallel()
   150  
   151  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   152  		http.Error(w, "error has occurred!", http.StatusTeapot)
   153  	}))
   154  	defer ts.Close()
   155  
   156  	c, err := DialHTTP(ts.URL)
   157  	if err != nil {
   158  		t.Fatal(err)
   159  	}
   160  
   161  	var r string
   162  	err = c.Call(&r, "test_method")
   163  	if err == nil {
   164  		t.Fatal("error was expected")
   165  	}
   166  
   167  	httpErr, ok := err.(HTTPError)
   168  	if !ok {
   169  		t.Fatalf("unexpected error type %T", err)
   170  	}
   171  
   172  	if httpErr.StatusCode != http.StatusTeapot {
   173  		t.Error("unexpected status code", httpErr.StatusCode)
   174  	}
   175  	if httpErr.Status != "418 I'm a teapot" {
   176  		t.Error("unexpected status text", httpErr.Status)
   177  	}
   178  	if body := string(httpErr.Body); body != "error has occurred!\n" {
   179  		t.Error("unexpected body", body)
   180  	}
   181  
   182  	if errMsg := httpErr.Error(); errMsg != "418 I'm a teapot: error has occurred!\n" {
   183  		t.Error("unexpected error message", errMsg)
   184  	}
   185  }
   186  
   187  func TestHTTPPeerInfo(t *testing.T) {
   188  	t.Parallel()
   189  
   190  	s := newTestServer()
   191  	defer s.Stop()
   192  	ts := httptest.NewServer(s)
   193  	defer ts.Close()
   194  
   195  	c, err := Dial(ts.URL)
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  	c.SetHeader("user-agent", "ua-testing")
   200  	c.SetHeader("origin", "origin.example.com")
   201  
   202  	// Request peer information.
   203  	var info PeerInfo
   204  	if err := c.Call(&info, "test_peerInfo"); err != nil {
   205  		t.Fatal(err)
   206  	}
   207  
   208  	if info.RemoteAddr == "" {
   209  		t.Error("RemoteAddr not set")
   210  	}
   211  	if info.Transport != "http" {
   212  		t.Errorf("wrong Transport %q", info.Transport)
   213  	}
   214  	if info.HTTP.Version != "HTTP/1.1" {
   215  		t.Errorf("wrong HTTP.Version %q", info.HTTP.Version)
   216  	}
   217  	if info.HTTP.UserAgent != "ua-testing" {
   218  		t.Errorf("wrong HTTP.UserAgent %q", info.HTTP.UserAgent)
   219  	}
   220  	if info.HTTP.Origin != "origin.example.com" {
   221  		t.Errorf("wrong HTTP.Origin %q", info.HTTP.UserAgent)
   222  	}
   223  }
   224  
   225  func TestNewContextWithHeaders(t *testing.T) {
   226  	t.Parallel()
   227  
   228  	expectedHeaders := 0
   229  	server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
   230  		for i := 0; i < expectedHeaders; i++ {
   231  			key, want := fmt.Sprintf("key-%d", i), fmt.Sprintf("val-%d", i)
   232  			if have := request.Header.Get(key); have != want {
   233  				t.Errorf("wrong request headers for %s, want: %s, have: %s", key, want, have)
   234  			}
   235  		}
   236  		writer.WriteHeader(http.StatusOK)
   237  		_, _ = writer.Write([]byte(`{}`))
   238  	}))
   239  	defer server.Close()
   240  
   241  	client, err := Dial(server.URL)
   242  	if err != nil {
   243  		t.Fatalf("failed to dial: %s", err)
   244  	}
   245  	defer client.Close()
   246  
   247  	newHdr := func(k, v string) http.Header {
   248  		header := http.Header{}
   249  		header.Set(k, v)
   250  		return header
   251  	}
   252  	ctx1 := NewContextWithHeaders(context.Background(), newHdr("key-0", "val-0"))
   253  	ctx2 := NewContextWithHeaders(ctx1, newHdr("key-1", "val-1"))
   254  	ctx3 := NewContextWithHeaders(ctx2, newHdr("key-2", "val-2"))
   255  
   256  	expectedHeaders = 3
   257  	if err := client.CallContext(ctx3, nil, "test"); err != ErrNoResult {
   258  		t.Error("call failed", err)
   259  	}
   260  
   261  	expectedHeaders = 2
   262  	if err := client.CallContext(ctx2, nil, "test"); err != ErrNoResult {
   263  		t.Error("call failed:", err)
   264  	}
   265  }