1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package http
17
18 import (
19 "bytes"
20 "context"
21 "encoding/json"
22 "errors"
23 "fmt"
24 "io"
25 "net/http"
26 "net/url"
27 "os"
28
29 wit_api "github.com/google/certificate-transparency-go/internal/witness/api"
30 )
31
32
33 var ErrSTHTooOld = errors.New("STH too old")
34
35
36 type Witness struct {
37 URL *url.URL
38 }
39
40
41 func (w Witness) GetLatestSTH(ctx context.Context, logID string) ([]byte, error) {
42 u, err := w.URL.Parse(fmt.Sprintf(wit_api.HTTPGetSTH, url.PathEscape(logID)))
43 if err != nil {
44 return nil, fmt.Errorf("failed to parse URL: %v", err)
45 }
46 req, err := http.NewRequest("GET", u.String(), nil)
47 if err != nil {
48 return nil, fmt.Errorf("failed to create request: %v", err)
49 }
50 resp, err := http.DefaultClient.Do(req.WithContext(ctx))
51 if err != nil {
52 return nil, fmt.Errorf("failed to do http request: %v", err)
53 }
54 defer resp.Body.Close()
55 if resp.StatusCode == 404 {
56 return nil, os.ErrNotExist
57 } else if resp.StatusCode != 200 {
58 return nil, fmt.Errorf("bad status response: %s", resp.Status)
59 }
60 return io.ReadAll(resp.Body)
61 }
62
63
64
65
66 func (w Witness) Update(ctx context.Context, logID string, sth []byte, proof [][]byte) ([]byte, error) {
67 reqBody, err := json.MarshalIndent(&wit_api.UpdateRequest{
68 STH: sth,
69 Proof: proof,
70 }, "", " ")
71 if err != nil {
72 return nil, fmt.Errorf("failed to marshal update request: %v", err)
73 }
74 u, err := w.URL.Parse(fmt.Sprintf(wit_api.HTTPUpdate, url.PathEscape(logID)))
75 if err != nil {
76 return nil, fmt.Errorf("failed to parse URL: %v", err)
77 }
78 req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(reqBody))
79 if err != nil {
80 return nil, fmt.Errorf("failed to create request: %v", err)
81 }
82 resp, err := http.DefaultClient.Do(req.WithContext(ctx))
83 if err != nil {
84 return nil, fmt.Errorf("failed to do http request: %v", err)
85 }
86 defer resp.Body.Close()
87 body, err := io.ReadAll(resp.Body)
88 if err != nil {
89 return nil, fmt.Errorf("failed to read body: %v", err)
90 }
91 if resp.StatusCode != 200 {
92 if resp.StatusCode == 409 {
93 return body, ErrSTHTooOld
94 }
95 return nil, fmt.Errorf("bad status response (%s): %q", resp.Status, body)
96 }
97 return body, nil
98 }
99
View as plain text