Skip to content

Commit

Permalink
Add readme and update client code
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-ikryvanos committed Nov 26, 2024
1 parent 86a0f2f commit 4e1cbab
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 52 deletions.
26 changes: 26 additions & 0 deletions services/httpoverrpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# HTTPOVERRPC
This directory contains the http over rpc services. It is a simple service that allows to send http requests to from target hosts


## Usage

### sanssh httpoverrpc get
Make a HTTP(-S) request to a specified port on the remote host.

```bash
sanssh <sanssh-args> get [-method <method>] [-header <header>] [-dialAddress <dial-address>] [-insecure-skip-tls-verify] [-show-response-headers] [-body <body>] [-protocol <protocol>] [-hostname <hostname>] <remoteport> <request_uri>:
```

Where:
- `<body>` body to send in request
- `<dial-address>` host:port to dial to. If not provided would dial to original host and port
- `<header>` Header to send in the request, may be specified multiple times.
- `<hostname>` ip address or domain name to specify host (default "localhost")
- `<method>` method to use in the HTTP request (default "GET")
- `<protocol>` protocol to communicate with specified hostname (default "http")
- `<remoteport>` port to connect to on the remote host
- `<request_uri>` URI to request

Flags:
- `-show-response-headers` If provided, print response code and headers
- `-insecure-skip-tls-verify` If provided, skip TLS verification
29 changes: 25 additions & 4 deletions services/httpoverrpc/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/Snowflake-Labs/sansshell/client"
pb "github.com/Snowflake-Labs/sansshell/services/httpoverrpc"
"github.com/Snowflake-Labs/sansshell/services/util"
"github.com/Snowflake-Labs/sansshell/services/util/validator"
)

const subPackage = "httpoverrpc"
Expand Down Expand Up @@ -226,16 +227,20 @@ type getCmd struct {
protocol string
hostname string
insecureSkipVerify bool
dialAddress string
}

func (*getCmd) Name() string { return "get" }
func (*getCmd) Synopsis() string { return "Makes a HTTP call to a port on a remote host" }
func (*getCmd) Synopsis() string { return "Makes a HTTP(-S) call to a port on a remote host" }
func (*getCmd) Usage() string {
return `get [-method METHOD] [-header Header...] [-body body] [-protocol Protocol] [-hostname Hostname] remoteport request_uri:
return `get [-method METHOD] [-header Header...] [-dialAddress dialAddress] [-body body] [-protocol Protocol] [-hostname Hostname] remoteport request_uri:
Make a HTTP request to a specified port on the remote host.
Example:
Examples:
# send get request to https://localhost:9090/hello
httpoverrpc get --hostname 10.1.23.4 --protocol https 9090 /hello
# send get request with url http://example.com:9090/hello, but deal to localhost:9090
httpoverrpc get --hostname example.com --dialAddress localhost:9090 9090 /hello
Note:
1. The prefix / in request_uri is always needed, even there is nothing to put
Expand All @@ -252,6 +257,7 @@ func (g *getCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&g.body, "body", "", "Body to send in request")
f.BoolVar(&g.showResponseHeaders, "show-response-headers", false, "If true, print response code and headers")
f.BoolVar(&g.insecureSkipVerify, "insecure-skip-tls-verify", false, "If true, skip TLS cert verification")
f.StringVar(&g.dialAddress, "dial-address", "", "host:port to dial to. If not provided would dial to original host and port")
}

func (g *getCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
Expand All @@ -270,6 +276,14 @@ func (g *getCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface
return subcommands.ExitUsageError
}

if g.dialAddress != "" {
_, _, err := validator.ParseHostAndPort(g.dialAddress)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to parse dial address \"%q\": %v\n", g.dialAddress, err)
return subcommands.ExitUsageError
}
}

