2019-02-15 06:28:46 +09:00
|
|
|
package pub
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"github.com/go-fed/activity/streams"
|
|
|
|
"net/http"
|
|
|
|
)
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
type HandlerFunc func(c context.Context, w http.ResponseWriter, r *http.Request) (isASRequest bool, err error)
|
|
|
|
|
|
|
|
// AuthenticateFunc is responsible for authenticating and authorizing a GET
|
|
|
|
// ActivityStreams request.
|
|
|
|
//
|
|
|
|
// If an error is returned, 'shouldReturn' is ignored. It is expected that the
|
|
|
|
// calling function will write to the ResponseWriter while handling the error.
|
|
|
|
//
|
|
|
|
// If 'shouldReturn' is true and no error is returned, then this function
|
|
|
|
// immediately returns to the caller. This function is responsible for writing
|
|
|
|
// the authentication or authorization failure on the ResponseWriter.
|
|
|
|
//
|
|
|
|
// If 'shouldReturn' is false and no error is returned, then processing of the
|
|
|
|
// request will continue.
|
|
|
|
type AuthenticateFunc func(c context.Context, w http.ResponseWriter, r *http.Request) (shouldReturn 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.
|
|
|
|
func NewActivityStreamsHandler(authFn AuthenticateFunc, db Database, clock Clock) 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
|
|
|
|
// Authenticate the request
|
|
|
|
var shouldReturn bool
|
|
|
|
if shouldReturn, err = authFn(c, w, r); err != nil {
|
|
|
|
return
|
|
|
|
} else if shouldReturn {
|
|
|
|
return
|
|
|
|
}
|
2019-04-13 07:00:35 +09:00
|
|
|
id := requestId(r)
|
2019-02-15 06:28:46 +09:00
|
|
|
// Lock and obtain a copy of the requested ActivityStreams value
|
|
|
|
err = db.Lock(c, id)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// WARNING: Unlock not deferred
|
|
|
|
t, err := db.Get(c, id)
|
|
|
|
if err != nil {
|
|
|
|
db.Unlock(c, id)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
db.Unlock(c, id)
|
|
|
|
// Unlock must have been called by this point and in every
|
|
|
|
// branch above
|
|
|
|
//
|
|
|
|
// Remove sensitive fields.
|
|
|
|
clearSensitiveFields(t)
|
|
|
|
// Serialize the fetched value.
|
2019-08-10 00:29:08 +09:00
|
|
|
m, err := streams.Serialize(t)
|
2019-02-15 06:28:46 +09:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
raw, err := json.Marshal(m)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Construct the response.
|
|
|
|
addResponseHeaders(w.Header(), clock, raw)
|
|
|
|
// Write the response.
|
2019-03-14 05:19:38 +09:00
|
|
|
if streams.IsOrExtendsActivityStreamsTombstone(t) {
|
2019-02-15 06:28:46 +09:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|