英文:
about http hijacking and keep-alive
问题
我使用以下代码:
resp, err := http.Get("http://example.com/")
// 获取 http.ResponseWriter
webConn, webBuf, err := hj.Hijack()
if err != nil {
// 处理错误
}
defer webConn.Close()
// 将 resp 写入 webBuf
resp.Write(webBuf)
我想要将获取到的 http.Response
写入到 http.ResponseWriter
中,但是只有 http.ResponseWriter
,所以我使用了 hijack 来实现。
然而,当我使用 hijack 后,HTTP 连接无法重用(keep-alive),导致速度变慢。
如何解决这个问题呢?
对不起,我的英语不太好。
更新 12/9
keep-alive,它保持两个 TCP 连接,并且可以重用。
但是当我使用 hijack 并关闭连接(conn.Close())后,无法重用旧的连接,所以每次刷新时都会创建一个新的 TCP 连接。
英文:
i use
resp, err := http.Get("http://example.com/")
get a http.Response, and i want to exactly write to a http handler, but only http.ResponseWriter, so i hijack it.
...
webConn, webBuf, err := hj.Hijack()
if err != nil {
// handle error
}
defer webConn.Close()
// Write resp
resp.Write(webBuf)
...
Write raw request
But When i hijack, http connection can't reuse (keep-alive), so it slow.
How to solve?
Thanks! Sorry for my pool English.
update 12/9
keep-alive, It keep two tcp connection, and can reuse.
but when i hijack, and conn.Close(), It can't reuse old connection, so it create a new tcp connection when i each refresh.
答案1
得分: 5
不要使用劫持,因为一旦劫持,HTTP服务器库将不会对连接执行其他任何操作,因此无法重用。
我改变了方法,复制了Header和Body,类似于反向代理(http://golang.org/src/pkg/net/http/httputil/reverseproxy.go),它是有效的。
示例:
func copyHeader(dst, src http.Header) {
for k, w := range src {
for _, v := range w {
dst.Add(k, v)
}
}
}
func copyResponse(r *http.Response, w http.ResponseWriter) {
copyHeader(w.Header(), r.Header)
w.WriteHeader(r.StatusCode)
io.Copy(w, r.Body)
}
func handler(w http.ResponseWriter, r *http.Response) {
resp, err := http.Get("http://www.example.com")
if err != nil {
// 处理错误
}
copyResponse(resp, w)
}
英文:
Do not use hijack, Because once hijack, the HTTP server library will not do anything else with the connection, So can't reuse.
I change way, copy Header and Body, look like reverse proxy (http://golang.org/src/pkg/net/http/httputil/reverseproxy.go), Is works.
Example:
func copyHeader(dst, src http.Header) {
for k, w := range src {
for _, v := range w {
dst.Add(k, v)
}
}
}
func copyResponse(r *http.Response, w http.ResponseWriter) {
copyHeader(w.Header(), r.Header)
w.WriteHeader(r.StatusCode)
io.Copy(w, r.Body)
}
func handler(w http.ResponseWriter, r *http.Response) {
resp, err := http.Get("http://www.example.com")
if err != nil {
// handle error
}
copyResponse(resp, w)
}
答案2
得分: 0
似乎一旦连接关闭,keep-alive连接也会关闭。
一种可能的解决方案是在期望之前阻止连接关闭,但我不确定这是否是一个好建议。
也许正确的解决方案是创建一个net.TCPConn的实例,将连接复制到其中,然后调用.SetKeepAlive(true)
。
在运行下面的示例之前,请在另一个终端中使用netstat -antc | grep 9090
命令启动。
示例中的路由:
localhost:9090/ok
是一个基本的(非劫持)连接<br>
localhost:9090
是一个持续10秒的劫持连接<br>
示例
package main
import (
"fmt"
"net/http"
"sync"
"time"
)
func checkError(e error) {
if e != nil {
panic(e)
}
}
var ka_seconds = 10
var conn_id = 0
func main() {
http.HandleFunc("/ok", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "ok")
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
conn_id++
fmt.Printf("Connection %v: Keep-alive is enabled %v seconds\n", conn_id, ka_seconds)
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
return
}
conn, bufrw, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Don't forget to close the connection:
time.AfterFunc(time.Second* time.Duration(ka_seconds), func() {
conn.Close()
fmt.Printf("Connection %v: Keep-alive is disabled.\n", conn_id)
})
resp, err := http.Get("http://www.example.com")
checkError(err)
resp.Write(bufrw)
bufrw.Flush()
})
fmt.Println("Listening to localhost:9090")
http.ListenAndServe(":9090", nil)
}
相关问题:http://code.google.com/p/go/issues/detail?id=5645
英文:
It seem that once the connection is closed the keep-alive connection closes as well.
One possible solution would be to prevent the connection from closing until desired, but I'm not sure if that good advise.
Maybe the correct solution involves creating a instance of net.TCPConn, copying the connection over it, then calling .SetKeepAlive(true)
.
Before running the below example, launch another terminal with netstat -antc | grep 9090
.
Routes in example:
localhost:9090/ok
is a basic (non-hijacked) connection<br>
localhost:9090
is a hijacked connection, lasting for 10 seconds.<br>
Example
package main
import (
"fmt"
"net/http"
"sync"
"time"
)
func checkError(e error) {
if e != nil {
panic(e)
}
}
var ka_seconds = 10
var conn_id = 0
func main() {
http.HandleFunc("/ok", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "ok")
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
conn_id++
fmt.Printf("Connection %v: Keep-alive is enabled %v seconds\n", conn_id, ka_seconds)
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
return
}
conn, bufrw, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Don't forget to close the connection:
time.AfterFunc(time.Second* time.Duration(ka_seconds), func() {
conn.Close()
fmt.Printf("Connection %v: Keep-alive is disabled.\n", conn_id)
})
resp, err := http.Get("http://www.example.com")
checkError(err)
resp.Write(bufrw)
bufrw.Flush()
})
fmt.Println("Listing to localhost:9090")
http.ListenAndServe(":9090", nil)
}
Related issue: http://code.google.com/p/go/issues/detail?id=5645
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论