| 
									
										
										
										
											2023-03-12 16:00:57 +01:00
										 |  |  | // GoToSocial | 
					
						
							|  |  |  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // 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/>. | 
					
						
							| 
									
										
										
										
											2021-12-11 17:50:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-02 13:10:50 +01:00
										 |  |  | package util | 
					
						
							| 
									
										
										
										
											2021-12-11 17:50:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/gin-gonic/gin" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ActivityPubAcceptHeaders represents the Accept headers mentioned here: | 
					
						
							| 
									
										
										
										
											2022-06-08 20:38:03 +02:00
										 |  |  | var ActivityPubAcceptHeaders = []MIME{ | 
					
						
							| 
									
										
										
										
											2021-12-11 17:50:00 +01:00
										 |  |  | 	AppActivityJSON, | 
					
						
							|  |  |  | 	AppActivityLDJSON, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // JSONAcceptHeaders is a slice of offers that just contains application/json types. | 
					
						
							| 
									
										
										
										
											2022-06-08 20:38:03 +02:00
										 |  |  | var JSONAcceptHeaders = []MIME{ | 
					
						
							|  |  |  | 	AppJSON, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-04 12:28:50 +02:00
										 |  |  | // WebfingerJSONAcceptHeaders is a slice of offers that prefers the | 
					
						
							|  |  |  | // jrd+json content type, but will be chill and fall back to app/json. | 
					
						
							|  |  |  | // This is to be used specifically for webfinger responses. | 
					
						
							|  |  |  | // See https://www.rfc-editor.org/rfc/rfc7033#section-10.2 | 
					
						
							|  |  |  | var WebfingerJSONAcceptHeaders = []MIME{ | 
					
						
							|  |  |  | 	AppJRDJSON, | 
					
						
							|  |  |  | 	AppJSON, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-12 08:16:41 +00:00
										 |  |  | // JSONOrHTMLAcceptHeaders is a slice of offers that prefers AppJSON and will | 
					
						
							|  |  |  | // fall back to HTML if necessary. This is useful for error handling, since it can | 
					
						
							| 
									
										
										
										
											2022-06-08 20:38:03 +02:00
										 |  |  | // be used to serve a nice HTML page if the caller accepts that, or just JSON if not. | 
					
						
							| 
									
										
										
										
											2023-05-12 08:16:41 +00:00
										 |  |  | var JSONOrHTMLAcceptHeaders = []MIME{ | 
					
						
							| 
									
										
										
										
											2021-12-11 17:50:00 +01:00
										 |  |  | 	AppJSON, | 
					
						
							| 
									
										
										
										
											2023-05-12 08:16:41 +00:00
										 |  |  | 	TextHTML, | 
					
						
							| 
									
										
										
										
											2021-12-11 17:50:00 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // HTMLAcceptHeaders is a slice of offers that just contains text/html types. | 
					
						
							| 
									
										
										
										
											2022-06-08 20:38:03 +02:00
										 |  |  | var HTMLAcceptHeaders = []MIME{ | 
					
						
							| 
									
										
										
										
											2021-12-11 17:50:00 +01:00
										 |  |  | 	TextHTML, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-08 20:38:03 +02:00
										 |  |  | // HTMLOrActivityPubHeaders matches text/html first, then activitypub types. | 
					
						
							|  |  |  | // This is useful for user URLs that a user might go to in their browser. | 
					
						
							|  |  |  | // https://www.w3.org/TR/activitypub/#retrieving-objects | 
					
						
							|  |  |  | var HTMLOrActivityPubHeaders = []MIME{ | 
					
						
							|  |  |  | 	TextHTML, | 
					
						
							|  |  |  | 	AppActivityJSON, | 
					
						
							|  |  |  | 	AppActivityLDJSON, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-09 18:55:45 +01:00
										 |  |  | var HostMetaHeaders = []MIME{ | 
					
						
							|  |  |  | 	AppXMLXRD, | 
					
						
							|  |  |  | 	AppXML, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-11 17:50:00 +01:00
										 |  |  | // 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 | 
					
						
							| 
									
										
										
										
											2022-06-08 20:38:03 +02:00
										 |  |  | func NegotiateAccept(c *gin.Context, offers ...MIME) (string, error) { | 
					
						
							| 
									
										
										
										
											2021-12-11 17:50:00 +01:00
										 |  |  | 	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 | 
					
						
							|  |  |  | } |