tavern/fed/network.go

78 lines
2.0 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
}
func (client ActorClient) SendToInboxes(ctx context.Context, localActor storage.LocalActor, actors []hasInbox, payload []byte) error {
for _, actor := range actors {
client.Logger.Info("sending payload to follower", zap.String("inbox", actor.GetInbox()))
err := client.SendToInbox(ctx, localActor, actor, payload)
if err != nil {
client.Logger.Error("unable to send payload to inbox", zap.String("inbox", actor.GetInbox()), 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.Actor.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))
}