rfc

package
v6.0.2+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 22, 2021 License: Apache-2.0, BSD-2-Clause, BSD-3-Clause, + 1 more Imports: 13 Imported by: 0

Documentation

Overview

Package rfc provides symbols for specifications, primarily IETF RFCs.

This includes strings, such as header names, as well as logic functions, such as cache control rules.

All symbols in this package should document the precise RFC and ideally the exact section where they are defined, to allow readers to easily verify correctness.

This is primarily intended for IETF HTTP RFCs, but may include other specifications as well (e.g. ANSI, W3C, WHATWG, etc). If a symbol documents simply "RFC" then it refers to an IETF RFC. If the symbol is an RFC from a different organization, it should document the organization or project, e.g. "Rust RFC 2436" or "ISO/IEC 23009."

Index

Examples

Constants

View Source
const (
	IfModifiedSince   = "If-Modified-Since" // RFC7232§3.3
	LastModified      = "Last-Modified"     // RFC7232§2.2
	ETagHeader        = "ETag"
	IfMatch           = "If-Match"
	IfUnmodifiedSince = "If-Unmodified-Since"
	Date              = "Date"
	ETagVersion       = 1
)
View Source
const (
	AcceptEncoding     = "Accept-Encoding"     // RFC7231§5.3.4
	CacheControl       = "Cache-Control"       // RFC7234§5.2
	ContentDisposition = "Content-Disposition" // RFC6266
	ContentEncoding    = "Content-Encoding"    // RFC7231§3.1.2.2
	ContentType        = "Content-Type"        // RFC7231§3.1.1.5
	PermissionsPolicy  = "Permissions-Policy"  // W3C "Permissions Policy"
	Server             = "Server"              // RFC7231§7.4.2
	UserAgent          = "User-Agent"          // RFC7231§5.5.3
	Vary               = "Vary"                // RFC7231§7.1.4
)

These are the names of HTTP Headers, for convenience and so that typos are caught at compile-time.

View Source
const (
	ApplicationJSON           = "application/json"         // RFC4627§6
	ApplicationOctetStream    = "application/octet-stream" // RFC2046§4.5.2
	ContentTypeMultiPartMixed = "multipart/mixed"          // RFC1341§7.2
	ContentTypeTextPlain      = "text/plain"               // RFC2046§4.1
	ContentTypeURIList        = "text/uri-list"            // RFC2483§5
	Gzip                      = "gzip"                     // RFC7230§4.2.3
)

These are (some) valid values for content encoding and MIME types, for convenience and so that typos are caught at compile-time.

View Source
const LastModifiedFormat = "Mon, 02 Jan 2006 15:04:05 MST" // RFC1123

LastModifiedFormat is the format used by dates in the HTTP Last-Modified header.

Variables

View Source
var CacheableRequestMethods = map[string]struct{}{
	http.MethodGet:  {},
	http.MethodHead: {},
}

CacheableRequestMethods is the list of all request methods which elicit cache-able responses.

CacheableResponseCodes provides fast lookup of whether a HTTP response code is cache-able by default.

View Source
var MIME_CSS = MimeType{
	Name:       "text/css",
	Parameters: map[string]string{"charset": "utf-8"},
}

MIME_CSS is a pre-defined MimeType for CSS data.

View Source
var MIME_HTML = MimeType{
	Name:       "text/html",
	Parameters: map[string]string{"charset": "utf-8"},
}

MIME_HTML is a pre-defined MimeType for HTML data.

View Source
var MIME_JS = MimeType{
	Name:       "text/javascript",
	Parameters: map[string]string{"charset": "utf-8"},
}

MIME_JS is a pre-defined MimeType for JavaScript data.

View Source
var MIME_JSON = MimeType{
	Name:       "application/json",
	Parameters: map[string]string{},
}

MIME_JSON is a pre-defined MimeType for JSON data.

View Source
var MIME_PLAINTEXT = MimeType{
	Name:       "text/plain",
	Parameters: map[string]string{"charset": "utf-8"},
}

MIME_PLAINTEXT is a pre-defined MimeType for plain text data.

