在问题#30694完成之后,Go
1.13可能将支持在请求上下文中存储net.Conn,这使它相当干净和简单:
package mainimport ( "net/http" "context" "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) ...}直到那时…对于在TCP端口上侦听的服务器,net.Conn.RemoteAddr()。String()对于每个连接都是唯一的,并且可以作为r.RemoteAddr在http.Handler中使用,因此可以用作康恩斯全球地图的关键:
package mainimport ( "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) ...}对于在UNIX套接字上侦听的服务器,net.Conn.RemoteAddr()。String()始终为“
@”,因此上述操作无效。为此,我们可以覆盖net.Listener.Accept(),并使用它覆盖net.Conn.RemoteAddr()。String(),以便为每个连接返回唯一的字符串:
package mainimport ( "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}


