ホームページ  >  記事  >  バックエンド開発  >  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 レスポンスの基礎となるソケットにアクセスする方法

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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。