View Source
var ValidHTTPCodes = map[int]struct{}{
	http.StatusContinue:           {},
	http.StatusSwitchingProtocols: {},
	http.StatusProcessing:         {},
	http.StatusEarlyHints:         {},

	http.StatusOK:                   {},
	http.StatusCreated:              {},
	http.StatusAccepted:             {},
	http.StatusNonAuthoritativeInfo: {},
	http.StatusNoContent:            {},
	http.StatusResetContent:         {},
	http.StatusPartialContent:       {},
	http.StatusMultiStatus:          {},
	http.StatusAlreadyReported:      {},
	http.StatusIMUsed:               {},

	http.StatusMultipleChoices:   {},
	http.StatusMovedPermanently:  {},
	http.StatusFound:             {},
	http.StatusSeeOther:          {},
	http.StatusNotModified:       {},
	http.StatusUseProxy:          {},
	306:                          {},
	http.StatusTemporaryRedirect: {},
	http.StatusPermanentRedirect: {},

	http.StatusBadRequest:                   {},
	http.StatusUnauthorized:                 {},
	http.StatusPaymentRequired:              {},
	http.StatusForbidden:                    {},
	http.StatusNotFound:                     {},
	http.StatusMethodNotAllowed:             {},
	http.StatusNotAcceptable:                {},
	http.StatusProxyAuthRequired:            {},
	http.StatusRequestTimeout:               {},
	http.StatusConflict:                     {},
	http.StatusGone:                         {},
	http.StatusLengthRequired:               {},
	http.StatusPreconditionFailed:           {},
	http.StatusRequestEntityTooLarge:        {},
	http.StatusRequestURITooLong:            {},
	http.StatusUnsupportedMediaType:         {},
	http.StatusRequestedRangeNotSatisfiable: {},
	http.StatusExpectationFailed:            {},
	http.StatusTeapot:                       {},
	http.StatusMisdirectedRequest:           {},
	http.StatusUnprocessableEntity:          {},
	http.StatusLocked:                       {},
	http.StatusFailedDependency:             {},
	http.StatusTooEarly:                     {},
	http.StatusUpgradeRequired:              {},
	http.StatusPreconditionRequired:         {},
	http.StatusTooManyRequests:              {},
	http.StatusRequestHeaderFieldsTooLarge:  {},
	http.StatusUnavailableForLegalReasons:   {},

	http.StatusInternalServerError:           {},
	http.StatusNotImplemented:                {},
	http.StatusBadGateway:                    {},
	http.StatusServiceUnavailable:            {},
	http.StatusGatewayTimeout:                {},
	http.StatusHTTPVersionNotSupported:       {},
	http.StatusVariantAlsoNegotiates:         {},
	http.StatusInsufficientStorage:           {},
	http.StatusLoopDetected:                  {},
	http.StatusNotExtended:                   {},
	http.StatusNetworkAuthenticationRequired: {},
}

ValidHTTPCodes provides fast lookup of whether a HTTP response code is valid.

Functions

func AcceptsGzip

func AcceptsGzip(r *http.Request) bool

AcceptsGzip returns whether r accepts gzip encoding, per RFC7231§5.3.4.

func CanCache

func CanCache(reqMethod string, reqHeaders http.Header, respCode int, respHeaders http.Header, strictRFC bool) bool

CanCache returns whether an object can be cached per RFC 7234, based on the request headers, response headers, and response code.

If strictRFC is false, this ignores request headers denying cacheability such as `no-cache`, in order to protect origins. TODO add options to ignore/violate request Cache-Control (to protect origins)

func ETag

func ETag(t time.Time) string

ETag takes the last time the object was modified, and returns an ETag string. Note the string is the complete header value, including quotes. ETags must be quoted strings.

func FormatHTTPDate

func FormatHTTPDate(t time.Time) string

FormatHTTPDate formats t as an RFC7231§7.1.1 HTTP-date.

func FreshFor

func FreshFor(respHeaders http.Header, respCC CacheControlMap, reqTime, respTime time.Time) time.Duration

FreshFor gives a duration for which an HTTP response may still be cached - from the time of the request.

respHeaders is the collection of headers passed in the original response respCC is the parsed Cache-Control header that was present in the original response reqTime is the time at which the request was made respTime is the time at which the original response was received

func GetHTTPDate

func GetHTTPDate(headers http.Header, key string) (time.Time, bool)

