/ vendor / github.com / btcsuite / btcd / cmd / btcctl / httpclient.go
httpclient.go
  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/btcd/btcjson"
 14  	"github.com/btcsuite/go-socks/socks"
 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  }