ginuerzh/gost · error · net.OpError
read not supported
Error message
read not supported
What it means
http2ServerConn wraps only the server side of an HTTP request/response (http.Request + http.ResponseWriter). Reading raw bytes is meaningless in that model, so Read always returns 0 bytes and a *net.OpError with "read not supported".
Source
Thrown at http2.go:890
}
func (c *http2Conn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *http2Conn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
// a dummy HTTP2 server conn used by HTTP2 handler
type http2ServerConn struct {
r *http.Request
w http.ResponseWriter
closed chan struct{}
}
func (c *http2ServerConn) Read(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "read", Net: "http2", Source: nil, Addr: nil, Err: errors.New("read not supported")}
}
func (c *http2ServerConn) Write(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "write", Net: "http2", Source: nil, Addr: nil, Err: errors.New("write not supported")}
}
func (c *http2ServerConn) Close() error {
select {
case <-c.closed:
default:
close(c.closed)
}
return nil
}
func (c *http2ServerConn) LocalAddr() net.Addr {
addr, _ := net.ResolveTCPAddr("tcp", c.r.Host)
return addrView on GitHub (pinned to a33fdbf4c9)
Solutions
- Read the request body via c.r.Body (the wrapped *http.Request) instead of conn.Read.
- Use http2Conn (from the h2 listener Accept path) when a bidirectional net.Conn is required.
- Refactor code that io.Copy's from the conn to consume r.Body and write to w explicitly.
- Guard with a type check so the dummy server conn is never fed to conn-oriented APIs.
Example fix
// before io.Copy(dst, serverConn) // read not supported // after io.Copy(dst, serverConn.r.Body) // read the HTTP request body
Defensive patterns
Strategy: type-guard
Validate before calling
// before relaying, ensure the conn supports reading
func readable(c net.Conn) bool {
_, isDummy := c.(interface{ IsServerDummy() bool })
return !isDummy // http2ServerConn cannot Read
} Type guard
func isH2ServerConn(c net.Conn) bool {
// http2ServerConn exposes RemoteAddr resolved from the HTTP request
_, ok := c.(interface{ Read([]byte) (int, error) })
_ = ok
// prefer explicit type assertion in-package:
_, isDummy := c.(*gosthttp2.ServerConnLike)
return !isDummy
} Try / catch
n, err := conn.Read(buf)
if err != nil {
var oe *net.OpError
if errors.As(err, &oe) && strings.Contains(err.Error(), "read not supported") {
return fmt.Errorf("use r.Body for handler-side http2 conns")
}
return err
} Prevention
- Never feed http2ServerConn to io.Copy/relay code expecting a bidirectional conn.
- Read request data from c.r.Body and write responses via c.w.
- Use h2Conn from the listener Accept path when a real net.Conn is needed.
When it happens
Trigger: Calling Read() on an http2ServerConn obtained from the HTTP/2 handler path, e.g. passing the conn to code expecting a bidirectional net.Conn (relay, io.Copy from the conn).
Common situations: Using the handler-side dummy conn in a tunneling relay where the developer expected a full-duplex conn; mixing up http2ServerConn (handler side) with http2Conn (dialer/listener side, which supports Read).
Related errors
- deadline not supported
- write not supported
- read not supported
- write not supported
- deadline not supported
AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02).
Data as JSON: /api/errors/9cf374cdedcf0bcf.
Report an issue: GitHub.