github.com/arcology-network/consensus-engine@v1.9.0/rpc/jsonrpc/server/http_server_test.go (about)

     1  package server
     2  
     3  import (
     4  	"crypto/tls"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     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/arcology-network/consensus-engine/libs/log"
    20  	types "github.com/arcology-network/consensus-engine/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 := ioutil.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  	WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(id, &sampleResult{"hello"}))
   116  	resp := w.Result()
   117  	body, err := ioutil.ReadAll(resp.Body)
   118  	_ = resp.Body.Close()
   119  	require.NoError(t, err)
   120  	assert.Equal(t, 200, resp.StatusCode)
   121  	assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
   122  	assert.Equal(t, `{
   123    "jsonrpc": "2.0",
   124    "id": -1,
   125    "result": {
   126      "value": "hello"
   127    }
   128  }`, string(body))
   129  
   130  	// multiple arguments
   131  	w = httptest.NewRecorder()
   132  	WriteRPCResponseHTTP(w,
   133  		types.NewRPCSuccessResponse(id, &sampleResult{"hello"}),
   134  		types.NewRPCSuccessResponse(id, &sampleResult{"world"}))
   135  	resp = w.Result()
   136  	body, err = ioutil.ReadAll(resp.Body)
   137  	_ = resp.Body.Close()
   138  	require.NoError(t, err)
   139  
   140  	assert.Equal(t, 200, resp.StatusCode)
   141  	assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
   142  	assert.Equal(t, `[
   143    {
   144      "jsonrpc": "2.0",
   145      "id": -1,
   146      "result": {
   147        "value": "hello"
   148      }
   149    },
   150    {
   151      "jsonrpc": "2.0",
   152      "id": -1,
   153      "result": {
   154        "value": "world"
   155      }
   156    }
   157  ]`, string(body))
   158  }
   159  
   160  func TestWriteRPCResponseHTTPError(t *testing.T) {
   161  	w := httptest.NewRecorder()
   162  	WriteRPCResponseHTTPError(w,
   163  		http.StatusInternalServerError,
   164  		types.RPCInternalError(types.JSONRPCIntID(-1), errors.New("foo")))
   165  	resp := w.Result()
   166  	body, err := ioutil.ReadAll(resp.Body)
   167  	_ = resp.Body.Close()
   168  	require.NoError(t, err)
   169  	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
   170  	assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
   171  	assert.Equal(t, `{
   172    "jsonrpc": "2.0",
   173    "id": -1,
   174    "error": {
   175      "code": -32603,
   176      "message": "Internal error",
   177      "data": "foo"
   178    }
   179  }`, string(body))
   180  }