访问 net/http 响应的底层套接字。

huangapple go评论95阅读模式
英文:

Accessing the underlying socket of a net/http response

问题

我是你的中文翻译助手,以下是翻译的内容:

我对Go语言还不熟悉,正在评估它是否适合我的项目。

我正在尝试使用net/http编写一个自定义处理程序来提供文件服务。
我不能使用默认的http.FileServer()处理程序,因为我需要访问底层的套接字(即内部的net.Conn),以便可以对其执行一些特定于平台的信息性的“系统调用”(主要是TCP_INFO)。

更具体地说:我需要在处理程序函数中访问http.ResponseWriter的底层套接字:

func myHandler(w http.ResponseWriter, r *http.Request) {
...
// 我需要 w 的 net.Conn
...
}

在以下代码中使用:

http.HandleFunc("/", myHandler)

有没有办法实现这个需求?我查看了websocket.Upgrade的实现方式,但它使用了Hijack(),这有点“过头”,因为这样我就需要在获取的原始TCP套接字上编写“HTTP通信”的代码。我只想要一个套接字的引用,而不是完全接管它。

英文:

I'm new to Go and evaluating it for a project.

I'm trying to write a custom handler to serve files with net/http.
I can't use the default http.FileServer() handler because I need to have access to the underlying socket (the internal net.Conn) so I can perform some informational platform specific "syscall" calls on it (mainly TCP_INFO).

More precisly: I need to access the underlying socket of the http.ResponseWriter in the handler function:

func myHandler(w http.ResponseWriter, r *http.Request) {
...
// I need the net.Conn of w
...
}

used in

http.HandleFunc("/", myHandler)

Is there a way to this. I looked at how websocket.Upgrade does this but it uses Hijack() which is 'too much' because then I have to code 'speaking http' over the raw tcp socket I get. I just want a reference to the socket and not taking over completely.

答案1

得分: 15

在完成Issue #30694之后,Go 1.13可能会支持将net.Conn存储在请求上下文中,这样做会更加清晰简单:

package main

import (
  "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()对于每个连接都是唯一的,并且可以作为全局Conns映射的键使用:

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)
  ...
}

对于监听UNIX套接字的服务器,net.Conn.RemoteAddr().String()始终为“@”,因此上述方法不起作用。为了使其工作,我们可以重写net.Listener.Accept(),并使用它来重写net.Conn.RemoteAddr().String(),以便为每个连接返回唯一的字符串:

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
}

[1]: https://github.com/golang/go/issues/30694
英文:

After Issue #30694 is completed, it looks like Go 1.13 will probably support storing the net.Conn in the Request Context, which makes this fairly clean and simple:

package main
import (
"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)
...
}

Until then ... For a server listening on a TCP port, net.Conn.RemoteAddr().String() is unique for each connection and is available to the http.Handler as r.RemoteAddr, so it can be used as a key to a global map of Conns:

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)
...
}

For a server listening on a UNIX socket, net.Conn.RemoteAddr().String() is always "@", so the above doesn't work. To make this work, we can override net.Listener.Accept(), and use that to override net.Conn.RemoteAddr().String() so that it returns a unique string for each connection:

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
}

答案2

得分: 14

请注意,尽管在当前实现中,http.ResponseWriter 是一个 *http.response(注意小写!)来保存连接,但该字段是未导出的,无法访问。

相反,可以查看 Server.ConnState 钩子:可以“注册”一个在连接状态改变时调用的函数,详见 http.ConnState。例如,在请求进入处理程序之前(http.StateNewhttp.StateActive 状态),您将获得 net.Conn

您可以通过创建自定义的 Server 来安装连接状态监听器,示例如下:

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

    s := &http.Server{
        Addr:           ":8081",
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
        ConnState:      ConnStateListener,
    }
    panic(s.ListenAndServe())
}

func ConnStateListener(c net.Conn, cs http.ConnState) {
    fmt.Printf("CONN STATE: %v, %v\n", cs, c)
}

这样,您将在调用处理程序之前(以及在调用处理程序期间和之后)获得所需的 net.Conn。缺点是它与 ResponseWriter 不是“配对”的,如果需要,您必须手动进行配对。

英文:

Note that although in current implementation http.ResponseWriter is a *http.response (note the lowercase!) which holds the connection, the field is unexported and you can't access it.

Instead take a look at the Server.ConnState hook: you can "register" a function which will be called when the connection state changes, see http.ConnState for details. For example you will get the net.Conn even before the request enters the handler (http.StateNew and http.StateActive states).

You can install a connection state listener by creating a custom Server like this:

