package api import ( "bytes" "fmt" "io" "io/ioutil" "log" "net/http" "git.sigsum.org/sigsum-go/pkg/requests" "git.sigsum.org/sigsum-go/pkg/types" ) type NetworkClient struct { http.Client LogURL string } func NewNetworkClient(httpClient http.Client, logURL string) *NetworkClient { return &NetworkClient{ Client: httpClient, LogURL: logURL, } } func (c *NetworkClient) GetToCosignTreeHead() (*types.SignedTreeHead, error) { return nil, fmt.Errorf("TODO") } func (c *NetworkClient) GetCosignedTreeHead() (*types.CosignedTreeHead, error) { rsp, err := c.get(types.EndpointGetTreeHeadCosigned.Path(c.LogURL)) if err != nil { return nil, fmt.Errorf("client: HTTP %d: %s", rsp.StatusCode, errorMessage(rsp.Body)) } defer rsp.Body.Close() if rsp.StatusCode != http.StatusOK { return nil, fmt.Errorf("client: HTTP %d: %s", rsp.StatusCode, errorMessage(rsp.Body)) } var cth types.CosignedTreeHead if err := cth.FromASCII(rsp.Body); err != nil { return nil, fmt.Errorf("client: HTTP %d: %s", rsp.StatusCode, errorMessage(rsp.Body)) } return &cth, nil } func (c *NetworkClient) GetInclusionProof(treeSize uint64, leafHash *types.Hash) (*types.InclusionProof, error) { data := requests.InclusionProof{ TreeSize: treeSize, LeafHash: *leafHash, } buf := bytes.NewBuffer(nil) if err := data.ToASCII(buf); err != nil { return nil, fmt.Errorf("client: invalid request data: %v", err) } rsp, err := c.post(types.EndpointGetInclusionProof.Path(c.LogURL), buf) if err != nil { return nil, fmt.Errorf("client: invalid proof request: %v", err) } defer rsp.Body.Close() if rsp.StatusCode != http.StatusOK { return nil, fmt.Errorf("client: HTTP %d: %s", rsp.StatusCode, errorMessage(rsp.Body)) } var proof types.InclusionProof if err := proof.FromASCII(rsp.Body, treeSize); err != nil { return nil, fmt.Errorf("client: failed parsing response: %v", err) } return &proof, nil } func (c *NetworkClient) GetConsistencyProof(oldSize, newSize uint64) (*types.ConsistencyProof, error) { return nil, fmt.Errorf("TODO") } func (c *NetworkClient) GetLeaves(startSize, endSize uint64) (*types.Leaves, error) { return nil, fmt.Errorf("TODO") } func (c *NetworkClient) AddLeaf(leaf *requests.Leaf) error { buf := bytes.NewBuffer(nil) if err := leaf.ToASCII(buf); err != nil { return fmt.Errorf("client: invalid request data: %v", err) } rsp, err := c.post(types.EndpointAddLeaf.Path(c.LogURL), buf) if err != nil { return fmt.Errorf("client: HTTP %d: %s", rsp.StatusCode, errorMessage(rsp.Body)) } defer rsp.Body.Close() if rsp.StatusCode != http.StatusOK { return fmt.Errorf("client: HTTP %d: %s", rsp.StatusCode, errorMessage(rsp.Body)) } return nil } func (c *NetworkClient) AddCosignature(*requests.Cosignature) error { return fmt.Errorf("TODO") } func (c *NetworkClient) get(url string) (*http.Response, error) { log.Printf("GET %s", url) return c.Get(url) } func (c *NetworkClient) post(url string, buf *bytes.Buffer) (*http.Response, error) { req, err := http.NewRequest("POST", url, buf) if err != nil { return nil, fmt.Errorf("client: invalid leaf request: %v", err) } log.Printf("POST %s", url) return c.Do(req) } func errorMessage(r io.Reader) string { b, _ := ioutil.ReadAll(r) return string(b) // TODO: the below doesn't work because log error messages are malformed //msg := struct { // Message string `ascii:"Error"` //}{} //if err := ascii.StdEncoding.Deserialize(r, &msg); err != nil { // return fmt.Sprintf("malformed error message: %v", err) //} //return msg.Message }