Skip to content
This repository has been archived by the owner on Jul 28, 2024. It is now read-only.

OnHttpRequestBody use DispatchHttpCall error #363

Closed
zjb1994 opened this issue Mar 8, 2023 · 11 comments
Closed

OnHttpRequestBody use DispatchHttpCall error #363

zjb1994 opened this issue Mar 8, 2023 · 11 comments

Comments

@zjb1994
Copy link

zjb1994 commented Mar 8, 2023

Describe the bug / error

I have two clusters, a and b, and the default request is to cluster a. I want to forward the request to cluster b in OnHttpRequestBody. I call DispatchHttpCall after EndOfStream=true in OnHttpRequestBody, and then I return types. ActionPause, but it seems that the request has not been aborted. Clusters a and b have received the request respectively

What is your Envoy/Istio version?

v1.21.1

What is the SDK version?

v0.16.1

What is your TinyGo version?

tinygo_0.22.0_amd64

URL or snippet of your code including Envoy configuration

package framework

import (
	"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
	"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
)

type FrameworkPlugin struct {
	types.DefaultHttpContext
	requestHeader  [][2]string
	requestTrailer [][2]string
}

func (c *FrameworkPlugin) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
	c.requestHeader, _ = proxywasm.GetHttpRequestHeaders()
	c.requestTrailer, _ = proxywasm.GetHttpRequestTrailers()
	return types.ActionContinue
}

func (c *FrameworkPlugin) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action {
	if !endOfStream {
		return types.ActionPause
	}

	proxywasm.LogWarn("aaaaaaa")
	body, _ := proxywasm.GetHttpRequestBody(0, bodySize)
	if _, err := proxywasm.DispatchHttpCall("zoneb", c.requestHeader, body, c.requestTrailer, 50000, c.httpCallResponseCallback); err != nil {
		return types.ActionContinue
	}
	proxywasm.LogWarn("bbbbbbb")
	return types.ActionPause
}

func (c *FrameworkPlugin) httpCallResponseCallback(numHeaders, bodySize, numTrailers int) {
	hs, err := proxywasm.GetHttpCallResponseHeaders()
	if err != nil {
		return
	}

	b, err := proxywasm.GetHttpCallResponseBody(0, bodySize)
	if err != nil {
		proxywasm.ResumeHttpRequest()
		return
	}

	err = proxywasm.SendHttpResponse(200, hs, b, -1)
	if err != nil {
		proxywasm.ResumeHttpResponse()
	}
}

func (c *FrameworkPlugin) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
	return types.ActionContinue
}

func (c *FrameworkPlugin) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action {
	return types.ActionContinue
}

Additional context (Optional)

@tetratelabs tetratelabs locked as resolved and limited conversation to collaborators Mar 8, 2023
@mathetake mathetake reopened this Mar 8, 2023
@tetratelabs tetratelabs unlocked this conversation Mar 8, 2023
@mathetake
Copy link
Member

As I suggested, we need "reproducer" including envoy.yaml. Could you add that here including steps to reproduce?

@zjb1994

This comment was marked as duplicate.

@mathetake
Copy link
Member

steps to reproduce? (command line curl snippet!)

@mathetake
Copy link
Member

also how to compile? how to run envoy? Could you please add the instruction step by step? That's what "reproducer" and "steps to reproduce" mean in software development.

@zjb1994

This comment was marked as off-topic.

@zjb1994
Copy link
Author

zjb1994 commented Mar 8, 2023

envoy.yml

admin:
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901
static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 10000
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          codec_type: auto
          stat_prefix: ingress_http
          route_config:
            name: echo
            virtual_hosts:
            - name: echo
              domains:
              - "*"
              routes:
              - match:
                  prefix: "/echo"
                route:
                  cluster: zonea

          http_filters:
          - name: envoy.filters.http.wasm
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
              config:
                vm_config:
                  runtime: envoy.wasm.runtime.v8
                  code:
                    local:
                      filename: "/tmp/envoy_filter_framework.wasm"
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
  - name: zonea
    type: LOGICAL_DNS
    dns_lookup_family: V4_ONLY
    load_assignment:
      cluster_name: zonea
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: host.docker.internal
                port_value: 10001

  - name: zoneb
    type: LOGICAL_DNS
    dns_lookup_family: V4_ONLY
    load_assignment:
      cluster_name: zoneb
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: host.docker.internal
                port_value: 10002

