Go library for HTTP content type negotiation

Content-Type support library for Go

This library can be used to parse the value Content-Type header (if one is present) and select an acceptable media type from the Accept header of HTTP request.

Usage

Media types are stored in MediaType structure which has Type (e.g. application), Subtype (e.g. json) and Parameters (e.g. charset: utf-8) attributes. Media types are not stored in a string because media type parameters are part of the media type (RFC 7231, 3.1.1.1. Media Type). To convert a string to MediaType use NewMediaType. To convert MediaType back to string use String function. If the Content-Type header is not present in the request, an empty MediaType is returned.

To get the MediaType of the incoming request call GetMediaType and pass the http.Request pointer to it. The function will return error if the Content-Type header is malformed according to RFC 7231, 3.1.1.5. Content-Type.

To get an acceptable media type from an Accept header of the incoming request call GetAcceptableMediaType and pass the http.Request pointer to it and an array of all the acceptable media types. The function will return the best match following the negotiation rules written in RFC 7231, 5.3.2. Accept or an error if the header is malformed or the content type in the Accept header is not supported. If the Accept header is not present in the request, the first media type from the acceptable type list is returned.

import (
	"log"
	"github.com/elnormous/contenttype"
)

func handleRequest(responseWriter http.ResponseWriter, request *http.Request) {
    mediaType, mediaTypeError := contenttype.GetMediaType(request)
    if mediaTypeError != nil {
        // handle the error
    }
    log.Println("Media type:", mediaType.String())

    availableMediaTypes := []MediaType{
        contenttype.NewMediaType("application/json"),
        contenttype.NewMediaType("application/xml"),
    }

    accepted, extParameters, acceptError := contenttype.GetAcceptableMediaType(request, availableMediaTypes)
    if acceptError != nil {
        // handle the error
    }
    log.Println("Accepted media type:", accepted.String(), "extension parameters:", extParameters)
}
Owner
Elviss Strazdins
C++ programmer, game developer, owner of @boolgames
Elviss Strazdins
Comments
  • Add Mime() to exclude parameters in generated string

    Add Mime() to exclude parameters in generated string

    Add a Mime() method on the ContentType to be able to get the MIME only part without the parameters for simpler comparison against other MIME-types in for example switch statements.

    Partially closes #3

  • Separate parsing logic into separate functions

    Separate parsing logic into separate functions

    This PR is a refactor to split out the parsing part of the logic for each of Content-Type and Accept into a separate function from the "get the header from the request" part, making it possible to parse MIME type and Accept header values that came from somewhere other than the usual-named headers of an http.Request.

    This fixes #1 (or at least works around it), but it also widens the usefulness of this library to parse values from other places such as the Content-Type of a multipart.Part (I appreciate this part is already possible with NewMediaType but that doesn't do the same error handling and reporting as GetMediaType). My particular use case is writing a default backend to handle HTTP error codes for the ingress-nginx Kubernetes ingress controller, where the original request's Accept header is passed to the error handler endpoint as a different header name X-Format.

  • Add compare/equal method to media type

    Add compare/equal method to media type

    Currently, there is no neat way to compare MediaTypes. E.g. when you need to handle multiple different media types, you have to add this ugly if else chain, with checks for type & subtype, because MediaType itself isn't comparable (because of the usage of map[..].. for the parameters):

    var MediaTypeEventStream contenttype.MediaType = contenttype.NewMediaType("application/vnd.my-product.v1+stream")
    var MediaTypeEventJSON contenttype.MediaType = contenttype.NewMediaType("application/vnd.my-product.v1+json")
    var MediaTypeEventXML contenttype.MediaType = contenttype.NewMediaType("application/xml")
    
    func HandleCall(rw http.ResponseWriter, r *http.Request) {
    	mediaType, _, err := contenttype.GetAcceptableMediaType(ctx.Request(), []contenttype.MediaType{
    		MediaTypeEventStream,
    		MediaTypeEventJSON,
    		MediaTypeEventXML,
    	})
    	// handle err
    
    	if mediaType.Type == MediaTypeEventStream.Type && mediaType.Subtype == MediaTypeEventStream.Subtype {
    		// write stream to rw
    	} else if mediaType.Type == MediaTypeEventJSON.Type && mediaType.Subtype == MediaTypeEventJSON.Subtype {
    		// write json to rw
    	} else {
    		// write xml to rw
    	}
    }
    

    It would be cool, if a EqualIgnoreParameters method could be added to the media type. Another even better solution would be to remove the parameter map from the MediaType, then it would be possible to use the media type in a switch case like this:

    var MediaTypeStream MyMediaType = MyMediaType{Type: "application", SubType: "application/vnd.my-product.v1+stream"}
    var MediaTypeJson MyMediaType = MyMediaType{Type: "application", SubType: "application/vnd.my-product.v1+json"}
    var MediaTypeXML MyMediaType = MyMediaType{Type: "application", SubType: "application/vnd.my-product.v1+xml"}
    
    func something(r *http.Request()) {
    	mt, parameters, err := parseMyMediaType(r, []MyMediaType{..})
    	switch mt {
    	case MediaTypeJson:
    		// write as json to response writer
    	case MediaTypeXML:
    		// write as xml to response writer
    	case MediaTypeStream:
    		// write as stream to response writer
    	}
    }
    

    Thanks for your package and have a great day!

  • Add commutative utility comparison methods

    Add commutative utility comparison methods

    Add a selection of commutative utility comparison methods.

    These additional methods further close of the material requested in #3

    The internally used compareMediaTypes is not commutative in that the code assumes the wildcard will only come as the MediaType compared to (checkMediaType), but not in the MediaType being compared against (mediaType). Secondly this same method only checks the parameters ins a single direction, such that the parameters in the checkMediaType argument only need to be a subset of those in mediaType. Didn't want to touch this in case it was intentional.

    Ended up using reflect.DeepEqual since it is the quicker way of comparing two maps, but it can be resolved by iterating over the map, comparing one to the other, and making sure they both have the same keys in each map (same length of keys) is a simple fix.

  • go.mod needs to be updated to 1.14 or later

    go.mod needs to be updated to 1.14 or later

    The go.mod references go version 1.13 https://github.com/elnormous/contenttype/blob/dcd30b18c159a48185c2fa84cf30d0194fbfc67c/go.mod#L3

    and the code uses net/http.Header.Values, which was added in go 1.14 https://github.com/elnormous/contenttype/blob/dcd30b18c159a48185c2fa84cf30d0194fbfc67c/contenttype.go#L318-L323

    Attempting to use the code or test it will result in:

    $ go version
    go version go1.13 linux/amd64
    
    $ go test ./...
    # github.com/elnormous/contenttype
    ./contenttype.go:355:38: request.Header.Values undefined (type http.Header has no field or method Values)
    ./contenttype.go:404:33: request.Header.Values undefined (type http.Header has no field or method Values)
    FAIL    github.com/elnormous/contenttype [build failed]
    FAIL
    

    The minimal upgrade needed would be to increase it to go1.14.

  • Add CodeQL workflow for GitHub code scanning

    Add CodeQL workflow for GitHub code scanning

    Hi elnormous/contenttype!

    This is a one-off automatically generated pull request from LGTM.com :robot:. You might have heard that we’ve integrated LGTM’s underlying CodeQL analysis engine natively into GitHub. The result is GitHub code scanning!

    With LGTM fully integrated into code scanning, we are focused on improving CodeQL within the native GitHub code scanning experience. In order to take advantage of current and future improvements to our analysis capabilities, we suggest you enable code scanning on your repository. Please take a look at our blog post for more information.

    This pull request enables code scanning by adding an auto-generated codeql.yml workflow file for GitHub Actions to your repository — take a look! We tested it before opening this pull request, so all should be working :heavy_check_mark:. In fact, you might already have seen some alerts appear on this pull request!

    Where needed and if possible, we’ve adjusted the configuration to the needs of your particular repository. But of course, you should feel free to tweak it further! Check this page for detailed documentation.

    Questions? Check out the FAQ below!

    FAQ

    Click here to expand the FAQ section

    How often will the code scanning analysis run?

    By default, code scanning will trigger a scan with the CodeQL engine on the following events:

    • On every pull request — to flag up potential security problems for you to investigate before merging a PR.
    • On every push to your default branch and other protected branches — this keeps the analysis results on your repository’s Security tab up to date.
    • Once a week at a fixed time — to make sure you benefit from the latest updated security analysis even when no code was committed or PRs were opened.

    What will this cost?

    Nothing! The CodeQL engine will run inside GitHub Actions, making use of your unlimited free compute minutes for public repositories.

    What types of problems does CodeQL find?

    The CodeQL engine that powers GitHub code scanning is the exact same engine that powers LGTM.com. The exact set of rules has been tweaked slightly, but you should see almost exactly the same types of alerts as you were used to on LGTM.com: we’ve enabled the security-and-quality query suite for you.

    How do I upgrade my CodeQL engine?

    No need! New versions of the CodeQL analysis are constantly deployed on GitHub.com; your repository will automatically benefit from the most recently released version.

    The analysis doesn’t seem to be working

    If you get an error in GitHub Actions that indicates that CodeQL wasn’t able to analyze your code, please follow the instructions here to debug the analysis.

    How do I disable LGTM.com?

    If you have LGTM’s automatic pull request analysis enabled, then you can follow these steps to disable the LGTM pull request analysis. You don’t actually need to remove your repository from LGTM.com; it will automatically be removed in the next few months as part of the deprecation of LGTM.com (more info here).

    Which source code hosting platforms does code scanning support?

    GitHub code scanning is deeply integrated within GitHub itself. If you’d like to scan source code that is hosted elsewhere, we suggest that you create a mirror of that code on GitHub.

    How do I know this PR is legitimate?

    This PR is filed by the official LGTM.com GitHub App, in line with the deprecation timeline that was announced on the official GitHub Blog. The proposed GitHub Action workflow uses the official open source GitHub CodeQL Action. If you have any other questions or concerns, please join the discussion here in the official GitHub community!

    I have another question / how do I get in touch?

    Please join the discussion here to ask further questions and send us suggestions!

