Chrome DevTools 协议 - 在 Golang 中使用 gzip body ContinueInterceptedRequest
Chrome DevTools Protocol - ContinueInterceptedRequest with gzip body in Golang
我一直在研究一个 golang 脚本,它使用 chrome devtools protocol 来:
1) 拦截一个请求
2) 获取拦截请求的响应body
3) 对html
文档进行一些修改
4) 继续拦截的请求
该脚本适用于 HTML 个文档,但 Content-Encoding
设置为 gzip
时除外。 step-by-step 过程如下所示
1) 拦截请求
s.Debugger.CallbackEvent("Network.requestIntercepted", func(params godet.Params) {
iid := params.String("interceptionId")
rtype := params.String("resourceType")
reason := responses[rtype]
headers := getHeadersString(params["responseHeaders"])
log.Println("[+] Request intercepted for", iid, rtype, params.Map("request")["url"])
if reason != "" {
log.Println(" abort with reason", reason)
}
// Alter HTML in request response
if s.Options.AlterDocument && rtype == "Document" && iid != "" {
res, err := s.Debugger.GetResponseBodyForInterception(iid)
if err != nil {
log.Println("[-] Unable to get intercepted response body!")
}
rawAlteredResponse, err := AlterDocument(res, headers)
if err != nil{
log.Println("[-] Unable to alter HTML")
}
if rawAlteredResponse != "" {
log.Println("[+] Sending modified body")
err := s.Debugger.ContinueInterceptedRequest(iid, godet.ErrorReason(reason), rawAlteredResponse, "", "", "", nil)
if err != nil {
fmt.Println("OH NOES AN ERROR!")
log.Println(err)
}
}
} else {
s.Debugger.ContinueInterceptedRequest(iid, godet.ErrorReason(reason), "", "", "", "", nil)
}
})
2) 改变响应 body
这里我对 procesHtml()
中的 HTML 标记做了一些小改动(但是那个函数的代码与这个问题无关,所以这里不会 post 它) .我还从请求中获取 headers,并在必要时更新 content-length
和 date
,然后再继续响应。然后,我在调用 r := gZipCompress([]byte(alteredBody)
时 gzip 压缩了 body,其中 returns 是一个字符串。然后将字符串连接到 headers,这样我就可以制作 rawResponse
.
func AlterDocument(debuggerResponse []byte, headers map[string]string) (string, error) {
alteredBody, err := processHtml(debuggerResponse)
if err != nil {
return "", err
}
alteredHeader := ""
for k, v := range headers{
switch strings.ToLower(k) {
case "content-length":
v = strconv.Itoa(len(alteredBody))
fmt.Println("Updating content-length to: " + strconv.Itoa(len(alteredBody)))
break
case "date":
v = fmt.Sprintf("%s", time.Now().Format(time.RFC3339))
break
}
alteredHeader += k + ": " + v + "\r\n"
}
r := gZipCompress([]byte(alteredBody))
rawAlteredResponse :=
base64.StdEncoding.EncodeToString([]byte("HTTP/1.1 200 OK" + "\r\n" + alteredHeader + "\r\n\r\n\r\n" + r))
return rawAlteredResponse, nil
}
注意:我现在正在 gzip 压缩所有响应的 body。以上是暂时的,我想办法解决这个问题。
gzip 压缩函数如下所示:
func gZipCompress(dataToWorkWith []byte) string{
var b bytes.Buffer
gz, err := gzip.NewWriterLevel(&b, 5)
if err != nil{
panic(err)
}
if _, err := gz.Write(dataToWorkWith); err != nil {
panic(err)
}
if err := gz.Flush(); err != nil {
panic(err)
}
if err := gz.Close(); err != nil {
panic(err)
}
return b.String()
}
如第一个代码片段所示,此处设置了响应 body 和 headers:
err := s.Debugger.ContinueInterceptedRequest(iid, godet.ErrorReason(reason), rawAlteredResponse, "", "", "", nil)
结果浏览器出现一堆乱码。对于非 gzip 请求,这在没有 gzip 函数的情况下有效。我也改变了压缩级别(但没有成功)。我是否以错误的顺序处理 body (string > []byte > gzip > string > base64)?这应该以不同的顺序完成吗?任何帮助将不胜感激。
响应看起来像这样,Chrome 放在 <body></body>
标签中
����r�� ��_A��Q%GH��Kʔ��vU�˷c�v�}
或在回复中:
我还可以看出它正在正确压缩,因为当我删除 headers 时,请求导致 .gz
文件下载,并且在未压缩时所有正确 .html
。此外,gZipCompress
中返回的 object 中的前几个字节告诉我它已正确压缩:
31 139 8
或
0x1f 0x8B 0x08
我最终使用了不同的库来更好、更高效地处理更大的响应。
现在,在调用 Network.GetResponseBodyForInterception
时,DevTools 协议 returns 响应主体在解压后但在浏览器中呈现之前出现。当然,这只是一个假设,因为我在 https://github.com/ChromeDevTools/devtools-protocol 中没有看到该方法的代码。该假设基于这样一个事实,即调用 Network.GetResponseBodyForInterception
时获得的响应主体未压缩(尽管它可能是 base64 编码的)。此外,该方法被标记为实验性的,文档中没有提及任何关于压缩响应的内容。基于该假设,我将进一步假设,当我们从 Network.GetResponseBodyForInterception
获得响应时,我们自己压缩主体为时已晚。我确认我正在使用的库不会压缩或解压缩 gzipped 响应。
我可以继续使用我的代码,而无需担心 gzip 压缩响应,因为我可以毫无问题地更改正文。
作为参考,我现在使用 https://github.com/wirepair/gcd,因为它在拦截较大的响应时更加稳健和稳定。
我一直在研究一个 golang 脚本,它使用 chrome devtools protocol 来:
1) 拦截一个请求
2) 获取拦截请求的响应body
3) 对html
文档进行一些修改
4) 继续拦截的请求
该脚本适用于 HTML 个文档,但 Content-Encoding
设置为 gzip
时除外。 step-by-step 过程如下所示
1) 拦截请求
s.Debugger.CallbackEvent("Network.requestIntercepted", func(params godet.Params) {
iid := params.String("interceptionId")
rtype := params.String("resourceType")
reason := responses[rtype]
headers := getHeadersString(params["responseHeaders"])
log.Println("[+] Request intercepted for", iid, rtype, params.Map("request")["url"])
if reason != "" {
log.Println(" abort with reason", reason)
}
// Alter HTML in request response
if s.Options.AlterDocument && rtype == "Document" && iid != "" {
res, err := s.Debugger.GetResponseBodyForInterception(iid)
if err != nil {
log.Println("[-] Unable to get intercepted response body!")
}
rawAlteredResponse, err := AlterDocument(res, headers)
if err != nil{
log.Println("[-] Unable to alter HTML")
}
if rawAlteredResponse != "" {
log.Println("[+] Sending modified body")
err := s.Debugger.ContinueInterceptedRequest(iid, godet.ErrorReason(reason), rawAlteredResponse, "", "", "", nil)
if err != nil {
fmt.Println("OH NOES AN ERROR!")
log.Println(err)
}
}
} else {
s.Debugger.ContinueInterceptedRequest(iid, godet.ErrorReason(reason), "", "", "", "", nil)
}
})
2) 改变响应 body
这里我对 procesHtml()
中的 HTML 标记做了一些小改动(但是那个函数的代码与这个问题无关,所以这里不会 post 它) .我还从请求中获取 headers,并在必要时更新 content-length
和 date
,然后再继续响应。然后,我在调用 r := gZipCompress([]byte(alteredBody)
时 gzip 压缩了 body,其中 returns 是一个字符串。然后将字符串连接到 headers,这样我就可以制作 rawResponse
.
func AlterDocument(debuggerResponse []byte, headers map[string]string) (string, error) {
alteredBody, err := processHtml(debuggerResponse)
if err != nil {
return "", err
}
alteredHeader := ""
for k, v := range headers{
switch strings.ToLower(k) {
case "content-length":
v = strconv.Itoa(len(alteredBody))
fmt.Println("Updating content-length to: " + strconv.Itoa(len(alteredBody)))
break
case "date":
v = fmt.Sprintf("%s", time.Now().Format(time.RFC3339))
break
}
alteredHeader += k + ": " + v + "\r\n"
}
r := gZipCompress([]byte(alteredBody))
rawAlteredResponse :=
base64.StdEncoding.EncodeToString([]byte("HTTP/1.1 200 OK" + "\r\n" + alteredHeader + "\r\n\r\n\r\n" + r))
return rawAlteredResponse, nil
}
注意:我现在正在 gzip 压缩所有响应的 body。以上是暂时的,我想办法解决这个问题。
gzip 压缩函数如下所示:
func gZipCompress(dataToWorkWith []byte) string{
var b bytes.Buffer
gz, err := gzip.NewWriterLevel(&b, 5)
if err != nil{
panic(err)
}
if _, err := gz.Write(dataToWorkWith); err != nil {
panic(err)
}
if err := gz.Flush(); err != nil {
panic(err)
}
if err := gz.Close(); err != nil {
panic(err)
}
return b.String()
}
如第一个代码片段所示,此处设置了响应 body 和 headers:
err := s.Debugger.ContinueInterceptedRequest(iid, godet.ErrorReason(reason), rawAlteredResponse, "", "", "", nil)
结果浏览器出现一堆乱码。对于非 gzip 请求,这在没有 gzip 函数的情况下有效。我也改变了压缩级别(但没有成功)。我是否以错误的顺序处理 body (string > []byte > gzip > string > base64)?这应该以不同的顺序完成吗?任何帮助将不胜感激。
响应看起来像这样,Chrome 放在 <body></body>
标签中
����r�� ��_A��Q%GH��Kʔ��vU�˷c�v�}
或在回复中:
我还可以看出它正在正确压缩,因为当我删除 headers 时,请求导致 .gz
文件下载,并且在未压缩时所有正确 .html
。此外,gZipCompress
中返回的 object 中的前几个字节告诉我它已正确压缩:
31 139 8
或
0x1f 0x8B 0x08
我最终使用了不同的库来更好、更高效地处理更大的响应。
现在,在调用 Network.GetResponseBodyForInterception
时,DevTools 协议 returns 响应主体在解压后但在浏览器中呈现之前出现。当然,这只是一个假设,因为我在 https://github.com/ChromeDevTools/devtools-protocol 中没有看到该方法的代码。该假设基于这样一个事实,即调用 Network.GetResponseBodyForInterception
时获得的响应主体未压缩(尽管它可能是 base64 编码的)。此外,该方法被标记为实验性的,文档中没有提及任何关于压缩响应的内容。基于该假设,我将进一步假设,当我们从 Network.GetResponseBodyForInterception
获得响应时,我们自己压缩主体为时已晚。我确认我正在使用的库不会压缩或解压缩 gzipped 响应。
我可以继续使用我的代码,而无需担心 gzip 压缩响应,因为我可以毫无问题地更改正文。
作为参考,我现在使用 https://github.com/wirepair/gcd,因为它在拦截较大的响应时更加稳健和稳定。