Я пытаюсь реализовать обратный прокси-сервер в Go, который проксирует трафик на разные хосты на основе некоторого клиента, встроенного в URL-адрес. Реализация выглядит так:
type Offloader struct {
tenantHostMap map[string]string // Map a tenant to its host:port
tenantProxyMap map[string](*httputil.ReverseProxy) // Map a tenant to its reverse proxy
}
func (o *Offloader) OnCreate() {
// Tenants Map
o.tenantHostMap = make(map[string]string)
o.tenantProxyMap = make(map[string]*httputil.ReverseProxy)
o.PopulateTenantHostMap()
// Rx
http.HandleFunc("/", o.ServeHTTP)
go http.ListenAndServe(":5555", nil)
}
// ServeHTTP is the callback that is called each time a Http Request is received.
func (o *Offloader) ServeHTTP(w http.ResponseWriter, req *http.Request) {
incomingUrl := req.URL.RequestURI()
tenant := o.GetTenantFromUrl(incomingUrl)
if proxy, ok := o.tenantProxyMap[tenant]; ok {
proxy.ServeHTTP(w, req)
}
if remoteHostAddr, ok := o.tenantHostMap[tenant]; ok {
remoteUrl, err := url.Parse(fmt.Sprintf("http://%s", remoteHostAddr))
if err != nil {
return
}
proxy := httputil.NewSingleHostReverseProxy(remoteUrl)
o.tenantProxyMap[tenant] = proxy
proxy.ServeHTTP(w, req) // non blocking
} else {
panic("Unknown Tenant")
}
}
При получении нового HTTP-запроса я получаю арендатора по URL-адресу. Если я впервые вижу этого арендатора, я создаю новый ReverseProxy, в противном случае я пытаюсь использовать тот, который я создал ранее и сохранил в tenantProxyMap.
Когда я тестирую это, я получаю следующую ошибку:
2022/04/05 12:31:01 http: proxy error: readfrom tcp ****: http: invalid Read on closed Body
2022/04/05 12:31:01 http: superfluous response.WriteHeader call from net/http/httputil.(*ReverseProxy).defaultErrorHandler (reverseproxy.go:190)
Если я создаю новый обратный прокси-сервер для каждого запроса, а не повторно использую один и тот же прокси-сервер, ошибка не возникает.
Я думал, что прокси-сервер предназначен для каждого хоста, а не для каждого запроса (как следует из названия), поэтому мне интересно, почему возникает эта ошибка?
Я знаю, что мне нужно защитить карты от одновременного чтения/записи, однако в данный момент это не имеет значения.
Спасибо,





Проблема в том, что в сценарии, где предыдущий прокси уже существовал, вы сначала передаете ему запрос, а затем снова создаете прокси и снова передаете запрос. Другими словами: вы делаете два прокси-запроса для каждого входящего запроса, когда для этого арендатора уже заполнена карта tenantProxyMap.
Реализация ReverseProxy закрывает req.Body, поэтому во второй раз, когда вы передаете запрос прокси-серверу, он пытается прочитать уже закрытое тело. В результате вы видите ошибку http: invalid Read on closed Body.
Что вы должны попробовать, так это вернуться после проксирования запроса, например. добавив return:
if proxy, ok := o.tenantProxyMap[tenant]; ok {
proxy.ServeHTTP(w, req)
return
}
Не могу поверить, что мне платят за код, и я пропустил это. Спасибо