Skip to content

Commit

Permalink
Allow empty path when HTTP method is CONNECT (#270)
Browse files Browse the repository at this point in the history
  • Loading branch information
pwjagrullar authored Jun 14, 2024
1 parent dd94477 commit d5eee99
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 17 deletions.
49 changes: 49 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,13 @@ func TestBadRequest(t *testing.T) {
},
msg: "Failed to get :method",
},
{
name: "missing method and path",
reqHdrs: [][2]string{
{":authority", "localhost"},
},
msg: "Failed to get :method",
},
}

vmTest(t, func(t *testing.T, vm types.VMContext) {
Expand Down Expand Up @@ -1331,6 +1338,48 @@ func TestParseServerName(t *testing.T) {
})
}

func TestHttpConnectRequest(t *testing.T) {
tests := []struct {
name string
reqHdrs [][2]string
logCount int
}{
{
name: "CONNECT",
reqHdrs: [][2]string{
{":method", "CONNECT"},
{":authority", "localhost"},
},
logCount: 0,
},
}

vmTest(t, func(t *testing.T, vm types.VMContext) {
for _, tc := range tests {
tt := tc
t.Run(tt.name, func(t *testing.T) {
conf := `{"directives_map": {"default": []}, "default_directives": "default"}`
opt := proxytest.
NewEmulatorOption().
WithVMContext(vm).
WithPluginConfiguration([]byte(conf))

host, reset := proxytest.NewHostEmulator(opt)
defer reset()

require.Equal(t, types.OnPluginStartStatusOK, host.StartPlugin())

id := host.InitializeHttpContext()

action := host.CallOnRequestHeaders(id, tt.reqHdrs, false)
require.Equal(t, types.ActionContinue, action)

require.Equal(t, len(host.GetErrorLogs()), tt.logCount)
})
}
})
}

func vmTest(t *testing.T, f func(*testing.T, types.VMContext)) {
t.Helper()

Expand Down
41 changes: 24 additions & 17 deletions wasmplugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"math"
"net"
"net/http"
"strconv"
"strings"

Expand Down Expand Up @@ -281,23 +282,6 @@ func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t

tx.ProcessConnection(srcIP, srcPort, dstIP, dstPort)

// Note the pseudo-header :path includes the query.
// See https://httpwg.org/specs/rfc9113.html#rfc.section.8.3.1
uri, err := proxywasm.GetHttpRequestHeader(":path")
if err != nil {
ctx.logger.Error().
Err(err).
Msg("Failed to get :path")
propPathRaw, propPathErr := proxywasm.GetProperty([]string{"request", "path"})
if propPathErr != nil {
ctx.logger.Error().
Err(propPathErr).
Msg("Failed to get property of path of the request")
return types.ActionContinue
}
uri = string(propPathRaw)
}

method, err := proxywasm.GetHttpRequestHeader(":method")
if err != nil {
ctx.logger.Error().
Expand All @@ -313,6 +297,29 @@ func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t
method = string(propMethodRaw)
}

uri := ""
if method == http.MethodConnect { // CONNECT requests does not have a path, see https://httpwg.org/specs/rfc9110#CONNECT
// Populate uri with authority to build a proper request line
uri = authority
} else {
// Note the pseudo-header :path includes the query.
// See https://httpwg.org/specs/rfc9113.html#rfc.section.8.3.1
uri, err = proxywasm.GetHttpRequestHeader(":path")
if err != nil {
ctx.logger.Error().
Err(err).
Msg("Failed to get :path")
propPathRaw, propPathErr := proxywasm.GetProperty([]string{"request", "path"})
if propPathErr != nil {
ctx.logger.Error().
Err(propPathErr).
Msg("Failed to get property of path of the request")
return types.ActionContinue
}
uri = string(propPathRaw)
}
}

protocol, err := proxywasm.GetProperty([]string{"request", "protocol"})
if err != nil {
// TODO(anuraaga): HTTP protocol is commonly required in WAF rules, we should probably
Expand Down

0 comments on commit d5eee99

Please sign in to comment.