Merge pull request #51 from lukaszlach/process-http-headers

Pass HTTP headers with the --pass-headers option
This commit is contained in:
Soren L. Hansen 2022-11-01 10:18:32 -07:00 committed by GitHub
commit 16c534e1a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 26 additions and 9 deletions

View File

@ -37,12 +37,12 @@ func (factory *Factory) Name() string {
return "local command" return "local command"
} }
func (factory *Factory) New(params map[string][]string) (server.Slave, error) { func (factory *Factory) New(params map[string][]string, headers map[string][]string) (server.Slave, error) {
argv := make([]string, len(factory.argv)) argv := make([]string, len(factory.argv))
copy(argv, factory.argv) copy(argv, factory.argv)
if params["arg"] != nil && len(params["arg"]) > 0 { if params["arg"] != nil && len(params["arg"]) > 0 {
argv = append(argv, params["arg"]...) argv = append(argv, params["arg"]...)
} }
return New(factory.command, argv, factory.opts...) return New(factory.command, argv, headers, factory.opts...)
} }

View File

@ -3,6 +3,7 @@ package localcommand
import ( import (
"os" "os"
"os/exec" "os/exec"
"strings"
"syscall" "syscall"
"time" "time"
@ -27,11 +28,22 @@ type LocalCommand struct {
ptyClosed chan struct{} ptyClosed chan struct{}
} }
func New(command string, argv []string, options ...Option) (*LocalCommand, error) { func New(command string, argv []string, headers map[string][]string, options ...Option) (*LocalCommand, error) {
cmd := exec.Command(command, argv...) cmd := exec.Command(command, argv...)
cmd.Env = append(os.Environ(), "TERM=xterm-256color") cmd.Env = append(os.Environ(), "TERM=xterm-256color")
// Combine headers into key=value pairs to set as env vars
// Prefix the headers with "http_" so we don't overwrite any other env vars
// which potentially has the same name and to bring these closer to what
// a (F)CGI server would proxy to a backend service
// Replace hyphen with underscore and make them all upper case
for key, values := range headers {
h := "HTTP_" + strings.Replace(strings.ToUpper(key), "-", "_", -1) + "=" + strings.Join(values, ",")
// log.Printf("Adding header: %s", h)
cmd.Env = append(cmd.Env, h)
}
pty, err := pty.Start(cmd) pty, err := pty.Start(cmd)
if err != nil { if err != nil {
// todo close cmd? // todo close cmd?

View File

@ -23,7 +23,7 @@ func TestNewFactory(t *testing.T) {
t.Errorf("factory.options = %v, expected %v", factory.options, &Options{}) t.Errorf("factory.options = %v, expected %v", factory.options, &Options{})
} }
slave, _ := factory.New(nil) slave, _ := factory.New(nil, nil)
lcmd := slave.(*LocalCommand) lcmd := slave.(*LocalCommand)
if lcmd.closeSignal != 123 { if lcmd.closeSignal != 123 {
t.Errorf("lcmd.closeSignal = %v, expected %v", lcmd.closeSignal, 123) t.Errorf("lcmd.closeSignal = %v, expected %v", lcmd.closeSignal, 123)
@ -40,7 +40,7 @@ func TestFactoryNew(t *testing.T) {
return return
} }
slave, err := factory.New(nil) slave, err := factory.New(nil, nil)
if err != nil { if err != nil {
t.Errorf("factory.New() returned error") t.Errorf("factory.New() returned error")
return return

View File

@ -72,7 +72,11 @@ func (server *Server) generateHandleWS(ctx context.Context, cancel context.Cance
} }
defer conn.Close() defer conn.Close()
err = server.processWSConn(ctx, conn) if server.options.PassHeaders {
err = server.processWSConn(ctx, conn, r.Header)
} else {
err = server.processWSConn(ctx, conn, nil)
}
switch err { switch err {
case ctx.Err(): case ctx.Err():
@ -87,7 +91,7 @@ func (server *Server) generateHandleWS(ctx context.Context, cancel context.Cance
} }
} }
func (server *Server) processWSConn(ctx context.Context, conn *websocket.Conn) error { func (server *Server) processWSConn(ctx context.Context, conn *websocket.Conn, headers map[string][]string) error {
typ, initLine, err := conn.ReadMessage() typ, initLine, err := conn.ReadMessage()
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to authenticate websocket connection") return errors.Wrapf(err, "failed to authenticate websocket connection")
@ -116,7 +120,7 @@ func (server *Server) processWSConn(ctx context.Context, conn *websocket.Conn) e
} }
params := query.Query() params := query.Query()
var slave Slave var slave Slave
slave, err = server.factory.New(params) slave, err = server.factory.New(params, headers)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to create backend") return errors.Wrapf(err, "failed to create backend")
} }

View File

@ -26,6 +26,7 @@ type Options struct {
Once bool `hcl:"once" flagName:"once" flagDescribe:"Accept only one client and exit on disconnection" default:"false"` Once bool `hcl:"once" flagName:"once" flagDescribe:"Accept only one client and exit on disconnection" default:"false"`
Timeout int `hcl:"timeout" flagName:"timeout" flagDescribe:"Timeout seconds for waiting a client(0 to disable)" default:"0"` Timeout int `hcl:"timeout" flagName:"timeout" flagDescribe:"Timeout seconds for waiting a client(0 to disable)" default:"0"`
PermitArguments bool `hcl:"permit_arguments" flagName:"permit-arguments" flagDescribe:"Permit clients to send command line arguments in URL (e.g. http://example.com:8080/?arg=AAA&arg=BBB)" default:"false"` PermitArguments bool `hcl:"permit_arguments" flagName:"permit-arguments" flagDescribe:"Permit clients to send command line arguments in URL (e.g. http://example.com:8080/?arg=AAA&arg=BBB)" default:"false"`
PassHeaders bool `hcl:"pass_headers" flagName:"pass-headers" flagDescribe:"Pass HTTP request headers as environment variables (e.g. Cookie becomes HTTP_COOKIE)" default:"false"`
Width int `hcl:"width" flagName:"width" flagDescribe:"Static width of the screen, 0(default) means dynamically resize" default:"0"` Width int `hcl:"width" flagName:"width" flagDescribe:"Static width of the screen, 0(default) means dynamically resize" default:"0"`
Height int `hcl:"height" flagName:"height" flagDescribe:"Static height of the screen, 0(default) means dynamically resize" default:"0"` Height int `hcl:"height" flagName:"height" flagDescribe:"Static height of the screen, 0(default) means dynamically resize" default:"0"`
WSOrigin string `hcl:"ws_origin" flagName:"ws-origin" flagDescribe:"A regular expression that matches origin URLs to be accepted by WebSocket. No cross origin requests are acceptable by default" default:""` WSOrigin string `hcl:"ws_origin" flagName:"ws-origin" flagDescribe:"A regular expression that matches origin URLs to be accepted by WebSocket. No cross origin requests are acceptable by default" default:""`

View File

@ -13,5 +13,5 @@ type Slave interface {
type Factory interface { type Factory interface {
Name() string Name() string
New(params map[string][]string) (Slave, error) New(params map[string][]string, headers map[string][]string) (Slave, error)
} }