2021-12-11 17:50:00 +01:00
/ *
GoToSocial
Copyright ( C ) 2021 GoToSocial Authors admin @ gotosocial . org
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU Affero General Public License for more details .
You should have received a copy of the GNU Affero General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
* /
package api
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
)
// Offer represents an offered mime-type.
type Offer string
const (
AppJSON Offer = ` application/json ` // AppJSON is the mime type for 'application/json'.
AppActivityJSON Offer = ` application/activity+json ` // AppActivityJSON is the mime type for 'application/activity+json'.
AppActivityLDJSON Offer = ` application/ld+json; profile="https://www.w3.org/ns/activitystreams" ` // AppActivityLDJSON is the mime type for 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
TextHTML Offer = ` text/html ` // TextHTML is the mime type for 'text/html'.
)
// ActivityPubAcceptHeaders represents the Accept headers mentioned here:
// https://www.w3.org/TR/activitypub/#retrieving-objects
var ActivityPubAcceptHeaders = [ ] Offer {
AppActivityJSON ,
AppActivityLDJSON ,
}
// JSONAcceptHeaders is a slice of offers that just contains application/json types.
var JSONAcceptHeaders = [ ] Offer {
AppJSON ,
}
// HTMLAcceptHeaders is a slice of offers that just contains text/html types.
var HTMLAcceptHeaders = [ ] Offer {
TextHTML ,
}
// NegotiateAccept takes the *gin.Context from an incoming request, and a
// slice of Offers, and performs content negotiation for the given request
// with the given content-type offers. It will return a string representation
// of the first suitable content-type, or an error if something goes wrong or
2021-12-20 11:08:54 +01:00
// a suitable content-type cannot be matched.
2021-12-11 17:50:00 +01:00
//
// For example, if the request in the *gin.Context has Accept headers of value
// [application/json, text/html], and the provided offers are of value
// [application/json, application/xml], then the returned string will be
// 'application/json', which indicates the content-type that should be returned.
//
2021-12-20 11:08:54 +01:00
// If the length of offers is 0, then an error will be returned, so this function
// should only be called in places where format negotiation is actually needed.
//
// If there are no Accept headers in the request, then the first offer will be returned,
// under the assumption that it's better to serve *something* than error out completely.
2021-12-11 17:50:00 +01:00
//
// Callers can use the offer slices exported in this package as shortcuts for
// often-used Accept types.
//
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation#server-driven_content_negotiation
func NegotiateAccept ( c * gin . Context , offers ... Offer ) ( string , error ) {
if len ( offers ) == 0 {
return "" , errors . New ( "no format offered" )
}
strings := [ ] string { }
for _ , o := range offers {
strings = append ( strings , string ( o ) )
}
2021-12-20 11:08:54 +01:00
accepts := c . Request . Header . Values ( "Accept" )
if len ( accepts ) == 0 {
// there's no accept header set, just return the first offer
return strings [ 0 ] , nil
}
2021-12-11 17:50:00 +01:00
format := c . NegotiateFormat ( strings ... )
if format == "" {
return "" , fmt . Errorf ( "no format can be offered for requested Accept header(s) %s; this endpoint offers %s" , accepts , offers )
}
return format , nil
}