Update interface definitions and wrapper improvements.

Wrapping default behavior can now be overridden.
This commit is contained in:
Cory Slep 2019-02-15 21:03:43 +01:00
parent bd8220a56c
commit cc83b751a1
11 changed files with 224 additions and 113 deletions

View File

@ -13,6 +13,10 @@ import (
// Protocol), client-to-server (Social API), or both. The Actor represents the // Protocol), client-to-server (Social API), or both. The Actor represents the
// server in either use case. // server in either use case.
// //
// An actor can be created by calling NewSocialActor (only the Social Protocol
// is supported), NewFederatingActor (only the Federating Protocol is
// supported), NewActor (both are supported), or NewCustomActor (neither are).
//
// Not all Actors have the same behaviors depending on the constructor used to // Not all Actors have the same behaviors depending on the constructor used to
// create them. Refer to the constructor's documentation to determine the exact // create them. Refer to the constructor's documentation to determine the exact
// behavior of the Actor on an application. // behavior of the Actor on an application.

View File

@ -124,11 +124,11 @@ func NewActor(c CommonBehavior,
// for the Social Protocol, Federating Protocol, or both. // for the Social Protocol, Federating Protocol, or both.
// //
// It still uses the library as a high-level scaffold, which has the benefit of // It still uses the library as a high-level scaffold, which has the benefit of
// allowing applications to grow into a custom solution without having to // allowing applications to grow into a custom ActivityPub solution without
// refactor the code that passes HTTP requests into the Actor. // having to refactor the code that passes HTTP requests into the Actor.
// //
// It is possible to create a DelegateActor that is not ActivityPub compliant. // It is possible to create a DelegateActor that is not ActivityPub compliant.
// Use with care. // Use with due care.
func NewCustomActor(delegate DelegateActor, func NewCustomActor(delegate DelegateActor,
enableSocialProtocol, enableFederatedProtocol bool, enableSocialProtocol, enableFederatedProtocol bool,
clock Clock) Actor { clock Clock) Actor {

View File

@ -7,6 +7,9 @@ import (
// Common contains functions required for both the Social API and Federating // Common contains functions required for both the Social API and Federating
// Protocol. // Protocol.
//
// It is passed to the library as a dependency injection from the client
// application.
type CommonBehavior interface { type CommonBehavior interface {
// AuthenticateGetInbox delegates the authentication of a GET to an // AuthenticateGetInbox delegates the authentication of a GET to an
// inbox. // inbox.

View File

@ -10,14 +10,17 @@ import (
// DelegateActor contains the detailed interface an application must satisfy in // DelegateActor contains the detailed interface an application must satisfy in
// order to implement the ActivityPub specification. // order to implement the ActivityPub specification.
// //
// Note that an implementation of this interface is implicitly provided in the
// calls to NewActor, NewSocialActor, and NewFederatingActor.
//
// Implementing the DelegateActor requires familiarity with the ActivityPub // Implementing the DelegateActor requires familiarity with the ActivityPub
// specification, it does not a strong enough abstraction for the client // specification because it does not a strong enough abstraction for the client
// application to ignore the ActivityPub spec. It is very possible to implement // application to ignore the ActivityPub spec. It is very possible to implement
// this interface and build a foot-gun that trashes the fediverse without being // this interface and build a foot-gun that trashes the fediverse without being
// ActivityPub compliant. Please use with due consideration. // ActivityPub compliant. Please use with due consideration.
// //
// Alternatively, build an application that uses the parts of the pub library // Alternatively, build an application that uses the parts of the pub library
// that does not require implementing a DelegateActor so that the ActivityPub // that do not require implementing a DelegateActor so that the ActivityPub
// implementation is completely provided out of the box. // implementation is completely provided out of the box.
type DelegateActor interface { type DelegateActor interface {
// AuthenticatePostInbox delegates the authentication of a POST to an // AuthenticatePostInbox delegates the authentication of a POST to an
@ -144,8 +147,8 @@ type DelegateActor interface {
// //
// If an error is returned, it is returned to the caller of PostOutbox. // If an error is returned, it is returned to the caller of PostOutbox.
Deliver(c context.Context, outbox *url.URL, activity Activity) error Deliver(c context.Context, outbox *url.URL, activity Activity) error
// AuthenticatePostOutbox delegates the authentication of a POST to an // AuthenticatePostOutbox delegates the authentication and authorization
// outbox. // of a POST to an outbox.
// //
// Only called if the Social API is enabled. // Only called if the Social API is enabled.
// //

View File

@ -12,6 +12,9 @@ import (
// //
// It is only required if the client application wants to support the server-to- // It is only required if the client application wants to support the server-to-
// server, or federating, protocol. // server, or federating, protocol.
//
// It is passed to the library as a dependency injection from the client
// application.
type FederatingProtocol interface { type FederatingProtocol interface {
// AuthenticatePostInbox delegates the authentication of a POST to an // AuthenticatePostInbox delegates the authentication of a POST to an
// inbox. // inbox.
@ -46,12 +49,21 @@ type FederatingProtocol interface {
// to be processed. // to be processed.
Blocked(c context.Context, actorIRIs []*url.URL) (blocked bool, err error) Blocked(c context.Context, actorIRIs []*url.URL) (blocked bool, err error)
// Callbacks returns the application logic that handles ActivityStreams // Callbacks returns the application logic that handles ActivityStreams
// received from federating peers. Note that certain types of callbacks // received from federating peers.
// will be 'wrapped' with default behaviors supported natively by the
// library. Other callbacks compatible with streams.TypeResolver can
// be specified by 'other'.
// //
// Note that the functions in 'wrapped' cannot be provided in 'other'. // Note that certain types of callbacks will be 'wrapped' with default
// behaviors supported natively by the library. Other callbacks
// compatible with streams.TypeResolver can be specified by 'other'.
//
// For example, setting the 'Create' field in the
// FederatingWrappedCallbacks lets an application dependency inject
// additional behaviors they want to take place, including the default
// behavior supplied by this library. This is guaranteed to be compliant
// with the ActivityPub Social protocol.
//
// To override the default behavior, instead supply the function in
// 'other', which does not guarantee the application will be compliant
// with the ActivityPub Social Protocol.
Callbacks(c context.Context) (wrapped FederatingWrappedCallbacks, other []interface{}) Callbacks(c context.Context) (wrapped FederatingWrappedCallbacks, other []interface{})
// MaxInboxForwardingRecursionDepth determines how deep to search within // MaxInboxForwardingRecursionDepth determines how deep to search within
// an activity to determine if inbox forwarding needs to occur. // an activity to determine if inbox forwarding needs to occur.

View File

@ -135,63 +135,91 @@ type FederatingWrappedCallbacks struct {
newTransport func(c context.Context, actorBoxIRI *url.URL, gofedAgent string) (t Transport, err error) newTransport func(c context.Context, actorBoxIRI *url.URL, gofedAgent string) (t Transport, err error)
} }
// disjoint ensures that the functions given do not share a type signature with // callbacks returns the WrappedCallbacks members into a single interface slice
// the functions being wrapped in FederatingWrappedCallbacks. // for use in streams.Resolver callbacks.
func (w FederatingWrappedCallbacks) disjoint(fns []interface{}) error { //
// TODO: Instead, if provided in "other" it should override this behavior. // If the given functions have a type that collides with the default behavior,
var s string // then disable our default behavior
func (w FederatingWrappedCallbacks) callbacks(fns []interface{}) []interface{} {
enableCreate := true
enableUpdate := true
enableDelete := true
enableFollow := true
enableAccept := true
enableReject := true
enableAdd := true
enableRemove := true
enableLike := true
enableAnnounce := true
enableUndo := true
enableBlock := true
for _, fn := range fns { for _, fn := range fns {
switch fn.(type) { switch fn.(type) {
default: default:
// OK, no collision
continue continue
case func(context.Context, vocab.ActivityStreamsCreate) error: case func(context.Context, vocab.ActivityStreamsCreate) error:
s = "Create" enableCreate = false
case func(context.Context, vocab.ActivityStreamsUpdate) error: case func(context.Context, vocab.ActivityStreamsUpdate) error:
s = "Update" enableUpdate = false
case func(context.Context, vocab.ActivityStreamsDelete) error: case func(context.Context, vocab.ActivityStreamsDelete) error:
s = "Delete" enableDelete = false
case func(context.Context, vocab.ActivityStreamsFollow) error: case func(context.Context, vocab.ActivityStreamsFollow) error:
s = "Follow" enableFollow = false
case func(context.Context, vocab.ActivityStreamsAccept) error: case func(context.Context, vocab.ActivityStreamsAccept) error:
s = "Accept" enableAccept = false
case func(context.Context, vocab.ActivityStreamsReject) error: case func(context.Context, vocab.ActivityStreamsReject) error:
s = "Reject" enableReject = false
case func(context.Context, vocab.ActivityStreamsAdd) error: case func(context.Context, vocab.ActivityStreamsAdd) error:
s = "Add" enableAdd = false
case func(context.Context, vocab.ActivityStreamsRemove) error: case func(context.Context, vocab.ActivityStreamsRemove) error:
s = "Remove" enableRemove = false
case func(context.Context, vocab.ActivityStreamsLike) error: case func(context.Context, vocab.ActivityStreamsLike) error:
s = "Like" enableLike = false
case func(context.Context, vocab.ActivityStreamsAnnounce) error: case func(context.Context, vocab.ActivityStreamsAnnounce) error:
s = "Announce" enableAnnounce = false
case func(context.Context, vocab.ActivityStreamsUndo) error: case func(context.Context, vocab.ActivityStreamsUndo) error:
s = "Undo" enableUndo = false
case func(context.Context, vocab.ActivityStreamsBlock) error: case func(context.Context, vocab.ActivityStreamsBlock) error:
s = "Block" enableBlock = false
} }
return fmt.Errorf("callback function handling type %q conflicts with FederatingWrappedCallbacks", s)
} }
return nil if enableCreate {
} fns = append(fns, w.create)
// callbacks returns the WrappedCallbacks members into a single interface slice
// for use in streams.Resolver callbacks.
func (w FederatingWrappedCallbacks) callbacks() []interface{} {
return []interface{}{
w.create,
w.update,
w.deleteFn,
w.follow,
w.accept,
w.reject,
w.add,
w.remove,
w.like,
w.announce,
w.undo,
w.block,
} }
if enableUpdate {
fns = append(fns, w.update)
}
if enableDelete {
fns = append(fns, w.deleteFn)
}
if enableFollow {
fns = append(fns, w.follow)
}
if enableAccept {
fns = append(fns, w.accept)
}
if enableReject {
fns = append(fns, w.reject)
}
if enableAdd {
fns = append(fns, w.add)
}
if enableRemove {
fns = append(fns, w.remove)
}
if enableLike {
fns = append(fns, w.like)
}
if enableAnnounce {
fns = append(fns, w.announce)
}
if enableUndo {
fns = append(fns, w.undo)
}
if enableBlock {
fns = append(fns, w.block)
}
return fns
} }
// create implements the federating Create activity side effects. // create implements the federating Create activity side effects.

View File

@ -5,12 +5,12 @@ import (
"net/url" "net/url"
) )
// inReplyToer is an ActivityStreams type with a 'inReplyTo' property // inReplyToer is an ActivityStreams type with an 'inReplyTo' property
type inReplyToer interface { type inReplyToer interface {
GetActivityStreamsInReplyTo() vocab.ActivityStreamsInReplyToProperty GetActivityStreamsInReplyTo() vocab.ActivityStreamsInReplyToProperty
} }
// objecter is an ActivityStreams type with a 'object' property // objecter is an ActivityStreams type with an 'object' property
type objecter interface { type objecter interface {
GetActivityStreamsObject() vocab.ActivityStreamsObjectProperty GetActivityStreamsObject() vocab.ActivityStreamsObjectProperty
} }
@ -30,13 +30,13 @@ type hrefer interface {
GetActivityStreamsHref() vocab.ActivityStreamsHrefProperty GetActivityStreamsHref() vocab.ActivityStreamsHrefProperty
} }
// itemser is an ActivityStreams type with a 'items' property // itemser is an ActivityStreams type with an 'items' property
type itemser interface { type itemser interface {
GetActivityStreamsItems() vocab.ActivityStreamsItemsProperty GetActivityStreamsItems() vocab.ActivityStreamsItemsProperty
SetActivityStreamsItems(vocab.ActivityStreamsItemsProperty) SetActivityStreamsItems(vocab.ActivityStreamsItemsProperty)
} }
// orderedItemser is an ActivityStreams type with a 'orderedItems' property // orderedItemser is an ActivityStreams type with an 'orderedItems' property
type orderedItemser interface { type orderedItemser interface {
GetActivityStreamsOrderedItems() vocab.ActivityStreamsOrderedItemsProperty GetActivityStreamsOrderedItems() vocab.ActivityStreamsOrderedItemsProperty
SetActivityStreamsOrderedItems(vocab.ActivityStreamsOrderedItemsProperty) SetActivityStreamsOrderedItems(vocab.ActivityStreamsOrderedItemsProperty)
@ -47,7 +47,7 @@ type publisheder interface {
GetActivityStreamsPublished() vocab.ActivityStreamsPublishedProperty GetActivityStreamsPublished() vocab.ActivityStreamsPublishedProperty
} }
// updateder is an ActivityStreams type with a 'updateder' property // updateder is an ActivityStreams type with an 'updateder' property
type updateder interface { type updateder interface {
GetActivityStreamsUpdated() vocab.ActivityStreamsUpdatedProperty GetActivityStreamsUpdated() vocab.ActivityStreamsUpdatedProperty
} }
@ -76,18 +76,18 @@ type bccer interface {
SetActivityStreamsBcc(i vocab.ActivityStreamsBccProperty) SetActivityStreamsBcc(i vocab.ActivityStreamsBccProperty)
} }
// audiencer is an ActivityStreams type with a 'audience' property // audiencer is an ActivityStreams type with an 'audience' property
type audiencer interface { type audiencer interface {
GetActivityStreamsAudience() vocab.ActivityStreamsAudienceProperty GetActivityStreamsAudience() vocab.ActivityStreamsAudienceProperty
SetActivityStreamsAudience(i vocab.ActivityStreamsAudienceProperty) SetActivityStreamsAudience(i vocab.ActivityStreamsAudienceProperty)
} }
// inboxer is an ActivityStreams type with a 'inbox' property // inboxer is an ActivityStreams type with an 'inbox' property
type inboxer interface { type inboxer interface {
GetActivityStreamsInbox() vocab.ActivityStreamsInboxProperty GetActivityStreamsInbox() vocab.ActivityStreamsInboxProperty
} }
// attributedToer is an ActivityStreams type with a 'attributedTo' property // attributedToer is an ActivityStreams type with an 'attributedTo' property
type attributedToer interface { type attributedToer interface {
GetActivityStreamsAttributedTo() vocab.ActivityStreamsAttributedToProperty GetActivityStreamsAttributedTo() vocab.ActivityStreamsAttributedToProperty
SetActivityStreamsAttributedTo(i vocab.ActivityStreamsAttributedToProperty) SetActivityStreamsAttributedTo(i vocab.ActivityStreamsAttributedToProperty)
@ -105,7 +105,7 @@ type shareser interface {
SetActivityStreamsShares(i vocab.ActivityStreamsSharesProperty) SetActivityStreamsShares(i vocab.ActivityStreamsSharesProperty)
} }
// actorer is an ActivityStreams type with a 'actor' property // actorer is an ActivityStreams type with an 'actor' property
type actorer interface { type actorer interface {
GetActivityStreamsActor() vocab.ActivityStreamsActorProperty GetActivityStreamsActor() vocab.ActivityStreamsActorProperty
SetActivityStreamsActor(i vocab.ActivityStreamsActorProperty) SetActivityStreamsActor(i vocab.ActivityStreamsActorProperty)

View File

@ -102,10 +102,7 @@ func (a *sideEffectActor) PostInbox(c context.Context, inboxIRI *url.URL, activi
wrapped.db = a.db wrapped.db = a.db
wrapped.inboxIRI = inboxIRI wrapped.inboxIRI = inboxIRI
wrapped.newTransport = a.s2s.NewTransport wrapped.newTransport = a.s2s.NewTransport
if err = wrapped.disjoint(other); err != nil { res, err := streams.NewTypeResolver(wrapped.callbacks(other))
return err
}
res, err := streams.NewTypeResolver(append(wrapped.callbacks(), other...))
if err != nil { if err != nil {
return err return err
} }
@ -283,10 +280,7 @@ func (a *sideEffectActor) PostOutbox(c context.Context, activity Activity, outbo
wrapped.rawActivity = rawJSON wrapped.rawActivity = rawJSON
wrapped.clock = a.clock wrapped.clock = a.clock
wrapped.deliverable = &deliverable wrapped.deliverable = &deliverable
if e = wrapped.disjoint(other); e != nil { res, err := streams.NewTypeResolver(wrapped.callbacks(other))
return
}
res, err := streams.NewTypeResolver(append(wrapped.callbacks(), other...))
if err != nil { if err != nil {
return return
} }

View File

@ -11,6 +11,9 @@ import (
// //
// It is only required if the client application wants to support the client-to- // It is only required if the client application wants to support the client-to-
// server, or social, protocol. // server, or social, protocol.
//
// It is passed to the library as a dependency injection from the client
// application.
type SocialProtocol interface { type SocialProtocol interface {
// AuthenticatePostOutbox delegates the authentication of a POST to an // AuthenticatePostOutbox delegates the authentication of a POST to an
// outbox. // outbox.
@ -32,12 +35,21 @@ type SocialProtocol interface {
// to be processed. // to be processed.
AuthenticatePostOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (shouldReturn bool, err error) AuthenticatePostOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (shouldReturn bool, err error)
// Callbacks returns the application logic that handles ActivityStreams // Callbacks returns the application logic that handles ActivityStreams
// received from C2S clients. Note that certain types of callbacks // received from C2S clients.
// will be 'wrapped' with default behaviors supported natively by the
// library. Other callbacks compatible with streams.TypeResolver can
// be specified by 'other'.
// //
// Note that the functions in 'wrapped' cannot be provided in 'other'. // Note that certain types of callbacks will be 'wrapped' with default
// behaviors supported natively by the library. Other callbacks
// compatible with streams.TypeResolver can be specified by 'other'.
//
// For example, setting the 'Create' field in the SocialWrappedCallbacks
// lets an application dependency inject additional behaviors they want
// to take place, including the default behavior supplied by this
// library. This is guaranteed to be compliant with the ActivityPub
// Social protocol.
//
// To override the default behavior, instead supply the function in
// 'other', which does not guarantee the application will be compliant
// with the ActivityPub Social Protocol.
Callbacks(c context.Context) (wrapped SocialWrappedCallbacks, other []interface{}) Callbacks(c context.Context) (wrapped SocialWrappedCallbacks, other []interface{})
// GetOutbox returns the OrderedCollection inbox of the actor for this // GetOutbox returns the OrderedCollection inbox of the actor for this
// context. It is up to the implementation to provide the correct // context. It is up to the implementation to provide the correct

View File

@ -42,22 +42,35 @@ type SocialWrappedCallbacks struct {
// Add handles additional side effects for the Add ActivityStreams // Add handles additional side effects for the Add ActivityStreams
// type. // type.
// //
// TODO: Describe //
// The wrapping function will add the 'object' IRIs to a specific
// 'target' collection if the 'target' collection(s) live on this
// server.
Add func(context.Context, vocab.ActivityStreamsAdd) error Add func(context.Context, vocab.ActivityStreamsAdd) error
// Remove handles additional side effects for the Remove ActivityStreams // Remove handles additional side effects for the Remove ActivityStreams
// type. // type.
// //
// TODO: Describe // The wrapping function will remove all 'object' IRIs from a specific
// 'target' collection if the 'target' collection(s) live on this
// server.
Remove func(context.Context, vocab.ActivityStreamsRemove) error Remove func(context.Context, vocab.ActivityStreamsRemove) error
// Like handles additional side effects for the Like ActivityStreams // Like handles additional side effects for the Like ActivityStreams
// type. // type.
// //
// TODO: Describe // The wrapping function will add the objects on the activity to the
// "liked" collection of this actor.
Like func(context.Context, vocab.ActivityStreamsLike) error Like func(context.Context, vocab.ActivityStreamsLike) error
// Undo handles additional side effects for the Undo ActivityStreams // Undo handles additional side effects for the Undo ActivityStreams
// type. // type.
// //
// TODO: Describe //
// The wrapping function ensures the 'actor' on the 'Undo'
// is be the same as the 'actor' on all Activities being undone.
// It enforces that the actors on the Undo must correspond to all of the
// 'object' actors in some manner.
//
// It is expected that the application will implement the proper
// reversal of activities that are being undone.
Undo func(context.Context, vocab.ActivityStreamsUndo) error Undo func(context.Context, vocab.ActivityStreamsUndo) error
// Block handles additional side effects for the Block ActivityStreams // Block handles additional side effects for the Block ActivityStreams
// type. // type.
@ -89,53 +102,73 @@ type SocialWrappedCallbacks struct {
deliverable *bool deliverable *bool
} }
// disjoint ensures that the functions given do not share a type signature with // callbacks returns the WrappedCallbacks members into a single interface slice
// the functions being wrapped in SocialWrappedCallbacks. // for use in streams.Resolver callbacks.
func (w SocialWrappedCallbacks) disjoint(fns []interface{}) error { //
var s string // If the given functions have a type that collides with the default behavior,
// then disable our default behavior
func (w SocialWrappedCallbacks) callbacks(fns []interface{}) []interface{} {
enableCreate := true
enableUpdate := true
enableDelete := true
enableFollow := true
enableAdd := true
enableRemove := true
enableLike := true
enableUndo := true
enableBlock := true
for _, fn := range fns { for _, fn := range fns {
switch fn.(type) { switch fn.(type) {
default: default:
// OK, no collision
continue continue
case func(context.Context, vocab.ActivityStreamsCreate) error: case func(context.Context, vocab.ActivityStreamsCreate) error:
s = "Create" enableCreate = false
case func(context.Context, vocab.ActivityStreamsUpdate) error: case func(context.Context, vocab.ActivityStreamsUpdate) error:
s = "Update" enableUpdate = false
case func(context.Context, vocab.ActivityStreamsDelete) error: case func(context.Context, vocab.ActivityStreamsDelete) error:
s = "Delete" enableDelete = false
case func(context.Context, vocab.ActivityStreamsFollow) error: case func(context.Context, vocab.ActivityStreamsFollow) error:
s = "Follow" enableFollow = false
case func(context.Context, vocab.ActivityStreamsAdd) error: case func(context.Context, vocab.ActivityStreamsAdd) error:
s = "Add" enableAdd = false
case func(context.Context, vocab.ActivityStreamsRemove) error: case func(context.Context, vocab.ActivityStreamsRemove) error:
s = "Remove" enableRemove = false
case func(context.Context, vocab.ActivityStreamsLike) error: case func(context.Context, vocab.ActivityStreamsLike) error:
s = "Like" enableLike = false
case func(context.Context, vocab.ActivityStreamsUndo) error: case func(context.Context, vocab.ActivityStreamsUndo) error:
s = "Undo" enableUndo = false
case func(context.Context, vocab.ActivityStreamsBlock) error: case func(context.Context, vocab.ActivityStreamsBlock) error:
s = "Block" enableBlock = false
} }
return fmt.Errorf("callback function handling type %q conflicts with SocialWrappedCallbacks", s)
} }
return nil if enableCreate {
} fns = append(fns, w.create)
// callbacks returns the WrappedCallbacks members into a single interface slice
// for use in streams.Resolver callbacks.
func (w SocialWrappedCallbacks) callbacks() []interface{} {
return []interface{}{
w.create,
w.update,
w.deleteFn,
w.follow,
w.add,
w.remove,
w.like,
w.undo,
w.block,
} }
if enableUpdate {
fns = append(fns, w.update)
}
if enableDelete {
fns = append(fns, w.deleteFn)
}
if enableFollow {
fns = append(fns, w.follow)
}
if enableAdd {
fns = append(fns, w.add)
}
if enableRemove {
fns = append(fns, w.remove)
}
if enableLike {
fns = append(fns, w.like)
}
if enableUndo {
fns = append(fns, w.undo)
}
if enableBlock {
fns = append(fns, w.block)
}
return fns
} }
// create implements the social Create activity side effects. // create implements the social Create activity side effects.

View File

@ -19,8 +19,15 @@ const (
acceptHeaderValue = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" acceptHeaderValue = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
) )
// Transport makes ActivityStreams calls to other servers in order to POST or // Transport makes ActivityStreams calls to other servers in order to send or
// GET ActivityStreams data. // receive ActivityStreams data.
//
// It is responsible for setting the appropriate request headers, signing the
// requests if needed, and facilitating the traffic between this server and
// another.
//
// The transport is exclusively used to issue requests on behalf of an actor,
// and is never sending requests on behalf of the server in general.
// //
// It may be reused multiple times, but never concurrently. // It may be reused multiple times, but never concurrently.
type Transport interface { type Transport interface {
@ -52,10 +59,24 @@ type HttpSigTransport struct {
privKey crypto.PrivateKey privKey crypto.PrivateKey
} }
// NewHttpSigTransport returns a new HttpSigTransport. // NewHttpSigTransport returns a new Transport.
//
// It sends requests specifically on behalf of a specific actor on this server.
// The actor's credentials are used to add an HTTP Signature to requests, which
// requires an actor's private key, a unique identifier for their public key,
// and an HTTP Signature signing algorithm.
//
// The client lets users issue requests through any HTTP client, including the
// standard library's HTTP client.
//
// The appAgent uniquely identifies the calling application's requests, so peers
// may aid debugging the requests incoming from this server. Note that the
// agent string will also include one for go-fed, so at minimum peer servers can
// reach out to the go-fed library to aid in notifying implementors of malformed
// or unsupported requests.
func NewHttpSigTransport( func NewHttpSigTransport(
client HttpClient, client HttpClient,
appAgent, gofedAgent string, appAgent string,
clock Clock, clock Clock,
signer httpsig.Signer, signer httpsig.Signer,
pubKeyId string, pubKeyId string,
@ -63,7 +84,7 @@ func NewHttpSigTransport(
return &HttpSigTransport{ return &HttpSigTransport{
client: client, client: client,
appAgent: appAgent, appAgent: appAgent,
gofedAgent: gofedAgent, gofedAgent: goFedUserAgent(),
clock: clock, clock: clock,
signer: signer, signer: signer,
pubKeyId: pubKeyId, pubKeyId: pubKeyId,
@ -71,7 +92,8 @@ func NewHttpSigTransport(
} }
} }
// Dereferences with a request signed with an HTTP Signature. // Dereference sends a GET request signed with an HTTP Signature to obtain an
// ActivityStreams value.
func (h HttpSigTransport) Dereference(c context.Context, iri *url.URL) ([]byte, error) { func (h HttpSigTransport) Dereference(c context.Context, iri *url.URL) ([]byte, error) {
req, err := http.NewRequest("GET", iri.String(), nil) req, err := http.NewRequest("GET", iri.String(), nil)
if err != nil { if err != nil {