Blame


1 1d5ddf5d 2024-02-20 o package apub
2 1d5ddf5d 2024-02-20 o
3 1d5ddf5d 2024-02-20 o import (
4 1d5ddf5d 2024-02-20 o "bytes"
5 1d5ddf5d 2024-02-20 o "crypto/rsa"
6 1d5ddf5d 2024-02-20 o "encoding/json"
7 71191436 2024-02-28 o "errors"
8 1d5ddf5d 2024-02-20 o "fmt"
9 71191436 2024-02-28 o "io"
10 1d5ddf5d 2024-02-20 o "net/http"
11 71191436 2024-02-28 o "os"
12 1d5ddf5d 2024-02-20 o "strings"
13 1d5ddf5d 2024-02-20 o )
14 1d5ddf5d 2024-02-20 o
15 1d5ddf5d 2024-02-20 o var defaultClient Client = Client{Client: http.DefaultClient}
16 1d5ddf5d 2024-02-20 o
17 1d5ddf5d 2024-02-20 o func Lookup(id string) (*Activity, error) {
18 1d5ddf5d 2024-02-20 o return defaultClient.Lookup(id)
19 1d5ddf5d 2024-02-20 o }
20 1d5ddf5d 2024-02-20 o
21 1d5ddf5d 2024-02-20 o func LookupActor(id string) (*Actor, error) {
22 1d5ddf5d 2024-02-20 o return defaultClient.LookupActor(id)
23 1d5ddf5d 2024-02-20 o }
24 1d5ddf5d 2024-02-20 o
25 1d5ddf5d 2024-02-20 o type Client struct {
26 1d5ddf5d 2024-02-20 o *http.Client
27 1d5ddf5d 2024-02-20 o Key *rsa.PrivateKey
28 1d5ddf5d 2024-02-20 o Actor *Actor
29 1d5ddf5d 2024-02-20 o }
30 1d5ddf5d 2024-02-20 o
31 1d5ddf5d 2024-02-20 o func (c *Client) Lookup(id string) (*Activity, error) {
32 1d5ddf5d 2024-02-20 o if !strings.HasPrefix(id, "http") {
33 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("id is not a HTTP URL")
34 1d5ddf5d 2024-02-20 o }
35 1d5ddf5d 2024-02-20 o if c.Client == nil {
36 1d5ddf5d 2024-02-20 o c.Client = http.DefaultClient
37 1d5ddf5d 2024-02-20 o }
38 1d5ddf5d 2024-02-20 o
39 1d5ddf5d 2024-02-20 o req, err := http.NewRequest(http.MethodGet, id, nil)
40 1d5ddf5d 2024-02-20 o if err != nil {
41 1d5ddf5d 2024-02-20 o return nil, err
42 1d5ddf5d 2024-02-20 o }
43 1d5ddf5d 2024-02-20 o req.Header.Set("Accept", ContentType)
44 1d5ddf5d 2024-02-20 o if c.Key != nil && c.Actor != nil {
45 1d5ddf5d 2024-02-20 o if err := Sign(req, c.Key, c.Actor.PublicKey.ID); err != nil {
46 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("sign http request: %w", err)
47 1d5ddf5d 2024-02-20 o }
48 1d5ddf5d 2024-02-20 o }
49 1d5ddf5d 2024-02-20 o resp, err := c.Do(req)
50 1d5ddf5d 2024-02-20 o if err != nil {
51 1d5ddf5d 2024-02-20 o return nil, err
52 1d5ddf5d 2024-02-20 o }
53 1d5ddf5d 2024-02-20 o defer resp.Body.Close()
54 1d5ddf5d 2024-02-20 o if resp.StatusCode == http.StatusNotFound {
55 71191436 2024-02-28 o return nil, ErrNotExist
56 1d5ddf5d 2024-02-20 o } else if resp.StatusCode >= 400 {
57 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("non-ok response status %s", resp.Status)
58 1d5ddf5d 2024-02-20 o }
59 1d5ddf5d 2024-02-20 o return Decode(resp.Body)
60 1d5ddf5d 2024-02-20 o }
61 1d5ddf5d 2024-02-20 o
62 1d5ddf5d 2024-02-20 o func (c *Client) LookupActor(id string) (*Actor, error) {
63 1d5ddf5d 2024-02-20 o activity, err := c.Lookup(id)
64 1d5ddf5d 2024-02-20 o if err != nil {
65 1d5ddf5d 2024-02-20 o return nil, err
66 1d5ddf5d 2024-02-20 o }
67 71191436 2024-02-28 o return activityToActor(activity), nil
68 71191436 2024-02-28 o }
69 71191436 2024-02-28 o
70 71191436 2024-02-28 o func activityToActor(activity *Activity) *Actor {
71 1d5ddf5d 2024-02-20 o return &Actor{
72 1d5ddf5d 2024-02-20 o AtContext: activity.AtContext,
73 1d5ddf5d 2024-02-20 o ID: activity.ID,
74 1d5ddf5d 2024-02-20 o Type: activity.Type,
75 1d5ddf5d 2024-02-20 o Name: activity.Name,
76 1d5ddf5d 2024-02-20 o Username: activity.Username,
77 1d5ddf5d 2024-02-20 o Inbox: activity.Inbox,
78 1d5ddf5d 2024-02-20 o Outbox: activity.Outbox,
79 71191436 2024-02-28 o Published: activity.Published,
80 71191436 2024-02-28 o Summary: activity.Summary,
81 71191436 2024-02-28 o }
82 1d5ddf5d 2024-02-20 o }
83 1d5ddf5d 2024-02-20 o
84 1d5ddf5d 2024-02-20 o func (c *Client) Send(inbox string, activity *Activity) (*Activity, error) {
85 1d5ddf5d 2024-02-20 o b, err := json.Marshal(activity)
86 1d5ddf5d 2024-02-20 o if err != nil {
87 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("encode outgoing activity: %w", err)
88 1d5ddf5d 2024-02-20 o }
89 1d5ddf5d 2024-02-20 o req, err := http.NewRequest(http.MethodPost, inbox, bytes.NewReader(b))
90 1d5ddf5d 2024-02-20 o if err != nil {
91 1d5ddf5d 2024-02-20 o return nil, err
92 1d5ddf5d 2024-02-20 o }
93 1d5ddf5d 2024-02-20 o req.Header.Set("Content-Type", ContentType)
94 1d5ddf5d 2024-02-20 o if err := Sign(req, c.Key, c.Actor.PublicKey.ID); err != nil {
95 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("sign outgoing request: %w", err)
96 1d5ddf5d 2024-02-20 o }
97 1d5ddf5d 2024-02-20 o resp, err := c.Do(req)
98 1d5ddf5d 2024-02-20 o if err != nil {
99 1d5ddf5d 2024-02-20 o return nil, err
100 1d5ddf5d 2024-02-20 o }
101 1d5ddf5d 2024-02-20 o switch resp.StatusCode {
102 1d5ddf5d 2024-02-20 o case http.StatusOK:
103 1d5ddf5d 2024-02-20 o if resp.ContentLength == 0 {
104 1d5ddf5d 2024-02-20 o return nil, nil
105 1d5ddf5d 2024-02-20 o }
106 1d5ddf5d 2024-02-20 o defer resp.Body.Close()
107 71191436 2024-02-28 o activity, err := Decode(resp.Body)
108 71191436 2024-02-28 o if errors.Is(err, io.EOF) {
109 71191436 2024-02-28 o return nil, nil
110 71191436 2024-02-28 o }
111 71191436 2024-02-28 o return activity, err
112 1d5ddf5d 2024-02-20 o case http.StatusAccepted, http.StatusNoContent:
113 1d5ddf5d 2024-02-20 o return nil, nil
114 1d5ddf5d 2024-02-20 o case http.StatusNotFound:
115 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("no such inbox %s", inbox)
116 1d5ddf5d 2024-02-20 o default:
117 1d5ddf5d 2024-02-20 o io.Copy(os.Stderr, resp.Body)
118 1d5ddf5d 2024-02-20 o resp.Body.Close()
119 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("non-ok response status %s", resp.Status)
120 1d5ddf5d 2024-02-20 o }
121 1d5ddf5d 2024-02-20 o }