简体   繁体   English

在Go反向代理中在handleFunc和ModifyResponse之间共享上下文

[英]Share context between handleFunc and modifyResponse in Go reverse proxy

I'm trying to learn Go and figured a nice little project would be an A/B testing proxy to put in front of a web server. 我正在尝试学习Go,并发现一个不错的小项目是放在Web服务器前的A / B测试代理。 Little did I know Go essentially offers a reverse proxy out of the box, so the setup was easy. 我几乎不知道Go本质上提供了开箱即用的反向代理,因此设置很容易。 I've got it to the point where I'm proxying traffic, but here's the thing, I have trouble implementing the actual functionality because wherever I have access to the response, I don't have access to assigned A/B test variations: 我已经到了代理流量的地步,但实际上,我无法实现实际的功能,因为无论我在哪里访问响应,都无法访问分配的A / B测试版本:

  • In the handleFunc I'm assigning variations of each test to the request, so the upstream server can also be aware of it and use if for implementations in it's backend. handleFunc我为请求分配了每个测试的变体,因此上游服务器也可以意识到这一点,并在后端的实现中使用if。
  • I'm setting a cookie with all tests and variations, both on the request that's being proxied to the upstream and on the response that's returned to the client. 我设置了一个包含所有测试和变体的cookie,包括在代理到上游的请求以及返回给客户端的响应上。
  • Tests that consist of a find/replace pair will do mutations on the response body after the response comes back from the upstream server. 在响应从上游服务器返回之后,由查找/替换对组成的测试将在响应主体上进行突变。
  • I'm trying to use the modifyResponse function of httputil.ReverseProxy to do the response mutation. 我试图使用modifyResponse的功能httputil.ReverseProxy做响应突变。

The problem is that I can't figure out how to share the assigned variations between the handleFunc and modifyResponse , without changing the upstream server. 问题是,我不知道如何在不更改上游服务器的情况下共享handleFuncmodifyResponse之间分配的变体。 I'd like to be able to share this context (basically a map[string]string somehow. 我希望能够共享此上下文(基本上是某种方式的map[string]string

Code sample: 代码示例:

Here's a distilled version of my code, where my question basically is, how can modifyRequest know about random assignments that happened in handleFunc ? 这是我的代码的modifyRequest版本,基本上我的问题是, modifyRequest如何知道在handleFunc中发生的随机分配?

package main

import (
    config2 "ab-proxy/config"
    "bytes"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/http/httputil"
    "net/url"
    "strconv"
    "strings"
)

var config config2.ProxyConfig
var reverseProxy *httputil.ReverseProxy
var tests config2.Tests

func overwriteCookie(req *http.Request, cookie *http.Cookie) {
    // omitted for brevity, will replace a cookie header, instead of adding a second value
}

func parseRequestCookiesToAssignedTests(req *http.Request) map[string]string {
    // omitted for brevity, builds a map where the key is the identifier of the test, the value the assigned variant
}

func renderCookieForAssignedTests(assignedTests map[string]string) string {
    // omitted for brevity, builds a cookie string
}

func main () {
    var err error

    if  config, err = config2.LoadConfig(); err != nil {
        fmt.Println(err)

        return
    }

    if tests, err = config2.LoadTests(); err != nil {
        fmt.Println(err)

        return
    }

    upstreamUrl, _ := url.Parse("0.0.0.0:80")

    reverseProxy = httputil.NewSingleHostReverseProxy(upstreamUrl)
    reverseProxy.ModifyResponse = modifyResponse

    http.HandleFunc("/", handleRequest)

    if err := http.ListenAndServe("0.0.0.0:80", nil); err != nil {
        fmt.Println("Could not start proxy")
    }
}

func handleRequest(res http.ResponseWriter, req *http.Request) {
    assigned := parseRequestCookiesToAssignedTests(req)

    newCookies := make(map[string]string)

    for _, test := range tests.Entries {
        val, ok := assigned[test.Identifier]

        if ok {
            newCookies[test.Identifier] = val
        } else {
            newCookies[test.Identifier] = "not-assigned-yet" // this will be replaced by random variation assignment
        }
    }

    testCookie := http.Cookie{Name: config.Cookie.Name, Value: renderCookieForAssignedTests(newCookies)}

    // Add cookie to request to be sent to upstream
    overwriteCookie(req, &testCookie)

    // Add cookie to response to be returned to client
    http.SetCookie(res, &testCookie)

    reverseProxy.ServeHTTP(res, req)
}

func modifyResponse (response *http.Response) error {
    body, err := ioutil.ReadAll(response.Body)

    if err != nil {
        return  err
    }

    err = response.Body.Close()

    if err != nil {
        return err
    }

    response.Body = ioutil.NopCloser(bytes.NewReader(body))
    response.ContentLength = int64(len(body))
    response.Header.Set("Content-Length", strconv.Itoa(len(body)))

    return nil
}

Use a standard context.Context . 使用标准context.Context This is accessible in your handler via the *http.Request . 这可以在您的处理程序中通过*http.Request And the request is also accessible via the *http.Response argument to modifyResponse . 而且该请求也可以通过*http.Response参数来modifyResponse

In your handler: 在您的处理程序中:

ctx := req.Context()
// Set values, deadlines, etc.
req = req.WithContext(ctx)
reverseProxy.ServeHTTP(res, req)

Then in modifyResponse : 然后在modifyResponse

ctx := response.Request.Context()
// fetch values, check for cancellation, etc

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM