Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Bagaimana untuk Mengakses Soket Dasar Tindak Balas net/http?

Bagaimana untuk Mengakses Soket Dasar Tindak Balas net/http?

Barbara Streisand
Barbara Streisandasal
2024-11-05 07:36:02171semak imbas

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

Cara Mengakses Soket Pendasar bagi net/http Response

Apabila bekerja dengan sambungan HTTP dalam Go, mungkin terdapat senario di mana pembangun perlu mengakses rangkaian asas soket. Pakej net/http menyediakan cara yang komprehensif untuk mengendalikan permintaan dan respons HTTP, tetapi ia tidak mendedahkan soket asas secara langsung. Berikut ialah cara untuk mendapatkan semula soket dalam program Go.

Menggunakan Konteks Permintaan (Go 1.13 dan ke atas)

Dengan keluaran Go 1.13, pakej net/http memperkenalkan keupayaan untuk menyimpan net.Conn dalam konteks permintaan. Ini menyediakan cara yang mudah dan bersih untuk mengakses soket:

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

Menggunakan ConnStateEvent untuk Soket TCP

Untuk pelayan mendengar pada port TCP, net.Conn.RemoteAddr().String( ) adalah unik untuk setiap sambungan. Oleh itu, ia boleh digunakan sebagai kunci kepada peta sambungan global:

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

Menggunakan ConnSaveListener untuk Soket UNIX

Untuk pelayan yang mendengar pada soket UNIX, net.Conn.RemoteAddr() .String() sentiasa mengembalikan "@". Untuk menjadikannya berfungsi, anda boleh mengatasi net.Listener.Accept() dan menggunakannya untuk mengatasi net.Conn.RemoteAddr().String(). Berikut ialah contoh:

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

Kesimpulan

Mengakses soket asas http.ResponseWriter boleh dicapai menggunakan kaedah yang diterangkan di atas. Pendekatan pilihan bergantung pada keperluan khusus dan versi Go yang digunakan.

Atas ialah kandungan terperinci Bagaimana untuk Mengakses Soket Dasar Tindak Balas net/http?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn