mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 09:53:48 +00:00
cmd/signer, rpc: Implement new signer. Add info about remote user to Context
This commit is contained in:
parent
6a49fd22c5
commit
53d6353ae1
5 changed files with 328 additions and 44 deletions
|
|
@ -17,15 +17,13 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"errors"
|
||||
|
||||
"context"
|
||||
|
||||
"math/big"
|
||||
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
|
|
@ -36,12 +34,97 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type SignerAPI struct {
|
||||
chainID *big.Int
|
||||
am *accounts.Manager
|
||||
ui SignerUI
|
||||
}
|
||||
|
||||
// Metadata about the request
|
||||
type Metadata struct {
|
||||
remote string
|
||||
local string
|
||||
scheme string
|
||||
}
|
||||
|
||||
// The SignTxRequest contains info about a transaction tos sign
|
||||
type SignTxRequest struct {
|
||||
transaction *types.Transaction
|
||||
from accounts.Account
|
||||
}
|
||||
|
||||
type ExportRequest struct {
|
||||
account accounts.Account
|
||||
file string
|
||||
}
|
||||
type ImportRequest struct {
|
||||
account accounts.Account
|
||||
}
|
||||
type SignDataRequest struct {
|
||||
account accounts.Account
|
||||
rawdata hexutil.Bytes
|
||||
message string
|
||||
hash hexutil.Bytes
|
||||
}
|
||||
type ApprovalStatus struct {
|
||||
hash common.Hash
|
||||
approved bool
|
||||
pw string
|
||||
}
|
||||
|
||||
type NewAccountRequest struct{}
|
||||
|
||||
type ListRequest struct {
|
||||
accounts []Account
|
||||
}
|
||||
type ListApproval struct {
|
||||
accounts []Account
|
||||
}
|
||||
|
||||
// SignerUI specifies what method a UI needs to implement to be able to be used as a UI
|
||||
// for the signer
|
||||
type SignerUI interface {
|
||||
ApproveTx(request *SignTxRequest, metadata Metadata, ch chan ApprovalStatus)
|
||||
ApproveSignData(request *SignDataRequest, metadata Metadata, ch chan ApprovalStatus)
|
||||
ApproveExport(request *ExportRequest, metadata Metadata, ch chan ApprovalStatus)
|
||||
ApproveImport(request *ImportRequest, metadata Metadata, ch chan ApprovalStatus)
|
||||
ApproveListing(request *ListRequest, metadata Metadata, ch chan ListApproval)
|
||||
ApproveNewAccount(requst *NewAccountRequest, metadata Metadata, ch chan bool)
|
||||
// In case signing fails, bad password etc
|
||||
ShowError(message string)
|
||||
ShowInfo(message string)
|
||||
}
|
||||
|
||||
type HeadlessUI struct {
|
||||
}
|
||||
|
||||
func (ui *HeadlessUI) ApproveTx(request *SignTxRequest, metadata Metadata, ch chan ApprovalStatus) {
|
||||
ch <- ApprovalStatus{request.transaction.Hash(), true, ""}
|
||||
}
|
||||
func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest, metadata Metadata, ch chan ApprovalStatus) {
|
||||
ch <- ApprovalStatus{common.Hash{}, true, ""}
|
||||
}
|
||||
func (ui *HeadlessUI) ApproveExport(request *ExportRequest, metadata Metadata, ch chan ApprovalStatus) {
|
||||
ch <- ApprovalStatus{common.Hash{}, true, ""}
|
||||
}
|
||||
func (ui *HeadlessUI) ApproveImport(request *ImportRequest, metadata Metadata, ch chan ApprovalStatus) {
|
||||
ch <- ApprovalStatus{common.Hash{}, true, ""}
|
||||
}
|
||||
func (ui *HeadlessUI) ApproveListing(request *ListRequest, metadata Metadata, ch chan ListApproval) {
|
||||
ch <- ListApproval{request.accounts}
|
||||
}
|
||||
func (ui *HeadlessUI) ApproveNewAccount(requst *NewAccountRequest, metadata Metadata, ch chan bool) {
|
||||
ch <- true
|
||||
}
|
||||
func (ui *HeadlessUI) ShowError(message string) {
|
||||
//stdout is used by communication
|
||||
fmt.Fprint(os.Stderr, message)
|
||||
}
|
||||
func (ui *HeadlessUI) ShowInfo(message string) {
|
||||
//stdout is used by communication
|
||||
fmt.Fprint(os.Stderr, message)
|
||||
}
|
||||
|
||||
// NewSignerAPI creates a new API that can be used for account management.
|
||||
|
|
@ -49,7 +132,7 @@ type SignerAPI struct {
|
|||
// key that is generated when a new account is created.
|
||||
// noUSB disables USB support that is required to support hardware devices such as
|
||||
// ledger and trezor.
|
||||
func NewSignerAPI(chainID int64, ksLocation string, noUSB bool) *SignerAPI {
|
||||
func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI) *SignerAPI {
|
||||
var backends []accounts.Backend
|
||||
|
||||
// support password based accounts
|
||||
|
|
@ -74,12 +157,30 @@ func NewSignerAPI(chainID int64, ksLocation string, noUSB bool) *SignerAPI {
|
|||
}
|
||||
}
|
||||
|
||||
return &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...)}
|
||||
return &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...), ui}
|
||||
}
|
||||
|
||||
func metaData(ctx context.Context) Metadata {
|
||||
m := Metadata{"NA", "NA", "NA"}
|
||||
|
||||
if v := ctx.Value("remote"); v != nil{
|
||||
m.remote = v.(string)
|
||||
}
|
||||
if v := ctx.Value("scheme"); v != nil{
|
||||
m.scheme = v.(string)
|
||||
}
|
||||
if v := ctx.Value("local"); v != nil{
|
||||
m.local = v.(string)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// List returns the set of wallet this signer manages. Each wallet can contain
|
||||
// multiple accounts.
|
||||
func (api *SignerAPI) List(ctx context.Context) []Account {
|
||||
func (api *SignerAPI) List(ctx context.Context) ([]Account, error) {
|
||||
|
||||
ch := make(chan ListApproval, 1)
|
||||
|
||||
var accounts []Account
|
||||
for _, wallet := range api.am.Wallets() {
|
||||
for _, acc := range wallet.Accounts() {
|
||||
|
|
@ -87,7 +188,12 @@ func (api *SignerAPI) List(ctx context.Context) []Account {
|
|||
accounts = append(accounts, acc)
|
||||
}
|
||||
}
|
||||
return accounts
|
||||
|
||||
api.ui.ApproveListing(&ListRequest{accounts: accounts}, metaData(ctx), ch)
|
||||
if result := <-ch; result.accounts != nil {
|
||||
return result.accounts, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Listing denied")
|
||||
}
|
||||
|
||||
// New creates a new password protected account. The private key is protected with
|
||||
|
|
@ -98,8 +204,13 @@ func (api *SignerAPI) New(ctx context.Context, passphrase string) (accounts.Acco
|
|||
if len(be) == 0 {
|
||||
return accounts.Account{}, errors.New("password based accounts not supported")
|
||||
}
|
||||
acc, err := be[0].(*keystore.KeyStore).NewAccount(passphrase)
|
||||
return acc, err
|
||||
ch := make(chan bool, 1)
|
||||
api.ui.ApproveNewAccount(&NewAccountRequest{}, metaData(ctx), ch)
|
||||
|
||||
if <-ch {
|
||||
return be[0].(*keystore.KeyStore).NewAccount(passphrase)
|
||||
}
|
||||
return accounts.Account{}, fmt.Errorf("Request denied")
|
||||
}
|
||||
|
||||
// SignTransaction signs the given transaction and returns it in an RLP encoded form
|
||||
|
|
@ -119,12 +230,23 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, from common.Address,
|
|||
tx = types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), args.Data)
|
||||
}
|
||||
|
||||
signedTx, err := wallet.SignTxWithPassphrase(acc, passwd, tx, api.chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
ch := make(chan ApprovalStatus, 1)
|
||||
api.ui.ApproveTx(&SignTxRequest{transaction: tx, from: acc}, metaData(ctx), ch)
|
||||
|
||||
if result := <-ch; result.approved {
|
||||
//Sanity check
|
||||
if result.hash != tx.Hash() {
|
||||
return nil, fmt.Errorf("Transaction hash mismatch")
|
||||
}
|
||||
signedTx, err := wallet.SignTxWithPassphrase(acc, passwd, tx, api.chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rlp.EncodeToBytes(signedTx)
|
||||
}
|
||||
|
||||
return rlp.EncodeToBytes(signedTx)
|
||||
return nil, fmt.Errorf("Transaction rejected")
|
||||
|
||||
}
|
||||
|
||||
// Sign calculates an Ethereum ECDSA signature for:
|
||||
|
|
@ -144,13 +266,25 @@ func (api *SignerAPI) Sign(ctx context.Context, addr common.Address, passwd stri
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Assemble sign the data with the wallet
|
||||
signature, err := wallet.SignHashWithPassphrase(account, passwd, signHash(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
ch := make(chan ApprovalStatus, 1)
|
||||
|
||||
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
|
||||
sighash := crypto.Keccak256([]byte(msg))
|
||||
|
||||
api.ui.ApproveSignData(&SignDataRequest{account: account, rawdata: data, message: msg, hash: sighash}, metaData(ctx), ch)
|
||||
|
||||
if (<-ch).approved {
|
||||
|
||||
// Assemble sign the data with the wallet
|
||||
signature, err := wallet.SignHashWithPassphrase(account, passwd, sighash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||
return signature, nil
|
||||
|
||||
}
|
||||
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||
return signature, nil
|
||||
return nil, fmt.Errorf("Signing rejected")
|
||||
}
|
||||
|
||||
// EcRecover returns the address for the account that was used to create the signature.
|
||||
|
|
@ -207,8 +341,14 @@ func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.Raw
|
|||
if url.Scheme != keystore.KeyStoreScheme {
|
||||
return nil, fmt.Errorf("account is not a password protected account")
|
||||
}
|
||||
ch := make(chan ApprovalStatus, 1)
|
||||
|
||||
return ioutil.ReadFile(url.Path)
|
||||
api.ui.ApproveExport(&ExportRequest{account: account, file: url.Path}, metaData(ctx), ch)
|
||||
|
||||
if (<-ch).approved {
|
||||
return ioutil.ReadFile(url.Path)
|
||||
}
|
||||
return nil, fmt.Errorf("Export rejected")
|
||||
}
|
||||
|
||||
// Imports tries to import the given keyJSON in the local keystore. The keyJSON data is expected to be
|
||||
|
|
@ -216,14 +356,23 @@ func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.Raw
|
|||
// decryption it will encrypt the key with the given newPassphrase and store it in the keystore.
|
||||
func (api *SignerAPI) Import(ctx context.Context, keyJSON json.RawMessage, passphrase, newPassphrase string) (Account, error) {
|
||||
be := api.am.Backends(keystore.KeyStoreType)
|
||||
|
||||
if len(be) == 0 {
|
||||
return Account{}, errors.New("password based accounts not supported")
|
||||
}
|
||||
|
||||
acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, passphrase, newPassphrase)
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
ch := make(chan ApprovalStatus, 1)
|
||||
|
||||
api.ui.ApproveImport(&ImportRequest{}, metaData(ctx), ch)
|
||||
if resp := <-ch; resp.approved {
|
||||
|
||||
acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, passphrase, newPassphrase)
|
||||
if err != nil {
|
||||
return Account{}, err
|
||||
}
|
||||
|
||||
return Account{Typ: "account", URL: acc.URL, Address: acc.Address}, nil
|
||||
}
|
||||
return Account{}, fmt.Errorf("Import rejected")
|
||||
|
||||
return Account{Typ: "account", URL: acc.URL, Address: acc.Address}, nil
|
||||
}
|
||||
117
cmd/signer/cliui.go
Normal file
117
cmd/signer/cliui.go
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"os"
|
||||
)
|
||||
|
||||
type CommandlineUI struct {
|
||||
}
|
||||
|
||||
func NewCommandlineUI() *CommandlineUI {
|
||||
return &CommandlineUI{}
|
||||
}
|
||||
func confirm() bool {
|
||||
fmt.Printf("Type 'Yes' to approve\n$>")
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
scanner.Scan()
|
||||
answer := scanner.Text()
|
||||
if answer == "Yes" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func showMetadata(metadata Metadata) {
|
||||
fmt.Printf("Request info: %v -> %v -> %v\n", metadata.remote, metadata.scheme, metadata.local)
|
||||
}
|
||||
|
||||
func (ui *CommandlineUI) ApproveTx(request *SignTxRequest, metadata Metadata, ch chan ApprovalStatus) {
|
||||
|
||||
fmt.Printf("--------- Transaction request-------------\n")
|
||||
fmt.Printf("to: %v\n", request.transaction.To())
|
||||
fmt.Printf("from: %v\n", request.from)
|
||||
fmt.Printf("value: %v\n", request.transaction.Value())
|
||||
fmt.Printf("data: %v\n", common.Bytes2Hex(request.transaction.Data()))
|
||||
fmt.Printf("-------------------------------------------\n")
|
||||
showMetadata(metadata)
|
||||
ch <- ApprovalStatus{common.Hash{}, confirm(), ""}
|
||||
}
|
||||
func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest, metadata Metadata, ch chan ApprovalStatus) {
|
||||
|
||||
fmt.Printf("-------- Sign data request--------------\n")
|
||||
fmt.Printf("account: %x\n", request.account.Address)
|
||||
fmt.Printf("message: \n%v\n", request.message)
|
||||
fmt.Printf("raw data: \n%v\n", request.rawdata)
|
||||
fmt.Printf("message hash: %v\n", request.hash)
|
||||
fmt.Printf("-------------------------------------------\n")
|
||||
showMetadata(metadata)
|
||||
ch <- ApprovalStatus{common.Hash{}, confirm(), ""}
|
||||
}
|
||||
func (ui *CommandlineUI) ApproveExport(request *ExportRequest, metadata Metadata, ch chan ApprovalStatus) {
|
||||
fmt.Printf("-------- Export account request--------------\n")
|
||||
fmt.Printf("A request has been made to export the (encrypted) keyfile\n")
|
||||
fmt.Printf("Approving this operation means that the caller obtains the (encrypted) contents\n")
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("account: %x\n", request.account.Address)
|
||||
fmt.Printf("keyfile: \n%v\n", request.file)
|
||||
fmt.Printf("-------------------------------------------\n")
|
||||
showMetadata(metadata)
|
||||
ch <- ApprovalStatus{common.Hash{}, confirm(), ""}
|
||||
}
|
||||
func (ui *CommandlineUI) ApproveImport(request *ImportRequest, metadata Metadata, ch chan ApprovalStatus) {
|
||||
fmt.Printf("-------- Export account request--------------\n")
|
||||
fmt.Printf("A request has been made to import an encrypted keyfile\n")
|
||||
fmt.Printf("-------------------------------------------\n")
|
||||
showMetadata(metadata)
|
||||
ch <- ApprovalStatus{common.Hash{}, confirm(), ""}
|
||||
}
|
||||
func (ui *CommandlineUI) ApproveListing(request *ListRequest, metadata Metadata, ch chan ListApproval) {
|
||||
|
||||
fmt.Printf("-------- List account request--------------\n")
|
||||
fmt.Printf("A request has been made to list all accounts. \n")
|
||||
fmt.Printf("You can select which accounts the caller can see\n")
|
||||
for _, account := range request.accounts {
|
||||
fmt.Printf("\t[x] %v\n", account.Address.Hex())
|
||||
}
|
||||
fmt.Printf("-------------------------------------------\n")
|
||||
showMetadata(metadata)
|
||||
if confirm() {
|
||||
ch <- ListApproval{request.accounts}
|
||||
} else {
|
||||
ch <- ListApproval{nil}
|
||||
}
|
||||
}
|
||||
func (ui *CommandlineUI) ApproveNewAccount(requst *NewAccountRequest, metadata Metadata, ch chan bool) {
|
||||
fmt.Printf("-------- New account request--------------\n")
|
||||
fmt.Printf("A request has been made to create a new. \n")
|
||||
fmt.Printf("Approving this operation means that a new account is created,\n")
|
||||
fmt.Printf("and the address show to the caller\n")
|
||||
showMetadata(metadata)
|
||||
ch <- confirm()
|
||||
}
|
||||
|
||||
func (ui *CommandlineUI) ShowError(message string) {
|
||||
//stdout is used by communication
|
||||
fmt.Printf("ERROR: %v", message)
|
||||
}
|
||||
func (ui *CommandlineUI) ShowInfo(message string) {
|
||||
//stdout is used by communication
|
||||
fmt.Printf("Info: %v", message)
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"io"
|
||||
"os"
|
||||
|
|
@ -29,6 +28,9 @@ import (
|
|||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"net"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -41,19 +43,29 @@ func main() {
|
|||
|
||||
var (
|
||||
server = rpc.NewServer()
|
||||
api = NewSignerAPI(*chainID, *ksLocation, true)
|
||||
api = NewSignerAPI(*chainID, *ksLocation, true, NewCommandlineUI())
|
||||
listener net.Listener
|
||||
err error
|
||||
)
|
||||
|
||||
// register signer API with server
|
||||
if err := server.RegisterName("account", api); err != nil {
|
||||
if err = server.RegisterName("account", api); err != nil {
|
||||
utils.Fatalf("Could not register signer API: %v", err)
|
||||
}
|
||||
|
||||
// start server with in-/output connected to stdin/stdout
|
||||
in, out := bufio.NewReader(os.Stdin), os.Stdout
|
||||
codec := rpc.NewJSONCodec(&rwc{in, out})
|
||||
server.ServeCodec(codec, rpc.OptionMethodInvocation|rpc.OptionSubscriptions)
|
||||
endpoint := "localhost:8550"
|
||||
|
||||
if listener, err = net.Listen("tcp", endpoint); err != nil {
|
||||
utils.Fatalf("Could not start http listener: %v", err)
|
||||
}
|
||||
log.Info(fmt.Sprintf("HTTP endpoint opened: http://%s", endpoint))
|
||||
fmt.Printf("HTTP endpoint opened: http://%s\n", endpoint)
|
||||
cors := []string{"*"}
|
||||
|
||||
rpc.NewHTTPServer(cors, server).Serve(listener)
|
||||
}
|
||||
// Create account
|
||||
// #curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_new","params":["test"],"id":67}' localhost:8550
|
||||
|
||||
type rwc struct {
|
||||
io.Reader
|
||||
|
|
|
|||
|
|
@ -169,12 +169,17 @@ func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
// All checks passed, create a codec that reads direct from the request body
|
||||
// untilEOF and writes the response to w and order the server to process a
|
||||
// single request.
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, "remote", r.RemoteAddr)
|
||||
ctx = context.WithValue(ctx, "scheme", r.Proto)
|
||||
ctx = context.WithValue(ctx, "local", r.Host)
|
||||
|
||||
body := io.LimitReader(r.Body, maxRequestContentLength)
|
||||
codec := NewJSONCodec(&httpReadWriteNopCloser{body, w})
|
||||
defer codec.Close()
|
||||
|
||||
w.Header().Set("content-type", contentType)
|
||||
srv.ServeSingleRequest(codec, OptionMethodInvocation)
|
||||
srv.ServeSingleRequest(codec, OptionMethodInvocation, ctx)
|
||||
}
|
||||
|
||||
// validateRequest returns a non-zero response code and error message if the
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ func (s *Server) RegisterName(name string, rcvr interface{}) error {
|
|||
// If singleShot is true it will process a single request, otherwise it will handle
|
||||
// requests until the codec returns an error when reading a request (in most cases
|
||||
// an EOF). It executes requests in parallel when singleShot is false.
|
||||
func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecOption) error {
|
||||
func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecOption, ctx context.Context) error {
|
||||
var pend sync.WaitGroup
|
||||
|
||||
defer func() {
|
||||
|
|
@ -140,7 +140,8 @@ func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecO
|
|||
s.codecsMu.Unlock()
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
// ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
// if the codec supports notification include a notifier that callbacks can use
|
||||
|
|
@ -215,14 +216,14 @@ func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecO
|
|||
// stopped. In either case the codec is closed.
|
||||
func (s *Server) ServeCodec(codec ServerCodec, options CodecOption) {
|
||||
defer codec.Close()
|
||||
s.serveRequest(codec, false, options)
|
||||
s.serveRequest(codec, false, options,context.Background())
|
||||
}
|
||||
|
||||
// ServeSingleRequest reads and processes a single RPC request from the given codec. It will not
|
||||
// close the codec unless a non-recoverable error has occurred. Note, this method will return after
|
||||
// a single request has been processed!
|
||||
func (s *Server) ServeSingleRequest(codec ServerCodec, options CodecOption) {
|
||||
s.serveRequest(codec, true, options)
|
||||
func (s *Server) ServeSingleRequest(codec ServerCodec, options CodecOption, ctx context.Context) {
|
||||
s.serveRequest(codec, true, options, ctx)
|
||||
}
|
||||
|
||||
// Stop will stop reading new requests, wait for stopPendingRequestTimeout to allow pending requests to finish,
|
||||
|
|
|
|||
Loading…
Reference in a new issue