Caddy Content Negotiation Plugin
Content negotiation is a mechanism of HTTP that allows client and server to agree on the best version of a resource to be delivered for the client’s needs given the server’s capabilities (see RFC). In short, when sending the request, the client can specify what content type, language, character set or encoding it prefers and the server responds with the best available version to fit the request.
This plugin to the Caddy 2 webserver allows you to configure named matchers for content negotiation parameters and/or store content negotiation results in variables.
The plugin can be configured via Caddyfile:
Syntax
@name {
conneg {
match_types <content-types...>
force_type_query_string <name>
var_type <name>
match_languages <language codes...>
force_language_query_string <name>
var_language <name>
match_charsets <character sets...>
force_charset_query_string <name>
var_charset <name>
match_encoding <language codes...>
force_encoding_query_string <name>
var_encoding <name>
}
}
match_types
takes one or more (space-separated) content types (a.k.a. mime types) that are available in this matcher. If the client requests a type (via HTTP’sAccept:
request header) compatible with one of those, the matcher returns true, if the request specifies types that cannot be satisfied by this list of offered types, the matcher returns false.force_type_query_string
allows to specify a URL query parameter’s key the value of which will override the HTTPAccept:
header. It works in both ways, i.e. it can cause and prevent a match. In order not to require typing full content types on the URL, there is a list of aliases hardcoded that allows URLs like...com/test?format=html
to be treated as requestingtetx/html
. Suggestions for extending the list are welcome, please open an issue for that.var_type
allows you to define a string that, prefixed withconneg_
specifies a variable name that will store the result of content type negotiation, i.e. the best content type according to the types and weights specified by the client and what is on offer by the server. You can access this variable with{vars.conneg_<name>}
in other places of your configuration.- All of the above are repeaed for languages (requested with the
Accept-Language:
header), character sets (requested with theAccept-Charset:
header), and encodings (which in reality are rather compression methods likezip
,deflate
,compress
etc., requested with theAccept-Encoding:
header). - The requirements in the same named matcher are AND’ed together. If you want to OR, i.e. match alternatively, just configure multiple named matchers.
- You must specify at least one of
match_types
,match_languages
,match_charsets
, andmatch_encodings
. And when you specify one of thevar_*
parameters, the correspondingmatch_
parameter must be defined as well. - Wildcards like
*
and*/*
should work. If they don’t behave as you expect, please open an issue.
A Caddyfile with some combinations for testing is provided with this repository. You can test it with commands like these:
$ curl -H "Accept: application/tei+xml" -H "Accept-Language: fr-FR" https://localhost/test?format=rdf\&lang=de\&enc=br
RDF auf deutsch oder englisch, de preferred!
$ curl -H "Accept: application/tei+xml" -H "Accept-Language: fr-FR" https://localhost/test?format=rdf
RDF en français!
$ curl -H "Accept: application/rdf+xml" -H "Accept-Language: en" https://localhost/test?lang=de
RDF auf deutsch oder englisch, de preferred!
$ curl -H "Accept: application/rdf+xml" -H "Accept-Language: en" https://localhost/test
RDF auf deutsch oder englisch, English / English preferred!
$ curl -H "Accept: application/rdf+xml" -H "Accept-Language: en, de;q=0.8" https://localhost/test
RDF auf deutsch oder englisch, English / English preferred!
$ curl -H "Accept: application/rdf+xml" -H "Accept-Language: de-DE" https://localhost/test
RDF auf deutsch oder englisch, German / Deutsch preferred!
$ curl -H "Accept: application/rdf+xml" https://localhost/test
RDF!
$ curl -H "Accept: text/html" -H "Accept-Language: fr-FR" -H "Accept-Encoding: br" https://localhost/test?format=html\&lang=de
HTML, but brotli-compressed!
Libraries
The plugin relies heavily on elnormous/contenttype and go’s own x/text/language libraries. (For the intricacies of language negotiation, you may want to have a glance at the blog post that accompanied the release of go’s language library.)
Some other libraries are mentioned in the connegmatcher.go
file. I’ve come across most of them in this go issue.
License
This software is licensed under the Apache License, Version 2.0.