关于HTTP劫持和保持活动状态

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

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

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)
}

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
", 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.
", 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