Start with API development. First endpoints are /status and /peer/self. More in development.

This commit is contained in:
Kleissner
2021-08-09 10:19:05 +02:00
parent 1ff5797d3c
commit 6d6a25b1d9
6 changed files with 346 additions and 73 deletions

166
API.go Normal file
View File

@@ -0,0 +1,166 @@
/*
File Name: API.go
Copyright: 2021 Peernet Foundation s.r.o.
Author: Peter Kleissner
*/
package main
import (
"crypto/tls"
"encoding/hex"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
"github.com/PeernetOfficial/core"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
)
func startAPI() {
if len(config.APIListen) == 0 {
return
}
// by default allow all requests
wsUpgrader.CheckOrigin = func(r *http.Request) bool {
return true
}
router := mux.NewRouter()
router.HandleFunc("/test", apiTest).Methods("GET")
router.HandleFunc("/status", apiStatus).Methods("GET")
router.HandleFunc("/peer/self", apiPeerSelf).Methods("GET")
router.HandleFunc("/console", apiConsole).Methods("GET")
for _, listen := range config.APIListen {
go startWebServer(listen, config.APIUseSSL, config.APICertificateFile, config.APICertificateKey, router, "API", parseDuration(config.APITimeoutRead), parseDuration(config.APITimeoutWrite))
}
}
// startWebServer starts a web-server with given parameters and logs the status. If may block forever and only returns if there is an error.
func startWebServer(WebListen string, UseSSL bool, CertificateFile, CertificateKey string, Handler http.Handler, Info string, ReadTimeout, WriteTimeout time.Duration) {
tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} // for security reasons disable TLS 1.0/1.1
server := &http.Server{
Addr: WebListen,
Handler: Handler,
ReadTimeout: ReadTimeout, // ReadTimeout is the maximum duration for reading the entire request, including the body.
WriteTimeout: WriteTimeout, // WriteTimeout is the maximum duration before timing out writes of the response. This includes processing time and is therefore the max time any HTTP function may take.
//IdleTimeout: IdleTimeout, // IdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled.
TLSConfig: tlsConfig,
}
if UseSSL {
// HTTPS
if err := server.ListenAndServeTLS(CertificateFile, CertificateKey); err != nil {
log.Printf("Error listening on '%s': %v\n", WebListen, err)
}
} else {
// HTTP
if err := server.ListenAndServe(); err != nil {
log.Printf("Error listening on '%s': %v\n", WebListen, err)
}
}
}
// parseDuration is the same as time.ParseDuration without returning an error. Valid units are ms, s, m, h. For example "10s".
func parseDuration(input string) (result time.Duration) {
result, _ = time.ParseDuration(input)
return
}
func apiEncodeJSON(w http.ResponseWriter, r *http.Request, data interface{}) (err error) {
w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(data)
if err != nil {
log.Printf("Error writing data for route '%s': %v\n", r.URL.Path, err)
}
return err
}
func apiTest(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
}
type apiResponseStatus struct {
Status int `json:"status"` // Status code: 0 = Ok.
IsConnected bool `json:"isconnected"` // Whether connected to Peernet.
CountPeerList int `json:"countpeerlist"` // Count of peers in the peer list. Note that this contains peers that are considered inactive, but have not yet been removed from the list.
}
/* apiStatus returns the current connectivity status to the network
Request: GET /status
Result: 200 with JSON structure Status
*/
func apiStatus(w http.ResponseWriter, r *http.Request) {
status := apiResponseStatus{Status: 0, CountPeerList: core.PeerlistCount()}
// Connected: If at leat 2 peers.
// This metric needs to be improved in the future, as root peers never disconnect.
// Instead, the core should keep a count of "active peers".
status.IsConnected = status.CountPeerList >= 2
apiEncodeJSON(w, r, status)
}
type apiResponsePeerSelf struct {
PeerID string `json:"peerid"` // Peer ID. This is derived from the public in compressed form.
NodeID string `json:"nodeid"` // Node ID. This is the blake3 hash of the peer ID and used in the DHT.
}
/* apiPeerSelf provides information about the self peer details
Request: GET /peer/self
Result: 200 with JSON structure apiResponsePeerSelf
*/
func apiPeerSelf(w http.ResponseWriter, r *http.Request) {
response := apiResponsePeerSelf{}
response.NodeID = hex.EncodeToString(core.SelfNodeID())
_, publicKey := core.ExportPrivateKey()
response.PeerID = hex.EncodeToString(publicKey.SerializeCompressed())
apiEncodeJSON(w, r, response)
}
var wsUpgrader = websocket.Upgrader{} // use default options
/* apiConsole provides a websocket to send/receive internal commands
Request: GET /console
Result: 200 with JSON structure apiResponsePeerSelf
*/
func apiConsole(w http.ResponseWriter, r *http.Request) {
c, err := wsUpgrader.Upgrade(w, r, nil)
if err != nil {
// May happen if request is simple HTTP request.
return
}
// start reader/writer to forward
defer c.Close()
for {
_, message, err := c.ReadMessage()
if err != nil {
//fmt.Println("read:", err)
break
}
//c.NextWriter(websocket.BinaryMessage)
//c.NextReader()
//userCommands()
fmt.Printf("recv: %s", message)
//err = c.WriteMessage(mt, message)
//if err != nil {
// //fmt.Println("write:", err)
// break
//}
}
}

