Go library for HTTP content type negotiation

Overview

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)
}
Issues
  • 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

    opened by gwvandesteeg 1
  • 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.

    opened by ianroberts 1
  • 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!

    opened by jmattheis 1
  • 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.

    opened by gwvandesteeg 0
  • 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.

    opened by gwvandesteeg 0
Releases(v1.0.3)
Owner
Elviss Strazdins
C++ programmer, game developer, owner of @boolgames
Elviss Strazdins
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 (

Ivan 50 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

Matt Sherman 1.4k Jun 20, 2022
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

Steve Francia 2.4k Jun 29, 2022
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 |

Matt Canty 1 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

Ethan P. 0 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

null 8 Jun 20, 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

Ringo Hoffmann 5 Apr 26, 2022
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

mlcsec 137 Jun 18, 2022
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

Maxime Soulé 305 Jun 29, 2022
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

Jijo Thomas John 1 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.

Evan Hazlett 1 May 22, 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.

null 61 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.

Chris Dzombak 18 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

Shyamsunder Rathi 26 May 10, 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).

Zachary Yedidia 36 Jun 13, 2022
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

Rahul Baruri 126 May 20, 2022
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

elastic 192 Jun 25, 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

Richard Artoul 354 Jun 8, 2022
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.

HashiCorp 1.2k Jun 25, 2022