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() }