forked from cerc-io/ipld-eth-server
* Add vendor dir so builds dont require dep * Pin specific version go-eth version
This commit is contained in:
+167
@@ -0,0 +1,167 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
)
|
||||
|
||||
const (
|
||||
showHelpMessage = "Specify -h to show available options"
|
||||
listCmdMessage = "Specify -l to list available commands"
|
||||
)
|
||||
|
||||
// commandUsage display the usage for a specific command.
|
||||
func commandUsage(method string) {
|
||||
usage, err := btcjson.MethodUsageText(method)
|
||||
if err != nil {
|
||||
// This should never happen since the method was already checked
|
||||
// before calling this function, but be safe.
|
||||
fmt.Fprintln(os.Stderr, "Failed to obtain command usage:", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "Usage:")
|
||||
fmt.Fprintf(os.Stderr, " %s\n", usage)
|
||||
}
|
||||
|
||||
// usage displays the general usage when the help flag is not displayed and
|
||||
// and an invalid command was specified. The commandUsage function is used
|
||||
// instead when a valid command was specified.
|
||||
func usage(errorMessage string) {
|
||||
appName := filepath.Base(os.Args[0])
|
||||
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
|
||||
fmt.Fprintln(os.Stderr, errorMessage)
|
||||
fmt.Fprintln(os.Stderr, "Usage:")
|
||||
fmt.Fprintf(os.Stderr, " %s [OPTIONS] <command> <args...>\n\n",
|
||||
appName)
|
||||
fmt.Fprintln(os.Stderr, showHelpMessage)
|
||||
fmt.Fprintln(os.Stderr, listCmdMessage)
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg, args, err := loadConfig()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(args) < 1 {
|
||||
usage("No command specified")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Ensure the specified method identifies a valid registered command and
|
||||
// is one of the usable types.
|
||||
method := args[0]
|
||||
usageFlags, err := btcjson.MethodUsageFlags(method)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unrecognized command '%s'\n", method)
|
||||
fmt.Fprintln(os.Stderr, listCmdMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
if usageFlags&unusableFlags != 0 {
|
||||
fmt.Fprintf(os.Stderr, "The '%s' command can only be used via "+
|
||||
"websockets\n", method)
|
||||
fmt.Fprintln(os.Stderr, listCmdMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Convert remaining command line args to a slice of interface values
|
||||
// to be passed along as parameters to new command creation function.
|
||||
//
|
||||
// Since some commands, such as submitblock, can involve data which is
|
||||
// too large for the Operating System to allow as a normal command line
|
||||
// parameter, support using '-' as an argument to allow the argument
|
||||
// to be read from a stdin pipe.
|
||||
bio := bufio.NewReader(os.Stdin)
|
||||
params := make([]interface{}, 0, len(args[1:]))
|
||||
for _, arg := range args[1:] {
|
||||
if arg == "-" {
|
||||
param, err := bio.ReadString('\n')
|
||||
if err != nil && err != io.EOF {
|
||||
fmt.Fprintf(os.Stderr, "Failed to read data "+
|
||||
"from stdin: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err == io.EOF && len(param) == 0 {
|
||||
fmt.Fprintln(os.Stderr, "Not enough lines "+
|
||||
"provided on stdin")
|
||||
os.Exit(1)
|
||||
}
|
||||
param = strings.TrimRight(param, "\r\n")
|
||||
params = append(params, param)
|
||||
continue
|
||||
}
|
||||
|
||||
params = append(params, arg)
|
||||
}
|
||||
|
||||
// Attempt to create the appropriate command using the arguments
|
||||
// provided by the user.
|
||||
cmd, err := btcjson.NewCmd(method, params...)
|
||||
if err != nil {
|
||||
// Show the error along with its error code when it's a
|
||||
// btcjson.Error as it reallistcally will always be since the
|
||||
// NewCmd function is only supposed to return errors of that
|
||||
// type.
|
||||
if jerr, ok := err.(btcjson.Error); ok {
|
||||
fmt.Fprintf(os.Stderr, "%s command: %v (code: %s)\n",
|
||||
method, err, jerr.ErrorCode)
|
||||
commandUsage(method)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// The error is not a btcjson.Error and this really should not
|
||||
// happen. Nevertheless, fallback to just showing the error
|
||||
// if it should happen due to a bug in the package.
|
||||
fmt.Fprintf(os.Stderr, "%s command: %v\n", method, err)
|
||||
commandUsage(method)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Marshal the command into a JSON-RPC byte slice in preparation for
|
||||
// sending it to the RPC server.
|
||||
marshalledJSON, err := btcjson.MarshalCmd(1, cmd)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Send the JSON-RPC request to the server using the user-specified
|
||||
// connection configuration.
|
||||
result, err := sendPostRequest(marshalledJSON, cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Choose how to display the result based on its type.
|
||||
strResult := string(result)
|
||||
if strings.HasPrefix(strResult, "{") || strings.HasPrefix(strResult, "[") {
|
||||
var dst bytes.Buffer
|
||||
if err := json.Indent(&dst, result, "", " "); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to format result: %v",
|
||||
err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(dst.String())
|
||||
|
||||
} else if strings.HasPrefix(strResult, `"`) {
|
||||
var str string
|
||||
if err := json.Unmarshal(result, &str); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to unmarshal result: %v",
|
||||
err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(str)
|
||||
|
||||
} else if strResult != "null" {
|
||||
fmt.Println(strResult)
|
||||
}
|
||||
}
|
||||
+349
@@ -0,0 +1,349 @@
|
||||
// Copyright (c) 2013-2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
"github.com/btcsuite/btcutil"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
const (
|
||||
// unusableFlags are the command usage flags which this utility are not
|
||||
// able to use. In particular it doesn't support websockets and
|
||||
// consequently notifications.
|
||||
unusableFlags = btcjson.UFWebsocketOnly | btcjson.UFNotification
|
||||
)
|
||||
|
||||
var (
|
||||
btcdHomeDir = btcutil.AppDataDir("btcd", false)
|
||||
btcctlHomeDir = btcutil.AppDataDir("btcctl", false)
|
||||
btcwalletHomeDir = btcutil.AppDataDir("btcwallet", false)
|
||||
defaultConfigFile = filepath.Join(btcctlHomeDir, "btcctl.conf")
|
||||
defaultRPCServer = "localhost"
|
||||
defaultRPCCertFile = filepath.Join(btcdHomeDir, "rpc.cert")
|
||||
defaultWalletCertFile = filepath.Join(btcwalletHomeDir, "rpc.cert")
|
||||
)
|
||||
|
||||
// listCommands categorizes and lists all of the usable commands along with
|
||||
// their one-line usage.
|
||||
func listCommands() {
|
||||
const (
|
||||
categoryChain uint8 = iota
|
||||
categoryWallet
|
||||
numCategories
|
||||
)
|
||||
|
||||
// Get a list of registered commands and categorize and filter them.
|
||||
cmdMethods := btcjson.RegisteredCmdMethods()
|
||||
categorized := make([][]string, numCategories)
|
||||
for _, method := range cmdMethods {
|
||||
flags, err := btcjson.MethodUsageFlags(method)
|
||||
if err != nil {
|
||||
// This should never happen since the method was just
|
||||
// returned from the package, but be safe.
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip the commands that aren't usable from this utility.
|
||||
if flags&unusableFlags != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
usage, err := btcjson.MethodUsageText(method)
|
||||
if err != nil {
|
||||
// This should never happen since the method was just
|
||||
// returned from the package, but be safe.
|
||||
continue
|
||||
}
|
||||
|
||||
// Categorize the command based on the usage flags.
|
||||
category := categoryChain
|
||||
if flags&btcjson.UFWalletOnly != 0 {
|
||||
category = categoryWallet
|
||||
}
|
||||
categorized[category] = append(categorized[category], usage)
|
||||
}
|
||||
|
||||
// Display the command according to their categories.
|
||||
categoryTitles := make([]string, numCategories)
|
||||
categoryTitles[categoryChain] = "Chain Server Commands:"
|
||||
categoryTitles[categoryWallet] = "Wallet Server Commands (--wallet):"
|
||||
for category := uint8(0); category < numCategories; category++ {
|
||||
fmt.Println(categoryTitles[category])
|
||||
for _, usage := range categorized[category] {
|
||||
fmt.Println(usage)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
// config defines the configuration options for btcctl.
|
||||
//
|
||||
// See loadConfig for details on the configuration load process.
|
||||
type config struct {
|
||||
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
|
||||
ListCommands bool `short:"l" long:"listcommands" description:"List all of the supported commands and exit"`
|
||||
ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"`
|
||||
RPCUser string `short:"u" long:"rpcuser" description:"RPC username"`
|
||||
RPCPassword string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"`
|
||||
RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
|
||||
RPCCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"`
|
||||
NoTLS bool `long:"notls" description:"Disable TLS"`
|
||||
Proxy string `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
|
||||
ProxyUser string `long:"proxyuser" description:"Username for proxy server"`
|
||||
ProxyPass string `long:"proxypass" default-mask:"-" description:"Password for proxy server"`
|
||||
TestNet3 bool `long:"testnet" description:"Connect to testnet"`
|
||||
SimNet bool `long:"simnet" description:"Connect to the simulation test network"`
|
||||
TLSSkipVerify bool `long:"skipverify" description:"Do not verify tls certificates (not recommended!)"`
|
||||
Wallet bool `long:"wallet" description:"Connect to wallet"`
|
||||
}
|
||||
|
||||
// normalizeAddress returns addr with the passed default port appended if
|
||||
// there is not already a port specified.
|
||||
func normalizeAddress(addr string, useTestNet3, useSimNet, useWallet bool) string {
|
||||
_, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
var defaultPort string
|
||||
switch {
|
||||
case useTestNet3:
|
||||
if useWallet {
|
||||
defaultPort = "18332"
|
||||
} else {
|
||||
defaultPort = "18334"
|
||||
}
|
||||
case useSimNet:
|
||||
if useWallet {
|
||||
defaultPort = "18554"
|
||||
} else {
|
||||
defaultPort = "18556"
|
||||
}
|
||||
default:
|
||||
if useWallet {
|
||||
defaultPort = "8332"
|
||||
} else {
|
||||
defaultPort = "8334"
|
||||
}
|
||||
}
|
||||
|
||||
return net.JoinHostPort(addr, defaultPort)
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
// cleanAndExpandPath expands environement variables and leading ~ in the
|
||||
// passed path, cleans the result, and returns it.
|
||||
func cleanAndExpandPath(path string) string {
|
||||
// Expand initial ~ to OS specific home directory.
|
||||
if strings.HasPrefix(path, "~") {
|
||||
homeDir := filepath.Dir(btcctlHomeDir)
|
||||
path = strings.Replace(path, "~", homeDir, 1)
|
||||
}
|
||||
|
||||
// NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
|
||||
// but they variables can still be expanded via POSIX-style $VARIABLE.
|
||||
return filepath.Clean(os.ExpandEnv(path))
|
||||
}
|
||||
|
||||
// loadConfig initializes and parses the config using a config file and command
|
||||
// line options.
|
||||
//
|
||||
// The configuration proceeds as follows:
|
||||
// 1) Start with a default config with sane settings
|
||||
// 2) Pre-parse the command line to check for an alternative config file
|
||||
// 3) Load configuration file overwriting defaults with any specified options
|
||||
// 4) Parse CLI options and overwrite/add any specified options
|
||||
//
|
||||
// The above results in functioning properly without any config settings
|
||||
// while still allowing the user to override settings with config files and
|
||||
// command line options. Command line options always take precedence.
|
||||
func loadConfig() (*config, []string, error) {
|
||||
// Default config.
|
||||
cfg := config{
|
||||
ConfigFile: defaultConfigFile,
|
||||
RPCServer: defaultRPCServer,
|
||||
RPCCert: defaultRPCCertFile,
|
||||
}
|
||||
|
||||
// Pre-parse the command line options to see if an alternative config
|
||||
// file, the version flag, or the list commands flag was specified. Any
|
||||
// errors aside from the help message error can be ignored here since
|
||||
// they will be caught by the final parse below.
|
||||
preCfg := cfg
|
||||
preParser := flags.NewParser(&preCfg, flags.HelpFlag)
|
||||
_, err := preParser.Parse()
|
||||
if err != nil {
|
||||
if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
fmt.Fprintln(os.Stderr, "The special parameter `-` "+
|
||||
"indicates that a parameter should be read "+
|
||||
"from the\nnext unread line from standard "+
|
||||
"input.")
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Show the version and exit if the version flag was specified.
|
||||
appName := filepath.Base(os.Args[0])
|
||||
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
|
||||
usageMessage := fmt.Sprintf("Use %s -h to show options", appName)
|
||||
if preCfg.ShowVersion {
|
||||
fmt.Println(appName, "version", version())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Show the available commands and exit if the associated flag was
|
||||
// specified.
|
||||
if preCfg.ListCommands {
|
||||
listCommands()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(preCfg.ConfigFile); os.IsNotExist(err) {
|
||||
// Use config file for RPC server to create default btcctl config
|
||||
var serverConfigPath string
|
||||
if preCfg.Wallet {
|
||||
serverConfigPath = filepath.Join(btcwalletHomeDir, "btcwallet.conf")
|
||||
} else {
|
||||
serverConfigPath = filepath.Join(btcdHomeDir, "btcd.conf")
|
||||
}
|
||||
|
||||
err := createDefaultConfigFile(preCfg.ConfigFile, serverConfigPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating a default config file: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Load additional config from file.
|
||||
parser := flags.NewParser(&cfg, flags.Default)
|
||||
err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
|
||||
if err != nil {
|
||||
if _, ok := err.(*os.PathError); !ok {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing config file: %v\n",
|
||||
err)
|
||||
fmt.Fprintln(os.Stderr, usageMessage)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Parse command line options again to ensure they take precedence.
|
||||
remainingArgs, err := parser.Parse()
|
||||
if err != nil {
|
||||
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
|
||||
fmt.Fprintln(os.Stderr, usageMessage)
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Multiple networks can't be selected simultaneously.
|
||||
numNets := 0
|
||||
if cfg.TestNet3 {
|
||||
numNets++
|
||||
}
|
||||
if cfg.SimNet {
|
||||
numNets++
|
||||
}
|
||||
if numNets > 1 {
|
||||
str := "%s: The testnet and simnet params can't be used " +
|
||||
"together -- choose one of the two"
|
||||
err := fmt.Errorf(str, "loadConfig")
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Override the RPC certificate if the --wallet flag was specified and
|
||||
// the user did not specify one.
|
||||
if cfg.Wallet && cfg.RPCCert == defaultRPCCertFile {
|
||||
cfg.RPCCert = defaultWalletCertFile
|
||||
}
|
||||
|
||||
// Handle environment variable expansion in the RPC certificate path.
|
||||
cfg.RPCCert = cleanAndExpandPath(cfg.RPCCert)
|
||||
|
||||
// Add default port to RPC server based on --testnet and --wallet flags
|
||||
// if needed.
|
||||
cfg.RPCServer = normalizeAddress(cfg.RPCServer, cfg.TestNet3,
|
||||
cfg.SimNet, cfg.Wallet)
|
||||
|
||||
return &cfg, remainingArgs, nil
|
||||
}
|
||||
|
||||
// createDefaultConfig creates a basic config file at the given destination path.
|
||||
// For this it tries to read the config file for the RPC server (either btcd or
|
||||
// btcwallet), and extract the RPC user and password from it.
|
||||
func createDefaultConfigFile(destinationPath, serverConfigPath string) error {
|
||||
// Read the RPC server config
|
||||
serverConfigFile, err := os.Open(serverConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer serverConfigFile.Close()
|
||||
content, err := ioutil.ReadAll(serverConfigFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Extract the rpcuser
|
||||
rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser=([^\s]+)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userSubmatches := rpcUserRegexp.FindSubmatch(content)
|
||||
if userSubmatches == nil {
|
||||
// No user found, nothing to do
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extract the rpcpass
|
||||
rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpass=([^\s]+)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passSubmatches := rpcPassRegexp.FindSubmatch(content)
|
||||
if passSubmatches == nil {
|
||||
// No password found, nothing to do
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extract the notls
|
||||
noTLSRegexp, err := regexp.Compile(`(?m)^\s*notls=(0|1)(?:\s|$)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
noTLSSubmatches := noTLSRegexp.FindSubmatch(content)
|
||||
|
||||
// Create the destination directory if it does not exists
|
||||
err = os.MkdirAll(filepath.Dir(destinationPath), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the destination file and write the rpcuser and rpcpass to it
|
||||
dest, err := os.OpenFile(destinationPath,
|
||||
os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dest.Close()
|
||||
|
||||
destString := fmt.Sprintf("rpcuser=%s\nrpcpass=%s\n",
|
||||
string(userSubmatches[1]), string(passSubmatches[1]))
|
||||
if noTLSSubmatches != nil {
|
||||
destString += fmt.Sprintf("notls=%s\n", noTLSSubmatches[1])
|
||||
}
|
||||
|
||||
dest.WriteString(destString)
|
||||
|
||||
return nil
|
||||
}
|
||||
+128
@@ -0,0 +1,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
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2013 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// semanticAlphabet
|
||||
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
|
||||
|
||||
// These constants define the application version and follow the semantic
|
||||
// versioning 2.0.0 spec (http://semver.org/).
|
||||
const (
|
||||
appMajor uint = 0
|
||||
appMinor uint = 12
|
||||
appPatch uint = 0
|
||||
|
||||
// appPreRelease MUST only contain characters from semanticAlphabet
|
||||
// per the semantic versioning spec.
|
||||
appPreRelease = "beta"
|
||||
)
|
||||
|
||||
// appBuild is defined as a variable so it can be overridden during the build
|
||||
// process with '-ldflags "-X main.appBuild foo' if needed. It MUST only
|
||||
// contain characters from semanticAlphabet per the semantic versioning spec.
|
||||
var appBuild string
|
||||
|
||||
// version returns the application version as a properly formed string per the
|
||||
// semantic versioning 2.0.0 spec (http://semver.org/).
|
||||
func version() string {
|
||||
// Start with the major, minor, and patch versions.
|
||||
version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch)
|
||||
|
||||
// Append pre-release version if there is one. The hyphen called for
|
||||
// by the semantic versioning spec is automatically appended and should
|
||||
// not be contained in the pre-release string. The pre-release version
|
||||
// is not appended if it contains invalid characters.
|
||||
preRelease := normalizeVerString(appPreRelease)
|
||||
if preRelease != "" {
|
||||
version = fmt.Sprintf("%s-%s", version, preRelease)
|
||||
}
|
||||
|
||||
// Append build metadata if there is any. The plus called for
|
||||
// by the semantic versioning spec is automatically appended and should
|
||||
// not be contained in the build metadata string. The build metadata
|
||||
// string is not appended if it contains invalid characters.
|
||||
build := normalizeVerString(appBuild)
|
||||
if build != "" {
|
||||
version = fmt.Sprintf("%s+%s", version, build)
|
||||
}
|
||||
|
||||
return version
|
||||
}
|
||||
|
||||
// normalizeVerString returns the passed string stripped of all characters which
|
||||
// are not valid according to the semantic versioning guidelines for pre-release
|
||||
// version and build metadata strings. In particular they MUST only contain
|
||||
// characters in semanticAlphabet.
|
||||
func normalizeVerString(str string) string {
|
||||
var result bytes.Buffer
|
||||
for _, r := range str {
|
||||
if strings.ContainsRune(semanticAlphabet, r) {
|
||||
// Ignoring the error here since it can only fail if
|
||||
// the the system is out of memory and there are much
|
||||
// bigger issues at that point.
|
||||
_, _ = result.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
Reference in New Issue
Block a user