mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[NOD-228] Added JSONRPCifyer to the project and created a Dockerfile for it. (#337)
This commit is contained in:
parent
6d20202354
commit
c3c429494f
32
cmd/jsonrpcifyer/config.go
Normal file
32
cmd/jsonrpcifyer/config.go
Normal file
@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Host string `long:"host" default:"localhost:18334" description:"IP:Port of the JSON-RPC endpoint"`
|
||||
ListenPort int `long:"port" default:"8080" description:"Port to listen on"`
|
||||
RPCCert string `long:"rpccert" description:"Path to certificate accepted by JSON-RPC endpoint"`
|
||||
RPCUser string `long:"rpcuser" required:"true" description:"Username to connect to JSON-RPC endpoint"`
|
||||
RPCPass string `long:"rpcpass" required:"true" description:"Password to connect to JSON-RPC endpoint"`
|
||||
DisableTLS bool `long:"notls" description:"Disable TLS"`
|
||||
}
|
||||
|
||||
func parseConfig() (*config, error) {
|
||||
cfg := &config{}
|
||||
parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag)
|
||||
_, err := parser.Parse()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.RPCCert == "" && !cfg.DisableTLS {
|
||||
return nil, errors.New("either --notls or --rpccert must be set")
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
33
cmd/jsonrpcifyer/docker/Dockerfile
Normal file
33
cmd/jsonrpcifyer/docker/Dockerfile
Normal file
@ -0,0 +1,33 @@
|
||||
# -- multistage docker build: stage #1: build stage
|
||||
FROM golang:1.12-alpine AS build
|
||||
|
||||
RUN mkdir -p /go/src/github.com/daglabs/btcd
|
||||
|
||||
WORKDIR /go/src/github.com/daglabs/btcd
|
||||
|
||||
RUN apk add --no-cache curl git
|
||||
|
||||
# GO111MODULE=on forces Go to use the go-module system
|
||||
# TODO: remove this once Go 1.13 is released
|
||||
ENV GO111MODULE=on
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN cd cmd/jsonrpcifyer && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o jsonrpcifyer .
|
||||
|
||||
# --- multistage docker build: stage #2: runtime image
|
||||
FROM alpine
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache tini
|
||||
|
||||
COPY --from=build /go/src/github.com/daglabs/btcd/cmd/jsonrpcifyer/jsonrpcifyer /app/
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
|
||||
CMD ["/app/jsonrpcifyer"]
|
5
cmd/jsonrpcifyer/docker/README
Normal file
5
cmd/jsonrpcifyer/docker/README
Normal file
@ -0,0 +1,5 @@
|
||||
1. To build docker image invoke following command from btcd root directory:
|
||||
docker build -t jsonrpcifyer -f ./cmd/jsonrpcifyer/docker/Dockerfile .
|
||||
|
||||
2. To run:
|
||||
docker run -v ~/.btcd:/root/.btcd -t jsonrpcifyer
|
48
cmd/jsonrpcifyer/jsonrpcifyer.go
Normal file
48
cmd/jsonrpcifyer/jsonrpcifyer.go
Normal file
@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/daglabs/btcd/signal"
|
||||
"log"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer handlePanic()
|
||||
|
||||
cfg, err := parseConfig()
|
||||
if err != nil {
|
||||
log.Printf("error parsing command-line arguments: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
server, err := newServer(cfg)
|
||||
if err != nil {
|
||||
log.Panicf("couldn't create server: %s", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := server.stop()
|
||||
if err != nil {
|
||||
log.Panicf("couldn't stop server: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
err = server.start()
|
||||
if err != nil {
|
||||
log.Panicf("server error: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
interrupt := signal.InterruptListener()
|
||||
<-interrupt
|
||||
}
|
||||
|
||||
func handlePanic() {
|
||||
err := recover()
|
||||
if err != nil {
|
||||
log.Printf("Fatal error: %s", err)
|
||||
log.Printf("Stack trace: %s", debug.Stack())
|
||||
}
|
||||
}
|
129
cmd/jsonrpcifyer/server.go
Normal file
129
cmd/jsonrpcifyer/server.go
Normal file
@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/daglabs/btcd/rpcclient"
|
||||
)
|
||||
|
||||
type server struct {
|
||||
cfg *config
|
||||
rpcConnConfig *rpcclient.ConnConfig
|
||||
|
||||
httpServer *http.Server
|
||||
rpcClient *rpcclient.Client
|
||||
}
|
||||
|
||||
func newServer(cfg *config) (*server, error) {
|
||||
server := server{
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
server.rpcConnConfig = &rpcclient.ConnConfig{
|
||||
Host: cfg.Host,
|
||||
Endpoint: "ws",
|
||||
User: cfg.RPCUser,
|
||||
Pass: cfg.RPCPass,
|
||||
DisableTLS: cfg.DisableTLS,
|
||||
}
|
||||
if !cfg.DisableTLS {
|
||||
certificate, err := ioutil.ReadFile(cfg.RPCCert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server.rpcConnConfig.Certificates = certificate
|
||||
}
|
||||
|
||||
return &server, nil
|
||||
}
|
||||
|
||||
func (s *server) start() error {
|
||||
log.Printf("Connecting RPC client to %s", s.cfg.Host)
|
||||
|
||||
rpcClient, err := rpcclient.New(s.rpcConnConfig, nil)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("failed to create RPC client: %s", err))
|
||||
}
|
||||
s.rpcClient = rpcClient
|
||||
|
||||
log.Printf("Starting server on port %d", s.cfg.ListenPort)
|
||||
|
||||
handler := http.NewServeMux()
|
||||
handler.HandleFunc("/", s.handleRequest)
|
||||
|
||||
s.httpServer = &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", s.cfg.ListenPort),
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
return s.httpServer.ListenAndServe()
|
||||
}
|
||||
|
||||
func (s *server) handleRequest(responseWriter http.ResponseWriter, request *http.Request) {
|
||||
s.allowCrossOrigin(responseWriter)
|
||||
if request.Method == "OPTIONS" {
|
||||
// OPTIONS must stop here or else CORS protection will throw a tantrum.
|
||||
return
|
||||
}
|
||||
|
||||
if request.Method != "POST" {
|
||||
responseWriter.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
|
||||
forwardedResponse, err := s.forwardRequest(request)
|
||||
if err != nil {
|
||||
responseWriter.WriteHeader(500)
|
||||
log.Printf("failed to forward request: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = responseWriter.Write([]byte(forwardedResponse))
|
||||
if err != nil {
|
||||
responseWriter.WriteHeader(500)
|
||||
log.Printf("failed to write response: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) allowCrossOrigin(responseWriter http.ResponseWriter) {
|
||||
responseWriter.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
responseWriter.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
responseWriter.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
|
||||
}
|
||||
|
||||
func (s *server) forwardRequest(request *http.Request) ([]byte, error) {
|
||||
jsonRPCMethod := strings.TrimPrefix(request.URL.Path, "/")
|
||||
|
||||
requestBody, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("failed to read request body: %s", err))
|
||||
}
|
||||
|
||||
var jsonRPCParams []json.RawMessage
|
||||
err = json.Unmarshal(requestBody, &jsonRPCParams)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("failed to parse params: %s", err))
|
||||
}
|
||||
|
||||
response, err := s.rpcClient.RawRequest(jsonRPCMethod, jsonRPCParams)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("request to rpc server failed: %s", err))
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *server) stop() error {
|
||||
log.Printf("Disconnecting RPC client")
|
||||
s.rpcClient.Disconnect()
|
||||
|
||||
log.Printf("Stopping server")
|
||||
return s.httpServer.Close()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user