github.com/BlockABC/godash@v0.0.0-20191112120524-f4aa3a32c566/cmd/btcctl/httpclient.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net"
    11  	"net/http"
    12  
    13  	"github.com/btcsuite/go-socks/socks"
    14  	"github.com/BlockABC/godash/btcjson"
    15  )
    16  
    17  // newHTTPClient returns a new HTTP client that is configured according to the
    18  // proxy and TLS settings in the associated connection configuration.
    19  func newHTTPClient(cfg *config) (*http.Client, error) {
    20  	// Configure proxy if needed.
    21  	var dial func(network, addr string) (net.Conn, error)
    22  	if cfg.Proxy != "" {
    23  		proxy := &socks.Proxy{
    24  			Addr:     cfg.Proxy,
    25  			Username: cfg.ProxyUser,
    26  			Password: cfg.ProxyPass,
    27  		}
    28  		dial = func(network, addr string) (net.Conn, error) {
    29  			c, err := proxy.Dial(network, addr)
    30  			if err != nil {
    31  				return nil, err
    32  			}
    33  			return c, nil
    34  		}
    35  	}
    36  
    37  	// Configure TLS if needed.
    38  	var tlsConfig *tls.Config
    39  	if !cfg.NoTLS && cfg.RPCCert != "" {
    40  		pem, err := ioutil.ReadFile(cfg.RPCCert)
    41  		if err != nil {
    42  			return nil, err
    43  		}
    44  
    45  		pool := x509.NewCertPool()
    46  		pool.AppendCertsFromPEM(pem)
    47  		tlsConfig = &tls.Config{
    48  			RootCAs:            pool,
    49  			InsecureSkipVerify: cfg.TLSSkipVerify,
    50  		}
    51  	}
    52  
    53  	// Create and return the new HTTP client potentially configured with a
    54  	// proxy and TLS.
    55  	client := http.Client{
    56  		Transport: &http.Transport{
    57  			Dial:            dial,
    58  			TLSClientConfig: tlsConfig,
    59  		},
    60  	}
    61  	return &client, nil
    62  }
    63  
    64  // sendPostRequest sends the marshalled JSON-RPC command using HTTP-POST mode
    65  // to the server described in the passed config struct.  It also attempts to
    66  // unmarshal the response as a JSON-RPC response and returns either the result
    67  // field or the error field depending on whether or not there is an error.
    68  func sendPostRequest(marshalledJSON []byte, cfg *config) ([]byte, error) {
    69  	// Generate a request to the configured RPC server.
    70  	protocol := "http"
    71  	if !cfg.NoTLS {
    72  		protocol = "https"
    73  	}
    74  	url := protocol + "://" + cfg.RPCServer
    75  	bodyReader := bytes.NewReader(marshalledJSON)
    76  	httpRequest, err := http.NewRequest("POST", url, bodyReader)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	httpRequest.Close = true
    81  	httpRequest.Header.Set("Content-Type", "application/json")
    82  
    83  	// Configure basic access authorization.
    84  	httpRequest.SetBasicAuth(cfg.RPCUser, cfg.RPCPassword)
    85  
    86  	// Create the new HTTP client that is configured according to the user-
    87  	// specified options and submit the request.
    88  	httpClient, err := newHTTPClient(cfg)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	httpResponse, err := httpClient.Do(httpRequest)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	// Read the raw bytes and close the response.
    98  	respBytes, err := ioutil.ReadAll(httpResponse.Body)
    99  	httpResponse.Body.Close()
   100  	if err != nil {
   101  		err = fmt.Errorf("error reading json reply: %v", err)
   102  		return nil, err
   103  	}
   104  
   105  	// Handle unsuccessful HTTP responses
   106  	if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
   107  		// Generate a standard error to return if the server body is
   108  		// empty.  This should not happen very often, but it's better
   109  		// than showing nothing in case the target server has a poor
   110  		// implementation.
   111  		if len(respBytes) == 0 {
   112  			return nil, fmt.Errorf("%d %s", httpResponse.StatusCode,
   113  				http.StatusText(httpResponse.StatusCode))
   114  		}
   115  		return nil, fmt.Errorf("%s", respBytes)
   116  	}
   117  
   118  	// Unmarshal the response.
   119  	var resp btcjson.Response
   120  	if err := json.Unmarshal(respBytes, &resp); err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	if resp.Error != nil {
   125  		return nil, resp.Error
   126  	}
   127  	return resp.Result, nil
   128  }