Skip to content

Commit

Permalink
add SSL, config dirs, and Echo http server
Browse files Browse the repository at this point in the history
  • Loading branch information
gsmachado committed Apr 21, 2020
1 parent 1bc5aa3 commit 343cf7a
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 351 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@
# Dependency directories (remove the comment below to include it)
# vendor/

.idea/
.idea/

cmd/go-jsonrpc-proxy
32 changes: 32 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
ORG_NAME := AxLabs
REPO_NAME := go-jsonrpc-proxy
PKG_ROOT := github.com/${ORG_NAME}/$(REPO_NAME)
PKG_LIST := go list ${PKG_ROOT}/...
PKG_GO_JSONRPC_PROXY := ${PKG_ROOT}/cmd
CMD_DIR := ./cmd

.PHONY: all lint vet test go-jsonrpc-proxy

.EXPORT_ALL_VARIABLES:

GO111MODULE=on

all: lint vet test go-jsonrpc-proxy

vet:
@go vet $(shell $(PKG_LIST))

# Lint the files
lint:
@golint -set_exit_status $(shell $(PKG_LIST))

# Run unit tests
test:
@go test -v -short -count=1 $(shell $(PKG_LIST))

go-jsonrpc-proxy: $(CMD_DIR)/go-jsonrpc-proxy

$(CMD_DIR)/go-jsonrpc-proxy:
@echo "Building $@..."
@go build -i -o $(CMD_DIR)/go-jsonrpc-proxy -v $(PKG_GO_JSONRPC_PROXY)
@chmod u+x $(CMD_DIR)/go-jsonrpc-proxy
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# go-jsonrpc-proxy
> Simple JSON-RPC proxy based on RPC methods.
Simple JSON-RPC proxy based on RPC methods.
## How to Run

TBD.

## Use Case

Expand All @@ -24,11 +26,15 @@ That's exactly what `go-jsonrpc-proxy` solves. :smiley:

## ToDos

- [x] SSL support
- [ ] Command line to support `--config=`
- [ ] Rate limits for each declared method (using Redis)
- [ ] Rate limits based on API keys (e.g., API key `X`
specified in the `Authorization` HTTP header can perform more
requests than API key `Y`)
- [ ] More load balancer strategies. Nowadays forwarding hosts
are randomly chosen.
- [ ] Admin API to add/remove nodes to serve specific JSON-RPC
methods (or method patterns)
- [ ] `docker-compose` file
- [ ] Integration tests
75 changes: 64 additions & 11 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,92 @@ package main
import (
"github.com/AxLabs/go-jsonrpc-proxy/config"
"github.com/AxLabs/go-jsonrpc-proxy/server"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/labstack/gommon/log"
"github.com/shibukawa/configdir"
"golang.org/x/crypto/acme/autocert"
"math/rand"
"net/http"
"os"
"time"
)

func main() {
// initialize random
initRand()

configFilePath := getEnv("JSONRPC_PROXY_CONFIG_FILE", "jsonrpc-proxy.json")
// load config
configFilePath := getEnvVar("JSONRPC_PROXY_CONFIG_FILE", "jsonrpc-proxy.json")
config := config.LoadConfigFile(configFilePath)
server.LoadMap(config)

http.HandleFunc(config.BaseURL, server.HandleRequestAndRedirect)
if err := http.ListenAndServe(getListenAddress(), nil); err != nil {
panic(err)
sslEnabled := getEnvVar("JSONRPC_PROXY_SSL", "off")
sslDomain := getEnvVar("JSONRPC_PROXY_SSL_DOMAIN", "")
httpRedirectToHTTPS := getEnvVar("JSONRPC_PROXY_REDIRECT_TO_HTTPS", "on")
configDir := getConfigDir()

e := echo.New()

if sslEnabled == "on" && httpRedirectToHTTPS == "on" {
e.Pre(middleware.HTTPSRedirect())
}

e.AutoTLSManager.Cache = autocert.DirCache(configDir)
e.Use(middleware.Recover())
e.Use(middleware.Logger())

e.POST("/", echo.WrapHandler(http.HandlerFunc(server.HandleRequestAndRedirect)))

if sslEnabled == "on" {
if len(sslDomain) == 0 {
e.Logger.Fatalf("You should specify the domain name for the SSL.")
}
e.AutoTLSManager.HostPolicy = autocert.HostWhitelist(sslDomain)
e.Logger.Fatal(e.StartAutoTLS(getListenAddress()))
} else {
e.Logger.Fatal(e.Start(getListenAddress()))
}
}

// Get env var or default
func getEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
// getEnvVar get an environment variable value, or returns a fallback value
func getEnvVar(key, fallback string) string {
value, _ := getEnvVarAndIfExists(key, fallback)
return value
}

// getEnvVarAndIfExists Retrieves an environment variable value, or returns a default (fallback) value
// It also returns true or false if the env variable exists or not
func getEnvVarAndIfExists(key, fallback string) (string, bool) {
value, exists := os.LookupEnv(key)
if len(value) == 0 {
return fallback, exists
}
return fallback
return value, exists
}

// Get the port to listen on
// getListenAddress get the port to listen on
func getListenAddress() string {
port := getEnv("PORT", "2000")
port := getEnvVar("PORT", "2000")
return ":" + port
}

// getConfigDir get the configuration dir
func getConfigDir() string {
configDirs := configdir.New("axlabs", "go-jsonrpc-proxy")
folders := configDirs.QueryFolders(configdir.Local)
configPath, ok := getEnvVarAndIfExists("JSONRPC_PROXY_CONFIG_PATH", folders[0].Path)
if ok {
configDirs.LocalPath = configPath
}
folders = configDirs.QueryFolders(configdir.Local)
err := folders[0].MkdirAll()
if err != nil {
log.Panicf("Not possible to create folder %v: %v", folders[0].Path, err)
}
return folders[0].Path
}

func initRand() {
// initialize global pseudo random generator
rand.Seed(time.Now().Unix())
Expand Down
17 changes: 12 additions & 5 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@ import (
"log"
)

// Config var with the configuration parsed from the .json file
var Config Configuration

// Configuration type representing the .json config file
type Configuration struct {
BaseURL string `json:"base-url"`
Methods []MethodsConfig `json:"methods"`
BaseURL string `json:"base-url"`
SSL bool `json:"ssl,omitempty"`
SSLDomain string `json:"ssl-domain,omitempty"`
Methods []MethodsConfig `json:"methods"`
}

// MethodsConfig type representing the config for each JSON-RPC method
type MethodsConfig struct {
Name string `json:"name"`
ProxyTo []string `json:"proxy-to"`
RateLimit int `json:"rate-limit"`
}

// LoadConfigFile loads the config file into an instance of Configuration type
func LoadConfigFile(filePath string) Configuration {
file, errReadFile := ioutil.ReadFile(filePath)
if errReadFile != nil {
Expand All @@ -27,11 +33,12 @@ func LoadConfigFile(filePath string) Configuration {
return LoadConfig(string(file))
}

// LoadConfig loads the content (as JSON) into an instance of Configuration type
func LoadConfig(content string) Configuration {
config := Configuration{}
errJson := json.Unmarshal([]byte(content), &config)
if errJson != nil {
log.Panicf("Could not read json content: %v", errJson)
errJSON := json.Unmarshal([]byte(content), &config)
if errJSON != nil {
log.Panicf("Could not read json content: %v", errJSON)
}
return config
}
10 changes: 7 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ module github.com/AxLabs/go-jsonrpc-proxy
go 1.13

require (
github.com/caddyserver/caddy v1.0.5
github.com/mholt/certmagic v0.8.3
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/labstack/echo/v4 v4.1.16
github.com/labstack/gommon v0.3.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0
github.com/stretchr/testify v1.4.0
github.com/teambition/jsonrpc-go v0.0.0-20170418060308-ff2654212329
golang.org/x/sync v0.0.0-20190423024810-112230192c58
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/square/go-jose.v2 v2.3.1
)
Loading

0 comments on commit 343cf7a

Please sign in to comment.