func main() {
http.HandleFunc(&quot;/&quot;, myHandler)
s := &amp;http.Server{
Addr:           &quot;:8081&quot;,
ReadTimeout:    10 * time.Second,
WriteTimeout:   10 * time.Second,
MaxHeaderBytes: 1 &lt;&lt; 20,
ConnState:      ConnStateListener,
}
panic(s.ListenAndServe())
}
func ConnStateListener(c net.Conn, cs http.ConnState) {
fmt.Printf(&quot;CONN STATE: %v, %v\n&quot;, cs, c)
}

This way you will have exactly the desired net.Conn even before (and also during and after) invoking the handler. The downside is that it is not "paired" with the ResponseWriter, you have to do that manually if you need that.

答案3

得分: 3

你可以使用HttpHijacker来接管ResponseWriter的TCP连接。一旦完成这一步骤,你就可以自由地使用socket来进行任何你想要的操作。

请参考http://golang.org/pkg/net/http/#Hijacker,其中也包含了一个很好的示例。

英文:

You can use an HttpHijacker to take over the TCP connection from the ResponseWriter. Once you've done that you're free to use the socket to do whatever you want.

See http://golang.org/pkg/net/http/#Hijacker, which also contains a good example.

答案4

得分: 1

这可以通过反射来实现。虽然有点"不太规范",但是可以工作:

package main

import "net/http"
import "fmt"
import "runtime"

import "reflect"

func myHandler(w http.ResponseWriter, r *http.Request) {

    ptrVal := reflect.ValueOf(w)
    val := reflect.Indirect(ptrVal)

    // w 是一个 "http.response" 结构体,我们可以从中获取 'conn' 字段
    valconn := val.FieldByName("conn")
    val1 := reflect.Indirect(valconn)

    // 它是一个 http.conn,我们可以从中获取 'rwc' 字段
    ptrRwc := val1.FieldByName("rwc").Elem()
    rwc := reflect.Indirect(ptrRwc)

    // 它是一个 net.TCPConn,我们可以从中获取嵌入的 conn
    val1conn := rwc.FieldByName("conn")
    val2 := reflect.Indirect(val1conn)

    // 它是一个 net.conn,我们可以从中获取 'fd' 字段
    fdmember := val2.FieldByName("fd")
    val3 := reflect.Indirect(fdmember)

    // 它是一个 netFD,我们可以从中获取 'sysfd' 字段
    netFdPtr := val3.FieldByName("sysfd")
    fmt.Printf("netFDPtr= %v\n", netFdPtr)

    // 它是系统套接字(类型取决于平台,对于 Linux 是 Int)
    if runtime.GOOS == "linux" {
        fd := int(netFdPtr.Int())
        fmt.Printf("fd = %v\n", fd)
        // fd 就是套接字 - 我们可以在其上调用 unix.Syscall6(unix.SYS_GETSOCKOPT, uintptr(fd),....) 等函数
    }

    fmt.Fprintf(w, "Hello World")
}

func main() {
    http.HandleFunc("/", myHandler)
    err := http.ListenAndServe(":8081", nil)
    fmt.Println(err.Error())
}

理想情况下,该库应该增加一个方法来获取底层的 net.Conn。

英文:

This can be done with reflection. it's a bit "dirty" but it works:

package main
import &quot;net/http&quot;
import &quot;fmt&quot;
import &quot;runtime&quot;
import &quot;reflect&quot;
func myHandler(w http.ResponseWriter, r *http.Request) {
ptrVal := reflect.ValueOf(w)
val := reflect.Indirect(ptrVal)
// w is a &quot;http.response&quot; struct from which we get the &#39;conn&#39; field
valconn := val.FieldByName(&quot;conn&quot;)
val1 := reflect.Indirect(valconn)
// which is a http.conn from which we get the &#39;rwc&#39; field
ptrRwc := val1.FieldByName(&quot;rwc&quot;).Elem()
rwc := reflect.Indirect(ptrRwc)
// which is net.TCPConn from which we get the embedded conn
val1conn := rwc.FieldByName(&quot;conn&quot;)
val2 := reflect.Indirect(val1conn)
// which is a net.conn from which we get the &#39;fd&#39; field
fdmember := val2.FieldByName(&quot;fd&quot;)
val3 := reflect.Indirect(fdmember)
// which is a netFD from which we get the &#39;sysfd&#39; field
netFdPtr := val3.FieldByName(&quot;sysfd&quot;)
fmt.Printf(&quot;netFDPtr= %v\n&quot;, netFdPtr)
// which is the system socket (type is plateform specific - Int for linux)
if runtime.GOOS == &quot;linux&quot; {
fd := int(netFdPtr.Int())
fmt.Printf(&quot;fd = %v\n&quot;, fd)
// fd is the socket - we can call unix.Syscall6(unix.SYS_GETSOCKOPT, uintptr(fd),....) on it for instance
}
fmt.Fprintf(w, &quot;Hello World&quot;)
}
func main() {
http.HandleFunc(&quot;/&quot;, myHandler)
err := http.ListenAndServe(&quot;:8081&quot;, nil)
fmt.Println(err.Error())
}

