tavern/fed/network.go

89 lines
2.1 KiB
Go

package fed
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
"github.com/yukimochi/httpsig"
"go.uber.org/zap"
"github.com/ngerakines/tavern/g"
"github.com/ngerakines/tavern/storage"
)
type hasInbox interface {
GetInbox() string
}
type fakeInbox string
func (f fakeInbox) GetInbox() string {
return string(f)
}
func (client ActorClient) Broadcast(ctx context.Context, store storage.Storage, localActor storage.LocalActor, payload []byte) error {
followers, err := store.FollowerInboxes(ctx, localActor.User.ID)
if err != nil {
return err
}
for _, follower := range followers {
client.Logger.Info("sending payload to follower", zap.String("inbox", follower))
err = client.SendToInbox(ctx, localActor, fakeInbox(follower), payload)
if err != nil {
client.Logger.Error("unable to send payload to inbox", zap.String("inbox", follower), zap.Error(err))
}
}
return nil
}
func (client ActorClient) SendToInbox(ctx context.Context, localActor storage.LocalActor, actor hasInbox, payload []byte) error {
sigConfig := []httpsig.Algorithm{httpsig.RSA_SHA256}
headersToSign := []string{httpsig.RequestTarget, "date"}
signer, _, err := httpsig.NewSigner(sigConfig, headersToSign, httpsig.Signature)
if err != nil {
return err
}
request, err := http.NewRequestWithContext(ctx, "POST", actor.GetInbox(), bytes.NewReader(payload))
if err != nil {
return err
}
request.Header.Add("content-type", `application/ld+json; profile="https://www.w3.org/ns/activitystreams"`)
request.Header.Add("date", time.Now().UTC().Format(http.TimeFormat))
request.Header.Set("User-Agent", g.UserAgent())
privateKey, err := localActor.User.GetPrivateKey()
if err != nil {
return err
}
if err = signer.SignRequest(privateKey, localActor.GetKeyID(), request); err != nil {
return err
}
resp, err := client.HTTPClient.Do(request)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return nil
}
lr := io.LimitReader(resp.Body, 500000)
data, err := ioutil.ReadAll(lr)
if err != nil {
return err
}
return fmt.Errorf("unexpected response %d: %s", resp.StatusCode, string(data))
}