91
API.md Normal file
View File

@@ -0,0 +1,91 @@
# API
The API is intended to be used by a local full standalone client that provides a frontend for the end-user. The API allows to use Peernet effectively; it provides functions to share files, search for content and download files.
Note: This API code is likely to be moved into the core soon.
## Use considerations
It is not intended to be directly used in a legacy web browser, and shall not be exposed to the internet. It shall only run on a loopback IP such as `127.0.0.1` or `::1`. Special HTTP headers (including the Access-Control headers) are intentionally not set.
The API is currently unauthenticated and intentionally provides direct access to the users blockchain.
### Notes
The API is still in development and endpoints are subject to change. The API should be currently only used for debugging and early phase development purposes.
## Configuartion
The configuration file (default `Config.yaml`) contains settings for the API.
## Overview
These are the functions provided by the API:
```
/status Provides current connectivity status to the network
/peer/self Provides information about the self peer details
/blockchain/self/header Header of the self peers blockchain
/blockchain/self/read Read the self peers blockchain
/blockchain/self/append Add a record to the blockchain
/share/list List all files and directories that are shared
/share/file Share a file via the peers blockchain
/search Search Peernet for a file based on keywords or hash
/download/start Download a file based on a hash
/download/status Get the status of a download
/status/ws Starts a websocket to receive updates on operations immediately (push instead of pull)
/console Console provides a websocket to send/receive internal commands
```
The `/share` functions are providing high-level functionality to work with files; the `/blockchain` functions provide low-level functionality.
## Status
This function informs about the current connection status of the client to the network. Additional fields will be added in the future.
```
Request: GET /status
Response: 200 with JSON structure apiResponseStatus
```
```go
type apiResponseStatus struct {
Status int `json:"status"` // Status code: 0 = Ok.
IsConnected bool `json:"isconnected"` // Whether connected to Peernet.
CountPeerList int `json:"countpeerlist"` // Count of peers in the peer list. Note that this contains peers that are considered inactive, but have not yet been removed from the list.
}
```
Example response data: Todo
## Self Information
This function returns information about the current peer.
```
Request: GET /peer/self
Response: 200 with JSON structure apiResponsePeerSelf
```
The peer and node IDs are returned as hex encoded strings.
```go
type apiResponsePeerSelf struct {
PeerID string `json:"peerid"` // Peer ID. This is derived from the public in compressed form.
NodeID string `json:"nodeid"` // Node ID. This is the blake3 hash of the peer ID and used in the DHT.
}
```
Example response data: Todo
## Console
The `/console` web-socket allows to execute internal commands. This should be only used for debugging purposes by the end-user. The same input and output as raw text as via the command-line is provided through this endpoint.
```
Request: ws://127.0.0.1:112/console
```

View File

