How to Use ServeMux with ServerConn?

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

How to Use ServeMux with ServerConn?

问题

我正在创建一个网络API,并希望用户能够使用ServeMux将请求路由到特定的端点。我需要使用自己的低级ServerConn而不是使用Server实例。这是因为我需要从同一个端口接收传入的HTTP请求和纯文本数据。

然而,问题在于,如果我想要使用我的ServeMux转发请求,我将使用它的ServeHTTP方法。为此,我需要提供一个ResponseWriter,但我不知道如何创建它的实例,因为它是一个接口,而不是一个结构体。我应该创建自己的ResponseWriter结构体吗?Golang标准库中是否提供了一个ResponseWriter?或者是否有其他解决方案?

英文:

Im creating a Networking API and want people to be able to route requests to specific endpoints using a ServeMux. Instead of using a Server instance, I need to use my own low level ServerConn. This is because I am receiving both incoming HTTP requests and plain text data from the same port.

The problem, however, is that if I want to forward a request using my ServeMux, I would use it's ServeHTTP method. For this, I need to provide a ResponseWriter, which I don't know how to create an instance of since it is an interface, not a struct. Should a I create my own ResponseWriter struct? Is there one given by the Golang Standard Library? Or is there an alternate solution to this altogether?

答案1

得分: 4

如果可能的话,我会尽量避免这样做。在同一个连接上混合使用不同的协议很可能会导致难以追踪的错误和意外行为。如果你真的想这样做,并且希望所有的http/1.1机制能够正常工作,尽量使用http包中的功能。

由于ResponseWriter是一个接口,你需要实现自己的类型来满足它。可以查看http包中未导出的response类型,以获取完整的示例。这需要做很多正确的事情,而且与ServerConn(文档中标记为“不要使用”)结合使用可能不是一个好主意。

在更低的级别上进行这样的操作应该在Server的net.Listener的Accept方法中进行。由于你需要两次解析每个请求的开头,你需要一个可以部分“回溯”的net.Conn。

创建一个net.Listener,在新连接上检查流的开头,如果看起来像是一个http请求,就返回一个net.Conn,在第一次读取时重放你从网络上读取的第一个数据块。类似这样:

type replayConn struct {
    net.Conn
    buf []byte
    pos int
}

func (c *replayConn) Read(b []byte) (int, error) {
    if c.pos < len(c.buf) {
        n := copy(b, c.buf[c.pos:])
        c.pos += n
        return n, nil
    }
    return c.Conn.Read(b)
}

如果连接不是http,则将连接发送到另一种类型的处理程序,并继续在Accept上阻塞。

英文:

I would avoid doing this altogether if at all possible. Mixing protocols on the same connection is bound to lead to hard-to-trace bugs, and unexpected behavior. If you really want to do it, and have all the http/1.1 mechanisms work correctly, leave as much as possible to the http package.

Since ResponseWriter is an interface, you would implement your own type to satisfy it. Look at the unexported response type in the http package for a full example. There's a lot to get right, and using it in combination with a ServerConn (which is documented as "do no use") is probably not a good idea.

The place to do this at a lower level would be in Accept inside the Server's net.Listener. Since you're going to have to parse the start of every request twice, you would need a net.Conn that can be "rewound" partly.

Make yourself a net.Listener that checks the start of the stream on a new connection, and if it looks like an http request, return a net.Conn that replays the first chunk you read off the wire on its first Reads. Something like:

type replayConn struct {
	net.Conn
	buf []byte
	pos int
}

func (c *replayConn) Read(b []byte) (int, error) {
	if c.pos &lt; len(c.buf) {
		n := copy(b, c.buf[c.pos:])
		c.pos += n
		return n, nil
	}
	return c.Conn.Read(b)
}

If the connection isn't http, then send the connection off to your other type of handler, and continue blocking on Accept.

huangapple
  • 本文由 发表于 2015年4月30日 09:38:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/29957596.html
匿名

发表评论

匿名网友

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

确定