Ideally the library should be augmented with a method to get the underlying net.Conn

答案5

得分: 1

扩展KGVJ的答案,使用反射来维护一个由net.Conn实例内存地址索引的连接映射的工作解决方案。

可以通过指针查找net.Conn的实例,并使用反射针对http.Response获取指针。

这有点恶心,但考虑到无法使用反射访问未发布的字段,这是我能想到的唯一方法。

// 连接数组,以连接地址为索引
var conns = make(map[uintptr]net.Conn)
var connMutex = sync.Mutex{}

// writerToConnPtr将http.ResponseWriter转换为用于索引的指针
func writerToConnPtr(w http.ResponseWriter) uintptr {
    ptrVal := reflect.ValueOf(w)
    val := reflect.Indirect(ptrVal)

    // http.conn
    valconn := val.FieldByName("conn")
    val1 := reflect.Indirect(valconn)

    // net.TCPConn
    ptrRwc := val1.FieldByName("rwc").Elem()
    rwc := reflect.Indirect(ptrRwc)

    // net.Conn
    val1conn := rwc.FieldByName("conn")
    val2 := reflect.Indirect(val1conn)

    return val2.Addr().Pointer()
}

// connToPtr将net.Conn转换为用于索引的指针
func connToPtr(c net.Conn) uintptr {
    ptrVal := reflect.ValueOf(c)
    return ptrVal.Pointer()
}

// ConnStateListener绑定到服务器并通过指针维护连接列表
func ConnStateListener(c net.Conn, cs http.ConnState) {
    connPtr := connToPtr(c)
    connMutex.Lock()
    defer connMutex.Unlock()

    switch cs {
    case http.StateNew:
        log.Printf("CONN Opened: 0x%x\n", connPtr)
        conns[connPtr] = c

    case http.StateClosed:
        log.Printf("CONN Closed: 0x%x\n", connPtr)
        delete(conns, connPtr)
    }
}

func HandleRequest(w http.ResponseWriter, r *http.Request) {
    connPtr := writerToConnPtr(w)
    connMutex.Lock()
    defer connMutex.Unlock()

    // 请求可以通过响应写入器对象的指针访问连接
    conn, ok := conns[connPtr]
    if !ok {
        log.Printf("error: no matching connection found")
        return
    }

    // 在这里处理连接...

}

// 与http.Server.ConnState = ConnStateListener绑定
英文:

Expanding on KGJV's answer, a working solution using reflection to maintain a map of connections indexed by net.Conn instance memory addresses.

Instances of net.Conn can be looked up by pointer, and pointers derived using reflection against http.Response.

It's a bit nasty, but given you can't access unpublished fields with reflection it's the only way I could see of doing it.

// Connection array indexed by connection address
var conns = make(map[uintptr]net.Conn)
var connMutex = sync.Mutex{}
// writerToConnPrt converts an http.ResponseWriter to a pointer for indexing
func writerToConnPtr(w http.ResponseWriter) uintptr {
ptrVal := reflect.ValueOf(w)
val := reflect.Indirect(ptrVal)
// http.conn
valconn := val.FieldByName(&quot;conn&quot;)
val1 := reflect.Indirect(valconn)
// net.TCPConn
ptrRwc := val1.FieldByName(&quot;rwc&quot;).Elem()
rwc := reflect.Indirect(ptrRwc)
// net.Conn
val1conn := rwc.FieldByName(&quot;conn&quot;)
val2 := reflect.Indirect(val1conn)
return val2.Addr().Pointer()
}
// connToPtr converts a net.Conn into a pointer for indexing
func connToPtr(c net.Conn) uintptr {
ptrVal := reflect.ValueOf(c)
return ptrVal.Pointer()
}
// ConnStateListener bound to server and maintains a list of connections by pointer
func ConnStateListener(c net.Conn, cs http.ConnState) {
connPtr := connToPtr(c)
connMutex.Lock()
defer connMutex.Unlock()
switch cs {
case http.StateNew:
log.Printf(&quot;CONN Opened: 0x%x\n&quot;, connPtr)
conns[connPtr] = c
case http.StateClosed:
log.Printf(&quot;CONN Closed: 0x%x\n&quot;, connPtr)
delete(conns, connPtr)
}
}
func HandleRequest(w http.ResponseWriter, r *http.Request) {
connPtr := writerToConnPtr(w)
connMutex.Lock()
defer connMutex.Unlock()
// Requests can access connections by pointer from the responseWriter object
conn, ok := conns[connPtr]
if !ok {
log.Printf(&quot;error: no matching connection found&quot;)
return
}
// Do something with connection here...
}
// Bind with http.Server.ConnState = ConnStateListener