@@ -11,8 +11,8 @@ import (
"bytes"
"encoding/hex"
"fmt"
"io"
"net"
"os"
"sort"
"strconv"
"strings"
@@ -82,33 +82,33 @@ func getUserOptionHash(reader *bufio.Reader) (hash []byte, valid bool) {
return hash, true
}
func showHelp() {
fmt.Print("Please enter a command:\n")
fmt.Print("help Show this help\n" +
"net list Lists all network adapters and their IPs\n" +
"status Get current status\n" +
"chat Send text to all peers\n" +
"peer list List current peers\n" +
"debug key create Create Public-Private Key pair\n" +
"debug key self List current Public-Private Key pair\n" +
"debug connect Attempts to connect to the target peer\n" +
"debug watch searches Watch all outgoing DHT searches\n" +
"debug watch incoming Watch all incoming information requests\n" +
"debug watch Watch packets and info requests for hash\n" +
"hash Create blake3 hash of input\n" +
"warehouse get Get data from local warehouse by hash\n" +
"warehouse store Store data into local warehouse\n" +
"dht get Get data via DHT by hash\n" +
"dht store Store data into DHT\n" +
"log error Set error log output\n" +
func showHelp(output io.Writer) {
fmt.Fprint(output, "Please enter a command:\n"+
"help Show this help\n"+
"net list Lists all network adapters and their IPs\n"+
"status Get current status\n"+
"chat Send text to all peers\n"+
"peer list List current peers\n"+
"debug key create Create Public-Private Key pair\n"+
"debug key self List current Public-Private Key pair\n"+
"debug connect Attempts to connect to the target peer\n"+
"debug watch searches Watch all outgoing DHT searches\n"+
"debug watch incoming Watch all incoming information requests\n"+
"debug watch Watch packets and info requests for hash\n"+
"hash Create blake3 hash of input\n"+
"warehouse get Get data from local warehouse by hash\n"+
"warehouse store Store data into local warehouse\n"+
"dht get Get data via DHT by hash\n"+
"dht store Store data into DHT\n"+
"log error Set error log output\n"+
"\n")
}
func userCommands() {
reader := bufio.NewReader(os.Stdin)
func userCommands(input io.Reader, output io.Writer) {
reader := bufio.NewReader(input)
fmt.Print(appName + " " + core.Version + "\n------------------------------\n")
showHelp()
fmt.Fprint(output, appName+" "+core.Version+"\n------------------------------\n")
showHelp(output)
for {
command, valid := getUserOptionString(reader)
@@ -120,25 +120,25 @@ func userCommands() {
switch command {
case "help", "?":
showHelp()
showHelp(output)
case "net list":
fmt.Print(NetworkListOutput())
fmt.Fprint(output, NetworkListOutput())
case "debug key create":
privateKey, publicKey, err := core.Secp256k1NewPrivateKey()
if err != nil {
fmt.Printf("Error: %s\n", err.Error())
fmt.Fprintf(output, "Error: %s\n", err.Error())
return
}
fmt.Printf("Private Key: %s\n", hex.EncodeToString(privateKey.Serialize()))
fmt.Printf("Public Key: %s\n", hex.EncodeToString(publicKey.SerializeCompressed()))
fmt.Fprintf(output, "Private Key: %s\n", hex.EncodeToString(privateKey.Serialize()))
fmt.Fprintf(output, "Public Key: %s\n", hex.EncodeToString(publicKey.SerializeCompressed()))
case "debug key self":
privateKey, publicKey := core.ExportPrivateKey()
fmt.Printf("Private Key: %s\n", hex.EncodeToString(privateKey.Serialize()))
fmt.Printf("Public Key: %s\n", hex.EncodeToString(publicKey.SerializeCompressed()))
fmt.Fprintf(output, "Private Key: %s\n", hex.EncodeToString(privateKey.Serialize()))
fmt.Fprintf(output, "Public Key: %s\n", hex.EncodeToString(publicKey.SerializeCompressed()))
case "peer list":
for _, peer := range GetPeerlistSorted() {
@@ -151,7 +151,7 @@ func userCommands() {
}
userAgent := strings.ToValidUTF8(peer.UserAgent, "?")
fmt.Printf("* Peer ID %s%s\n Node ID %s\n User Agent: %s\n\n%s\n Packets sent: %d\n Packets received: %d\n\n", hex.EncodeToString(peer.PublicKey.SerializeCompressed()), info, hex.EncodeToString(peer.NodeID), userAgent, textPeerConnections(peer), peer.StatsPacketSent, peer.StatsPacketReceived)
fmt.Fprintf(output, "* Peer ID %s%s\n Node ID %s\n User Agent: %s\n\n%s\n Packets sent: %d\n Packets received: %d\n\n", hex.EncodeToString(peer.PublicKey.SerializeCompressed()), info, hex.EncodeToString(peer.NodeID), userAgent, textPeerConnections(peer), peer.StatsPacketSent, peer.StatsPacketReceived)
}
case "chat all", "chat":
@@ -162,7 +162,7 @@ func userCommands() {
case "status":
_, publicKey := core.ExportPrivateKey()
nodeID := core.SelfNodeID()
fmt.Printf("----------------\nPublic Key: %s\nNode ID: %s\n\n", hex.EncodeToString(publicKey.SerializeCompressed()), hex.EncodeToString(nodeID))
fmt.Fprintf(output, "----------------\nPublic Key: %s\nNode ID: %s\n\n", hex.EncodeToString(publicKey.SerializeCompressed()), hex.EncodeToString(nodeID))
features := ""
featureSupport := core.FeatureSupport()
@@ -176,9 +176,9 @@ func userCommands() {
features += "IPv6"
}
fmt.Printf("User Agent: %s\nFeatures: %s\n\n", core.UserAgent, features)
fmt.Fprintf(output, "User Agent: %s\nFeatures: %s\n\n", core.UserAgent, features)
fmt.Printf("Listen Address Multicast IP out External Address\n")
fmt.Fprintf(output, "Listen Address Multicast IP out External Address\n")
for _, network := range core.GetNetworks(4) {
address, _, broadcastIPv4, ipExternal, externalPort := network.GetListen()
@@ -206,7 +206,7 @@ func userCommands() {
externalAddress = net.JoinHostPort(externalIPA, externalPortA)
}
fmt.Printf("%-46s %-32s %s\n", address.String(), broadcastIPsA, externalAddress)
fmt.Fprintf(output, "%-46s %-32s %s\n", address.String(), broadcastIPsA, externalAddress)
}
for _, network := range core.GetNetworks(6) {
address, multicastIP, _, _, externalPort := network.GetListen()
@@ -216,10 +216,10 @@ func userCommands() {
externalPortA = strconv.Itoa(int(externalPort))
}
fmt.Printf("%-46s %-31s %s\n", address.String(), multicastIP.String(), externalPortA)
fmt.Fprintf(output, "%-46s %-31s %s\n", address.String(), multicastIP.String(), externalPortA)
}
fmt.Printf("\nPeer ID Sent Received IP Flags RTT \n")
fmt.Fprintf(output, "\nPeer ID Sent Received IP Flags RTT \n")
for _, peer := range GetPeerlistSorted() {
addressA := "N/A"
rttA := "N/A"
@@ -236,75 +236,75 @@ func userCommands() {
if peer.IsBehindNAT() {
flagsA += "N"
}
fmt.Printf("%-66s %-8d %-8d %-35s %-6s %-6s\n", hex.EncodeToString(peer.PublicKey.SerializeCompressed()), peer.StatsPacketSent, peer.StatsPacketReceived, addressA, flagsA, rttA)
fmt.Fprintf(output, "%-66s %-8d %-8d %-35s %-6s %-6s\n", hex.EncodeToString(peer.PublicKey.SerializeCompressed()), peer.StatsPacketSent, peer.StatsPacketReceived, addressA, flagsA, rttA)
}
fmt.Printf("\n")
fmt.Fprintf(output, "\n")
case "hash":
if text, valid := getUserOptionString(reader); valid {
hash := core.Data2Hash([]byte(text))
fmt.Printf("blake3 hash: %s\n", hex.EncodeToString(hash))
fmt.Fprintf(output, "blake3 hash: %s\n", hex.EncodeToString(hash))
}
case "warehouse get":
if hash, valid := getUserOptionHash(reader); valid {
data, found := core.GetDataLocal(hash)
if !found {
fmt.Printf("Not found.\n")
fmt.Fprintf(output, "Not found.\n")
} else {
fmt.Printf("Data hex: %s\n", hex.EncodeToString(data))
fmt.Printf("Data string: %s\n", string(data))
fmt.Fprintf(output, "Data hex: %s\n", hex.EncodeToString(data))
fmt.Fprintf(output, "Data string: %s\n", string(data))
}
} else {
fmt.Printf("Invalid hash. Hex-encoded blake3 hash as input is required.\n")
fmt.Fprintf(output, "Invalid hash. Hex-encoded blake3 hash as input is required.\n")
}
case "warehouse store":
if text, valid := getUserOptionString(reader); valid {
if err := core.StoreDataLocal([]byte(text)); err != nil {
fmt.Printf("Error storing data: %s\n", err.Error())
fmt.Fprintf(output, "Error storing data: %s\n", err.Error())
break
}
fmt.Printf("Stored via hash: %s\n", hex.EncodeToString(core.Data2Hash([]byte(text))))
fmt.Fprintf(output, "Stored via hash: %s\n", hex.EncodeToString(core.Data2Hash([]byte(text))))
}
case "dht store":
if text, valid := getUserOptionString(reader); valid {
if err := core.StoreDataDHT([]byte(text), 5); err != nil {
fmt.Printf("Error storing data: %s\n", err.Error())
fmt.Fprintf(output, "Error storing data: %s\n", err.Error())
break
}
fmt.Printf("Stored via hash: %s\n", hex.EncodeToString(core.Data2Hash([]byte(text))))
fmt.Fprintf(output, "Stored via hash: %s\n", hex.EncodeToString(core.Data2Hash([]byte(text))))
}
case "dht get":
if hash, valid := getUserOptionHash(reader); valid {
data, sender, found := core.GetDataDHT(hash)
if !found {
fmt.Printf("Not found.\n")
fmt.Fprintf(output, "Not found.\n")
} else {
fmt.Printf("\nSender: %s\n", hex.EncodeToString(sender))
fmt.Printf("Data hex: %s\n", hex.EncodeToString(data))
fmt.Printf("Data string: %s\n", string(data))
fmt.Fprintf(output, "\nSender: %s\n", hex.EncodeToString(sender))
fmt.Fprintf(output, "Data hex: %s\n", hex.EncodeToString(data))
fmt.Fprintf(output, "Data string: %s\n", string(data))
}
} else {
fmt.Printf("Invalid hash. Hex-encoded blake3 hash as input is required.\n")
fmt.Fprintf(output, "Invalid hash. Hex-encoded blake3 hash as input is required.\n")
}
case "log error":
fmt.Printf("Please choose the target output of error messages:\n0 = Log file (default)\n1 = Command line\n2 = Log file + command line\n3 = None\n")
fmt.Fprintf(output, "Please choose the target output of error messages:\n0 = Log file (default)\n1 = Command line\n2 = Log file + command line\n3 = None\n")
if number, valid := getUserOptionInt(reader); !valid || number < 0 || number > 3 {
fmt.Printf("Invalid option.\n")
fmt.Fprintf(output, "Invalid option.\n")
} else {
config.ErrorOutput = number
}
case "debug connect":
fmt.Printf("Please specify the target peer to connect to via DHT lookup, either by peer ID or node ID:\n")
fmt.Fprintf(output, "Please specify the target peer to connect to via DHT lookup, either by peer ID or node ID:\n")
text, valid := getUserOptionString(reader)
if !valid || (len(text) != 66 && len(text) != 64) {
fmt.Printf("Invalid peer ID or node ID. It must be hex-encoded and 66 (peer ID) or 64 characters (node ID) long.\n")
fmt.Fprintf(output, "Invalid peer ID or node ID. It must be hex-encoded and 66 (peer ID) or 64 characters (node ID) long.\n")
break
}
@@ -316,13 +316,13 @@ func userCommands() {
// Assume peer ID was supplied.
publicKeyB, err := hex.DecodeString(text)
if err != nil || len(publicKeyB) != 33 {
fmt.Printf("Invalid peer ID encoding.\n")
fmt.Fprintf(output, "Invalid peer ID encoding.\n")
break
}
publicKey, err := btcec.ParsePubKey(publicKeyB, btcec.S256())
if err != nil {
fmt.Printf("Invalid peer ID (public key decoding failed).\n")
fmt.Fprintf(output, "Invalid peer ID (public key decoding failed).\n")
continue
}
@@ -330,58 +330,58 @@ func userCommands() {
} else {
// Node ID was supplied.
if nodeID, err = hex.DecodeString(text); err != nil || len(nodeID) != 256/8 {
fmt.Printf("Invalid node ID encoding.\n")
fmt.Fprintf(output, "Invalid node ID encoding.\n")
break
}
}
// is self?
if bytes.Equal(nodeID, core.SelfNodeID()) {
fmt.Printf("Target node is self.\n")
fmt.Fprintf(output, "Target node is self.\n")
break
}
debugCmdConnect(nodeID)
case "debug watch searches":
fmt.Printf("Enable (1) or disable (0) watching of all outgoing DHT searches? (current setting: %t)\n", enableMonitorAll)
fmt.Fprintf(output, "Enable (1) or disable (0) watching of all outgoing DHT searches? (current setting: %t)\n", enableMonitorAll)
if number, valid := getUserOptionInt(reader); !valid || number < 0 || number > 1 {
fmt.Printf("Invalid option.\n")
fmt.Fprintf(output, "Invalid option.\n")
} else {
enableMonitorAll = number == 1
}
case "debug watch incoming":
fmt.Printf("Enable (1) or disable (0) watching of all incoming information requests? (current setting: %t)\n", enableWatchIncomingAll)
fmt.Fprintf(output, "Enable (1) or disable (0) watching of all incoming information requests? (current setting: %t)\n", enableWatchIncomingAll)
if number, valid := getUserOptionInt(reader); !valid || number < 0 || number > 1 {
fmt.Printf("Invalid option.\n")
fmt.Fprintf(output, "Invalid option.\n")
} else {
enableWatchIncomingAll = number == 1
}
case "debug bucket refresh":
fmt.Printf("Disable (1) or enable (0) bucket refresh. This can be useful to disable bucket refresh when debugging outgoing DHT searches. (current setting: %t)\n", dht.DisableBucketRefresh)
fmt.Fprintf(output, "Disable (1) or enable (0) bucket refresh. This can be useful to disable bucket refresh when debugging outgoing DHT searches. (current setting: %t)\n", dht.DisableBucketRefresh)
if number, valid := getUserOptionInt(reader); !valid || number < 0 || number > 1 {
fmt.Printf("Invalid option.\n")
fmt.Fprintf(output, "Invalid option.\n")
} else {
dht.DisableBucketRefresh = number == 1
}
case "debug watch":
fmt.Printf("Enter hash of data or node ID to watch. This monitors info requests and packets. Enter same hash again to remove from list.\n")
fmt.Fprintf(output, "Enter hash of data or node ID to watch. This monitors info requests and packets. Enter same hash again to remove from list.\n")
text, _ := getUserOptionString(reader)
var hash []byte
var err error
if hash, err = hex.DecodeString(text); err != nil || len(hash) != 256/8 {
fmt.Printf("Invalid hash. Hex-encoded 64 character hash expected.\n")
fmt.Fprintf(output, "Invalid hash. Hex-encoded 64 character hash expected.\n")
break
}
added := hashMonitorControl(hash, 2)
if added {
fmt.Printf("The hash was added to the monitoring list.\n")
fmt.Fprintf(output, "The hash was added to the monitoring list.\n")
} else {
fmt.Printf("The hash was removed from the monitoring list.\n")
fmt.Fprintf(output, "The hash was removed from the monitoring list.\n")
}
}
}

12
Main.go
View File

@@ -19,6 +19,14 @@ const appName = "Peernet Cmd"
var config struct {
// Log settings
ErrorOutput int `yaml:"ErrorOutput"` // 0 = Log file (default), 1 = Command line, 2 = Log file + command line, 3 = None
// API settings
APIListen []string `yaml:"APIListen"` // WebListen is in format IP:Port and declares where the web-interface should listen on. IP can also be ommitted to listen on any.
APIUseSSL bool `yaml:"APIUseSSL"` // Enables SSL.
APICertificateFile string `yaml:"APICertificateFile"` // This is the certificate received from the CA. This can also include the intermediate certificate from the CA.
APICertificateKey string `yaml:"APICertificateKey"` // This is the private key.
APITimeoutRead string `yaml:"HTTPTimeoutRead"` // The maximum duration for reading the entire request, including the body.
APITimeoutWrite string `yaml:"HTTPTimeoutWrite"` // The maximum duration before timing out writes of the response. This includes processing time and is therefore the max time any HTTP function may take.
}
func init() {
@@ -58,7 +66,9 @@ func init() {
}
func main() {
startAPI()
core.Connect()
userCommands()
userCommands(os.Stdin, os.Stdout)
}

2
go.mod
View File

@@ -5,4 +5,6 @@ go 1.16
require (
github.com/PeernetOfficial/core v0.0.0-20210731152914-2937154c8d7b
github.com/btcsuite/btcd v0.22.0-beta.0.20210625194946-86a17263b0ff
github.com/gorilla/mux v1.8.1-0.20200912192056-d07530f46e1e
github.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7
)

4
go.sum
View File

@@ -22,6 +22,10 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/mux v1.8.1-0.20200912192056-d07530f46e1e h1:8+CIGbDW29nl9niCoMrpF8+ACFz3rLRpIjuqnx4rNKg=
github.com/gorilla/mux v1.8.1-0.20200912192056-d07530f46e1e/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7 h1:L89uC9ATI61/V2eNgZYtQHyjjyjEplemB+aky4HdyzQ=
github.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=