GetHTTPDate is a helper function which gets an HTTP date from the given map (which is typically a `http.Header` or `CacheControl`.

Returns false if the given key doesn't exist in the map, or if the value isn't a valid HTTP Date per RFC2616§3.3.

Example
hdrs := http.Header{}
hdrs.Set("Date", "Tue, 30 Jun 2020 19:07:15 GMT")

t, ok := GetHTTPDate(hdrs, "Date")
if !ok {
	fmt.Println("Failed to get date")
	return
}

fmt.Printf("%s\n", t)
Output:

2020-06-30 19:07:15 +0000 GMT

func GetHTTPDeltaSeconds

func GetHTTPDeltaSeconds(m map[string][]string, key string) (time.Duration, bool)

GetHTTPDeltaSeconds is a helper function which gets an HTTP Delta Seconds from the given map (which is typically a `http.Header` or `CacheControl`). Returns false if the given key doesn't exist in the map, or if the value isn't a valid Delta Seconds per RFC2616§3.3.2.

func GetUnmodifiedTime

func GetUnmodifiedTime(h http.Header) (time.Time, bool)

GetUnmodifiedTime gets the latest time out of the Etags (if present), or the If-Unmodified-Since (if present)

func ParseETag

func ParseETag(e string) (time.Time, error)

ParseETag takes a complete ETag header string, including the quotes (if the client correctly set them), and returns the last modified time encoded in the ETag.

func ParseETags

func ParseETags(eTags []string) (time.Time, bool)

ParseETags the latest time of any valid ETag, and whether a valid ETag was found.

func ParseHTTPDate

func ParseHTTPDate(d string) (time.Time, bool)

ParseHTTPDate parses the given RFC7231§7.1.1 HTTP-date.

Example
t, ok := ParseHTTPDate("Tue, 30 Jun 2020 19:07:15 GMT")
if !ok {
	fmt.Println("Failed to parse date")
	return
}

fmt.Printf("%s\n", t)
Output:

2020-06-30 19:07:15 +0000 GMT

func SortMimeTypes

func SortMimeTypes(m []MimeType)

SortMimeTypes sorts the passed MimeTypes according to their "quality value". See MimeType.Less for more information on how MimeTypes are compared.

Example
// Normally don't do this, but for the sake of brevity in an example I will
mimes := []MimeType{
	MimeType{
		"text/html",
		map[string]string{},
	},
	MimeType{
		"text/xml",
		map[string]string{"q": "0.9"},
	},
	MimeType{
		"text/*",
		map[string]string{"q": "0.9"},
	},
	MimeType{
		"*/*",
		map[string]string{},
	},
}

SortMimeTypes(mimes)
for _, m := range mimes {
	fmt.Printf("%s, ", m)
}
fmt.Println()
Output:

text/html, */*, text/xml; q=0.9, text/*; q=0.9,

Types

type CacheControlMap

type CacheControlMap map[string]string

CacheControlMap is the parameters found in an HTTP Cache-Control header, each mapped to its specified value.

func ParseCacheControl

func ParseCacheControl(h http.Header) CacheControlMap

ParseCacheControl parses the Cache-Control header from the headers object, and returns the parsed map of cache control directives.

TODO verify Header/CacheControl are properly CanonicalCase/lowercase. Put cache-control text in constants?

Example
hdrs := http.Header{}

hdrs.Set(CacheControl, "no-store, no-cache, must-revalidate, post-check=0, pre-check=0")
cchm := ParseCacheControl(hdrs)
fmt.Println(cchm.Has("no-store"))
fmt.Println(cchm.Has("no-cache"))
fmt.Println(cchm.Has("must-revalidate"))
fmt.Println(cchm["post-check"])
fmt.Println(cchm["pre-check"])
Output:

true
true
true
0
0

func (CacheControlMap) Has

func (ccm CacheControlMap) Has(param string) bool

Has returns whether or not the CacheControlMap contains the named parameter.

Example
hdrs := http.Header{}
hdrs.Set(CacheControl, "no-cache")

ccm := ParseCacheControl(hdrs)
if ccm.Has("no-cache") {
	fmt.Println("Has 'no-cache'")
}
if ccm.Has("no-store") {
	fmt.Println("Has 'no-store'")
}
Output:

Has 'no-cache'

func (CacheControlMap) String

func (ccm CacheControlMap) String() string

String implements the Stringer interface by returning a textual representation of the CacheControlMap.

type EmailAddress

type EmailAddress struct{ mail.Address }

EmailAddress is an alias of net/mail.Address that implements JSON encoding and decoding, as well as scanning from database driver values.

func (EmailAddress) MarshalJSON

func (a EmailAddress) MarshalJSON() ([]byte, error)

MarshalJSON implements the encoding/json.Marshaler interface

func (*EmailAddress) Scan

func (a *EmailAddress) Scan(src interface{}) error

Scan implements the database/sql.Scanner interface

func (*EmailAddress) UnmarshalJSON

func (a *EmailAddress) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the encoding/json.Unmarshaler interface

type MimeType

type MimeType struct {
	// Name is the full name of the MIME Type, e.g. 'application/json'.
	// Usually for printing, it's better to call MimeType.String
	Name string
	// Parameters contains a map of provided parameter names to corresponding values. Note that for
	// MimeTypes constructed with NewMimeType, this will always be initialized, even when empty.
	Parameters map[string]string
}

MimeType represents a "Media Type" as defined by RFC6838, along with some ease-of-use functionality.

Note that this structure is in no way guaranteed to represent a *real* MIME Type, only one that is *syntactically valid*. The hope is that it will make content negotiation easier for developers, and should not be considered a security measure by any standard.

func MimeTypesFromAccept

func MimeTypesFromAccept(a string) ([]MimeType, error)

MimeTypesFromAccept constructs a *sorted* list of MimeTypes from the provided text, which is assumed to be from an HTTP 'Accept' header. The list is sorted using to SortMimeTypes.

If a is an empty string, this will return an empty slice and no error.

Example
const acceptLine = "text/html,text/xml;q=0.9,text/*;q=0.9,*/*"
mimes, err := MimeTypesFromAccept(acceptLine)
if err != nil {
	fmt.Println(err.Error())
	return
}

for _, m := range mimes {
	fmt.Printf("%s, ", m)
}
fmt.Println()
Output:

text/html, */*, text/xml; q=0.9, text/*; q=0.9,

func NewMimeType

func NewMimeType(v string) (m MimeType, err error)

NewMimeType creates a new MimeType, initializing its Parameters map.

If v cannot be parsed as a valid MIME (by mime.ParseMediaType), returns an error.

Example
m, err := NewMimeType("text/plain;charset=utf-8")
if err != nil {
	fmt.Println(err.Error())
	return
}

fmt.Println("Name:", m.Name, "Parameters:", m.Parameters)
Output:

Name: text/plain Parameters: map[charset:utf-8]

func (MimeType) Charset

func (m MimeType) Charset() string

Charset retrieves the "charset" parameter of a MimeType.

Returns an empty string if no charset exists in the parameters, or if the parameters themselves are not initialized.

Example
m, err := NewMimeType("text/plain;charset=utf-8")
if err != nil {
	fmt.Println(err.Error())
	return
}
c := m.Charset() // it's okay to ignore this error, because I was a good boy and used NewMimeType
fmt.Println(c)
Output:

utf-8

func (MimeType) Facet

func (m MimeType) Facet() string

Facet returns the MimeType's "facet" if one exists, otherwise an empty string.

Example
m, err := NewMimeType("text/vnd.plain+crlf;charset=utf-8")
if err != nil {
	fmt.Println(err.Error())
	return
}
fmt.Println(m.Facet())
Output:

vnd

func (MimeType) Less

func (m MimeType) Less(o MimeType) bool

Less checks whether or not this MimeType is "less than" some other MimeType, o.

This is done using a comparison of "quality value" of the two MimeTypes, as specified in RFC7231.

See Also: The MDN documentation on "quality value" comparisons: https://developer.mozilla.org/en-US/docs/Glossary/Quality_values

Example
one, err := NewMimeType("text/plain")
if err != nil {
	fmt.Println(err.Error())
	return
}

two, err := NewMimeType("text/*")
if err != nil {
	fmt.Println(err.Error())
	return
}

three, err := NewMimeType("text/plain;q=0.9")
if err != nil {
	fmt.Println(err.Error())
	return
}

fmt.Println(one.Less(two), two.Less(three), one.Less(three))
Output:

false false false

func (MimeType) Quality

func (m MimeType) Quality() float64

Quality retrieves and parses the "quality" parameter of a MimeType.

As specified in RFC7231, the quality parameter's name is "q", not actually "quality". To obtain a literal "quality" parameter value, access MimeType.Parameters directly. MimeTypes with no "q" parameter implicitly have a "quality" of 1.0.

Example
m, err := NewMimeType("text/plain;charset=utf-8")
if err != nil {
	fmt.Println(err.Error())
	return
}
q := m.Quality()
fmt.Print(q)

m.Parameters["q"] = "0.9"
q = m.Quality()
fmt.Println("", q)
Output:

1 0.9

func (MimeType) Satisfy

func (m MimeType) Satisfy(o MimeType) bool

Satisfy checks whether or not the MimeType "satifies" some other MimeType, o.

Note that this does not check if the two are literally the *same*. Specifically, if the Type or SubType of the given MimeType o is the special '*' name, then this will instead check whether or not this MimeType can *satisfy* the other according to RFC7231. This means that this satisfaction check is NOT associative - that is a.Satisfy(b) does not imply b.Satisfy(a).

See Also: The MDN documentation on the Accept Header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept

Example
m, err := NewMimeType("text/plain")
if err != nil {
	fmt.Println(err.Error())
	return
}

o, err := NewMimeType("text/*")
if err != nil {
	fmt.Println(err.Error())
	return
}

fmt.Println(m.Satisfy(o), o.Satisfy(m))
Output:

true false

func (MimeType) String

func (m MimeType) String() string

String implements the Stringer interface using mime.FormatMediaType.

Example
m, err := NewMimeType("text/plain;foo=bar;charset=utf-8")
if err != nil {
	fmt.Println(err.Error())
	return
}
fmt.Println(m)
Output:

text/plain; charset=utf-8; foo=bar

func (MimeType) SubType

func (m MimeType) SubType() string

SubType returns only the "sub" type of a MimeType.

Example
m, err := NewMimeType("text/vnd.plain+crlf;charset=utf-8")
if err != nil {
	fmt.Println(err.Error())
	return
}
fmt.Println(m.SubType())
Output:

vnd.plain+crlf

func (MimeType) Syntax

func (m MimeType) Syntax() string

Syntax returns the MimeType's "syntax suffix" if one exists, otherwise an empty string.

Example
m, err := NewMimeType("text/vnd.plain+crlf;charset=utf-8")
if err != nil {
	fmt.Println(err.Error())
	return
}
fmt.Println(m.Syntax())
Output:

crlf

func (MimeType) Type

func (m MimeType) Type() string

Type returns only the "main" type of a MimeType.

Example
m, err := NewMimeType("text/plain;charset=utf-8")
if err != nil {
	fmt.Println(err.Error())
	return
}
fmt.Println(m.Type())
Output:

text

type Reuse

type Reuse int

Reuse is an "enumerated" type describing the necessary behavior of a cache with regard to its cached objects.

const (
	// ReuseCan indicates that the cached response may be served.
	ReuseCan Reuse = iota
	// ReuseCannot indicates that the cached response must not be served.
	ReuseCannot
	// ReuseMustRevalidate indicates that the cached response must be
	// revalidated, and cannot be served stale if revalidation fails for some
	// reason.
	ReuseMustRevalidate
	// ReuseMustRevalidateCanStale indicates the response must be revalidated,
	// but if the parent cannot be reached, may be served stale, per
	// RFC7234§4.2.4.
	ReuseMustRevalidateCanStale
)

func CanReuseStored

func CanReuseStored(
	reqHeaders http.Header,
	respHeaders http.Header,
	reqCC CacheControlMap,
	respCC CacheControlMap,
	respReqHeaders http.Header,
	reqTime time.Time,
	respTime time.Time,
	strictRFC bool,
) Reuse

CanReuseStored checks the constraints in RFC7234§4.

func (Reuse) String

func (r Reuse) String() string

String implements the fmt.Stringer interface by returning the name of the Reuse constant the value indicates.

type URL

type URL struct{ url.URL }

URL is an alias of net/url.URL that implements JSON encoding and decoding, as well as scanning from database driver values.

func (URL) MarshalJSON

func (u URL) MarshalJSON() ([]byte, error)

MarshalJSON implements the encoding/json.Marshaler interface

func (*URL) Scan

func (u *URL) Scan(src interface{}) error

Scan implements the database/sql.Scanner interface

func (*URL) UnmarshalJSON

func (u *URL) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the encoding/json.Unmarshaler interface

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL