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 }