about http hijacking and keep-alive

2020-06-20 05:13发布

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 keepalive keepalive2 keep-alive, It keep two tcp connection, and can reuse.

nokeepalive nokeepalive2 but when i hijack, and conn.Close(), It can't reuse old connection, so it create a new tcp connection when i each refresh.

标签: http go
2条回答
我只想做你的唯一
2楼-- · 2020-06-20 05:39

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)
}
查看更多
Summer. ? 凉城
3楼-- · 2020-06-20 05:44

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
localhost:9090 is a hijacked connection, lasting for 10 seconds.

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

查看更多
登录 后发表回答