Skip to content

How to clone echo context? #1633

@kunal-sanghvi

Description

@kunal-sanghvi

Issue Description

Using echo an asynchronous web server is developed. It accepts request body synchronously; spawns a go routine to process the request data and concurrently returns 200 OK to client

The flow could be imagined to be as below

  1. Request received at web server, echo sends a context c to custom middleware
  2. Custom middleware parses the http.Request from echo context c, basically extracting the headers, body, etc
  3. Custom middleware populates these details into echo context store by using the c.Set("myKey", "myValue") method
  4. At this point
    --> A go routine is spawned to process this data set inside echo context store
    --> Return from middleware by setting a static response - c.String(200, "Accepted")
  5. Within the handler function, when we try to do c.Get("myKey"), we receive nil for keys set during step 3

Shouldn't we receive myValue during step 5?

We are suspecting the context might be getting Reset here. We are thinking to clone the data inside echo Context c into either a custom context (which implements echo.Context) or maybe use AcquireContext / ReleaseContext utility functions to find a workaround
Any suggestions on a safer way to deal with this?

Working code to debug

type AuthData struct {
	Agent string
	TxnID string
}

func createAuthData(c echo.Context) AuthData {
	return AuthData{
		Agent: c.Request().UserAgent(),
		TxnID: c.Request().Header.Get("X-TXN-ID"),
	}
}

func extractionMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		c.Set("myAuthData", createAuthData(c))
		go func() {
			_ = next(c)
		}()
		return c.String(http.StatusOK, "Accepted")
	}
}

func main() {
	router := echo.New()
	router.Use(extractionMiddleware)

	router.GET("/record_stats/", StreamPush)
}

func StreamPush(c echo.Context) error {
	if ad := c.Get("myAuthData"); ad == nil {
		fmt.Print("context is nil")
	} else {
		fmt.Printf("performing further action for %s (%s)", ad.(AuthData).Agent, ad.(AuthData).TxnID)
		// push AuthData to downstream service for processing
	}
	return nil
}

Expected behaviour

In the StreamPush HandlerFunc, we extract AuthData from echo Context and proceed with our further logic to parse the data, which is the else block

Actual behaviour

While most of the times we do not see any issue. But in roughly 2-3% of requests (during high traffic on our platform) we could see the context is nil getting logged

Steps to reproduce

Run some load onto your webserver using vegeta or any other load test tool

Version/commit

v3.3.10

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions