Skip to content
This repository was archived by the owner on Apr 24, 2025. It is now read-only.

Commit b1db0ac

Browse files
authored
Add onLog inteface, and impl on_delete (#104)
1 parent 58c5a07 commit b1db0ac

File tree

14 files changed

+271
-18
lines changed

14 files changed

+271
-18
lines changed

e2e/e2e_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ func Test_E2E(t *testing.T) {
9999
staticReply: 8009,
100100
admin: 28309,
101101
}, configurationFromRoot))
102+
103+
t.Run("access_logger", testRunnerGetter(envoyPorts{
104+
endpoint: 11010,
105+
staticReply: 8010,
106+
admin: 28310,
107+
}, accessLogger))
102108
}
103109

104110
type runner = func(t *testing.T, nps envoyPorts, stdErr *bytes.Buffer)
@@ -326,3 +332,17 @@ func configurationFromRoot(t *testing.T, ps envoyPorts, stdErr *bytes.Buffer) {
326332
assert.Contains(t, out, "plugin config from root context")
327333
assert.Contains(t, out, "name\": \"plugin configuration")
328334
}
335+
336+
func accessLogger(t *testing.T, ps envoyPorts, stdErr *bytes.Buffer) {
337+
exp := "/this/is/my/path"
338+
req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%d%s", ps.endpoint, exp), nil)
339+
require.NoError(t, err)
340+
341+
r, err := http.DefaultClient.Do(req)
342+
require.NoError(t, err)
343+
defer r.Body.Close()
344+
345+
out := stdErr.String()
346+
fmt.Println(out)
347+
assert.Contains(t, out, exp)
348+
}

examples/access_logger/envoy.yaml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
static_resources:
2+
listeners:
3+
- name: main
4+
address:
5+
socket_address:
6+
address: 0.0.0.0
7+
port_value: 18000
8+
filter_chains:
9+
- filters:
10+
- name: envoy.http_connection_manager
11+
typed_config:
12+
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
13+
stat_prefix: ingress_http
14+
codec_type: auto
15+
route_config:
16+
name: httpbin
17+
virtual_hosts:
18+
- name: httpbin
19+
domains:
20+
- "*"
21+
routes:
22+
- match:
23+
prefix: "/"
24+
route:
25+
cluster: httpbin
26+
http_filters:
27+
- name: envoy.filters.http.router
28+
typed_config: {}
29+
access_log:
30+
- name: envoy.access_loggers.wasm
31+
typed_config:
32+
"@type": type.googleapis.com/envoy.extensions.access_loggers.wasm.v3.WasmAccessLog
33+
config:
34+
name: "access_logger"
35+
root_id: "root_id"
36+
vm_config:
37+
vm_id: "vm_id"
38+
runtime: envoy.wasm.runtime.v8
39+
code:
40+
local:
41+
filename: "./examples/access_logger/main.go.wasm"
42+
43+
clusters:
44+
- name: httpbin
45+
connect_timeout: 5000s
46+
type: strict_dns
47+
lb_policy: round_robin
48+
load_assignment:
49+
cluster_name: httpbin
50+
endpoints:
51+
- lb_endpoints:
52+
- endpoint:
53+
address:
54+
socket_address:
55+
address: httpbin.org
56+
port_value: 80
57+
58+
admin:
59+
access_log_path: "/dev/null"
60+
address:
61+
socket_address:
62+
address: 0.0.0.0
63+
port_value: 8001

examples/access_logger/main.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2020 Tetrate
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
19+
)
20+
21+
func main() {
22+
proxywasm.SetNewRootContext(newAccessLogger)
23+
}
24+
25+
type accessLogger struct {
26+
// you must embed the default context so that you need not to reimplement all the methods by yourself
27+
proxywasm.DefaultRootContext
28+
}
29+
30+
func newAccessLogger(contextID uint32) proxywasm.RootContext {
31+
return &accessLogger{}
32+
}
33+
34+
// override
35+
func (ctx *accessLogger) OnLog() {
36+
hdr, err := proxywasm.GetHttpRequestHeader(":path")
37+
if err != nil {
38+
proxywasm.LogCritical(err.Error())
39+
return
40+
}
41+
42+
proxywasm.LogInfof("OnLog: :path = %s", hdr)
43+
}

