| 
									
										
										
										
											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-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | package testrig | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2023-03-08 13:57:41 +01:00
										 |  |  | 	"encoding/xml" | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2022-08-31 17:31:21 +02:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:12:26 +01:00
										 |  |  | 	"github.com/superseriousbusiness/activity/pub" | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 	"github.com/superseriousbusiness/activity/streams" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/activity/streams/vocab" | 
					
						
							| 
									
										
										
										
											2025-01-23 17:18:23 +00:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/ap" | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/federation" | 
					
						
							| 
									
										
										
										
											2022-11-11 12:18:38 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/httpclient" | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | 
					
						
							| 
									
										
										
										
											2023-03-01 18:26:53 +00:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/state" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/transport" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	applicationJSON         = "application/json" | 
					
						
							|  |  |  | 	applicationActivityJSON = "application/activity+json" | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 	textCSV                 = "text/csv" | 
					
						
							|  |  |  | 	textPlain               = "text/plain" | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | // NewTestTransportController returns a test transport controller with the given http client. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Obviously for testing purposes you should not be making actual http calls to other servers. | 
					
						
							|  |  |  | // To obviate this, use the function NewMockHTTPClient in this package to return a mock http | 
					
						
							|  |  |  | // client that doesn't make any remote calls but just returns whatever you tell it to. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Unlike the other test interfaces provided in this package, you'll probably want to call this function | 
					
						
							|  |  |  | // PER TEST rather than per suite, so that the do function can be set on a test by test (or even more granular) | 
					
						
							|  |  |  | // basis. | 
					
						
							| 
									
										
										
										
											2024-04-02 12:12:26 +01:00
										 |  |  | func NewTestTransportController(state *state.State, client pub.HttpClient) transport.Controller { | 
					
						
							| 
									
										
										
										
											2023-03-08 13:57:41 +01:00
										 |  |  | 	return transport.NewController(state, NewTestFederatingDB(state), &federation.Clock{}, client) | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | type MockHTTPClient struct { | 
					
						
							|  |  |  | 	do func(req *http.Request) (*http.Response, error) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-03 20:03:05 +00:00
										 |  |  | 	TestRemoteStatuses    map[string]vocab.ActivityStreamsNote | 
					
						
							|  |  |  | 	TestRemotePeople      map[string]vocab.ActivityStreamsPerson | 
					
						
							|  |  |  | 	TestRemoteGroups      map[string]vocab.ActivityStreamsGroup | 
					
						
							|  |  |  | 	TestRemoteServices    map[string]vocab.ActivityStreamsService | 
					
						
							|  |  |  | 	TestRemoteAttachments map[string]RemoteAttachmentFile | 
					
						
							|  |  |  | 	TestRemoteEmojis      map[string]vocab.TootEmoji | 
					
						
							|  |  |  | 	TestTombstones        map[string]*gtsmodel.Tombstone | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 17:31:21 +02:00
										 |  |  | 	SentMessages sync.Map | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewMockHTTPClient returns a client that conforms to the pub.HttpClient interface. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // If do is nil, then a standard response set will be mocked out, which includes models stored in the | 
					
						
							|  |  |  | // testrig, and webfinger responses as well. | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | // If do is not nil, then the given do function will always be used, which allows callers | 
					
						
							|  |  |  | // to customize how the client is mocked. | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | // | 
					
						
							|  |  |  | // Note that you should never ever make ACTUAL http calls with this thing. | 
					
						
							| 
									
										
										
										
											2025-01-23 17:18:23 +00:00
										 |  |  | func NewMockHTTPClient(do func(req *http.Request) (*http.Response, error), relativeMediaPath string, extraPeople ...ap.Accountable) *MockHTTPClient { | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 	mockHTTPClient := &MockHTTPClient{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if do != nil { | 
					
						
							|  |  |  | 		mockHTTPClient.do = do | 
					
						
							|  |  |  | 		return mockHTTPClient | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-03 20:03:05 +00:00
										 |  |  | 	mockHTTPClient.TestRemoteStatuses = NewTestFediStatuses() | 
					
						
							|  |  |  | 	mockHTTPClient.TestRemotePeople = NewTestFediPeople() | 
					
						
							|  |  |  | 	mockHTTPClient.TestRemoteGroups = NewTestFediGroups() | 
					
						
							|  |  |  | 	mockHTTPClient.TestRemoteServices = NewTestFediServices() | 
					
						
							|  |  |  | 	mockHTTPClient.TestRemoteAttachments = NewTestFediAttachments(relativeMediaPath) | 
					
						
							|  |  |  | 	mockHTTPClient.TestRemoteEmojis = NewTestFediEmojis() | 
					
						
							|  |  |  | 	mockHTTPClient.TestTombstones = NewTestTombstones() | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mockHTTPClient.do = func(req *http.Request) (*http.Response, error) { | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		var ( | 
					
						
							|  |  |  | 			responseCode          = http.StatusNotFound | 
					
						
							|  |  |  | 			responseBytes         = []byte(`{"error":"404 not found"}`) | 
					
						
							|  |  |  | 			responseContentType   = applicationJSON | 
					
						
							|  |  |  | 			responseContentLength = len(responseBytes) | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 			extraHeaders          = make(map[string]string, 0) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 			reqURLString          = req.URL.String() | 
					
						
							|  |  |  | 		) | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if req.Method == http.MethodPost { | 
					
						
							|  |  |  | 			b, err := io.ReadAll(req.Body) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-31 17:31:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 			if sI, loaded := mockHTTPClient.SentMessages.LoadOrStore(reqURLString, [][]byte{b}); loaded { | 
					
						
							| 
									
										
										
										
											2022-08-31 17:31:21 +02:00
										 |  |  | 				s, ok := sI.([][]byte) | 
					
						
							|  |  |  | 				if !ok { | 
					
						
							|  |  |  | 					panic("SentMessages entry wasn't [][]byte") | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				s = append(s, b) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 				mockHTTPClient.SentMessages.Store(reqURLString, s) | 
					
						
							| 
									
										
										
										
											2022-08-31 17:31:21 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 			responseBytes = []byte(`{"ok":"accepted"}`) | 
					
						
							|  |  |  | 			responseContentType = applicationJSON | 
					
						
							|  |  |  | 			responseContentLength = len(responseBytes) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else if strings.Contains(reqURLString, ".well-known/webfinger") { | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 			responseCode, responseBytes, responseContentType, responseContentLength, extraHeaders = WebfingerResponse(req) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else if strings.Contains(reqURLString, ".weird-webfinger-location/webfinger") { | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 			responseCode, responseBytes, responseContentType, responseContentLength, extraHeaders = WebfingerResponse(req) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else if strings.Contains(reqURLString, ".well-known/host-meta") { | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 			responseCode, responseBytes, responseContentType, responseContentLength, extraHeaders = HostMetaResponse(req) | 
					
						
							|  |  |  | 		} else if strings.Contains(reqURLString, "lists.example.org") { | 
					
						
							|  |  |  | 			responseCode, responseBytes, responseContentType, responseContentLength, extraHeaders = DomainPermissionSubscriptionResponse(req) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else if note, ok := mockHTTPClient.TestRemoteStatuses[reqURLString]; ok { | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 			// the request is for a note that we have stored | 
					
						
							|  |  |  | 			noteI, err := streams.Serialize(note) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			noteJSON, err := json.Marshal(noteI) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 			responseBytes = noteJSON | 
					
						
							|  |  |  | 			responseContentType = applicationActivityJSON | 
					
						
							|  |  |  | 			responseContentLength = len(noteJSON) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else if person, ok := mockHTTPClient.TestRemotePeople[reqURLString]; ok { | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 			// the request is for a person that we have stored | 
					
						
							|  |  |  | 			personI, err := streams.Serialize(person) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			personJSON, err := json.Marshal(personI) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 			responseBytes = personJSON | 
					
						
							|  |  |  | 			responseContentType = applicationActivityJSON | 
					
						
							|  |  |  | 			responseContentLength = len(personJSON) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else if group, ok := mockHTTPClient.TestRemoteGroups[reqURLString]; ok { | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 			// the request is for a person that we have stored | 
					
						
							|  |  |  | 			groupI, err := streams.Serialize(group) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			groupJSON, err := json.Marshal(groupI) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 			responseBytes = groupJSON | 
					
						
							|  |  |  | 			responseContentType = applicationActivityJSON | 
					
						
							|  |  |  | 			responseContentLength = len(groupJSON) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else if service, ok := mockHTTPClient.TestRemoteServices[reqURLString]; ok { | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 			serviceI, err := streams.Serialize(service) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			serviceJSON, err := json.Marshal(serviceI) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 			responseBytes = serviceJSON | 
					
						
							|  |  |  | 			responseContentType = applicationActivityJSON | 
					
						
							|  |  |  | 			responseContentLength = len(serviceJSON) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else if emoji, ok := mockHTTPClient.TestRemoteEmojis[reqURLString]; ok { | 
					
						
							| 
									
										
										
										
											2022-09-26 11:56:01 +02:00
										 |  |  | 			emojiI, err := streams.Serialize(emoji) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			emojiJSON, err := json.Marshal(emojiI) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				panic(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 			responseBytes = emojiJSON | 
					
						
							|  |  |  | 			responseContentType = applicationActivityJSON | 
					
						
							|  |  |  | 			responseContentLength = len(emojiJSON) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else if attachment, ok := mockHTTPClient.TestRemoteAttachments[reqURLString]; ok { | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 			responseBytes = attachment.Data | 
					
						
							|  |  |  | 			responseContentType = attachment.ContentType | 
					
						
							|  |  |  | 			responseContentLength = len(attachment.Data) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else if _, ok := mockHTTPClient.TestTombstones[reqURLString]; ok { | 
					
						
							| 
									
										
										
										
											2022-11-11 12:18:38 +01:00
										 |  |  | 			responseCode = http.StatusGone | 
					
						
							|  |  |  | 			responseBytes = []byte{} | 
					
						
							|  |  |  | 			responseContentType = "text/html" | 
					
						
							|  |  |  | 			responseContentLength = 0 | 
					
						
							| 
									
										
										
										
											2023-09-12 11:43:12 +02:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			for _, person := range extraPeople { | 
					
						
							|  |  |  | 				// For any extra people, check if the | 
					
						
							|  |  |  | 				// request matches one of: | 
					
						
							|  |  |  | 				// | 
					
						
							|  |  |  | 				//   - Public key URI | 
					
						
							|  |  |  | 				//   - ActivityPub URI/id | 
					
						
							|  |  |  | 				//   - Web URL. | 
					
						
							|  |  |  | 				// | 
					
						
							|  |  |  | 				// Since this is a test environment, | 
					
						
							|  |  |  | 				// just assume all these values have | 
					
						
							|  |  |  | 				// been properly set. | 
					
						
							|  |  |  | 				if reqURLString == person.GetW3IDSecurityV1PublicKey().At(0).Get().GetJSONLDId().GetIRI().String() || | 
					
						
							|  |  |  | 					reqURLString == person.GetJSONLDId().GetIRI().String() || | 
					
						
							|  |  |  | 					reqURLString == person.GetActivityStreamsUrl().At(0).GetIRI().String() { | 
					
						
							|  |  |  | 					personI, err := streams.Serialize(person) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						panic(err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					personJSON, err := json.Marshal(personI) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						panic(err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					responseCode = http.StatusOK | 
					
						
							|  |  |  | 					responseBytes = personJSON | 
					
						
							|  |  |  | 					responseContentType = applicationActivityJSON | 
					
						
							|  |  |  | 					responseContentLength = len(personJSON) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-17 12:02:29 +01:00
										 |  |  | 		log.Debugf(nil, "returning response %s", string(responseBytes)) | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		reader := bytes.NewReader(responseBytes) | 
					
						
							|  |  |  | 		readCloser := io.NopCloser(reader) | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		header := http.Header{ | 
					
						
							|  |  |  | 			"Content-Type": {responseContentType}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for k, v := range extraHeaders { | 
					
						
							|  |  |  | 			header.Add(k, v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		return &http.Response{ | 
					
						
							| 
									
										
										
										
											2023-05-21 17:59:14 +01:00
										 |  |  | 			Request:       req, | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 			StatusCode:    responseCode, | 
					
						
							|  |  |  | 			Body:          readCloser, | 
					
						
							|  |  |  | 			ContentLength: int64(responseContentLength), | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 			Header:        header, | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		}, nil | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 	return mockHTTPClient | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	return m.do(req) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-28 16:45:21 +01:00
										 |  |  | func (m *MockHTTPClient) DoSigned(req *http.Request, sign httpclient.SignFunc) (*http.Response, error) { | 
					
						
							|  |  |  | 	return m.do(req) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | func HostMetaResponse(req *http.Request) ( | 
					
						
							|  |  |  | 	responseCode int, | 
					
						
							|  |  |  | 	responseBytes []byte, | 
					
						
							|  |  |  | 	responseContentType string, | 
					
						
							|  |  |  | 	responseContentLength int, | 
					
						
							|  |  |  | 	extraHeaders map[string]string, | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2023-03-08 13:57:41 +01:00
										 |  |  | 	var hm *apimodel.HostMeta | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if req.URL.String() == "https://misconfigured-instance.com/.well-known/host-meta" { | 
					
						
							|  |  |  | 		hm = &apimodel.HostMeta{ | 
					
						
							|  |  |  | 			XMLNS: "http://docs.oasis-open.org/ns/xri/xrd-1.0", | 
					
						
							|  |  |  | 			Link: []apimodel.Link{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Rel:      "lrdd", | 
					
						
							|  |  |  | 					Type:     "application/xrd+xml", | 
					
						
							|  |  |  | 					Template: "https://misconfigured-instance.com/.weird-webfinger-location/webfinger?resource={uri}", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if hm == nil { | 
					
						
							|  |  |  | 		log.Debugf(nil, "hostmeta response not available for %s", req.URL) | 
					
						
							|  |  |  | 		responseCode = http.StatusNotFound | 
					
						
							|  |  |  | 		responseBytes = []byte(``) | 
					
						
							|  |  |  | 		responseContentType = "application/xml" | 
					
						
							|  |  |  | 		responseContentLength = len(responseBytes) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hmXML, err := xml.Marshal(hm) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		panic(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	responseCode = http.StatusOK | 
					
						
							|  |  |  | 	responseBytes = hmXML | 
					
						
							|  |  |  | 	responseContentType = "application/xml" | 
					
						
							|  |  |  | 	responseContentLength = len(hmXML) | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | func WebfingerResponse(req *http.Request) ( | 
					
						
							|  |  |  | 	responseCode int, | 
					
						
							|  |  |  | 	responseBytes []byte, | 
					
						
							|  |  |  | 	responseContentType string, | 
					
						
							|  |  |  | 	responseContentLength int, | 
					
						
							|  |  |  | 	extraHeaders map[string]string, | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 	var wfr *apimodel.WellKnownResponse | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch req.URL.String() { | 
					
						
							| 
									
										
										
										
											2023-03-08 13:57:41 +01:00
										 |  |  | 	case "https://unknown-instance.com/.well-known/webfinger?resource=acct%3Asome_group%40unknown-instance.com": | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		wfr = &apimodel.WellKnownResponse{ | 
					
						
							|  |  |  | 			Subject: "acct:some_group@unknown-instance.com", | 
					
						
							|  |  |  | 			Links: []apimodel.Link{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Rel:  "self", | 
					
						
							|  |  |  | 					Type: applicationActivityJSON, | 
					
						
							|  |  |  | 					Href: "https://unknown-instance.com/groups/some_group", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-08 13:57:41 +01:00
										 |  |  | 	case "https://owncast.example.org/.well-known/webfinger?resource=acct%3Argh%40owncast.example.org": | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		wfr = &apimodel.WellKnownResponse{ | 
					
						
							|  |  |  | 			Subject: "acct:rgh@example.org", | 
					
						
							|  |  |  | 			Links: []apimodel.Link{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Rel:  "self", | 
					
						
							|  |  |  | 					Type: applicationActivityJSON, | 
					
						
							|  |  |  | 					Href: "https://owncast.example.org/federation/user/rgh", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-08 13:57:41 +01:00
										 |  |  | 	case "https://unknown-instance.com/.well-known/webfinger?resource=acct%3Abrand_new_person%40unknown-instance.com": | 
					
						
							| 
									
										
										
										
											2024-07-26 13:11:07 +02:00
										 |  |  | 		wfr = &apimodel.WellKnownResponse{ | 
					
						
							|  |  |  | 			Subject: "acct:brand_new_person@unknown-instance.com", | 
					
						
							|  |  |  | 			Links: []apimodel.Link{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Rel:  "self", | 
					
						
							|  |  |  | 					Type: applicationActivityJSON, | 
					
						
							|  |  |  | 					Href: "https://unknown-instance.com/users/brand_new_person", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case "https://xn--pnycde-zxa8b.example.org/.well-known/webfinger?resource=acct%3Abrand_new_person%40xn--pnycde-zxa8b.example.org": | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		wfr = &apimodel.WellKnownResponse{ | 
					
						
							|  |  |  | 			Subject: "acct:brand_new_person@unknown-instance.com", | 
					
						
							|  |  |  | 			Links: []apimodel.Link{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Rel:  "self", | 
					
						
							|  |  |  | 					Type: applicationActivityJSON, | 
					
						
							|  |  |  | 					Href: "https://unknown-instance.com/users/brand_new_person", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-08 13:57:41 +01:00
										 |  |  | 	case "https://turnip.farm/.well-known/webfinger?resource=acct%3Aturniplover6969%40turnip.farm": | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		wfr = &apimodel.WellKnownResponse{ | 
					
						
							|  |  |  | 			Subject: "acct:turniplover6969@turnip.farm", | 
					
						
							|  |  |  | 			Links: []apimodel.Link{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Rel:  "self", | 
					
						
							|  |  |  | 					Type: applicationActivityJSON, | 
					
						
							|  |  |  | 					Href: "https://turnip.farm/users/turniplover6969", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-08 13:57:41 +01:00
										 |  |  | 	case "https://fossbros-anonymous.io/.well-known/webfinger?resource=acct%3Afoss_satan%40fossbros-anonymous.io": | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		wfr = &apimodel.WellKnownResponse{ | 
					
						
							|  |  |  | 			Subject: "acct:foss_satan@fossbros-anonymous.io", | 
					
						
							|  |  |  | 			Links: []apimodel.Link{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Rel:  "self", | 
					
						
							|  |  |  | 					Type: applicationActivityJSON, | 
					
						
							| 
									
										
										
										
											2023-02-03 20:03:05 +00:00
										 |  |  | 					Href: "http://fossbros-anonymous.io/users/foss_satan", | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-08 13:57:41 +01:00
										 |  |  | 	case "https://example.org/.well-known/webfinger?resource=acct%3ASome_User%40example.org": | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		wfr = &apimodel.WellKnownResponse{ | 
					
						
							| 
									
										
										
										
											2022-11-30 17:44:02 +01:00
										 |  |  | 			Subject: "acct:Some_User@example.org", | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 			Links: []apimodel.Link{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Rel:  "self", | 
					
						
							|  |  |  | 					Type: applicationActivityJSON, | 
					
						
							| 
									
										
										
										
											2022-11-30 17:44:02 +01:00
										 |  |  | 					Href: "https://example.org/users/Some_User", | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-08 13:57:41 +01:00
										 |  |  | 	case "https://misconfigured-instance.com/.weird-webfinger-location/webfinger?resource=acct%3Asomeone%40misconfigured-instance.com": | 
					
						
							|  |  |  | 		wfr = &apimodel.WellKnownResponse{ | 
					
						
							|  |  |  | 			Subject: "acct:someone@misconfigured-instance.com", | 
					
						
							|  |  |  | 			Links: []apimodel.Link{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Rel:  "self", | 
					
						
							|  |  |  | 					Type: applicationActivityJSON, | 
					
						
							|  |  |  | 					Href: "https://misconfigured-instance.com/users/someone", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if wfr == nil { | 
					
						
							| 
									
										
										
										
											2023-02-17 12:02:29 +01:00
										 |  |  | 		log.Debugf(nil, "webfinger response not available for %s", req.URL) | 
					
						
							| 
									
										
										
										
											2022-06-11 11:01:34 +02:00
										 |  |  | 		responseCode = http.StatusNotFound | 
					
						
							|  |  |  | 		responseBytes = []byte(`{"error":"not found"}`) | 
					
						
							|  |  |  | 		responseContentType = applicationJSON | 
					
						
							|  |  |  | 		responseContentLength = len(responseBytes) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wfrJSON, err := json.Marshal(wfr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		panic(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	responseCode = http.StatusOK | 
					
						
							|  |  |  | 	responseBytes = wfrJSON | 
					
						
							|  |  |  | 	responseContentType = applicationJSON | 
					
						
							|  |  |  | 	responseContentLength = len(wfrJSON) | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | func DomainPermissionSubscriptionResponse(req *http.Request) ( | 
					
						
							|  |  |  | 	responseCode int, | 
					
						
							|  |  |  | 	responseBytes []byte, | 
					
						
							|  |  |  | 	responseContentType string, | 
					
						
							|  |  |  | 	responseContentLength int, | 
					
						
							|  |  |  | 	extraHeaders map[string]string, | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const ( | 
					
						
							| 
									
										
										
										
											2025-01-20 09:56:00 +00:00
										 |  |  | 		lastModified        = "Sat, 21 Sep 2024 22:00:00 GMT" | 
					
						
							|  |  |  | 		futureLastModified  = "Mon, 15 Jan 2300 22:00:00 GMT" | 
					
						
							|  |  |  | 		garbageLastModified = "I LIKE BIG BUTTS AND I CANNOT LIE" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 		csvResp = `#domain,#severity,#reject_media,#reject_reports,#public_comment,#obfuscate | 
					
						
							|  |  |  | bumfaces.net,suspend,false,false,big jerks,false | 
					
						
							|  |  |  | peepee.poopoo,suspend,false,false,harassment,false | 
					
						
							|  |  |  | nothanks.com,suspend,false,false,,false` | 
					
						
							| 
									
										
										
										
											2025-01-20 09:56:00 +00:00
										 |  |  | 		csvRespETag = "\"bigbums6969\"" | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		textResp = `bumfaces.net | 
					
						
							|  |  |  | peepee.poopoo | 
					
						
							|  |  |  | nothanks.com` | 
					
						
							| 
									
										
										
										
											2025-01-20 09:56:00 +00:00
										 |  |  | 		textRespETag = "\"this is a legit etag i swear\"" | 
					
						
							|  |  |  | 		jsonResp     = `[ | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  |   { | 
					
						
							|  |  |  |     "domain": "bumfaces.net", | 
					
						
							|  |  |  |     "suspended_at": "2020-05-13T13:29:12.000Z", | 
					
						
							|  |  |  |     "public_comment": "big jerks" | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     "domain": "peepee.poopoo", | 
					
						
							|  |  |  |     "suspended_at": "2020-05-13T13:29:12.000Z", | 
					
						
							|  |  |  |     "public_comment": "harassment" | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     "domain": "nothanks.com", | 
					
						
							|  |  |  |     "suspended_at": "2020-05-13T13:29:12.000Z" | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ]` | 
					
						
							| 
									
										
										
										
											2025-01-20 09:56:00 +00:00
										 |  |  | 		jsonRespETag = "\"don't modify me daddy\"" | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch req.URL.String() { | 
					
						
							|  |  |  | 	case "https://lists.example.org/baddies.csv": | 
					
						
							| 
									
										
										
										
											2025-01-20 09:56:00 +00:00
										 |  |  | 		extraHeaders = map[string]string{ | 
					
						
							|  |  |  | 			"Last-Modified": lastModified, | 
					
						
							|  |  |  | 			"ETag":          csvRespETag, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 		if req.Header.Get("If-None-Match") == csvRespETag { | 
					
						
							|  |  |  | 			// Cached. | 
					
						
							|  |  |  | 			responseCode = http.StatusNotModified | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			responseBytes = []byte(csvResp) | 
					
						
							|  |  |  | 			responseContentType = textCSV | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		responseContentLength = len(responseBytes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case "https://lists.example.org/baddies.txt": | 
					
						
							| 
									
										
										
										
											2025-01-20 09:56:00 +00:00
										 |  |  | 		extraHeaders = map[string]string{ | 
					
						
							|  |  |  | 			"Last-Modified": lastModified, | 
					
						
							|  |  |  | 			"ETag":          textRespETag, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 		if req.Header.Get("If-None-Match") == textRespETag { | 
					
						
							|  |  |  | 			// Cached. | 
					
						
							|  |  |  | 			responseCode = http.StatusNotModified | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			responseBytes = []byte(textResp) | 
					
						
							|  |  |  | 			responseContentType = textPlain | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		responseContentLength = len(responseBytes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case "https://lists.example.org/baddies.json": | 
					
						
							| 
									
										
										
										
											2025-01-20 09:56:00 +00:00
										 |  |  | 		extraHeaders = map[string]string{ | 
					
						
							|  |  |  | 			"Last-Modified": lastModified, | 
					
						
							|  |  |  | 			"ETag":          jsonRespETag, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 		if req.Header.Get("If-None-Match") == jsonRespETag { | 
					
						
							|  |  |  | 			// Cached. | 
					
						
							|  |  |  | 			responseCode = http.StatusNotModified | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			responseBytes = []byte(jsonResp) | 
					
						
							|  |  |  | 			responseContentType = applicationJSON | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		responseContentLength = len(responseBytes) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-20 09:56:00 +00:00
										 |  |  | 	case "https://lists.example.org/baddies.csv?future=true": | 
					
						
							|  |  |  | 		extraHeaders = map[string]string{ | 
					
						
							|  |  |  | 			// Provide the future last modified value. | 
					
						
							|  |  |  | 			"Last-Modified": futureLastModified, | 
					
						
							|  |  |  | 			"ETag":          csvRespETag, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if req.Header.Get("If-None-Match") == csvRespETag { | 
					
						
							|  |  |  | 			// Cached. | 
					
						
							|  |  |  | 			responseCode = http.StatusNotModified | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			responseBytes = []byte(csvResp) | 
					
						
							|  |  |  | 			responseContentType = textCSV | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		responseContentLength = len(responseBytes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case "https://lists.example.org/baddies.csv?garbage=true": | 
					
						
							|  |  |  | 		extraHeaders = map[string]string{ | 
					
						
							|  |  |  | 			// Provide the garbage last modified value. | 
					
						
							|  |  |  | 			"Last-Modified": garbageLastModified, | 
					
						
							|  |  |  | 			"ETag":          csvRespETag, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if req.Header.Get("If-None-Match") == csvRespETag { | 
					
						
							|  |  |  | 			// Cached. | 
					
						
							|  |  |  | 			responseCode = http.StatusNotModified | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			responseBytes = []byte(csvResp) | 
					
						
							|  |  |  | 			responseContentType = textCSV | 
					
						
							|  |  |  | 			responseCode = http.StatusOK | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		responseContentLength = len(responseBytes) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-08 11:29:40 +01:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		responseCode = http.StatusNotFound | 
					
						
							|  |  |  | 		responseBytes = []byte(`{"error":"not found"}`) | 
					
						
							|  |  |  | 		responseContentType = applicationJSON | 
					
						
							|  |  |  | 		responseContentLength = len(responseBytes) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } |