| 
									
										
										
										
											2021-08-12 21:03:24 +02:00
										 |  |  | package pub | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-25 15:15:36 +02:00
										 |  |  | 	"code.superseriousbusiness.org/activity/streams" | 
					
						
							| 
									
										
										
										
											2021-08-12 21:03:24 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ErrNotFound = errors.New("go-fed/activity: ActivityStreams data not found") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // HandlerFunc determines whether an incoming HTTP request is an ActivityStreams | 
					
						
							|  |  |  | // GET request, and if so attempts to serve ActivityStreams data. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // If an error is returned, then the calling function is responsible for writing | 
					
						
							|  |  |  | // to the ResponseWriter as part of error handling. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // If 'isASRequest' is false and there is no error, then the calling function | 
					
						
							|  |  |  | // may continue processing the request, and the HandlerFunc will not have | 
					
						
							|  |  |  | // written anything to the ResponseWriter. For example, a webpage may be served | 
					
						
							|  |  |  | // instead. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // If 'isASRequest' is true and there is no error, then the HandlerFunc | 
					
						
							|  |  |  | // successfully served the request and wrote to the ResponseWriter. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Callers are responsible for authorized access to this resource. | 
					
						
							|  |  |  | type HandlerFunc func(c context.Context, w http.ResponseWriter, r *http.Request) (isASRequest bool, err error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewActivityStreamsHandler creates a HandlerFunc to serve ActivityStreams | 
					
						
							|  |  |  | // requests which are coming from other clients or servers that wish to obtain | 
					
						
							|  |  |  | // an ActivityStreams representation of data. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Strips retrieved ActivityStreams values of sensitive fields ('bto' and 'bcc') | 
					
						
							|  |  |  | // before responding with them. Sets the appropriate HTTP status code for | 
					
						
							|  |  |  | // Tombstone Activities as well. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Defaults to supporting content to be retrieved by HTTPS only. | 
					
						
							|  |  |  | func NewActivityStreamsHandler(db Database, clock Clock) HandlerFunc { | 
					
						
							|  |  |  | 	return NewActivityStreamsHandlerScheme(db, clock, "https") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewActivityStreamsHandlerScheme creates a HandlerFunc to serve | 
					
						
							|  |  |  | // ActivityStreams requests which are coming from other clients or servers that | 
					
						
							|  |  |  | // wish to obtain an ActivityStreams representation of data provided by the | 
					
						
							|  |  |  | // specified protocol scheme. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Strips retrieved ActivityStreams values of sensitive fields ('bto' and 'bcc') | 
					
						
							|  |  |  | // before responding with them. Sets the appropriate HTTP status code for | 
					
						
							|  |  |  | // Tombstone Activities as well. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Specifying the "scheme" allows for retrieving ActivityStreams content with | 
					
						
							|  |  |  | // identifiers such as HTTP, HTTPS, or other protocol schemes. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Returns ErrNotFound when the database does not retrieve any data and no | 
					
						
							|  |  |  | // errors occurred during retrieval. | 
					
						
							|  |  |  | func NewActivityStreamsHandlerScheme(db Database, clock Clock, scheme string) HandlerFunc { | 
					
						
							|  |  |  | 	return func(c context.Context, w http.ResponseWriter, r *http.Request) (isASRequest bool, err error) { | 
					
						
							|  |  |  | 		// Do nothing if it is not an ActivityPub GET request | 
					
						
							|  |  |  | 		if !isActivityPubGet(r) { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		isASRequest = true | 
					
						
							|  |  |  | 		id := requestId(r, scheme) | 
					
						
							| 
									
										
										
										
											2022-04-28 10:18:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		var unlock func() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-12 21:03:24 +02:00
										 |  |  | 		// Lock and obtain a copy of the requested ActivityStreams value | 
					
						
							| 
									
										
										
										
											2022-04-28 10:18:27 +01:00
										 |  |  | 		unlock, err = db.Lock(c, id) | 
					
						
							| 
									
										
										
										
											2021-08-12 21:03:24 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// WARNING: Unlock not deferred | 
					
						
							|  |  |  | 		t, err := db.Get(c, id) | 
					
						
							| 
									
										
										
										
											2022-04-28 10:18:27 +01:00
										 |  |  | 		unlock() // unlock even on error | 
					
						
							| 
									
										
										
										
											2021-08-12 21:03:24 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Unlock must have been called by this point and in every | 
					
						
							|  |  |  | 		// branch above | 
					
						
							|  |  |  | 		if t == nil { | 
					
						
							|  |  |  | 			err = ErrNotFound | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Remove sensitive fields. | 
					
						
							|  |  |  | 		clearSensitiveFields(t) | 
					
						
							|  |  |  | 		// Serialize the fetched value. | 
					
						
							|  |  |  | 		m, err := streams.Serialize(t) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		raw, err := json.Marshal(m) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Construct the response. | 
					
						
							|  |  |  | 		addResponseHeaders(w.Header(), clock, raw) | 
					
						
							|  |  |  | 		// Write the response. | 
					
						
							|  |  |  | 		if streams.IsOrExtendsActivityStreamsTombstone(t) { | 
					
						
							|  |  |  | 			w.WriteHeader(http.StatusGone) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			w.WriteHeader(http.StatusOK) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		n, err := w.Write(raw) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} else if n != len(raw) { | 
					
						
							|  |  |  | 			err = fmt.Errorf("only wrote %d of %d bytes", n, len(raw)) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |