mirror of https://gitlab.com/ngerakines/tavern.git
89 lines
2.1 KiB
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))
|
|
}
|