gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/rpc/rpcclient/http.go (about) 1 // Copyright 2018 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The aquachain library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the aquachain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "io" 24 "io/ioutil" 25 "net" 26 "net/http" 27 "sync" 28 "time" 29 ) 30 31 const ( 32 contentType = "application/json" 33 ) 34 35 var nullAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:0") 36 37 type httpConn struct { 38 client *http.Client 39 req *http.Request 40 closeOnce sync.Once 41 closed chan struct{} 42 } 43 44 // httpConn is treated specially by Client. 45 func (hc *httpConn) LocalAddr() net.Addr { return nullAddr } 46 func (hc *httpConn) RemoteAddr() net.Addr { return nullAddr } 47 func (hc *httpConn) SetReadDeadline(time.Time) error { return nil } 48 func (hc *httpConn) SetWriteDeadline(time.Time) error { return nil } 49 func (hc *httpConn) SetDeadline(time.Time) error { return nil } 50 func (hc *httpConn) Write([]byte) (int, error) { panic("Write called") } 51 52 func (hc *httpConn) Read(b []byte) (int, error) { 53 <-hc.closed 54 return 0, io.EOF 55 } 56 57 func (hc *httpConn) Close() error { 58 hc.closeOnce.Do(func() { close(hc.closed) }) 59 return nil 60 } 61 62 // DialHTTPCustom creates a new RPC client that connects to an RPC server over HTTP 63 // using the provided HTTP Client and sets headers 64 func DialHTTPCustom(endpoint string, client *http.Client, headers map[string]string) (*Client, error) { 65 return dialHTTPWithClient(endpoint, client, headers) 66 } 67 68 // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP 69 // using the provided HTTP Client. 70 func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { 71 return dialHTTPWithClient(endpoint, client, nil) 72 } 73 func dialHTTPWithClient(endpoint string, client *http.Client, headers map[string]string) (*Client, error) { 74 req, err := http.NewRequest(http.MethodPost, endpoint, nil) 75 if err != nil { 76 return nil, err 77 } 78 req.Header.Set("Content-Type", contentType) 79 req.Header.Set("Accept", contentType) 80 81 for i, v := range headers { 82 req.Header.Set(i, v) 83 } 84 85 initctx := context.Background() 86 return newClient(initctx, func(context.Context) (net.Conn, error) { 87 return &httpConn{client: client, req: req, closed: make(chan struct{})}, nil 88 }) 89 } 90 91 // DialHTTP creates a new RPC client that connects to an RPC server over HTTP. 92 func DialHTTP(endpoint string) (*Client, error) { 93 return DialHTTPWithClient(endpoint, new(http.Client)) 94 } 95 96 func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { 97 hc := c.writeConn.(*httpConn) 98 respBody, err := hc.doRequest(ctx, msg) 99 if err != nil { 100 return err 101 } 102 defer respBody.Close() 103 var respmsg jsonrpcMessage 104 if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { 105 return err 106 } 107 op.resp <- &respmsg 108 return nil 109 } 110 111 func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonrpcMessage) error { 112 hc := c.writeConn.(*httpConn) 113 respBody, err := hc.doRequest(ctx, msgs) 114 if err != nil { 115 return err 116 } 117 defer respBody.Close() 118 var respmsgs []jsonrpcMessage 119 if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { 120 return err 121 } 122 for i := 0; i < len(respmsgs); i++ { 123 op.resp <- &respmsgs[i] 124 } 125 return nil 126 } 127 128 func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { 129 body, err := json.Marshal(msg) 130 if err != nil { 131 return nil, err 132 } 133 req := hc.req.WithContext(ctx) 134 req.Body = ioutil.NopCloser(bytes.NewReader(body)) 135 req.ContentLength = int64(len(body)) 136 137 resp, err := hc.client.Do(req) 138 if err != nil { 139 return nil, err 140 } 141 return resp.Body, nil 142 }