quic-go / masque-go

MASQUE: Proxying UDP in HTTP/3, RFC 9298
MIT License
81 stars 9 forks source link

alternative request handling API #43

Closed marten-seemann closed 4 months ago

marten-seemann commented 4 months ago

There are a number of issues highlighting deficiencies with the current proxy API: #3, #4, #13, #41. There's a lot of back-and-forth between the request handler and the application, if we want to enable the fine-grained access control that's table stakes for a production-ready proxy.

The reason that implementing a HTTP CONNECT proxy is so much easier is because the http.Request contains all fields that are needed to make the proxying decison.

We could replicate this by implementing a ParseRequest function on the Proxy:

type Proxy struct {
     // Template is the URI template that clients will use to configure this UDP proxy.
     Template *uritemplate.Template
}

func (p *Proxy) ParseRequest(*http.Request) (*Request, error) {
     // parse the request using the configure URI template
}

Borrowing the idea from https://github.com/quic-go/masque-go/issues/41#issuecomment-2197993818, the returned error could implement a ParseError, allowing the application to set the correct HTTP status code (while also allowing it to ignore the suggestion and set their own status):

type ParsingError struct {
    HTTPStatus int
    Error string
}

func (e *ParsingError) Error() string { return e.Error }

After ParseRequest, it's the application's responsibility to decide how to proceed with the request. In case it wishes to continue, it needs to pass the masque.Request back to the Proxy, together with a connection to the target host:

func (p *Proxy) HandleRequest(*Request, *net.UDPConn) error {
    // proxy packets back and forth
}