| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | // 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/>. | 
					
						
							| 
									
										
										
										
											2016-04-14 18:18:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | package rpc | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 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) | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 		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)) | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 		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]) | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | func TestSubscriptions(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 		namespaces        = []string{"eth", "shh", "bzz"} | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 		service           = ¬ificationTestService{} | 
					
						
							|  |  |  | 		subCount          = len(namespaces) | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 		notificationCount = 3 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 		server                 = NewServer() | 
					
						
							|  |  |  | 		clientConn, serverConn = net.Pipe() | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 		out                    = json.NewEncoder(clientConn) | 
					
						
							|  |  |  | 		in                     = json.NewDecoder(clientConn) | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 		successes              = make(chan subConfirmation) | 
					
						
							|  |  |  | 		notifications          = make(chan subscriptionResult) | 
					
						
							|  |  |  | 		errors                 = make(chan error, subCount*notificationCount+1) | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// setup and start server | 
					
						
							|  |  |  | 	for _, namespace := range namespaces { | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 		if err := server.RegisterName(namespace, service); err != nil { | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 			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 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	go waitForMessages(in, successes, notifications, errors) | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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", | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 			"params":  []interface{}{"someSubscription", notificationCount, i}, | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if err := out.Encode(&request); err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Could not create subscription: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	timeout := time.After(30 * time.Second) | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 	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 { | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 				done = false | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 		return done | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for !allReceived() { | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 		select { | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 		case confirmation := <-successes: // subscription created | 
					
						
							|  |  |  | 			subids[namespaces[confirmation.reqid]] = string(confirmation.subid) | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 		case notification := <-notifications: | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 			count[notification.ID]++ | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 		case err := <-errors: | 
					
						
							|  |  |  | 			t.Fatal(err) | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 		case <-timeout: | 
					
						
							|  |  |  | 			for _, namespace := range namespaces { | 
					
						
							|  |  |  | 				subid, found := subids[namespace] | 
					
						
							|  |  |  | 				if !found { | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 					t.Errorf("subscription for %q not created", namespace) | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 				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) | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-10-09 16:34:24 +02:00
										 |  |  | 			t.Fatal("timed out") | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // This test checks that unsubscribing works. | 
					
						
							|  |  |  | func TestServerUnsubscribe(t *testing.T) { | 
					
						
							|  |  |  | 	// Start the server. | 
					
						
							|  |  |  | 	server := newTestServer() | 
					
						
							|  |  |  | 	service := ¬ificationTestService{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 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |