Skip to content

Commit

Permalink
145: Support rpc path prefix options
Browse files Browse the repository at this point in the history
  • Loading branch information
tolga-coplu committed Jul 21, 2023
1 parent 0ff2614 commit 75bd50a
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 3 deletions.
31 changes: 28 additions & 3 deletions rpc/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ type HttpServer struct {
// HttpConfig holds both http and websocket configuration elements
type HttpConfig struct {
HttpEndpoint string
HttpPathPrefix string
HttpCors []string
HttpCompress bool
HttpTimeout time.Duration
WsEndpoint string
WsPathPrefix string
WsHandshakeTimeout time.Duration
WsOnly bool
}
Expand Down Expand Up @@ -112,14 +114,26 @@ func (h *HttpServer) mainHandler(ctx *fasthttp.RequestCtx) {
// The following code differentiates the cases based on the configurations and Config.WsOnly variable
if strings.EqualFold(h.Config.HttpEndpoint, h.Config.WsEndpoint) {
if isWebsocket(r) {
h.fastWsHandler(ctx)
if checkPath(r, h.Config.WsPathPrefix) {
h.fastWsHandler(ctx)
} else {
ctx.SetStatusCode(fasthttp.StatusNotFound)
}
} else if !h.Config.WsOnly {
h.fastHTTPHandler(ctx, r)
if checkPath(r, h.Config.HttpPathPrefix) {
h.fastHTTPHandler(ctx, r)
} else {
ctx.SetStatusCode(fasthttp.StatusNotFound)
}
} else {
ctx.SetStatusCode(fasthttp.StatusNotFound)
}
} else {
h.fastHTTPHandler(ctx, r)
if checkPath(r, h.Config.HttpPathPrefix) {
h.fastHTTPHandler(ctx, r)
} else {
ctx.SetStatusCode(fasthttp.StatusNotFound)
}
}
}

Expand Down Expand Up @@ -250,3 +264,14 @@ func isWebsocket(r *http.Request) bool {
return strings.EqualFold(r.Header.Get("Upgrade"), "websocket") &&
strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade")
}

// checkPath checks whether a given request URL matches a given path prefix
func checkPath(r *http.Request, path string) bool {
if path == "*" { // if prefix is `*`, allow all paths
return true
} else if path == "" { // if no prefix has been specified, request URL must be on root
return r.URL.Path == "/"
} else { // otherwise, check to make sure prefix matches
return len(r.URL.Path) >= len(path) && r.URL.Path[:len(path)] == path
}
}
5 changes: 5 additions & 0 deletions rpc/node/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var (
defaultHttpTimeout time.Duration = 300
defaultWsHandshakeTimeout time.Duration = 10
defaultMaxBatchRequests uint = 1000
DefaultPathPrefix string = "*"
)

type Config struct {
Expand All @@ -29,8 +30,10 @@ type Config struct {
HttpCors []string `mapstructure:"httpCors"`
HttpCompress bool `mapstructure:"httpCompress"`
HttpTimeout time.Duration `mapstructure:"httpTimeout"`
HttpPathPrefix string `mapstructure:"httpPathPrefix"`
WsPort int16 `mapstructure:"wsPort"`
WsHost string `mapstructure:"wsHost"`
WsPathPrefix string `mapstructure:"wsPathPrefix"`
WsHandshakeTimeout time.Duration `mapstructure:"wsHandshakeTimeout"`
MaxBatchRequests uint `mapstructure:"maxBatchRequests"`
}
Expand All @@ -57,9 +60,11 @@ func defaultConfig() *Config {
return &Config{
HttpPort: defaultHttpPort,
HttpHost: defaultHttpHost,
HttpPathPrefix: DefaultPathPrefix,
HttpCors: []string{},
HttpCompress: defaultHttpCompress,
HttpTimeout: defaultHttpTimeout,
WsPathPrefix: DefaultPathPrefix,
WsHandshakeTimeout: defaultWsHandshakeTimeout,
MaxBatchRequests: defaultMaxBatchRequests,
}
Expand Down
29 changes: 29 additions & 0 deletions rpc/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package node

import (
"context"
"fmt"
"strings"

"github.com/aurora-is-near/relayer2-base/broker"
Expand All @@ -26,12 +27,20 @@ func NewWithConf(config *Config) (*RpcNode, error) {

// If httpEndpoint is not empty, then a HttpServer should be initialized (no matters if it is http only or http and ws)
if config.httpEndpoint() != "" {
if err := validatePath(config.HttpPathPrefix); err != nil {
logger.Fatal().Err(err).Msg("HTTP config err:")
} else if err := validatePath(config.WsPathPrefix); err != nil {
logger.Fatal().Err(err).Msg("Websocket config err:")
}

httpCfg := rpc.HttpConfig{
HttpEndpoint: config.httpEndpoint(),
HttpPathPrefix: config.HttpPathPrefix,
HttpCors: config.HttpCors,
HttpCompress: config.HttpCompress,
HttpTimeout: config.HttpTimeout,
WsEndpoint: config.wsEndpoint(),
WsPathPrefix: config.WsPathPrefix,
WsHandshakeTimeout: config.WsHandshakeTimeout,
WsOnly: false,
}
Expand All @@ -41,12 +50,18 @@ func NewWithConf(config *Config) (*RpcNode, error) {
// If wsEndpoint is not empty and different from httpEndpoint, then another HttpServer should be initialized to
// handle ws connections. Please note that WsOnly field is used to understand to case while handling the incoming request
if config.wsEndpoint() != "" && !strings.EqualFold(config.httpEndpoint(), config.wsEndpoint()) {
if err := validatePath(config.WsPathPrefix); err != nil {
logger.Fatal().Err(err).Msg("Websocket config err:")
}

httpCfg := rpc.HttpConfig{
HttpEndpoint: config.wsEndpoint(),
HttpPathPrefix: config.HttpPathPrefix,
HttpCors: []string{},
HttpCompress: false,
HttpTimeout: config.HttpTimeout,
WsEndpoint: config.wsEndpoint(),
WsPathPrefix: config.WsPathPrefix,
WsHandshakeTimeout: config.WsHandshakeTimeout,
WsOnly: true,
}
Expand Down Expand Up @@ -77,3 +92,17 @@ func (n *RpcNode) Start() {
}
}()
}

// validatePath checks if 'path' is a valid configuration value for the RPC prefix option
func validatePath(path string) error {
if path == "*" || path == "" {
return nil
}
if path[0] != '/' {
return fmt.Errorf("RPC path prefix %q does not contain leading `/`", path)
}
if strings.ContainsAny(path, "?#") {
return fmt.Errorf("RPC path prefix %q contains URL meta-characters", path)
}
return nil
}

0 comments on commit 75bd50a

Please sign in to comment.