summaryrefslogtreecommitdiff
path: root/vendor/github.com/superseriousbusiness/activity/pub/handlers.go
blob: bc7eeb9d814ccb9553ccc081beac8f656572e9ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package pub

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	"github.com/superseriousbusiness/activity/streams"
)

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)
		// 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
		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
	}
}