This repository has been archived by the owner on Sep 13, 2024. It is now read-only.
forked from btcsuite/btcd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
httpclient.go
128 lines (115 loc) · 3.37 KB
/
httpclient.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
}