答案6

得分: 0

看起来你无法将套接字(或net.Conn)与http.Request或http.ResponseWriter进行“配对”。

但是你可以实现自己的Listener:

package main

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

func main() {
	// 初始化HTTP服务器
	m := &MyHandler{}
	s := &http.Server{
		Handler: m,
	}

	// 创建自定义监听器
	nl, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}
	l := &MyListener{nl}

	// 通过自定义监听器提供服务
	err = s.Serve(l)
	if err != nil {
		log.Fatal(err)
	}
}

// net.Conn
type MyConn struct {
	nc net.Conn
}

func (c MyConn) Read(b []byte) (n int, err error) {
	return c.nc.Read(b)
}

func (c MyConn) Write(b []byte) (n int, err error) {
	return c.nc.Write(b)
}

func (c MyConn) Close() error {
	return c.nc.Close()
}

func (c MyConn) LocalAddr() net.Addr {
	return c.nc.LocalAddr()
}

func (c MyConn) RemoteAddr() net.Addr {
	return c.nc.RemoteAddr()
}

func (c MyConn) SetDeadline(t time.Time) error {
	return c.nc.SetDeadline(t)
}

func (c MyConn) SetReadDeadline(t time.Time) error {
	return c.nc.SetReadDeadline(t)
}

func (c MyConn) SetWriteDeadline(t time.Time) error {
	return c.nc.SetWriteDeadline(t)
}

// net.Listener
type MyListener struct {
	nl net.Listener
}

func (l MyListener) Accept() (c net.Conn, err error) {
	nc, err := l.nl.Accept()
	if err != nil {
		return nil, err
	}
	return MyConn{nc}, nil
}

func (l MyListener) Close() error {
	return l.nl.Close()
}

func (l MyListener) Addr() net.Addr {
	return l.nl.Addr()
}

// http.Handler
type MyHandler struct {
	// ...
}

func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World")
}

这段代码演示了如何实现自定义的Listener,并将其用于HTTP服务器。你可以根据自己的需求修改MyHandler的实现。

英文:

It looks like you cannot "pair" a socket (or net.Conn) to either http.Request or http.ResponseWriter.

But you can implement your own Listener:

package main
import (
&quot;fmt&quot;
&quot;net&quot;
&quot;net/http&quot;
&quot;time&quot;
&quot;log&quot;
)
func main() {
// init http server
m := &amp;MyHandler{}
s := &amp;http.Server{
Handler:        m,
}
// create custom listener
nl, err := net.Listen(&quot;tcp&quot;, &quot;:8080&quot;)
if err != nil {
log.Fatal(err)
}
l := &amp;MyListener{nl}
// serve through custom listener
err = s.Serve(l)
if err != nil {
log.Fatal(err)
}
}
// net.Conn
type MyConn struct {
nc net.Conn
}
func (c MyConn) Read(b []byte) (n int, err error) {
return c.nc.Read(b)
}
func (c MyConn) Write(b []byte) (n int, err error) {
return c.nc.Write(b)
}
func (c MyConn) Close() error {
return c.nc.Close()
}
func (c MyConn) LocalAddr() net.Addr {
return c.nc.LocalAddr()
}
func (c MyConn) RemoteAddr() net.Addr {
return c.nc.RemoteAddr()
}
func (c MyConn) SetDeadline(t time.Time) error {
return c.nc.SetDeadline(t)
}
func (c MyConn) SetReadDeadline(t time.Time) error {
return c.nc.SetReadDeadline(t)
}
func (c MyConn) SetWriteDeadline(t time.Time) error {
return c.nc.SetWriteDeadline(t)
}
// net.Listener
type MyListener struct {
nl net.Listener
}
func (l MyListener) Accept() (c net.Conn, err error) {
nc, err := l.nl.Accept()
if err != nil {
return nil, err
}
return MyConn{nc}, nil
}
func (l MyListener) Close() error {
return l.nl.Close()
}
func (l MyListener) Addr() net.Addr {
return l.nl.Addr()
}
// http.Handler
type MyHandler struct {
// ...
}
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, &quot;Hello World&quot;)
}

huangapple
  • 本文由 发表于 2015年4月9日 15:08:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/29531993.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定