Skip to content

proposal: net/url: add QueryParser and QueryEncoder interfaces #56300

@chrisguiney

Description

@chrisguiney

Summary

I would like to propose adding new interfaces to net/url to allow users to have control over url query string parsing and encoding implementations.

This can be done in a backwards compatible manner by

  1. introducing the interface
  2. providing a default implementation of the interface that implements current behavior
  3. exposing the default implementations as package level variables
  4. updating call sites in the standard library to accept interface values to prefer over the default implementation

With the DefaultQueryParser and DefaultQueryEncoder being package level variables, a user can set the implementations once in main or init. This allows the user to have confidence that their chosen implementation will be used, even if some usages have not yet been updated to accept a custom implementation.

This approach allows users to gradually widen their API to accept a QueryParser or QueryEncoder, allowing for implementation to be spread over multiple releases if necessary.

Proposed Interface Addition

var (
    DefaultQueryParser QueryParser = qsParser{}
    DefaultQueryEncoder QueryEncoder = qsParser{}
)

type QueryParser interface {
    Parse(string) (Values, error)
}

type QueryEncoder interface {
    Encode(Values) (string, error)
}

type qsParser struct{}

func (qsParser) Parse(s string) (Values, error) {
    // body would be the current body of ParseQuery
}

func (qsParser) Encode(v Values) (string, error) {
    // body would be the current body of Values.Encode
}


// update existing apis to use the DefaultQueryParser
// and DefaultQueryEncoder

func ParseQuery(s string) (Values, error) {
    return DefaultQueryParser.Parse(s)
}

func (v Values) Encode() string {
     s, _ := DefaultQueryEncoder.Encode()
    return s
}

Rationale

Other systems have their own interpretations of the url RFCs, which are often at odds with how go interprets them. This creates situations where a behavior can be a security issue for one user, but an intentional and relied upon behavior for others. There is no single implementation that can serve all users.

By allowing custom implementations to be supplied, we're able to provide a safe default that covers the majority of use cases, but still allow users with very specific needs the flexibility to provide their own implementations.

Users of the package include http.Server and httputil.ReverseProxy`. Both are too large to reasonably expect users to fork in order to call their own parsing and encoding implementations.

Historical Issues

There have been a number of issues surrounding query string parsing and encoding. Security issues have caused a number of backwards incompatible changes, which then motivated other changes to allow prior behavior to be restored.

For users sensitive to these changes, some of the above issues caused operational disruption. In other cases, the backwards compatibility breakages were in point releases.

Issues since proposal

edit: forgot interface keyword on QueryParser and QueryEncoder

2022/11/21 edit: added Issues since proposal

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Incoming

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions