This repository has been archived on 2024-04-07. You can view files and clone it, but cannot push or open issues or pull requests.
go-monero/pkg/http/client.go
Ciro S. Costa 7012486a16 source formatting
Signed-off-by: Ciro S. Costa <utxobr@protonmail.com>
2021-07-18 15:56:51 -04:00

144 lines
3.5 KiB
Go

package http
import (
"crypto/tls"
"fmt"
"net/http"
"time"
)
// ClientConfig provides extra configuration to tweak the behavior of the HTTP
// client instantiated via `NewClient`.
//
type ClientConfig struct {
// TLSSkipVerify indicates that the client should not perform any
// hostname or certificate chain of trust validations.
//
TLSSkipVerify bool
// TLSClientCert is the path to a TLS client certificate to be used
// when connecting to a TLS server.
//
// ps.: must be supplied together with TLSClientKey.
//
TLSClientCert string
// TLSClientKey is the path to a TLS private key certificate to be used
// when connecting to a TLS server.
//
// ps.: must be supplied together with TLSClientCert.
//
TLSClientKey string
// TLSCACert is the path to a certificate authority certificate that
// should be included in the chain of trust.
//
TLSCACert string
// Verbose dictates whether the transport should dump all request and
// response information to stderr.
//
Verbose bool
// RequestTimeout places a deadline on every request issued by this
// client.
//
RequestTimeout time.Duration
// Username is the name of the user to send in the header of every HTTP
// call - must match the first portion of
// `--rpc-login=<username>:[password]` provided to `monerod`.
//
Username string
// Password is the user's password to send in the header of every HTTP
// call - must match the second portion of
// `--rpc-login=<username>:[password]` provided to `monerod` or the
// password interactively supplied during the daemon's startup.
//
// Note that because the `monerod` performs digest auth, the password
// won't be sent solely in plain base64 encoding, but the rest of the
// body of every request and response will still be cleartext.
//
Password string
}
func (c ClientConfig) Validate() error {
if c.TLSClientCert != "" && c.TLSClientKey == "" {
return fmt.Errorf("tls client certificate specified " +
"but tls client key not")
}
if c.TLSClientKey != "" && c.TLSClientCert == "" {
return fmt.Errorf("tls client key specified but " +
"tls client key")
}
if c.Username != "" && c.Password == "" {
return fmt.Errorf("username specified but password not")
}
if c.Password != "" && c.Username == "" {
return fmt.Errorf("password specified but username not")
}
return nil
}
// NewClient instantiates a new `http.Client` based on the client configuration
// supplied.
//
func NewClient(cfg ClientConfig) (*http.Client, error) {
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("validate: %w", err)
}
tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12}
if cfg.TLSCACert != "" {
if err := WithCACert(cfg.TLSCACert)(tlsConfig); err != nil {
return nil, fmt.Errorf("with tls ca cert: %w", err)
}
}
if cfg.TLSClientCert != "" {
err := WithClientCertificate(
cfg.TLSClientCert, cfg.TLSClientKey,
)(tlsConfig)
if err != nil {
return nil, fmt.Errorf(
"with tls client certificate: %w", err)
}
}
if cfg.TLSSkipVerify {
WithInsecureSkipVerify()(tlsConfig)
}
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = tlsConfig
client := &http.Client{
Timeout: cfg.RequestTimeout,
Transport: transport,
}
if cfg.Verbose {
client.Transport = NewDumpTransport(client.Transport)
}
if cfg.Username != "" {
client.Transport = NewDigestAuthTransport(
cfg.Username, cfg.Password,
client.Transport,
)
}
return client, nil
}
func WithTransport(rt http.RoundTripper) func(*http.Client) {
return func(c *http.Client) {
c.Transport = rt
}
}