aboutsummaryrefslogtreecommitdiff
path: root/rpc
diff options
context:
space:
mode:
authorDeterminant <[email protected]>2020-06-30 17:05:50 -0400
committerDeterminant <[email protected]>2020-06-30 17:05:50 -0400
commit7feec02902d52a3abf722613eb9e218e015b723c (patch)
tree3ca683e866cdaba9c06480f825682677826557f3 /rpc
parent3a872747058e9fd32810d0864e19a197529b7d79 (diff)
make mc tx work
Diffstat (limited to 'rpc')
-rw-r--r--rpc/client_example_test.go88
-rw-r--r--rpc/client_test.go569
-rw-r--r--rpc/http_test.go54
-rw-r--r--rpc/server_test.go152
-rw-r--r--rpc/subscription_test.go206
-rw-r--r--rpc/testservice_test.go180
-rw-r--r--rpc/types_test.go66
-rw-r--r--rpc/websocket_test.go259
8 files changed, 0 insertions, 1574 deletions
diff --git a/rpc/client_example_test.go b/rpc/client_example_test.go
deleted file mode 100644
index 149de2c..0000000
--- a/rpc/client_example_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package rpc_test
-
-import (
- "context"
- "fmt"
- "math/big"
- "time"
-
- "github.com/ava-labs/go-ethereum/rpc"
-)
-
-// In this example, our client wishes to track the latest 'block number'
-// known to the server. The server supports two methods:
-//
-// eth_getBlockByNumber("latest", {})
-// returns the latest block object.
-//
-// eth_subscribe("newBlocks")
-// creates a subscription which fires block objects when new blocks arrive.
-
-type Block struct {
- Number *big.Int
-}
-
-func ExampleClientSubscription() {
- // Connect the client.
- client, _ := rpc.Dial("ws://127.0.0.1:8485")
- subch := make(chan Block)
-
- // Ensure that subch receives the latest block.
- go func() {
- for i := 0; ; i++ {
- if i > 0 {
- time.Sleep(2 * time.Second)
- }
- subscribeBlocks(client, subch)
- }
- }()
-
- // Print events from the subscription as they arrive.
- for block := range subch {
- fmt.Println("latest block:", block.Number)
- }
-}
-
-// subscribeBlocks runs in its own goroutine and maintains
-// a subscription for new blocks.
-func subscribeBlocks(client *rpc.Client, subch chan Block) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- // Subscribe to new blocks.
- sub, err := client.EthSubscribe(ctx, subch, "newHeads")
- if err != nil {
- fmt.Println("subscribe error:", err)
- return
- }
-
- // The connection is established now.
- // Update the channel with the current block.
- var lastBlock Block
- if err := client.CallContext(ctx, &lastBlock, "eth_getBlockByNumber", "latest"); err != nil {
- fmt.Println("can't get latest block:", err)
- return
- }
- subch <- lastBlock
-
- // The subscription will deliver events to the channel. Wait for the
- // subscription to end for any reason, then loop around to re-establish
- // the connection.
- fmt.Println("connection lost: ", <-sub.Err())
-}
diff --git a/rpc/client_test.go b/rpc/client_test.go
deleted file mode 100644
index 79ea32e..0000000
--- a/rpc/client_test.go
+++ /dev/null
@@ -1,569 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package rpc
-
-import (
- "context"
- "fmt"
- "math/rand"
- "net"
- "net/http"
- "net/http/httptest"
- "os"
- "reflect"
- "runtime"
- "sync"
- "testing"
- "time"
-
- "github.com/davecgh/go-spew/spew"
- "github.com/ava-labs/go-ethereum/log"
-)
-
-func TestClientRequest(t *testing.T) {
- server := newTestServer()
- defer server.Stop()
- client := DialInProc(server)
- defer client.Close()
-
- var resp Result
- if err := client.Call(&resp, "test_echo", "hello", 10, &Args{"world"}); err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(resp, Result{"hello", 10, &Args{"world"}}) {
- t.Errorf("incorrect result %#v", resp)
- }
-}
-
-func TestClientBatchRequest(t *testing.T) {
- server := newTestServer()
- defer server.Stop()
- client := DialInProc(server)
- defer client.Close()
-
- batch := []BatchElem{
- {
- Method: "test_echo",
- Args: []interface{}{"hello", 10, &Args{"world"}},
- Result: new(Result),
- },
- {
- Method: "test_echo",
- Args: []interface{}{"hello2", 11, &Args{"world"}},
- Result: new(Result),
- },
- {
- Method: "no_such_method",
- Args: []interface{}{1, 2, 3},
- Result: new(int),
- },
- }
- if err := client.BatchCall(batch); err != nil {
- t.Fatal(err)
- }
- wantResult := []BatchElem{
- {
- Method: "test_echo",
- Args: []interface{}{"hello", 10, &Args{"world"}},
- Result: &Result{"hello", 10, &Args{"world"}},
- },
- {
- Method: "test_echo",
- Args: []interface{}{"hello2", 11, &Args{"world"}},
- Result: &Result{"hello2", 11, &Args{"world"}},
- },
- {
- Method: "no_such_method",
- Args: []interface{}{1, 2, 3},
- Result: new(int),
- Error: &jsonError{Code: -32601, Message: "the method no_such_method does not exist/is not available"},
- },
- }
- if !reflect.DeepEqual(batch, wantResult) {
- t.Errorf("batch results mismatch:\ngot %swant %s", spew.Sdump(batch), spew.Sdump(wantResult))
- }
-}
-
-func TestClientNotify(t *testing.T) {
- server := newTestServer()
- defer server.Stop()
- client := DialInProc(server)
- defer client.Close()
-
- if err := client.Notify(context.Background(), "test_echo", "hello", 10, &Args{"world"}); err != nil {
- t.Fatal(err)
- }
-}
-
-// func TestClientCancelInproc(t *testing.T) { testClientCancel("inproc", t) }
-func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) }
-func TestClientCancelHTTP(t *testing.T) { testClientCancel("http", t) }
-func TestClientCancelIPC(t *testing.T) { testClientCancel("ipc", t) }
-
-// This test checks that requests made through CallContext can be canceled by canceling
-// the context.
-func testClientCancel(transport string, t *testing.T) {
- // These tests take a lot of time, run them all at once.
- // You probably want to run with -parallel 1 or comment out
- // the call to t.Parallel if you enable the logging.
- t.Parallel()
-
- server := newTestServer()
- defer server.Stop()
-
- // What we want to achieve is that the context gets canceled
- // at various stages of request processing. The interesting cases
- // are:
- // - cancel during dial
- // - cancel while performing a HTTP request
- // - cancel while waiting for a response
- //
- // To trigger those, the times are chosen such that connections
- // are killed within the deadline for every other call (maxKillTimeout
- // is 2x maxCancelTimeout).
- //
- // Once a connection is dead, there is a fair chance it won't connect
- // successfully because the accept is delayed by 1s.
- maxContextCancelTimeout := 300 * time.Millisecond
- fl := &flakeyListener{
- maxAcceptDelay: 1 * time.Second,
- maxKillTimeout: 600 * time.Millisecond,
- }
-
- var client *Client
- switch transport {
- case "ws", "http":
- c, hs := httpTestClient(server, transport, fl)
- defer hs.Close()
- client = c
- case "ipc":
- c, l := ipcTestClient(server, fl)
- defer l.Close()
- client = c
- default:
- panic("unknown transport: " + transport)
- }
-
- // The actual test starts here.
- var (
- wg sync.WaitGroup
- nreqs = 10
- ncallers = 6
- )
- caller := func(index int) {
- defer wg.Done()
- for i := 0; i < nreqs; i++ {
- var (
- ctx context.Context
- cancel func()
- timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout)))
- )
- if index < ncallers/2 {
- // For half of the callers, create a context without deadline
- // and cancel it later.
- ctx, cancel = context.WithCancel(context.Background())
- time.AfterFunc(timeout, cancel)
- } else {
- // For the other half, create a context with a deadline instead. This is
- // different because the context deadline is used to set the socket write
- // deadline.
- ctx, cancel = context.WithTimeout(context.Background(), timeout)
- }
- // Now perform a call with the context.
- // The key thing here is that no call will ever complete successfully.
- sleepTime := maxContextCancelTimeout + 20*time.Millisecond
- err := client.CallContext(ctx, nil, "test_sleep", sleepTime)
- if err != nil {
- log.Debug(fmt.Sprint("got expected error:", err))
- } else {
- t.Errorf("no error for call with %v wait time", timeout)
- }
- cancel()
- }
- }
- wg.Add(ncallers)
- for i := 0; i < ncallers; i++ {
- go caller(i)
- }
- wg.Wait()
-}
-
-func TestClientSubscribeInvalidArg(t *testing.T) {
- server := newTestServer()
- defer server.Stop()
- client := DialInProc(server)
- defer client.Close()
-
- check := func(shouldPanic bool, arg interface{}) {
- defer func() {
- err := recover()
- if shouldPanic && err == nil {
- t.Errorf("EthSubscribe should've panicked for %#v", arg)
- }
- if !shouldPanic && err != nil {
- t.Errorf("EthSubscribe shouldn't have panicked for %#v", arg)
- buf := make([]byte, 1024*1024)
- buf = buf[:runtime.Stack(buf, false)]
- t.Error(err)
- t.Error(string(buf))
- }
- }()
- client.EthSubscribe(context.Background(), arg, "foo_bar")
- }
- check(true, nil)
- check(true, 1)
- check(true, (chan int)(nil))
- check(true, make(<-chan int))
- check(false, make(chan int))
- check(false, make(chan<- int))
-}
-
-func TestClientSubscribe(t *testing.T) {
- server := newTestServer()
- defer server.Stop()
- client := DialInProc(server)
- defer client.Close()
-
- nc := make(chan int)
- count := 10
- sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", count, 0)
- if err != nil {
- t.Fatal("can't subscribe:", err)
- }
- for i := 0; i < count; i++ {
- if val := <-nc; val != i {
- t.Fatalf("value mismatch: got %d, want %d", val, i)
- }
- }
-
- sub.Unsubscribe()
- select {
- case v := <-nc:
- t.Fatal("received value after unsubscribe:", v)
- case err := <-sub.Err():
- if err != nil {
- t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err)
- }
- case <-time.After(1 * time.Second):
- t.Fatalf("subscription not closed within 1s after unsubscribe")
- }
-}
-
-// In this test, the connection drops while Subscribe is waiting for a response.
-func TestClientSubscribeClose(t *testing.T) {
- server := newTestServer()
- service := &notificationTestService{
- gotHangSubscriptionReq: make(chan struct{}),
- unblockHangSubscription: make(chan struct{}),
- }
- if err := server.RegisterName("nftest2", service); err != nil {
- t.Fatal(err)
- }
-
- defer server.Stop()
- client := DialInProc(server)
- defer client.Close()
-
- var (
- nc = make(chan int)
- errc = make(chan error)
- sub *ClientSubscription
- err error
- )
- go func() {
- sub, err = client.Subscribe(context.Background(), "nftest2", nc, "hangSubscription", 999)
- errc <- err
- }()
-
- <-service.gotHangSubscriptionReq
- client.Close()
- service.unblockHangSubscription <- struct{}{}
-
- select {
- case err := <-errc:
- if err == nil {
- t.Errorf("Subscribe returned nil error after Close")
- }
- if sub != nil {
- t.Error("Subscribe returned non-nil subscription after Close")
- }
- case <-time.After(1 * time.Second):
- t.Fatalf("Subscribe did not return within 1s after Close")
- }
-}
-
-// This test reproduces https://github.com/ethereum/go-ethereum/issues/17837 where the
-// client hangs during shutdown when Unsubscribe races with Client.Close.
-func TestClientCloseUnsubscribeRace(t *testing.T) {
- server := newTestServer()
- defer server.Stop()
-
- for i := 0; i < 20; i++ {
- client := DialInProc(server)
- nc := make(chan int)
- sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", 3, 1)
- if err != nil {
- t.Fatal(err)
- }
- go client.Close()
- go sub.Unsubscribe()
- select {
- case <-sub.Err():
- case <-time.After(5 * time.Second):
- t.Fatal("subscription not closed within timeout")
- }
- }
-}
-
-// This test checks that Client doesn't lock up when a single subscriber
-// doesn't read subscription events.
-func TestClientNotificationStorm(t *testing.T) {
- server := newTestServer()
- defer server.Stop()
-
- doTest := func(count int, wantError bool) {
- client := DialInProc(server)
- defer client.Close()
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- // Subscribe on the server. It will start sending many notifications
- // very quickly.
- nc := make(chan int)
- sub, err := client.Subscribe(ctx, "nftest", nc, "someSubscription", count, 0)
- if err != nil {
- t.Fatal("can't subscribe:", err)
- }
- defer sub.Unsubscribe()
-
- // Process each notification, try to run a call in between each of them.
- for i := 0; i < count; i++ {
- select {
- case val := <-nc:
- if val != i {
- t.Fatalf("(%d/%d) unexpected value %d", i, count, val)
- }
- case err := <-sub.Err():
- if wantError && err != ErrSubscriptionQueueOverflow {
- t.Fatalf("(%d/%d) got error %q, want %q", i, count, err, ErrSubscriptionQueueOverflow)
- } else if !wantError {
- t.Fatalf("(%d/%d) got unexpected error %q", i, count, err)
- }
- return
- }
- var r int
- err := client.CallContext(ctx, &r, "nftest_echo", i)
- if err != nil {
- if !wantError {
- t.Fatalf("(%d/%d) call error: %v", i, count, err)
- }
- return
- }
- }
- if wantError {
- t.Fatalf("didn't get expected error")
- }
- }
-
- doTest(8000, false)
- doTest(21000, true)
-}
-
-func TestClientHTTP(t *testing.T) {
- server := newTestServer()
- defer server.Stop()
-
- client, hs := httpTestClient(server, "http", nil)
- defer hs.Close()
- defer client.Close()
-
- // Launch concurrent requests.
- var (
- results = make([]Result, 100)
- errc = make(chan error)
- wantResult = Result{"a", 1, new(Args)}
- )
- defer client.Close()
- for i := range results {
- i := i
- go func() {
- errc <- client.Call(&results[i], "test_echo",
- wantResult.String, wantResult.Int, wantResult.Args)
- }()
- }
-
- // Wait for all of them to complete.
- timeout := time.NewTimer(5 * time.Second)
- defer timeout.Stop()
- for i := range results {
- select {
- case err := <-errc:
- if err != nil {
- t.Fatal(err)
- }
- case <-timeout.C:
- t.Fatalf("timeout (got %d/%d) results)", i+1, len(results))
- }
- }
-
- // Check results.
- for i := range results {
- if !reflect.DeepEqual(results[i], wantResult) {
- t.Errorf("result %d mismatch: got %#v, want %#v", i, results[i], wantResult)
- }
- }
-}
-
-func TestClientReconnect(t *testing.T) {
- startServer := func(addr string) (*Server, net.Listener) {
- srv := newTestServer()
- l, err := net.Listen("tcp", addr)
- if err != nil {
- t.Fatal("can't listen:", err)
- }
- go http.Serve(l, srv.WebsocketHandler([]string{"*"}))
- return srv, l
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
- defer cancel()
-
- // Start a server and corresponding client.
- s1, l1 := startServer("127.0.0.1:0")
- client, err := DialContext(ctx, "ws://"+l1.Addr().String())
- if err != nil {
- t.Fatal("can't dial", err)
- }
-
- // Perform a call. This should work because the server is up.
- var resp Result
- if err := client.CallContext(ctx, &resp, "test_echo", "", 1, nil); err != nil {
- t.Fatal(err)
- }
-
- // Shut down the server and allow for some cool down time so we can listen on the same
- // address again.
- l1.Close()
- s1.Stop()
- time.Sleep(2 * time.Second)
-
- // Try calling again. It shouldn't work.
- if err := client.CallContext(ctx, &resp, "test_echo", "", 2, nil); err == nil {
- t.Error("successful call while the server is down")
- t.Logf("resp: %#v", resp)
- }
-
- // Start it up again and call again. The connection should be reestablished.
- // We spawn multiple calls here to check whether this hangs somehow.
- s2, l2 := startServer(l1.Addr().String())
- defer l2.Close()
- defer s2.Stop()
-
- start := make(chan struct{})
- errors := make(chan error, 20)
- for i := 0; i < cap(errors); i++ {
- go func() {
- <-start
- var resp Result
- errors <- client.CallContext(ctx, &resp, "test_echo", "", 3, nil)
- }()
- }
- close(start)
- errcount := 0
- for i := 0; i < cap(errors); i++ {
- if err = <-errors; err != nil {
- errcount++
- }
- }
- t.Logf("%d errors, last error: %v", errcount, err)
- if errcount > 1 {
- t.Errorf("expected one error after disconnect, got %d", errcount)
- }
-}
-
-func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, *httptest.Server) {
- // Create the HTTP server.
- var hs *httptest.Server
- switch transport {
- case "ws":
- hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"}))
- case "http":
- hs = httptest.NewUnstartedServer(srv)
- default:
- panic("unknown HTTP transport: " + transport)
- }
- // Wrap the listener if required.
- if fl != nil {
- fl.Listener = hs.Listener
- hs.Listener = fl
- }
- // Connect the client.
- hs.Start()
- client, err := Dial(transport + "://" + hs.Listener.Addr().String())
- if err != nil {
- panic(err)
- }
- return client, hs
-}
-
-func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) {
- // Listen on a random endpoint.
- endpoint := fmt.Sprintf("go-ethereum-test-ipc-%d-%d", os.Getpid(), rand.Int63())
- if runtime.GOOS == "windows" {
- endpoint = `\\.\pipe\` + endpoint
- } else {
- endpoint = os.TempDir() + "/" + endpoint
- }
- l, err := ipcListen(endpoint)
- if err != nil {
- panic(err)
- }
- // Connect the listener to the server.
- if fl != nil {
- fl.Listener = l
- l = fl
- }
- go srv.ServeListener(l)
- // Connect the client.
- client, err := Dial(endpoint)
- if err != nil {
- panic(err)
- }
- return client, l
-}
-
-// flakeyListener kills accepted connections after a random timeout.
-type flakeyListener struct {
- net.Listener
- maxKillTimeout time.Duration
- maxAcceptDelay time.Duration
-}
-
-func (l *flakeyListener) Accept() (net.Conn, error) {
- delay := time.Duration(rand.Int63n(int64(l.maxAcceptDelay)))
- time.Sleep(delay)
-
- c, err := l.Listener.Accept()
- if err == nil {
- timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout)))
- time.AfterFunc(timeout, func() {
- log.Debug(fmt.Sprintf("killing conn %v after %v", c.LocalAddr(), timeout))
- c.Close()
- })
- }
- return c, err
-}
diff --git a/rpc/http_test.go b/rpc/http_test.go
deleted file mode 100644
index b3f694d..0000000
--- a/rpc/http_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package rpc
-
-import (
- "net/http"
- "net/http/httptest"
- "strings"
- "testing"
-)
-
-func TestHTTPErrorResponseWithDelete(t *testing.T) {
- testHTTPErrorResponse(t, http.MethodDelete, contentType, "", http.StatusMethodNotAllowed)
-}
-
-func TestHTTPErrorResponseWithPut(t *testing.T) {
- testHTTPErrorResponse(t, http.MethodPut, contentType, "", http.StatusMethodNotAllowed)
-}
-
-func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) {
- body := make([]rune, maxRequestContentLength+1)
- testHTTPErrorResponse(t,
- http.MethodPost, contentType, string(body), http.StatusRequestEntityTooLarge)
-}
-
-func TestHTTPErrorResponseWithEmptyContentType(t *testing.T) {
- testHTTPErrorResponse(t, http.MethodPost, "", "", http.StatusUnsupportedMediaType)
-}
-
-func TestHTTPErrorResponseWithValidRequest(t *testing.T) {
- testHTTPErrorResponse(t, http.MethodPost, contentType, "", 0)
-}
-
-func testHTTPErrorResponse(t *testing.T, method, contentType, body string, expected int) {
- request := httptest.NewRequest(method, "http://url.com", strings.NewReader(body))
- request.Header.Set("content-type", contentType)
- if code, _ := validateRequest(request); code != expected {
- t.Fatalf("response code should be %d not %d", expected, code)
- }
-}
diff --git a/rpc/server_test.go b/rpc/server_test.go
deleted file mode 100644
index 3909954..0000000
--- a/rpc/server_test.go
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package rpc
-
-import (
- "bufio"
- "bytes"
- "io"
- "io/ioutil"
- "net"
- "path/filepath"
- "strings"
- "testing"
- "time"
-)
-
-func TestServerRegisterName(t *testing.T) {
- server := NewServer()
- service := new(testService)
-
- if err := server.RegisterName("test", service); err != nil {
- t.Fatalf("%v", err)
- }
-
- if len(server.services.services) != 2 {
- t.Fatalf("Expected 2 service entries, got %d", len(server.services.services))
- }
-
- svc, ok := server.services.services["test"]
- if !ok {
- t.Fatalf("Expected service calc to be registered")
- }
-
- wantCallbacks := 7
- if len(svc.callbacks) != wantCallbacks {
- t.Errorf("Expected %d callbacks for service 'service', got %d", wantCallbacks, len(svc.callbacks))
- }
-}
-
-func TestServer(t *testing.T) {
- files, err := ioutil.ReadDir("testdata")
- if err != nil {
- t.Fatal("where'd my testdata go?")
- }
- for _, f := range files {
- if f.IsDir() || strings.HasPrefix(f.Name(), ".") {
- continue
- }
- path := filepath.Join("testdata", f.Name())
- name := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
- t.Run(name, func(t *testing.T) {
- runTestScript(t, path)
- })
- }
-}
-
-func runTestScript(t *testing.T, file string) {
- server := newTestServer()
- content, err := ioutil.ReadFile(file)
- if err != nil {
- t.Fatal(err)
- }
-
- clientConn, serverConn := net.Pipe()
- defer clientConn.Close()
- go server.ServeCodec(NewJSONCodec(serverConn), OptionMethodInvocation|OptionSubscriptions)
- readbuf := bufio.NewReader(clientConn)
- for _, line := range strings.Split(string(content), "\n") {
- line = strings.TrimSpace(line)
- switch {
- case len(line) == 0 || strings.HasPrefix(line, "//"):
- // skip comments, blank lines
- continue
- case strings.HasPrefix(line, "--> "):
- t.Log(line)
- // write to connection
- clientConn.SetWriteDeadline(time.Now().Add(5 * time.Second))
- if _, err := io.WriteString(clientConn, line[4:]+"\n"); err != nil {
- t.Fatalf("write error: %v", err)
- }
- case strings.HasPrefix(line, "<-- "):
- t.Log(line)
- want := line[4:]
- // read line from connection and compare text
- clientConn.SetReadDeadline(time.Now().Add(5 * time.Second))
- sent, err := readbuf.ReadString('\n')
- if err != nil {
- t.Fatalf("read error: %v", err)
- }
- sent = strings.TrimRight(sent, "\r\n")
- if sent != want {
- t.Errorf("wrong line from server\ngot: %s\nwant: %s", sent, want)
- }
- default:
- panic("invalid line in test script: " + line)
- }
- }
-}
-
-// This test checks that responses are delivered for very short-lived connections that
-// only carry a single request.
-func TestServerShortLivedConn(t *testing.T) {
- server := newTestServer()
- defer server.Stop()
-
- listener, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatal("can't listen:", err)
- }
- defer listener.Close()
- go server.ServeListener(listener)
-
- var (
- request = `{"jsonrpc":"2.0","id":1,"method":"rpc_modules"}` + "\n"
- wantResp = `{"jsonrpc":"2.0","id":1,"result":{"nftest":"1.0","rpc":"1.0","test":"1.0"}}` + "\n"
- deadline = time.Now().Add(10 * time.Second)
- )
- for i := 0; i < 20; i++ {
- conn, err := net.Dial("tcp", listener.Addr().String())
- if err != nil {
- t.Fatal("can't dial:", err)
- }
- defer conn.Close()
- conn.SetDeadline(deadline)
- // Write the request, then half-close the connection so the server stops reading.
- conn.Write([]byte(request))
- conn.(*net.TCPConn).CloseWrite()
- // Now try to get the response.
- buf := make([]byte, 2000)
- n, err := conn.Read(buf)
- if err != nil {
- t.Fatal("read error:", err)
- }
- if !bytes.Equal(buf[:n], []byte(wantResp)) {
- t.Fatalf("wrong response: %s", buf[:n])
- }
- }
-}
diff --git a/rpc/subscription_test.go b/rpc/subscription_test.go
deleted file mode 100644
index eba1924..0000000
--- a/rpc/subscription_test.go
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package rpc
-
-import (
- "encoding/json"
- "fmt"
- "net"
- "strings"
- "testing"
- "time"
-)
-
-func TestNewID(t *testing.T) {
- hexchars := "0123456789ABCDEFabcdef"
- for i := 0; i < 100; i++ {
- id := string(NewID())
- if !strings.HasPrefix(id, "0x") {
- t.Fatalf("invalid ID prefix, want '0x...', got %s", id)
- }
-
- id = id[2:]
- if len(id) == 0 || len(id) > 32 {
- t.Fatalf("invalid ID length, want len(id) > 0 && len(id) <= 32), got %d", len(id))
- }
-
- for i := 0; i < len(id); i++ {
- if strings.IndexByte(hexchars, id[i]) == -1 {
- t.Fatalf("unexpected byte, want any valid hex char, got %c", id[i])
- }
- }
- }
-}
-
-func TestSubscriptions(t *testing.T) {
- var (
- namespaces = []string{"eth", "shh", "bzz"}
- service = &notificationTestService{}
- subCount = len(namespaces)
- notificationCount = 3
-
- server = NewServer()
- clientConn, serverConn = net.Pipe()
- out = json.NewEncoder(clientConn)
- in = json.NewDecoder(clientConn)
- successes = make(chan subConfirmation)
- notifications = make(chan subscriptionResult)
- errors = make(chan error, subCount*notificationCount+1)
- )
-
- // setup and start server
- for _, namespace := range namespaces {
- if err := server.RegisterName(namespace, service); err != nil {
- t.Fatalf("unable to register test service %v", err)
- }
- }
- go server.ServeCodec(NewJSONCodec(serverConn), OptionMethodInvocation|OptionSubscriptions)
- defer server.Stop()
-
- // wait for message and write them to the given channels
- go waitForMessages(in, successes, notifications, errors)
-
- // create subscriptions one by one
- for i, namespace := range namespaces {
- request := map[string]interface{}{
- "id": i,
- "method": fmt.Sprintf("%s_subscribe", namespace),
- "version": "2.0",
- "params": []interface{}{"someSubscription", notificationCount, i},
- }
- if err := out.Encode(&request); err != nil {
- t.Fatalf("Could not create subscription: %v", err)
- }
- }
-
- timeout := time.After(30 * time.Second)
- subids := make(map[string]string, subCount)
- count := make(map[string]int, subCount)
- allReceived := func() bool {
- done := len(count) == subCount
- for _, c := range count {
- if c < notificationCount {
- done = false
- }
- }
- return done
- }
- for !allReceived() {
- select {
- case confirmation := <-successes: // subscription created
- subids[namespaces[confirmation.reqid]] = string(confirmation.subid)
- case notification := <-notifications:
- count[notification.ID]++
- case err := <-errors:
- t.Fatal(err)
- case <-timeout:
- for _, namespace := range namespaces {
- subid, found := subids[namespace]
- if !found {
- t.Errorf("subscription for %q not created", namespace)
- continue
- }
- if count, found := count[subid]; !found || count < notificationCount {
- t.Errorf("didn't receive all notifications (%d<%d) in time for namespace %q", count, notificationCount, namespace)
- }
- }
- t.Fatal("timed out")
- }
- }
-}
-
-// This test checks that unsubscribing works.
-func TestServerUnsubscribe(t *testing.T) {
- // Start the server.
- server := newTestServer()
- service := &notificationTestService{unsubscribed: make(chan string)}
- server.RegisterName("nftest2", service)
- p1, p2 := net.Pipe()
- go server.ServeCodec(NewJSONCodec(p1), OptionMethodInvocation|OptionSubscriptions)
-
- p2.SetDeadline(time.Now().Add(10 * time.Second))
-
- // Subscribe.
- p2.Write([]byte(`{"jsonrpc":"2.0","id":1,"method":"nftest2_subscribe","params":["someSubscription",0,10]}`))
-
- // Handle received messages.
- resps := make(chan subConfirmation)
- notifications := make(chan subscriptionResult)
- errors := make(chan error)
- go waitForMessages(json.NewDecoder(p2), resps, notifications, errors)
-
- // Receive the subscription ID.
- var sub subConfirmation
- select {
- case sub = <-resps:
- case err := <-errors:
- t.Fatal(err)
- }
-
- // Unsubscribe and check that it is handled on the server side.
- p2.Write([]byte(`{"jsonrpc":"2.0","method":"nftest2_unsubscribe","params":["` + sub.subid + `"]}`))
- for {
- select {
- case id := <-service.unsubscribed:
- if id != string(sub.subid) {
- t.Errorf("wrong subscription ID unsubscribed")
- }
- return
- case err := <-errors:
- t.Fatal(err)
- case <-notifications:
- // drop notifications
- }
- }
-}
-
-type subConfirmation struct {
- reqid int
- subid ID
-}
-
-func waitForMessages(in *json.Decoder, successes chan subConfirmation, notifications chan subscriptionResult, errors chan error) {
- for {
- var msg jsonrpcMessage
- if err := in.Decode(&msg); err != nil {
- errors <- fmt.Errorf("decode error: %v", err)
- return
- }
- switch {
- case msg.isNotification():
- var res subscriptionResult
- if err := json.Unmarshal(msg.Params, &res); err != nil {
- errors <- fmt.Errorf("invalid subscription result: %v", err)
- } else {
- notifications <- res
- }
- case msg.isResponse():
- var c subConfirmation
- if msg.Error != nil {
- errors <- msg.Error
- } else if err := json.Unmarshal(msg.Result, &c.subid); err != nil {
- errors <- fmt.Errorf("invalid response: %v", err)
- } else {
- json.Unmarshal(msg.ID, &c.reqid)
- successes <- c
- }
- default:
- errors <- fmt.Errorf("unrecognized message: %v", msg)
- return
- }
- }
-}
diff --git a/rpc/testservice_test.go b/rpc/testservice_test.go
deleted file mode 100644
index 98871b5..0000000
--- a/rpc/testservice_test.go
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2019 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package rpc
-
-import (
- "context"
- "encoding/binary"
- "errors"
- "sync"
- "time"
-)
-
-func newTestServer() *Server {
- server := NewServer()
- server.idgen = sequentialIDGenerator()
- if err := server.RegisterName("test", new(testService)); err != nil {
- panic(err)
- }
- if err := server.RegisterName("nftest", new(notificationTestService)); err != nil {
- panic(err)
- }
- return server
-}
-
-func sequentialIDGenerator() func() ID {
- var (
- mu sync.Mutex
- counter uint64
- )
- return func() ID {
- mu.Lock()
- defer mu.Unlock()
- counter++
- id := make([]byte, 8)
- binary.BigEndian.PutUint64(id, counter)
- return encodeID(id)
- }
-}
-
-type testService struct{}
-
-type Args struct {
- S string
-}
-
-type Result struct {
- String string
- Int int
- Args *Args
-}
-
-func (s *testService) NoArgsRets() {}
-
-func (s *testService) Echo(str string, i int, args *Args) Result {
- return Result{str, i, args}
-}
-
-func (s *testService) EchoWithCtx(ctx context.Context, str string, i int, args *Args) Result {
- return Result{str, i, args}
-}
-
-func (s *testService) Sleep(ctx context.Context, duration time.Duration) {
- time.Sleep(duration)
-}
-
-func (s *testService) Rets() (string, error) {
- return "", nil
-}
-
-func (s *testService) InvalidRets1() (error, string) {
- return nil, ""
-}
-
-func (s *testService) InvalidRets2() (string, string) {
- return "", ""
-}
-
-func (s *testService) InvalidRets3() (string, string, error) {
- return "", "", nil
-}
-
-func (s *testService) CallMeBack(ctx context.Context, method string, args []interface{}) (interface{}, error) {
- c, ok := ClientFromContext(ctx)
- if !ok {
- return nil, errors.New("no client")
- }
- var result interface{}
- err := c.Call(&result, method, args...)
- return result, err
-}
-
-func (s *testService) CallMeBackLater(ctx context.Context, method string, args []interface{}) error {
- c, ok := ClientFromContext(ctx)
- if !ok {
- return errors.New("no client")
- }
- go func() {
- <-ctx.Done()
- var result interface{}
- c.Call(&result, method, args...)
- }()
- return nil
-}
-
-func (s *testService) Subscription(ctx context.Context) (*Subscription, error) {
- return nil, nil
-}
-
-type notificationTestService struct {
- unsubscribed chan string
- gotHangSubscriptionReq chan struct{}
- unblockHangSubscription chan struct{}
-}
-
-func (s *notificationTestService) Echo(i int) int {
- return i
-}
-
-func (s *notificationTestService) Unsubscribe(subid string) {
- if s.unsubscribed != nil {
- s.unsubscribed <- subid
- }
-}
-
-func (s *notificationTestService) SomeSubscription(ctx context.Context, n, val int) (*Subscription, error) {
- notifier, supported := NotifierFromContext(ctx)
- if !supported {
- return nil, ErrNotificationsUnsupported
- }
-
- // By explicitly creating an subscription we make sure that the subscription id is send
- // back to the client before the first subscription.Notify is called. Otherwise the
- // events might be send before the response for the *_subscribe method.
- subscription := notifier.CreateSubscription()
- go func() {
- for i := 0; i < n; i++ {
- if err := notifier.Notify(subscription.ID, val+i); err != nil {
- return
- }
- }
- select {
- case <-notifier.Closed():
- case <-subscription.Err():
- }
- if s.unsubscribed != nil {
- s.unsubscribed <- string(subscription.ID)
- }
- }()
- return subscription, nil
-}
-
-// HangSubscription blocks on s.unblockHangSubscription before sending anything.
-func (s *notificationTestService) HangSubscription(ctx context.Context, val int) (*Subscription, error) {
- notifier, supported := NotifierFromContext(ctx)
- if !supported {
- return nil, ErrNotificationsUnsupported
- }
- s.gotHangSubscriptionReq <- struct{}{}
- <-s.unblockHangSubscription
- subscription := notifier.CreateSubscription()
-
- go func() {
- notifier.Notify(subscription.ID, val)
- }()
- return subscription, nil
-}
diff --git a/rpc/types_test.go b/rpc/types_test.go
deleted file mode 100644
index 0465849..0000000
--- a/rpc/types_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package rpc
-
-import (
- "encoding/json"
- "testing"
-
- "github.com/ava-labs/go-ethereum/common/math"
-)
-
-func TestBlockNumberJSONUnmarshal(t *testing.T) {
- tests := []struct {
- input string
- mustFail bool
- expected BlockNumber
- }{
- 0: {`"0x"`, true, BlockNumber(0)},
- 1: {`"0x0"`, false, BlockNumber(0)},
- 2: {`"0X1"`, false, BlockNumber(1)},
- 3: {`"0x00"`, true, BlockNumber(0)},
- 4: {`"0x01"`, true, BlockNumber(0)},
- 5: {`"0x1"`, false, BlockNumber(1)},
- 6: {`"0x12"`, false, BlockNumber(18)},
- 7: {`"0x7fffffffffffffff"`, false, BlockNumber(math.MaxInt64)},
- 8: {`"0x8000000000000000"`, true, BlockNumber(0)},
- 9: {"0", true, BlockNumber(0)},
- 10: {`"ff"`, true, BlockNumber(0)},
- 11: {`"pending"`, false, PendingBlockNumber},
- 12: {`"latest"`, false, LatestBlockNumber},
- 13: {`"earliest"`, false, EarliestBlockNumber},
- 14: {`someString`, true, BlockNumber(0)},
- 15: {`""`, true, BlockNumber(0)},
- 16: {``, true, BlockNumber(0)},
- }
-
- for i, test := range tests {
- var num BlockNumber
- err := json.Unmarshal([]byte(test.input), &num)
- if test.mustFail && err == nil {
- t.Errorf("Test %d should fail", i)
- continue
- }
- if !test.mustFail && err != nil {
- t.Errorf("Test %d should pass but got err: %v", i, err)
- continue
- }
- if num != test.expected {
- t.Errorf("Test %d got unexpected value, want %d, got %d", i, test.expected, num)
- }
- }
-}
diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go
deleted file mode 100644
index 9dc1084..0000000
--- a/rpc/websocket_test.go
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package rpc
-
-import (
- "context"
- "net"
- "net/http"
- "net/http/httptest"
- "reflect"
- "strings"
- "testing"
- "time"
-
- "github.com/gorilla/websocket"
-)
-
-func TestWebsocketClientHeaders(t *testing.T) {
- t.Parallel()
-
- endpoint, header, err := wsClientHeaders("wss://testuser:[email protected]:1234", "https://example.com")
- if err != nil {
- t.Fatalf("wsGetConfig failed: %s", err)
- }
- if endpoint != "wss://example.com:1234" {
- t.Fatal("User should have been stripped from the URL")
- }
- if header.Get("authorization") != "Basic dGVzdHVzZXI6dGVzdC1QQVNTXzAx" {
- t.Fatal("Basic auth header is incorrect")
- }
- if header.Get("origin") != "https://example.com" {
- t.Fatal("Origin not set")
- }
-}
-
-// This test checks that the server rejects connections from disallowed origins.
-func TestWebsocketOriginCheck(t *testing.T) {
- t.Parallel()
-
- var (
- srv = newTestServer()
- httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"http://example.com"}))
- wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:")
- )
- defer srv.Stop()
- defer httpsrv.Close()
-
- client, err := DialWebsocket(context.Background(), wsURL, "http://ekzample.com")
- if err == nil {
- client.Close()
- t.Fatal("no error for wrong origin")
- }
- wantErr := wsHandshakeError{websocket.ErrBadHandshake, "403 Forbidden"}
- if !reflect.DeepEqual(err, wantErr) {
- t.Fatalf("wrong error for wrong origin: %q", err)
- }
-
- // Connections without origin header should work.
- client, err = DialWebsocket(context.Background(), wsURL, "")
- if err != nil {
- t.Fatal("error for empty origin")
- }
- client.Close()
-}
-
-// This test checks whether calls exceeding the request size limit are rejected.
-func TestWebsocketLargeCall(t *testing.T) {
- t.Parallel()
-
- var (
- srv = newTestServer()
- httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"*"}))
- wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:")
- )
- defer srv.Stop()
- defer httpsrv.Close()
-
- client, err := DialWebsocket(context.Background(), wsURL, "")
- if err != nil {
- t.Fatalf("can't dial: %v", err)
- }
- defer client.Close()
-
- // This call sends slightly less than the limit and should work.
- var result Result
- arg := strings.Repeat("x", maxRequestContentLength-200)
- if err := client.Call(&result, "test_echo", arg, 1); err != nil {
- t.Fatalf("valid call didn't work: %v", err)
- }
- if result.String != arg {
- t.Fatal("wrong string echoed")
- }
-
- // This call sends twice the allowed size and shouldn't work.
- arg = strings.Repeat("x", maxRequestContentLength*2)
- err = client.Call(&result, "test_echo", arg)
- if err == nil {
- t.Fatal("no error for too large call")
- }
-}
-
-// This test checks that client handles WebSocket ping frames correctly.
-func TestClientWebsocketPing(t *testing.T) {
- t.Parallel()
-
- var (
- sendPing = make(chan struct{})
- server = wsPingTestServer(t, sendPing)
- ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
- )
- defer cancel()
- defer server.Shutdown(ctx)
-
- client, err := DialContext(ctx, "ws://"+server.Addr)
- if err != nil {
- t.Fatalf("client dial error: %v", err)
- }
- resultChan := make(chan int)
- sub, err := client.EthSubscribe(ctx, resultChan, "foo")
- if err != nil {
- t.Fatalf("client subscribe error: %v", err)
- }
-
- // Wait for the context's deadline to be reached before proceeding.
- // This is important for reproducing https://github.com/ethereum/go-ethereum/issues/19798
- <-ctx.Done()
- close(sendPing)
-
- // Wait for the subscription result.
- timeout := time.NewTimer(5 * time.Second)
- for {
- select {
- case err := <-sub.Err():
- t.Error("client subscription error:", err)
- case result := <-resultChan:
- t.Log("client got result:", result)
- return
- case <-timeout.C:
- t.Error("didn't get any result within the test timeout")
- return
- }
- }
-}
-
-// wsPingTestServer runs a WebSocket server which accepts a single subscription request.
-// When a value arrives on sendPing, the server sends a ping frame, waits for a matching
-// pong and finally delivers a single subscription result.
-func wsPingTestServer(t *testing.T, sendPing <-chan struct{}) *http.Server {
- var srv http.Server
- shutdown := make(chan struct{})
- srv.RegisterOnShutdown(func() {
- close(shutdown)
- })
- srv.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // Upgrade to WebSocket.
- upgrader := websocket.Upgrader{
- CheckOrigin: func(r *http.Request) bool { return true },
- }
- conn, err := upgrader.Upgrade(w, r, nil)
- if err != nil {
- t.Errorf("server WS upgrade error: %v", err)
- return
- }
- defer conn.Close()
-
- // Handle the connection.
- wsPingTestHandler(t, conn, shutdown, sendPing)
- })
-
- // Start the server.
- listener, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatal("can't listen:", err)
- }
- srv.Addr = listener.Addr().String()
- go srv.Serve(listener)
- return &srv
-}
-
-func wsPingTestHandler(t *testing.T, conn *websocket.Conn, shutdown, sendPing <-chan struct{}) {
- // Canned responses for the eth_subscribe call in TestClientWebsocketPing.
- const (
- subResp = `{"jsonrpc":"2.0","id":1,"result":"0x00"}`
- subNotify = `{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x00","result":1}}`
- )
-
- // Handle subscribe request.
- if _, _, err := conn.ReadMessage(); err != nil {
- t.Errorf("server read error: %v", err)
- return
- }
- if err := conn.WriteMessage(websocket.TextMessage, []byte(subResp)); err != nil {
- t.Errorf("server write error: %v", err)
- return
- }
-
- // Read from the connection to process control messages.
- var pongCh = make(chan string)
- conn.SetPongHandler(func(d string) error {
- t.Logf("server got pong: %q", d)
- pongCh <- d
- return nil
- })
- go func() {
- for {
- typ, msg, err := conn.ReadMessage()
- if err != nil {
- return
- }
- t.Logf("server got message (%d): %q", typ, msg)
- }
- }()
-
- // Write messages.
- var (
- sendResponse <-chan time.Time
- wantPong string
- )
- for {
- select {
- case _, open := <-sendPing:
- if !open {
- sendPing = nil
- }
- t.Logf("server sending ping")
- conn.WriteMessage(websocket.PingMessage, []byte("ping"))
- wantPong = "ping"
- case data := <-pongCh:
- if wantPong == "" {
- t.Errorf("unexpected pong")
- } else if data != wantPong {
- t.Errorf("got pong with wrong data %q", data)
- }
- wantPong = ""
- sendResponse = time.NewTimer(200 * time.Millisecond).C
- case <-sendResponse:
- t.Logf("server sending response")
- conn.WriteMessage(websocket.TextMessage, []byte(subNotify))
- sendResponse = nil
- case <-shutdown:
- conn.Close()
- return
- }
- }
-}