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

Commit 1701721

Browse files
authored
Fix nil pointer during http call dispatch on RootContexts (#111)
1 parent c68f546 commit 1701721

File tree

12 files changed

+288
-40
lines changed

12 files changed

+288
-40
lines changed

e2e/e2e_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ func Test_E2E(t *testing.T) {
105105
staticReply: 8010,
106106
admin: 28310,
107107
}, accessLogger))
108+
t.Run("dispatch_call_on_tick", testRunnerGetter(envoyPorts{
109+
endpoint: 11011,
110+
staticReply: 8011,
111+
admin: 28311,
112+
}, dispatchCallOnTick))
108113
}
109114

110115
type runner = func(t *testing.T, nps envoyPorts, stdErr *bytes.Buffer)
@@ -346,3 +351,12 @@ func accessLogger(t *testing.T, ps envoyPorts, stdErr *bytes.Buffer) {
346351
fmt.Println(out)
347352
assert.Contains(t, out, exp)
348353
}
354+
355+
func dispatchCallOnTick(t *testing.T, ps envoyPorts, stdErr *bytes.Buffer) {
356+
time.Sleep(3 * time.Second)
357+
out := stdErr.String()
358+
fmt.Println(out)
359+
for i := 1; i < 6; i++ {
360+
assert.Contains(t, out, fmt.Sprintf("called! %d", i))
361+
}
362+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## dispatch_call_on_tick
2+
3+
this example periodically dispatches http calls
4+
5+
```
6+
wasm log dispatch: called! 1
7+
wasm log dispatch: called! 1
8+
wasm log dispatch: called! 1
9+
wasm log dispatch: called! 2
10+
wasm log dispatch: called! 2
11+
wasm log dispatch: called! 2
12+
wasm log dispatch: called! 3
13+
wasm log dispatch: called! 3
14+
wasm log dispatch: called! 3
15+
wasm log dispatch: called! 4
16+
wasm log dispatch: called! 4
17+
wasm log dispatch: called! 4
18+
wasm log dispatch: called! 5
19+
wasm log dispatch: called! 5
20+
wasm log dispatch: called! 5
21+
wasm log dispatch: called! 6
22+
wasm log dispatch: called! 6
23+
wasm log dispatch: called! 6
24+
wasm log dispatch: called! 7
25+
wasm log dispatch: called! 7
26+
wasm log dispatch: called! 7
27+
wasm log dispatch: called! 8
28+
wasm log dispatch: called! 8
29+
wasm log dispatch: called! 8
30+
wasm log dispatch: called! 9
31+
wasm log dispatch: called! 9
32+
wasm log dispatch: called! 9
33+
wasm log dispatch: called! 10
34+
wasm log dispatch: called! 10
35+
wasm log dispatch: called! 10
36+
37+
```
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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: local_route
17+
virtual_hosts:
18+
- name: local_service
19+
domains:
20+
- "*"
21+
routes:
22+
- match:
23+
prefix: "/"
24+
route:
25+
cluster: web_service
26+
http_filters:
27+
- name: envoy.filters.http.wasm
28+
typed_config:
29+
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
30+
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
31+
value:
32+
config:
33+
name: "dispatch"
34+
root_id: "dispatch"
35+
vm_config:
36+
vm_id: "dispatch"
37+
runtime: "envoy.wasm.runtime.v8"
38+
code:
39+
local:
40+
filename: "./examples/dispatch_call_on_tick/main.go.wasm"
41+
- name: envoy.filters.http.router
42+
typed_config: {}
43+
44+
- name: staticreply
45+
address:
46+
socket_address:
47+
address: 127.0.0.1
48+
port_value: 8099
49+
filter_chains:
50+
- filters:
51+
- name: envoy.http_connection_manager
52+
typed_config:
53+
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
54+
stat_prefix: ingress_http
55+
codec_type: auto
56+
route_config:
57+
name: local_route
58+
virtual_hosts:
59+
- name: local_service
60+
domains:
61+
- "*"
62+
routes:
63+
- match:
64+
prefix: "/"
65+
direct_response:
66+
status: 200
67+
body:
68+
inline_string: "example body\n"
69+
http_filters:
70+
- name: envoy.filters.http.router
71+
typed_config: {}
72+
73+
clusters:
74+
- name: web_service
75+
connect_timeout: 0.25s
76+
type: STATIC
77+
lb_policy: ROUND_ROBIN
78+
load_assignment:
79+
cluster_name: mock_service
80+
endpoints:
81+
- lb_endpoints:
82+
- endpoint:
83+
address:
84+
socket_address:
85+
address: 127.0.0.1
86+
port_value: 8099
87+
88+
admin:
89+
access_log_path: "/dev/null"
90+
address:
91+
socket_address:
92+
address: 0.0.0.0
93+
port_value: 8001
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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+
const tickMilliseconds uint32 = 100
22+
23+
func main() {
24+
proxywasm.SetNewRootContext(newRootContext)
25+
}
26+
27+
type rootContext struct {
28+
// you must embed the default context so that you need not to reimplement all the methods by yourself
29+
proxywasm.DefaultRootContext
30+
contextID uint32
31+
}
32+
33+
func newRootContext(contextID uint32) proxywasm.RootContext {
34+
return &rootContext{contextID: contextID}
35+
}
36+
37+
// override
38+
func (ctx *rootContext) OnVMStart(vmConfigurationSize int) bool {
39+
if err := proxywasm.SetTickPeriodMilliSeconds(tickMilliseconds); err != nil {
40+
proxywasm.LogCriticalf("failed to set tick period: %v", err)
41+
}
42+
proxywasm.LogInfof("set tick period milliseconds: %d", tickMilliseconds)
43+
return true
44+
}
45+
46+
func (ctx *rootContext) OnTick() {
47+
hs := [][2]string{{":method", "GET"}, {":authority", "some_authority"}, {":path", "/path/to/service"}, {"accept", "*/*"}}
48+
if _, err := proxywasm.DispatchHttpCall("web_service", hs, "", [][2]string{},
49+
5000, callback); err != nil {
50+
proxywasm.LogCriticalf("dispatch httpcall failed: %v", err)
51+
}
52+
}
53+
54+
var cnt int
55+
56+
func callback(numHeaders, bodySize, numTrailers int) {
57+
cnt++
58+
proxywasm.LogInfof("called! %d", cnt)
59+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/tetratelabs/proxy-wasm-go-sdk/proxytest"
12+
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
13+
)
14+
15+
func TestRootContext_OnTick(t *testing.T) {
16+
opt := proxytest.NewEmulatorOption().
17+
WithNewRootContext(newRootContext)
18+
host := proxytest.NewHostEmulator(opt)
19+
defer host.Done() // release the host emulation lock so that other test cases can insert their own host emulation
20+
21+
host.StartVM() // call OnVMStart
22+
23+
assert.Equal(t, tickMilliseconds, host.GetTickPeriod())
24+
25+
for i := 1; i < 10; i++ {
26+
host.Tick() // call OnTick
27+
attrs := host.GetCalloutAttributesFromContext(proxytest.RootContextID)
28+
require.Equal(t, len(attrs), i) // verify DispatchHttpCall is called
29+
host.PutCalloutResponse(attrs[0].CalloutID, nil, nil, nil) // receive callout response
30+
31+
logs := host.GetLogs(types.LogLevelInfo)
32+
require.Greater(t, len(logs), 0)
33+
msg := logs[len(logs)-1]
34+
35+
assert.True(t, strings.Contains(msg, fmt.Sprintf("called! %d", i)))
36+
}
37+
38+
}
39+
40+
func TestRootContext_OnVMStart(t *testing.T) {
41+
opt := proxytest.NewEmulatorOption().
42+
WithNewRootContext(newRootContext)
43+
host := proxytest.NewHostEmulator(opt)
44+
defer host.Done() // release the host emulation lock so that other test cases can insert their own host emulation
45+
46+
host.StartVM() // call OnVMStart
47+
assert.Equal(t, tickMilliseconds, host.GetTickPeriod())
48+
}

proxytest/http.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ func (h *httpHostEmulator) ProxySendLocalResponse(statusCode uint32,
303303
// impl HostEmulator
304304
func (h *httpHostEmulator) HttpFilterInitContext() (contextID uint32) {
305305
contextID = getNextContextID()
306-
proxywasm.ProxyOnContextCreate(contextID, rootContextID)
306+
proxywasm.ProxyOnContextCreate(contextID, RootContextID)
307307
h.httpStreams[contextID] = &httpStreamState{action: types.ActionContinue}
308308
return
309309
}
@@ -465,12 +465,12 @@ func (h *httpHostEmulator) HttpFilterGetSentLocalResponse(contextID uint32) *Loc
465465

466466
// impl HostEmulator
467467
func (h *httpHostEmulator) CallOnLogForAccessLogger(requestHeaders, responseHeaders [][2]string) {
468-
h.httpStreams[rootContextID] = &httpStreamState{
468+
h.httpStreams[RootContextID] = &httpStreamState{
469469
requestHeaders: requestHeaders,
470470
responseHeaders: responseHeaders,
471471
requestTrailers: nil,
472472
responseTrailers: nil,
473473
}
474474

475-
proxywasm.ProxyOnLog(rootContextID)
475+
proxywasm.ProxyOnLog(RootContextID)
476476
}

proxytest/network.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func (n *networkHostEmulator) NetworkFilterPutDownstreamData(contextID uint32, d
113113
// impl HostEmulator
114114
func (n *networkHostEmulator) NetworkFilterInitConnection() (contextID uint32) {
115115
contextID = getNextContextID()
116-
proxywasm.ProxyOnContextCreate(contextID, rootContextID)
116+
proxywasm.ProxyOnContextCreate(contextID, RootContextID)
117117
proxywasm.ProxyOnNewConnection(contextID)
118118
n.streamStates[contextID] = &streamState{}
119119
return

proxytest/proxytest.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ type HostEmulator interface {
5656
}
5757

5858
const (
59-
rootContextID uint32 = 1 // TODO: support multiple rootContext
59+
RootContextID uint32 = 1 // TODO: support multiple rootContext
6060
)
6161

6262
var (
6363
hostMux = sync.Mutex{}
64-
nextContextID = rootContextID + 1
64+
nextContextID = RootContextID + 1
6565
)
6666

6767
type hostEmulator struct {
@@ -92,7 +92,7 @@ func NewHostEmulator(opt *EmulatorOption) HostEmulator {
9292
proxywasm.SetNewHttpContext(opt.newHttpContext)
9393

9494
// create root context: TODO: support multiple root contexts
95-
proxywasm.ProxyOnContextCreate(rootContextID, 0)
95+
proxywasm.ProxyOnContextCreate(RootContextID, 0)
9696

9797
return emulator
9898
}

proxytest/root.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func (r *rootHostEmulator) ProxyEnqueueSharedQueue(queueID uint32, valueData *by
141141

142142
// note that this behavior is not accurate for some old host implementations:
143143
// see: https://github.com/proxy-wasm/proxy-wasm-cpp-host/pull/36
144-
proxywasm.ProxyOnQueueReady(rootContextID, queueID) // Note that this behavior is not accurate on Istio before 1.8.x
144+
proxywasm.ProxyOnQueueReady(RootContextID, queueID) // Note that this behavior is not accurate on Istio before 1.8.x
145145
return types.StatusOK
146146
}
147147

@@ -362,7 +362,7 @@ func (r *rootHostEmulator) GetTickPeriod() uint32 {
362362

363363
// impl HostEmulator
364364
func (r *rootHostEmulator) Tick() {
365-
proxywasm.ProxyOnTick(rootContextID)
365+
proxywasm.ProxyOnTick(RootContextID)
366366
}
367367

368368
// impl HostEmulator
@@ -378,12 +378,12 @@ func (r *rootHostEmulator) GetCalloutAttributesFromContext(contextID uint32) []H
378378

379379
// impl HostEmulator
380380
func (r *rootHostEmulator) StartVM() {
381-
proxywasm.ProxyOnVMStart(rootContextID, len(r.vmConfiguration))
381+
proxywasm.ProxyOnVMStart(RootContextID, len(r.vmConfiguration))
382382
}
383383

384384
// impl HostEmulator
385385
func (r *rootHostEmulator) StartPlugin() {
386-
proxywasm.ProxyOnConfigure(rootContextID, len(r.pluginConfiguration))
386+
proxywasm.ProxyOnConfigure(RootContextID, len(r.pluginConfiguration))
387387
}
388388

389389
// impl HostEmulator
@@ -393,17 +393,17 @@ func (r *rootHostEmulator) PutCalloutResponse(calloutID uint32, headers, trailer
393393
body []byte
394394
}{headers: headers, trailers: trailers, body: body}
395395

396-
// rootContextID, calloutID uint32, numHeaders, bodySize, numTrailers in
396+
// RootContextID, calloutID uint32, numHeaders, bodySize, numTrailers in
397397
r.activeCalloutID = calloutID
398398
defer func() {
399399
r.activeCalloutID = 0
400400
delete(r.httpCalloutResponse, calloutID)
401401
delete(r.httpCalloutIDToContextID, calloutID)
402402
}()
403-
proxywasm.ProxyOnHttpCallResponse(rootContextID, calloutID, len(headers), len(body), len(trailers))
403+
proxywasm.ProxyOnHttpCallResponse(RootContextID, calloutID, len(headers), len(body), len(trailers))
404404
}
405405

406406
// impl HostEmulator
407407
func (r *rootHostEmulator) FinishVM() {
408-
proxywasm.ProxyOnDone(rootContextID)
408+
proxywasm.ProxyOnDone(RootContextID)
409409
}

0 commit comments

Comments
 (0)