In the realm of network programming with Go's net/http package, sometimes we may need to directly access the underlying network connection(net.Conn).
A developer is trying to serve files using net/http, but encounters a problem: needing access to the underlying socket of http.ResponseWriter in the handler function ( net.Conn) to make platform-specific system calls.
In Go 1.13 and above, net.Conn can be stored in the request context by following these steps:
Prior to this release, there were two alternatives:
Use the remote address string
For listening on a TCP port For servers, net.Conn.RemoteAddr().String() is unique for each connection and can be used as the key to the global connection map.
Override net.Listener.Accept()
For servers listening on UNIX sockets, we can override net.Listener.Accept() to Use the file descriptor to return a more unique value.
Go 1.13 and above
<code class="go">// SaveConnInContext stores the net.Conn in the request context. func SaveConnInContext(ctx context.Context, c net.Conn) context.Context { return context.WithValue(ctx, ConnContextKey, c) } // GetConn retrieves the net.Conn from the request context. func GetConn(r *http.Request) net.Conn { return r.Context().Value(ConnContextKey).(net.Conn) }</code>
for TCP connections
<code class="go">// ConnStateEvent handles connection state events. 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()) } } // GetConn retrieves the net.Conn from a map using the remote address as a key. func GetConn(r *http.Request) net.Conn { return conns[r.RemoteAddr] }</code>
for UNIX connections
<code class="go">// NewUnixListener creates a new UNIX listener with a modified Accept() method. func NewUnixListener(path string) (net.Listener, error) { // ... (setup code) l, err := net.Listen("unix", path) if err != nil { return nil, err } return NewConnSaveListener(l), nil } // NewConnSaveListener wraps a listener and overrides the Accept() method. func NewConnSaveListener(wrap net.Listener) net.Listener { return connSaveListener{wrap} } // remoteAddrPtrConn overrides the RemoteAddr() method to return a unique value. type remoteAddrPtrConn struct { net.Conn ptrStr string } func (self remoteAddrPtrConn) RemoteAddr() net.Addr { return remoteAddrPtr{self.ptrStr} } // remoteAddrPtr implements the net.Addr interface. type remoteAddrPtr struct { ptrStr string } func (remoteAddrPtr) Network() string { return "" } func (self remoteAddrPtr) String() string { return self.ptrStr } // Accept overrides the default Accept() method to store the net.Conn in a map. 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 }</code>
Through these methods, developers can easily access the underlying socket of http.ResponseWriter, opening up a range of possibilities for their custom network handling.
The above is the detailed content of How Can I Access the Underlying Socket of a net/http Response in Go?. For more information, please follow other related articles on the PHP Chinese website!