github.com/btcsuite/btcd@v0.24.0/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/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 }