github.com/vipernet-xyz/tm@v0.34.24/rpc/jsonrpc/server/http_server_test.go (about)

     1  package server
     2  
     3  import (
     4  	"crypto/tls"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"sync"
    12  	"sync/atomic"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/vipernet-xyz/tm/libs/log"
    20  	types "github.com/vipernet-xyz/tm/rpc/jsonrpc/types"
    21  )
    22  
    23  type sampleResult struct {
    24  	Value string `json:"value"`
    25  }
    26  
    27  func TestMaxOpenConnections(t *testing.T) {
    28  	const max = 5 // max simultaneous connections
    29  
    30  	// Start the server.
    31  	var open int32
    32  	mux := http.NewServeMux()
    33  	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    34  		if n := atomic.AddInt32(&open, 1); n > int32(max) {
    35  			t.Errorf("%d open connections, want <= %d", n, max)
    36  		}
    37  		defer atomic.AddInt32(&open, -1)
    38  		time.Sleep(10 * time.Millisecond)
    39  		fmt.Fprint(w, "some body")
    40  	})
    41  	config := DefaultConfig()
    42  	config.MaxOpenConnections = max
    43  	l, err := Listen("tcp://127.0.0.1:0", config)
    44  	require.NoError(t, err)
    45  	defer l.Close()
    46  	go Serve(l, mux, log.TestingLogger(), config) //nolint:errcheck // ignore for tests
    47  
    48  	// Make N GET calls to the server.
    49  	attempts := max * 2
    50  	var wg sync.WaitGroup
    51  	var failed int32
    52  	for i := 0; i < attempts; i++ {
    53  		wg.Add(1)
    54  		go func() {
    55  			defer wg.Done()
    56  			c := http.Client{Timeout: 3 * time.Second}
    57  			r, err := c.Get("http://" + l.Addr().String())
    58  			if err != nil {
    59  				atomic.AddInt32(&failed, 1)
    60  				return
    61  			}
    62  			defer r.Body.Close()
    63  		}()
    64  	}
    65  	wg.Wait()
    66  
    67  	// We expect some Gets to fail as the server's accept queue is filled,
    68  	// but most should succeed.
    69  	if int(failed) >= attempts/2 {
    70  		t.Errorf("%d requests failed within %d attempts", failed, attempts)
    71  	}
    72  }
    73  
    74  func TestServeTLS(t *testing.T) {
    75  	ln, err := net.Listen("tcp", "localhost:0")
    76  	require.NoError(t, err)
    77  	defer ln.Close()
    78  
    79  	mux := http.NewServeMux()
    80  	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    81  		fmt.Fprint(w, "some body")
    82  	})
    83  
    84  	chErr := make(chan error, 1)
    85  	go func() {
    86  		// FIXME This goroutine leaks
    87  		chErr <- ServeTLS(ln, mux, "test.crt", "test.key", log.TestingLogger(), DefaultConfig())
    88  	}()
    89  
    90  	select {
    91  	case err := <-chErr:
    92  		require.NoError(t, err)
    93  	case <-time.After(100 * time.Millisecond):
    94  	}
    95  
    96  	tr := &http.Transport{
    97  		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    98  	}
    99  	c := &http.Client{Transport: tr}
   100  	res, err := c.Get("https://" + ln.Addr().String())
   101  	require.NoError(t, err)
   102  	defer res.Body.Close()
   103  	assert.Equal(t, http.StatusOK, res.StatusCode)
   104  
   105  	body, err := io.ReadAll(res.Body)
   106  	require.NoError(t, err)
   107  	assert.Equal(t, []byte("some body"), body)
   108  }
   109  
   110  func TestWriteRPCResponseHTTP(t *testing.T) {
   111  	id := types.JSONRPCIntID(-1)
   112  
   113  	// one argument
   114  	w := httptest.NewRecorder()
   115  	err := WriteCacheableRPCResponseHTTP(w, types.NewRPCSuccessResponse(id, &sampleResult{"hello"}))
   116  	require.NoError(t, err)
   117  	resp := w.Result()
   118  	body, err := io.ReadAll(resp.Body)
   119  	_ = resp.Body.Close()
   120  	require.NoError(t, err)
   121  	assert.Equal(t, 200, resp.StatusCode)
   122  	assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
   123  	assert.Equal(t, "public, max-age=86400", resp.Header.Get("Cache-control"))
   124  	assert.Equal(t, `{"jsonrpc":"2.0","id":-1,"result":{"value":"hello"}}`, string(body))
   125  
   126  	// multiple arguments
   127  	w = httptest.NewRecorder()
   128  	err = WriteRPCResponseHTTP(w,
   129  		types.NewRPCSuccessResponse(id, &sampleResult{"hello"}),
   130  		types.NewRPCSuccessResponse(id, &sampleResult{"world"}))
   131  	require.NoError(t, err)
   132  	resp = w.Result()
   133  	body, err = io.ReadAll(resp.Body)
   134  	_ = resp.Body.Close()
   135  	require.NoError(t, err)
   136  
   137  	assert.Equal(t, 200, resp.StatusCode)
   138  	assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
   139  	assert.Equal(t, `[{"jsonrpc":"2.0","id":-1,"result":{"value":"hello"}},{"jsonrpc":"2.0","id":-1,"result":{"value":"world"}}]`, string(body))
   140  }
   141  
   142  func TestWriteRPCResponseHTTPError(t *testing.T) {
   143  	w := httptest.NewRecorder()
   144  	err := WriteRPCResponseHTTPError(
   145  		w,
   146  		http.StatusInternalServerError,
   147  		types.RPCInternalError(types.JSONRPCIntID(-1), errors.New("foo")))
   148  	require.NoError(t, err)
   149  	resp := w.Result()
   150  	body, err := io.ReadAll(resp.Body)
   151  	_ = resp.Body.Close()
   152  	require.NoError(t, err)
   153  	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
   154  	assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
   155  	assert.Equal(t, `{"jsonrpc":"2.0","id":-1,"error":{"code":-32603,"message":"Internal error","data":"foo"}}`, string(body))
   156  }