var reqHeaders []*pb.Header
for _, v := range g.headers {
split := strings.SplitN(v, ":", 2)
Expand All @@ -280,7 +294,12 @@ func (g *getCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface
reqHeaders = append(reqHeaders, &pb.Header{Key: split[0], Values: []string{strings.TrimSpace(split[1])}})
}

proxy := pb.NewHTTPOverRPCClientProxy(state.Conn)
var dialConfig *pb.DialConfig = nil
if g.dialAddress != "" {
dialConfig = &pb.DialConfig{
DialAddress: &g.dialAddress,
}
}

req := &pb.HostHTTPRequest{
Request: &pb.HTTPRequest{
Expand All @@ -295,8 +314,10 @@ func (g *getCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface
Tlsconfig: &pb.TLSConfig{
InsecureSkipVerify: g.insecureSkipVerify,
},
Dialconfig: dialConfig,
}

proxy := pb.NewHTTPOverRPCClientProxy(state.Conn)
resp, err := proxy.HostOneMany(ctx, req)
retCode := subcommands.ExitSuccess
if err != nil {
Expand Down
18 changes: 17 additions & 1 deletion services/httpoverrpc/client/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,16 @@ var (
errInvalidURLMissingHost = fmt.Errorf("no host in the request URL")
)

// HTTPTransporter implementation of [http.RoundTripper], which proxy HTTP requests over RPC to target host to send request from it
type HTTPTransporter struct {
conn *proxy.Conn
insecureSkipVerify bool
dialConfigFactory func(req http.Request) *pb.DialConfig
}

type httpTransporterOptions struct {
insecureSkipVerify bool
dialConfigFactory func(req http.Request) *pb.DialConfig
}

type Option interface {
Expand All @@ -63,7 +66,13 @@ func WithInsecureSkipVerify(insecureSkipVerify bool) Option {
})
}

func NewHTTPTransporter(conn *proxy.Conn, opts ...Option) *HTTPTransporter {
func WithCustomDialConfig(dialConfigFactory func(req http.Request) *pb.DialConfig) Option {
return optionFunc(func(o *httpTransporterOptions) {
o.dialConfigFactory = dialConfigFactory
})
}

func NewHTTPTransporter(conn *proxy.Conn, opts ...Option) http.RoundTripper {
options := &httpTransporterOptions{
insecureSkipVerify: false,
}
Expand All @@ -74,6 +83,7 @@ func NewHTTPTransporter(conn *proxy.Conn, opts ...Option) *HTTPTransporter {
return &HTTPTransporter{
conn: conn,
insecureSkipVerify: options.insecureSkipVerify,
dialConfigFactory: options.dialConfigFactory,
}
}

Expand Down Expand Up @@ -152,6 +162,11 @@ func (c *HTTPTransporter) RoundTrip(req *http.Request) (*http.Response, error) {
return nil, err
}
}

var dialConfig *pb.DialConfig = nil
if c.dialConfigFactory != nil {
dialConfig = c.dialConfigFactory(*req)
}
reqPb := &pb.HostHTTPRequest{
Request: &pb.HTTPRequest{
RequestUri: req.URL.RequestURI(),
Expand All @@ -164,6 +179,7 @@ func (c *HTTPTransporter) RoundTrip(req *http.Request) (*http.Response, error) {
Tlsconfig: &pb.TLSConfig{
InsecureSkipVerify: c.insecureSkipVerify,
},
Dialconfig: dialConfig,
}

port, errPort := getPort(req, reqPb.Protocol)
Expand Down
90 changes: 47 additions & 43 deletions services/httpoverrpc/httpoverrpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions services/httpoverrpc/httpoverrpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ message HostHTTPRequest {
// TLS Config for the request
TLSConfig tlsconfig = 5;
// Dial Config for request, if not provided uses default dialer configuration
DialConfig dialconfig = 6;
optional DialConfig dialconfig = 6;
}

message TLSConfig {
Expand All @@ -49,7 +49,7 @@ message TLSConfig {

message DialConfig {
// address to dial to, if not provided uses original one
string dialAddress = 1;
optional string dialAddress = 1;
}

message Header {
Expand Down
2 changes: 1 addition & 1 deletion services/httpoverrpc/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (s *server) Host(ctx context.Context, req *pb.HostHTTPRequest) (*pb.HTTPRep
}
}

if req.Dialconfig != nil {
if req.GetDialconfig() != nil {
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
dailAddress := addr
if req.Dialconfig.GetDialAddress() != "" {
Expand Down
2 changes: 1 addition & 1 deletion services/httpoverrpc/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ func TestServer(t *testing.T) {
Hostname: targetHost,
Protocol: "http",
Dialconfig: &httpoverrpc.DialConfig{
DialAddress: addressToDial,
DialAddress: &addressToDial,
},
})
if err != nil {
Expand Down

0 comments on commit 4e1cbab

Please sign in to comment.