go: net/http: easier access to HTTP/2 error codes

x/net/http2 exports the StreamError type. Because this type is exported, it’s easy to use errors.As to extract the HTTP/2 error code (if any) from any error. Unfortunately, the StreamError type gets unexported when x/net/http2 is vendored into net/http. Since we can’t use errors.As, extracting HTTP/2 error codes requires some brittle string parsing. Now that we’ve all gotten used to post-1.13 errors, this feels bad. 😿

It would be nice if net/http exposed HTTP/2 error codes more cleanly. A simple approach might be to export an alias from net/http:

type HTTP2StreamError = http2StreamError

This is concise and requires minimal coordination with x/net/http2. However, it leaves the http2ErrCode type unexported, along with the error code constants. An easier-to-use but more elaborate approach might be to define new StreamError and ErrCode types, along with an As method on http2StreamError:

type ErrCode uint32

const (
  ErrCodeNo = ErrCode(http2ErrCodeNo)
  ...similarly for other codes...
)

type StreamError struct {
  StreamID uint32
  Code ErrCode
  cause error
}

func (e *StreamError) Error() string { ... }

func (e *StreamError) Unwrap() error { 
  return e.cause
}

func (e *http2StreamError) As(target any) bool {
  if se, ok := target.(*StreamError); ok {
    *se = StreamError{
      StreamID: e.StreamID,
      Code: ErrCode(e.Code),
      cause: e.Cause,
    }
    return true
  }
  if e.Cause == nil {
    return false
  }
  return errors.As(e.Cause, target)
}

Is there any appetite for the latter approach?

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 4
  • Comments: 19 (17 by maintainers)

Commits related to this issue

Most upvoted comments

Based on the discussion above, this proposal seems like a likely accept. — rsc for the proposal review group

I don’t really want to copy all the HTTP/2 error code constants into net/http. Without doing so, we can’t move http2.StreamError into net/http, however.

We could introduce a more limited "net/http".HTTP2StreamError type and have http2.StreamError convert to it.

But I think that it’s reasonable to say that if you need access to specific HTTP/2 error codes, you should use the golang.org/x/net/http2 package. As @seankhliao suggests, you can use http2.ConfigureTransports function to use the x/net implementation rather than the bundled one. The net/http package docs already suggest this approach when access to lower-level HTTP/2 features is required.