You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
3.4 KiB
128 lines
3.4 KiB
package main |
|
|
|
import ( |
|
"bytes" |
|
"crypto/tls" |
|
"crypto/x509" |
|
"encoding/json" |
|
"fmt" |
|
"io/ioutil" |
|
"net" |
|
"net/http" |
|
|
|
"github.com/btcsuite/btcd/btcjson" |
|
"github.com/btcsuite/go-socks/socks" |
|
) |
|
|
|
// newHTTPClient returns a new HTTP client that is configured according to the |
|
// proxy and TLS settings in the associated connection configuration. |
|
func newHTTPClient(cfg *config) (*http.Client, error) { |
|
// Configure proxy if needed. |
|
var dial func(network, addr string) (net.Conn, error) |
|
if cfg.Proxy != "" { |
|
proxy := &socks.Proxy{ |
|
Addr: cfg.Proxy, |
|
Username: cfg.ProxyUser, |
|
Password: cfg.ProxyPass, |
|
} |
|
dial = func(network, addr string) (net.Conn, error) { |
|
c, err := proxy.Dial(network, addr) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return c, nil |
|
} |
|
} |
|
|
|
// Configure TLS if needed. |
|
var tlsConfig *tls.Config |
|
if !cfg.NoTLS && cfg.RPCCert != "" { |
|
pem, err := ioutil.ReadFile(cfg.RPCCert) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
pool := x509.NewCertPool() |
|
pool.AppendCertsFromPEM(pem) |
|
tlsConfig = &tls.Config{ |
|
RootCAs: pool, |
|
InsecureSkipVerify: cfg.TLSSkipVerify, |
|
} |
|
} |
|
|
|
// Create and return the new HTTP client potentially configured with a |
|
// proxy and TLS. |
|
client := http.Client{ |
|
Transport: &http.Transport{ |
|
Dial: dial, |
|
TLSClientConfig: tlsConfig, |
|
}, |
|
} |
|
return &client, nil |
|
} |
|
|
|
// sendPostRequest sends the marshalled JSON-RPC command using HTTP-POST mode |
|
// to the server described in the passed config struct. It also attempts to |
|
// unmarshal the response as a JSON-RPC response and returns either the result |
|
// field or the error field depending on whether or not there is an error. |
|
func sendPostRequest(marshalledJSON []byte, cfg *config) ([]byte, error) { |
|
// Generate a request to the configured RPC server. |
|
protocol := "http" |
|
if !cfg.NoTLS { |
|
protocol = "https" |
|
} |
|
url := protocol + "://" + cfg.RPCServer |
|
bodyReader := bytes.NewReader(marshalledJSON) |
|
httpRequest, err := http.NewRequest("POST", url, bodyReader) |
|
if err != nil { |
|
return nil, err |
|
} |
|
httpRequest.Close = true |
|
httpRequest.Header.Set("Content-Type", "application/json") |
|
|
|
// Configure basic access authorization. |
|
httpRequest.SetBasicAuth(cfg.RPCUser, cfg.RPCPassword) |
|
|
|
// Create the new HTTP client that is configured according to the user- |
|
// specified options and submit the request. |
|
httpClient, err := newHTTPClient(cfg) |
|
if err != nil { |
|
return nil, err |
|
} |
|
httpResponse, err := httpClient.Do(httpRequest) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
// Read the raw bytes and close the response. |
|
respBytes, err := ioutil.ReadAll(httpResponse.Body) |
|
httpResponse.Body.Close() |
|
if err != nil { |
|
err = fmt.Errorf("error reading json reply: %v", err) |
|
return nil, err |
|
} |
|
|
|
// Handle unsuccessful HTTP responses |
|
if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 { |
|
// Generate a standard error to return if the server body is |
|
// empty. This should not happen very often, but it's better |
|
// than showing nothing in case the target server has a poor |
|
// implementation. |
|
if len(respBytes) == 0 { |
|
return nil, fmt.Errorf("%d %s", httpResponse.StatusCode, |
|
http.StatusText(httpResponse.StatusCode)) |
|
} |
|
return nil, fmt.Errorf("%s", respBytes) |
|
} |
|
|
|
// Unmarshal the response. |
|
var resp btcjson.Response |
|
if err := json.Unmarshal(respBytes, &resp); err != nil { |
|
return nil, err |
|
} |
|
|
|
if resp.Error != nil { |
|
return nil, resp.Error |
|
} |
|
return resp.Result, nil |
|
}
|
|
|