aboutsummaryrefslogtreecommitdiff
path: root/rpc
diff options
context:
space:
mode:
authorDeterminant <tederminant@gmail.com>2020-06-30 17:05:50 -0400
committerDeterminant <tederminant@gmail.com>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)