examples/access_logger/main_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package main
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/tetratelabs/proxy-wasm-go-sdk/proxytest"
11+
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
12+
)
13+
14+
func TestHelloWorld_OnTick(t *testing.T) {
15+
opt := proxytest.NewEmulatorOption().
16+
WithNewRootContext(newAccessLogger)
17+
host := proxytest.NewHostEmulator(opt)
18+
defer host.Done() // release the host emulation lock so that other test cases can insert their own host emulation
19+
20+
host.CallOnLogForAccessLogger([][2]string{{":path", "/this/is/path"}}, nil) // call OnLog
21+
22+
logs := host.GetLogs(types.LogLevelInfo)
23+
require.Greater(t, len(logs), 0)
24+
msg := logs[len(logs)-1]
25+
26+
assert.True(t, strings.Contains(msg, "/this/is/path"))
27+
}

examples/helloworld/main_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import (
44
"strings"
55
"testing"
66

7-
"github.com/stretchr/testify/require"
8-
97
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
109

1110
"github.com/tetratelabs/proxy-wasm-go-sdk/proxytest"
1211
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"

proxytest/http.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,13 @@ func (h *httpHostEmulator) HttpFilterGetResponseBody(contextID uint32) []byte {
440440

441441
// impl HostEmulator
442442
func (h *httpHostEmulator) HttpFilterCompleteHttpStream(contextID uint32) {
443+
// https://github.com/envoyproxy/envoy/blob/867b9e23d2e48350bd1b0d1fbc392a8355f20e35/include/envoy/http/filter.h#L542-L553
444+
// https://github.com/envoyproxy/envoy/blob/867b9e23d2e48350bd1b0d1fbc392a8355f20e35/source/extensions/common/wasm/context.cc#L1463-L1482
445+
proxywasm.ProxyOnLog(contextID)
446+
447+
// https://github.com/envoyproxy/envoy/blob/867b9e23d2e48350bd1b0d1fbc392a8355f20e35/source/extensions/common/wasm/context.cc#L1491-L1497
443448
proxywasm.ProxyOnDone(contextID)
449+
proxywasm.ProxyOnDelete(contextID)
444450
}
445451

446452
// impl HostEmulator
@@ -456,3 +462,15 @@ func (h *httpHostEmulator) HttpFilterGetCurrentStreamAction(contextID uint32) ty
456462
func (h *httpHostEmulator) HttpFilterGetSentLocalResponse(contextID uint32) *LocalHttpResponse {
457463
return h.httpStreams[contextID].sentLocalResponse
458464
}
465+
466+
// impl HostEmulator
467+
func (h *httpHostEmulator) CallOnLogForAccessLogger(requestHeaders, responseHeaders [][2]string) {
468+
h.httpStreams[rootContextID] = &httpStreamState{
469+
requestHeaders: requestHeaders,
470+
responseHeaders: responseHeaders,
471+
requestTrailers: nil,
472+
responseTrailers: nil,
473+
}
474+
475+
proxywasm.ProxyOnLog(rootContextID)
476+
}

proxytest/network.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ func (n *networkHostEmulator) NetworkFilterPutUpstreamData(contextID uint32, dat
8383
case types.ActionPause:
8484
return
8585
case types.ActionContinue:
86-
// TODO: verify the behavior is correct
8786
stream.upstream = []byte{}
8887
default:
8988
log.Fatalf("invalid action type: %d", action)
@@ -105,7 +104,6 @@ func (n *networkHostEmulator) NetworkFilterPutDownstreamData(contextID uint32, d
105104
case types.ActionPause:
106105
return
107106
case types.ActionContinue:
108-
// TODO: verify the behavior is correct
109107
stream.downstream = []byte{}
110108
default:
111109
log.Fatalf("invalid action type: %d", action)
@@ -133,6 +131,9 @@ func (n *networkHostEmulator) NetworkFilterCloseDownstreamConnection(contextID u
133131

134132
// impl HostEmulator
135133
func (n *networkHostEmulator) NetworkFilterCompleteConnection(contextID uint32) {
134+
// https://github.com/envoyproxy/envoy/blob/867b9e23d2e48350bd1b0d1fbc392a8355f20e35/source/extensions/common/wasm/context.cc#L169-L171
136135
proxywasm.ProxyOnDone(contextID)
136+
proxywasm.ProxyOnLog(contextID)
137+
proxywasm.ProxyOnDelete(contextID)
137138
delete(n.streamStates, contextID)
138139
}

proxytest/proxytest.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ type HostEmulator interface {
5252
HttpFilterCompleteHttpStream(contextID uint32)
5353
HttpFilterGetCurrentStreamAction(contextID uint32) types.Action
5454
HttpFilterGetSentLocalResponse(contextID uint32) *LocalHttpResponse
55+
CallOnLogForAccessLogger(requestHeaders, responseHeaders [][2]string)
5556
}
5657

5758
const (

proxytest/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ func (r *rootHostEmulator) ProxyHttpCall(upstreamData *byte, upstreamSize int, h
252252
Upstream: upstream,
253253
Headers: headers,
254254
Trailers: trailers,
255-
Body: []byte(body),
255+
Body: []byte(body),
256256
})
257257

258258
*calloutIDPtr = calloutID

proxywasm/abi_lifecycle.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,51 @@ func proxyOnContextCreate(contextID uint32, rootContextID uint32) {
2727
}
2828
}
2929

30+
//export proxy_on_log
31+
func proxyOnLog(contextID uint32) {
32+
if ctx, ok := currentState.streams[contextID]; ok {
33+
currentState.setActiveContextID(contextID)
34+
ctx.OnLog()
35+
} else if ctx, ok := currentState.httpStreams[contextID]; ok {
36+
currentState.setActiveContextID(contextID)
37+
ctx.OnLog()
38+
} else if ctx, ok := currentState.rootContexts[contextID]; ok {
39+
currentState.setActiveContextID(contextID)
40+
ctx.context.OnLog()
41+
} else {
42+
panic("invalid context on proxy_on_done")
43+
}
44+
}
45+
3046
//export proxy_on_done
3147
func proxyOnDone(contextID uint32) bool {
32-
defer func() {
33-
delete(currentState.contextIDToRootID, contextID)
34-
}()
3548
if ctx, ok := currentState.streams[contextID]; ok {
3649
currentState.setActiveContextID(contextID)
37-
delete(currentState.streams, contextID)
3850
ctx.OnStreamDone()
3951
return true
4052
} else if ctx, ok := currentState.httpStreams[contextID]; ok {
4153
currentState.setActiveContextID(contextID)
4254
ctx.OnHttpStreamDone()
43-
delete(currentState.httpStreams, contextID)
4455
return true
4556
} else if ctx, ok := currentState.rootContexts[contextID]; ok {
4657
currentState.setActiveContextID(contextID)
4758
response := ctx.context.OnVMDone()
48-
delete(currentState.rootContexts, contextID)
4959
return response
5060
} else {
5161
panic("invalid context on proxy_on_done")
5262
}
5363
}
64+
65+
//export proxy_on_delete
66+
func proxyOnDelete(contextID uint32) {
67+
delete(currentState.contextIDToRootID, contextID)
68+
if _, ok := currentState.streams[contextID]; ok {
69+
delete(currentState.streams, contextID)
70+
} else if _, ok = currentState.httpStreams[contextID]; ok {
71+
delete(currentState.httpStreams, contextID)
72+
} else if _, ok = currentState.rootContexts[contextID]; ok {
73+
delete(currentState.rootContexts, contextID)
74+
} else {
75+
panic("invalid context on proxy_on_done")
76+
}
77+
}

0 commit comments

Comments
 (0)