go-httpaccept/httpaccept.go

137 lines
3.1 KiB
Go

package httpaccept
import (
"sourcecode.social/reiver/go-httpaccept/mediarange"
"strings"
)
// HTTPAccept represents the values of the Accept headers of an HTTP request.
//
// Note that an HTTP request can have multople Accept headers.
type HTTPAccept struct {
mediaranges []mediarange.MediaRange
}
// Create creates an HTTPAccept.
//
// For example:
//
// // Accept: application/activity+json
// accept := httpaccept.Create(
// mediarange.Create("application", "activity+json"), // application/activity+json
// )
//
// // Accept: image/png,image/gif
// accept := httpaccept.Create(
// mediarange.Create("image", "png"), // image/png
// mediarange.Create("image", "gif"), // image/gif
// )
func Create(mediaranges ...mediarange.MediaRange) HTTPAccept {
return HTTPAccept{
mediaranges:mediaranges,
}
}
// Parse parses a list of strings and returns an HTTPAccept.
//
// For example:
//
// mr, err := mediaaccept.Parse("text/html,application/xhtml+xml, application/xml , image/avif,image/webp, */*")
//
// And:
//
// mr, err := mediaaccept.Parse("image/png,image/gif", "text/html,text/plain", "*/*")
func Parse(headers ...string) (HTTPAccept, error) {
var accepts []mediarange.MediaRange
for _, header := range headers {
//@TODO: This is an inefficient way of doing this.
acceptValues := strings.Split(header, ",") //@TODO: This isn't correct, as per the specification, but it will probably work most of the time.
for _, acceptValue := range acceptValues {
mediaRange, err := mediarange.Parse(acceptValue)
if nil != err {
return HTTPAccept{}, err
}
accepts = append(accepts, mediaRange)
}
}
return Create(accepts...), nil
}
// Negotiate figures out if there is an acceptable media-type, and if there is returns the "best" one.
//
// For example:
//
// var accept httpaccept.HTTPAccept
//
// // ...
//
// contentType, err := accept.Negotiate("text/gemini", "text/html", "application/activity+json")
func (receiver HTTPAccept) Negotiate(mediatypes ...string) (string, error) {
var temp []string
{
temp, mediatypes = mediatypes, nil
for _, mediatype := range temp {
a := strings.SplitN(mediatype, ";", 2)
if len(a) < 1 {
continue
}
mediatypes = append(mediatypes, a[0])
}
}
for _, mediarange := range receiver.mediaranges {
for _, mediatype := range mediatypes {
if mediarange.Match(mediatype) {
return mediatype, nil
}
}
}
return "", internalNotAcceptable{temp}
}
// String returns the serialized HTTP Accept header.
//
// For example:
//
// // "Accept: text/html,application/xhtml+xml\r\n"
// s := httpaccept.Create(mediarange.Create("text", "html"), mediarange.Create("application","xhtml+xml")).String()
func (receiver HTTPAccept) String() string {
var mediaranges []mediarange.MediaRange = receiver.mediaranges
if len(mediaranges) < 1 {
return ""
}
var buffer strings.Builder
{
buffer.WriteString("Accept: ")
for i, mediarange := range receiver.mediaranges {
if 0 != i {
buffer.WriteRune(',')
}
buffer.WriteString(mediarange.String())
}
buffer.WriteString("\r\n")
}
return buffer.String()
}