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  }