handlers.go (3742B)
1 package pub 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "net/http" 9 10 "github.com/superseriousbusiness/activity/streams" 11 ) 12 13 var ErrNotFound = errors.New("go-fed/activity: ActivityStreams data not found") 14 15 // HandlerFunc determines whether an incoming HTTP request is an ActivityStreams 16 // GET request, and if so attempts to serve ActivityStreams data. 17 // 18 // If an error is returned, then the calling function is responsible for writing 19 // to the ResponseWriter as part of error handling. 20 // 21 // If 'isASRequest' is false and there is no error, then the calling function 22 // may continue processing the request, and the HandlerFunc will not have 23 // written anything to the ResponseWriter. For example, a webpage may be served 24 // instead. 25 // 26 // If 'isASRequest' is true and there is no error, then the HandlerFunc 27 // successfully served the request and wrote to the ResponseWriter. 28 // 29 // Callers are responsible for authorized access to this resource. 30 type HandlerFunc func(c context.Context, w http.ResponseWriter, r *http.Request) (isASRequest bool, err error) 31 32 // NewActivityStreamsHandler creates a HandlerFunc to serve ActivityStreams 33 // requests which are coming from other clients or servers that wish to obtain 34 // an ActivityStreams representation of data. 35 // 36 // Strips retrieved ActivityStreams values of sensitive fields ('bto' and 'bcc') 37 // before responding with them. Sets the appropriate HTTP status code for 38 // Tombstone Activities as well. 39 // 40 // Defaults to supporting content to be retrieved by HTTPS only. 41 func NewActivityStreamsHandler(db Database, clock Clock) HandlerFunc { 42 return NewActivityStreamsHandlerScheme(db, clock, "https") 43 } 44 45 // NewActivityStreamsHandlerScheme creates a HandlerFunc to serve 46 // ActivityStreams requests which are coming from other clients or servers that 47 // wish to obtain an ActivityStreams representation of data provided by the 48 // specified protocol scheme. 49 // 50 // Strips retrieved ActivityStreams values of sensitive fields ('bto' and 'bcc') 51 // before responding with them. Sets the appropriate HTTP status code for 52 // Tombstone Activities as well. 53 // 54 // Specifying the "scheme" allows for retrieving ActivityStreams content with 55 // identifiers such as HTTP, HTTPS, or other protocol schemes. 56 // 57 // Returns ErrNotFound when the database does not retrieve any data and no 58 // errors occurred during retrieval. 59 func NewActivityStreamsHandlerScheme(db Database, clock Clock, scheme string) HandlerFunc { 60 return func(c context.Context, w http.ResponseWriter, r *http.Request) (isASRequest bool, err error) { 61 // Do nothing if it is not an ActivityPub GET request 62 if !isActivityPubGet(r) { 63 return 64 } 65 isASRequest = true 66 id := requestId(r, scheme) 67 68 var unlock func() 69 70 // Lock and obtain a copy of the requested ActivityStreams value 71 unlock, err = db.Lock(c, id) 72 if err != nil { 73 return 74 } 75 // WARNING: Unlock not deferred 76 t, err := db.Get(c, id) 77 unlock() // unlock even on error 78 if err != nil { 79 return 80 } 81 // Unlock must have been called by this point and in every 82 // branch above 83 if t == nil { 84 err = ErrNotFound 85 return 86 } 87 // Remove sensitive fields. 88 clearSensitiveFields(t) 89 // Serialize the fetched value. 90 m, err := streams.Serialize(t) 91 if err != nil { 92 return 93 } 94 raw, err := json.Marshal(m) 95 if err != nil { 96 return 97 } 98 // Construct the response. 99 addResponseHeaders(w.Header(), clock, raw) 100 // Write the response. 101 if streams.IsOrExtendsActivityStreamsTombstone(t) { 102 w.WriteHeader(http.StatusGone) 103 } else { 104 w.WriteHeader(http.StatusOK) 105 } 106 n, err := w.Write(raw) 107 if err != nil { 108 return 109 } else if n != len(raw) { 110 err = fmt.Errorf("only wrote %d of %d bytes", n, len(raw)) 111 return 112 } 113 return 114 } 115 }