main.go

package main

import (
	"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
	"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
)

func main()  {
	proxywasm.SetVMContext(&FilterFrameworkVMContext{})
}

type FilterFrameworkVMContext struct {
	types.DefaultVMContext
}

func (*FilterFrameworkVMContext) NewPluginContext(contextID uint32) types.PluginContext {
	return &PluginContext{}
}

type PluginContext struct {
	types.DefaultPluginContext
	ContextID   uint32
	callBack    func(numHeaders, bodySize, numTrailers int)
	cnt         int
}

func (c *PluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
	return types.OnPluginStartStatusOK
}

func (c *PluginContext) OnPluginDone() bool {
	return true
}

func (c *PluginContext) NewHttpContext(contextID uint32) types.HttpContext {
	return &FrameworkPlugin{
	}
}

type FrameworkPlugin struct {
	types.DefaultHttpContext
	requestHeader  [][2]string
	requestTrailer [][2]string
}

func (c *FrameworkPlugin) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
	c.requestHeader, _ = proxywasm.GetHttpRequestHeaders()
	c.requestTrailer, _ = proxywasm.GetHttpRequestTrailers()
	return types.ActionContinue
}

func (c *FrameworkPlugin) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action {
	if !endOfStream {
		return types.ActionPause
	}

	proxywasm.LogWarn("aaaaaaa")
	body, _ := proxywasm.GetHttpRequestBody(0, bodySize)
	if _, err := proxywasm.DispatchHttpCall("zoneb", c.requestHeader, body, c.requestTrailer, 50000, c.httpCallResponseCallback); err != nil {
		return types.ActionContinue
	}
	proxywasm.LogWarn("cccccc")
	return types.ActionPause
}

func (c *FrameworkPlugin) httpCallResponseCallback(numHeaders, bodySize, numTrailers int) {
	hs, err := proxywasm.GetHttpCallResponseHeaders()
	if err != nil {
		return
	}

	b, err := proxywasm.GetHttpCallResponseBody(0, bodySize)
	if err != nil {
		proxywasm.LogErrorf("LiveDifferentPlaces httpCallResponseCallback failed to get response body: %v", err)
		proxywasm.ResumeHttpRequest()
		return
	}
	proxywasm.LogDebugf("LiveDifferentPlaces httpCallResponseCallback,ResponseBody = %v", string(b))

	err = proxywasm.SendHttpResponse(200, hs, b, -1)
	if err != nil {
		proxywasm.LogErrorf("LiveDifferentPlaces httpCallResponseCallback SendHttpResponse err: %s", err.Error())
		proxywasm.ResumeHttpResponse()
	}
}

func (c *FrameworkPlugin) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
	return types.ActionContinue
}

func (c *FrameworkPlugin) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action {
	return types.ActionContinue
}

tinygo build -o /tmp/envoy_filter_framework.wasm -scheduler=none -target=wasi ./main.go

curl -X GET -H "Content-type: application/json" http://localhost:10000/echo -d '{"aaa":"bbb"}'

@mathetake
Copy link
Member

address: host.docker.internal exists? are you really sure you haven't reached this error block?

	if _, err := proxywasm.DispatchHttpCall("zoneb", c.requestHeader, body, c.requestTrailer, 50000, c.httpCallResponseCallback); err != nil {
		return types.ActionContinue

@zjb1994
Copy link
Author

zjb1994 commented Mar 8, 2023

Yes, the address exists, I am sure that the error block has not been reached

@zjb1994
Copy link
Author

zjb1994 commented Mar 8, 2023

image
image
No bbbb is printed here to prove that the error block has not been reached

image
Here, the services of both clusters have received the request log

@lwsbox
Copy link

lwsbox commented Mar 8, 2023

i have the same problem...

@mathetake
Copy link
Member

ok, this is the expected behavior. the same reason as #364:

at the point when executing OnHttpResponseBody, the headers have already been sent to the client, and that's the limitation due to the design of Proxy-Wasm. proxy-wasm/proxy-wasm-cpp-host#143

tldr is that the original upstream only receives the header, and that cannot be avoided by the current design of Proxy-Wasm. This is not our issue, but rather a universal issue in Proxy-Wasm Spec.

@tetratelabs tetratelabs locked as resolved and limited conversation to collaborators Mar 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants