英文:
Golang DumpResponse gzip issue when logging response body (reverseproxy)
问题
我已经在我的main.go中创建了一个简单的反向代理,代码如下:
reverseproxy := httputil.NewSingleHostReverseProxy("https://someurl/someuri")
这个代码运行良好,但是我想记录从服务器返回的响应。我可以使用以下代码在golang中的标准RoundTrip方法中实现:
response, err := http.DefaultTransport.RoundTrip(request)
dumpresp, err := httputil.DumpResponse(response, true)
if err != nil {
return nil, err
}
log.Printf("%s", dumpresp)
上述代码都按预期工作,除了一个问题,当响应的Content-Encoding为gzip时,日志中的字符串显示为非UTF-8的gzip字符。这似乎是golang的一个疏忽,但也许在文档中有我忽略的内容,我已经完整阅读了文档几次。我无法在这里发布日志,因为这些字符是非UTF-8的,所以它们在这个网站上无法显示。所以,我知道你在想什么,只需获取gzip内容并使用方法去除gzip压缩即可。如果DumpResponse的响应不是部分gzip和部分标准UTF-8,而是可以将这些部分分开的话,那就太好了。
所以我知道你要说什么,为什么不直接获取原始响应,如下所示进行gzip解码,而不使用DumpResponse。嗯,我也可以这样做,但是以下存在一个问题:
var reader io.Reader
startreader := httputility.NewChunkedReader(reader)
switch response.Header.Get("Content-Encoding") {
case "gzip":
startreader, err = gzip.NewReader(response.Body)
log.Println("Response body gzip:")
buf := new(bytes.Buffer)
buf.ReadFrom(startreader)
b := buf.Bytes()
s := *(*string)(unsafe.Pointer(&b))
for name, value := range response.Header {
var hvaluecomp string = ""
for i := 0; i < len(value); i++ {
hvaluecomp += value[i]
}
response.Header.Add(name,hvaluecomp)
}
log.Printf("%s", s)
default:
startreader = response.Body
buf := new(bytes.Buffer)
buf.ReadFrom(startreader)
b := buf.Bytes()
s := *(*string)(unsafe.Pointer(&b))
for name, value := range response.Header {
fmt.Printf("%v: %v\n", name, value)
}
log.Printf("%s", s)
}
上述代码的问题是,响应只能通过reader读取一次,之后反向代理就无法再读取响应了,这会使代理响应返回给浏览器为nil =),所以我又失败了。我无法想象golang的开发人员会忽略解码gzip,这很奇怪,似乎很简单。
所以最后,我的问题是,DumpResponse是否提供了解压缩gzip的能力,以便我可以记录实际的响应而不是非UTF-8字符?当调试生产产品的问题时,这对日志读取器没有任何帮助。这将使我认为内置的golang反向代理在我眼中变得无用,我将开始开发自己的代理。
英文:
I have created a simple reverse proxy in my main.go as follows:
reverseproxy := httputil.NewSingleHostReverseProxy("https://someurl/someuri")
this is working fine, however, I would like to log the response that comes back from the server. I can also do this utilizing the following code inside the standard RoundTrip method recommended by golang:
response, err := http.DefaultTransport.RoundTrip(request)
dumpresp, err := httputil.DumpResponse(response, true)
if err != nil {
return nil, err
}
log.Printf("%s", dumpresp)
All the above works as expected aside from one thing, the response, when Content-Encoding: gzip, the string appears to the logs as non-utf8 gzip characters. it seems to be a golang oversight but maybe there is something i have missed in the documentation, which I have read to its completion a few times. I can't post the logs here because the characters are non utf8 so they would not display on this site anyway. So, I know what your thinking, just grab the gzip content and use a method to remove the gzip compression. That would be great if the response from DumpResponse was not partly gzip and partly standard utf8 with no way to separate the sections from each other.
So I know what your going to say, why not just take the raw response like the following and gzip decode, and "not" use DumpResponse. Well I can do that as well, but there is an issue with the following:
var reader io.Reader
startreader := httputility.NewChunkedReader(reader)
switch response.Header.Get("Content-Encoding") {
case "gzip":
startreader, err = gzip.NewReader(response.Body)
log.Println("Response body gzip: ")
buf := new(bytes.Buffer)
buf.ReadFrom(startreader)
b := buf.Bytes()
s := *(*string)(unsafe.Pointer(&b))
for name, value := range response.Header {
var hvaluecomp string = ""
for i := 0; i < len(value); i++ {
hvaluecomp += value[i]
}
response.Header.Add(name,hvaluecomp)
}
log.Printf("%s", s)
default:
startreader = response.Body
buf := new(bytes.Buffer)
buf.ReadFrom(startreader)
b := buf.Bytes()
s := *(*string)(unsafe.Pointer(&b))
for name, value := range response.Header {
fmt.Printf("%v: %v\n", name, value)
}
log.Printf("%s", s)
}
The issue with the above is, responses can only be read one time via the reader, after that the response cannot be read by the reverse proxy anymore and it makes the proxy response back to the browser nil =), so again I was met with failure. I cant imagine that the folks coding golang would have missed decoding gzip, just strange, it seems to be so simple.
So in the end, my question is, does DumpResponse give me the ability to decompress gzip so I can log the actual response instead of non utf8 characters? This does the log reader no good when debugging an issue for the production product. This would render the built in golang reverse proxy useless in my eyes and I would start development on my own.
答案1
得分: 1
答案是复制流,然后可以使用第二个变量将流重新发送到request.body对象,这样就不会丢失任何数据。
buf, _ := ioutil.ReadAll(response.Body)
responseuse1 := ioutil.NopCloser(bytes.NewBuffer(buf))
responsehold := ioutil.NopCloser(bytes.NewBuffer(buf))
提取日志的方法:extractLogging(responseuse1)
将保留的流重新放回到body中,以保持其不变:response.Body = responsehold
返回response。
英文:
The answer is to copy the stream, then you can use the second variable to re-post the stream back to the request.body object so you don't lose any data.
buf, _ := ioutil.ReadAll(response.Body)
responseuse1 := ioutil.NopCloser(bytes.NewBuffer(buf))
responsehold := ioutil.NopCloser(bytes.NewBuffer(buf))
Method to pull logging: extractLogging(responseuse1)
Push the hold back to the body so its untouched: response.Body = responsehold
return response
答案2
得分: 0
不,它不提供解压缩gzip的功能。
在我看来,主要问题既不是反向代理也不是DumpResponse,而是你试图“记录”二进制数据:无论是gzip还是其他二进制数据,比如图像。只需修复你的记录逻辑:如果原始正文是二进制的,你应该呈现一些表示或转换它的方式。对于gzip压缩的内容,首先解压缩它(但这可能仍然是二进制的、“非utf8”数据,不适合记录)。专注于真正的问题:如何“记录”二进制数据。
英文:
> does DumpResponse give me the ability to decompress gzip
No it does not.
To me it seems as if the major problem is neither the reverse proxy nor DumpResponse but that you are trying to "log" binary data: Be it gzip, or other binary data like images. Just fix your logging logic: If the raw body is binary you should render some kind of representation or transformation of it. For gziped stuff gunzip it first (but this might still be binary "non utf8" data unsuitable for logging). Focus on the real problem: How to "log" binary data.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论