efaceconv - Code generation tool for high performance conversion from interface{} to immutable type without allocations.

efaceconv High performance conversion from interface{} to immutable types without additional allocations This is tool for go generate and common lib (

May 14, 2022
Type-driven code generation for Go

What’s this? gen is a code-generation tool for Go. It’s intended to offer generics-like functionality on your types. Out of the box, it offers offers

Jan 4, 2023
safe and easy casting from one type to another in Go

cast Easy and safe casting from one type to another in Go Don’t Panic! ... Cast What is Cast? Cast is a library to convert between different go types

Jan 1, 2023
Quickly query a Terraform provider's data type.

Terraform Query Quickly query a Terraform provider's data type. Such as a GitHub repository: ➜ ~ tfq github_repository full_name hashicorp/terraform |

Oct 12, 2021
Optional type using Go 1.18 generics.

go.eth-p.dev/goptional Generic Optional (or Go Optional, if you prefer) goptional is a package that provides an implementation of an Optional[T] monad

Apr 2, 2022
A protoc plugin that generates fieldmask paths as static type properties for proto messages

protoc-gen-fieldmask A protoc plugin that generates fieldmask paths as static ty

Nov 3, 2022
Di - A (very) WIP Go 1.18+ generic dependency injection package based on type reflection

di A (very) WIP Go 1.18+ generic dependency injection package based on type refl

Apr 26, 2022
Customisable and automated HTTP header injection
Customisable  and automated HTTP header injection

headi Customisable and automated HTTP header injection. Example run from the HTB machine Control: InsecureSkipVerify is not currently configured, if y

Dec 31, 2022
Extremely flexible golang deep comparison, extends the go testing package, tests HTTP APIs and provides tests suite
Extremely flexible golang deep comparison, extends the go testing package, tests HTTP APIs and provides tests suite

go-testdeep Extremely flexible golang deep comparison, extends the go testing package. Latest news Synopsis Description Installation Functions Availab

Jan 5, 2023
Code generator that generates boilerplate code for a go http server

http-bootstrapper This is a code generator that uses go templates to generate a bootstrap code for a go http server. Usage Generate go http server cod

Nov 20, 2021
This is a simple HTTP application that returns system info

sysinfo This is a simple HTTP application that returns system info. Trace Support There is also simple OpenTelemetry tracing support via the -t flag.

Dec 25, 2022
Govalid is a data validation library that can validate most data types supported by golang

Govalid is a data validation library that can validate most data types supported by golang. Custom validators can be used where the supplied ones are not enough.

Apr 22, 2022
Maintain a lower-bitrate copy of a music library in sync with the main copy.

msync Maintain a lower-bitrate copy of your music library, in sync with the main copy.

Mar 6, 2022
Golang library to act on structure fields at runtime. Similar to Python getattr(), setattr(), hasattr() APIs.

go-attr Golang library to act on structure fields at runtime. Similar to Python getattr(), setattr(), hasattr() APIs. This package provides user frien

Dec 16, 2022
A tool and library for using structural regular expressions.

Structural Regular Expressions sregx is a package and tool for using structural regular expressions as described by Rob Pike (link).

Dec 7, 2022
A super simple Lodash like utility library with essential functions that empowers the development in Go
A super simple Lodash like utility library with essential functions that empowers the development in Go

A simple Utility library for Go Go does not provide many essential built in functions when it comes to the data structure such as slice and map. This

Jan 4, 2023
go-sysinfo is a library for collecting system information.

go-sysinfo go-sysinfo is a library for collecting system information. This includes information about the host machine and processes running on the ho

Dec 26, 2022
Molecule is a Go library for parsing protobufs in an efficient and zero-allocation manner

Molecule Molecule is a Go library for parsing protobufs in an efficient and zero-allocation manner. The API is loosely based on this excellent Go JSON

Jan 5, 2023
A Go (golang) library for parsing and verifying versions and version constraints.

go-version is a library for parsing versions and version constraints, and verifying versions against a set of constraints. go-version can sort a collection of versions properly, handles prerelease/beta versions, can increment versions, etc.

Jan 9, 2023