首頁  >  文章  >  後端開發  >  如何存取 net/http 回應的底層套接字?

如何存取 net/http 回應的底層套接字?

Barbara Streisand
Barbara Streisand原創
2024-11-05 07:36:02171瀏覽

How to Access the Underlying Socket of a net/http Response?

如何存取 net/http 回應的底層 Socket

在 Go 中使用 HTTP 連接時,可能存在開發人員需要存取底層網路的場景。 net/http 套件提供了一種處理 HTTP 請求和回應的全面方法,但它不會直接公開底層套接字。以下是如何在 Go 程式中檢索套接字。

使用請求上下文(Go 1.13 及更高版本)

隨著 Go 1.13 的發布,net/http 套件引入了儲存請求上下文的功能請求上下文中的 net.Conn。這提供了一種方便而乾淨的方式來存取套接字:

<code class="go">package main

import (
    "context"
    "net/http"
    "net"
    "log"
)

type contextKey struct {
    key string
}

var ConnContextKey = &contextKey{"http-conn"}

func SaveConnInContext(ctx context.Context, c net.Conn) (context.Context) {
    return context.WithValue(ctx, ConnContextKey, c)
}

func GetConn(r *http.Request) (net.Conn) {
    return r.Context().Value(ConnContextKey).(net.Conn)
}

func main() {
    http.HandleFunc("/", myHandler)

    server := http.Server{
        Addr: ":8080",
        ConnContext: SaveConnInContext,
    }
    server.ListenAndServe()
}

func myHandler(w http.ResponseWriter, r *http.Request) {
    conn := GetConn(r)
    ...
}</code>

對TCP 套接字使用ConnStateEvent

對於偵聽TCP 連接埠的伺服器,net.Conn. RemoteAddr().String( ) 對於每個連線都是唯一的。因此,它可以用作全域連接映射的鍵:

<code class="go">package main

import (
    "net/http"
    "net"
    "fmt"
    "log"
)

var conns = make(map[string]net.Conn)

func ConnStateEvent(conn net.Conn, event http.ConnState) {
    if event == http.StateActive {
        conns[conn.RemoteAddr().String()] = conn
    } else if event == http.StateHijacked || event == http.StateClosed {
        delete(conns, conn.RemoteAddr().String())
    }
}

func GetConn(r *http.Request) (net.Conn) {
    return conns[r.RemoteAddr]
}

func main() {
    http.HandleFunc("/", myHandler)

    server := http.Server{
        Addr: ":8080",
        ConnState: ConnStateEvent,
    }
    server.ListenAndServe()
}

func myHandler(w http.ResponseWriter, r *http.Request) {
    conn := GetConn(r)
    ...
}</code>

對UNIX 套接字使用ConnSaveListener

對於在UNIX 套接字上偵聽的伺服器,net.Conn. RemoteAddr() .String() 總是傳回“@”。要使其正常工作,您可以重寫 net.Listener.Accept() 並使用它來重寫 net.Conn.RemoteAddr().String()。 以下是範例:

<code class="go">package main

import (
    "net/http"
    "net"
    "os"
    "golang.org/x/sys/unix"
    "fmt"
    "log"
)

func main() {
    http.HandleFunc("/", myHandler)

    listenPath := "/var/run/go_server.sock"
    l, err := NewUnixListener(listenPath)
    if err != nil {
        log.Fatal(err)
    }
    defer os.Remove(listenPath)

    server := http.Server{
        ConnState: ConnStateEvent,
    }
    server.Serve(NewConnSaveListener(l))
}

func myHandler(w http.ResponseWriter, r *http.Request) {
    conn := GetConn(r)
    if unixConn, isUnix := conn.(*net.UnixConn); isUnix {
        f, _ := unixConn.File()
        pcred, _ := unix.GetsockoptUcred(int(f.Fd()), unix.SOL_SOCKET, unix.SO_PEERCRED)
        f.Close()
        log.Printf("Remote UID: %d", pcred.Uid)
    }
}

var conns = make(map[string]net.Conn)

type connSaveListener struct {
    net.Listener
}

func NewConnSaveListener(wrap net.Listener) (net.Listener) {
    return connSaveListener{wrap}
}

func (self connSaveListener) Accept() (net.Conn, error) {
    conn, err := self.Listener.Accept()
    ptrStr := fmt.Sprintf("%d", &conn)
    conns[ptrStr] = conn
    return remoteAddrPtrConn{conn, ptrStr}, err
}

func GetConn(r *http.Request) (net.Conn) {
    return conns[r.RemoteAddr]
}

func ConnStateEvent(conn net.Conn, event http.ConnState) {
    if event == http.StateHijacked || event == http.StateClosed {
        delete(conns, conn.RemoteAddr().String())
    }
}

type remoteAddrPtrConn struct {
    net.Conn
    ptrStr string
}

func (self remoteAddrPtrConn) RemoteAddr() (net.Addr) {
    return remoteAddrPtr{self.ptrStr}
}

type remoteAddrPtr struct {
    ptrStr string
}

func (remoteAddrPtr) Network() (string) {
    return ""
}

func (self remoteAddrPtr) String() (string) {
    return self.ptrStr
}

func NewUnixListener(path string) (net.Listener, error) {
    if err := unix.Unlink(path); err != nil & os.IsNotExist(err) {
        return nil, err
    }
    mask := unix.Umask(0777)
    defer unix.Umask(mask)

    l, err := net.Listen("unix", path)
    if err != nil {
        return nil, err
    }

    if err := os.Chmod(path, 0660); err != nil {
        l.Close()
        return nil, err
    }

    return l, nil
}</code>

結論

可以使用上述方法來存取 http.ResponseWriter 的底層套接字。首選方法取決於特定要求和所使用的 Go 版本。

以上是如何存取 net/http 回應的底層套接字?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn