diff options
Diffstat (limited to 'endpoint.go')
| -rw-r--r-- | endpoint.go | 185 | 
1 files changed, 185 insertions, 0 deletions
| diff --git a/endpoint.go b/endpoint.go new file mode 100644 index 0000000..d3da95e --- /dev/null +++ b/endpoint.go @@ -0,0 +1,185 @@ +package stfe + +import ( +	"context" +	"fmt" +	"strings" + +	"net/http" + +	"github.com/golang/glog" +	"github.com/google/trillian" +	"github.com/system-transparency/stfe/types" +) + +// Endpoint is a named HTTP API endpoint +type Endpoint string + +const ( +	EndpointAddEntry            = Endpoint("add-entry") +	EndpointAddCosignature      = Endpoint("add-cosignature") +	EndpointGetLatestSth        = Endpoint("get-latest-sth") +	EndpointGetStableSth        = Endpoint("get-stable-sth") +	EndpointGetCosignedSth      = Endpoint("get-cosigned-sth") +	EndpointGetProofByHash      = Endpoint("get-proof-by-hash") +	EndpointGetConsistencyProof = Endpoint("get-consistency-proof") +	EndpointGetEntries          = Endpoint("get-entries") +) + +// Path joins a number of components to form a full endpoint path, e.g., base +// ("example.com"), prefix ("st/v1"), and the endpoint itself ("get-sth"). +func (e Endpoint) Path(components ...string) string { +	return strings.Join(append(components, string(e)), "/") +} + +func addEntry(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { +	glog.V(3).Info("handling add-entry request") +	item, err := i.LogParameters.parseAddEntryV1Request(r) +	if err != nil { +		return http.StatusBadRequest, fmt.Errorf("parseAddEntryV1Request: %v", err) +	} +	leaf, err := types.Marshal(*item) +	if err != nil { +		return http.StatusInternalServerError, fmt.Errorf("Marshal: %v", err) // should never happen +	} +	trsp, err := i.Client.QueueLeaf(ctx, &trillian.QueueLeafRequest{ +		LogId: i.LogParameters.TreeId, +		Leaf: &trillian.LogLeaf{ +			LeafValue: leaf, +			ExtraData: nil, +		}, +	}) +	if errInner := checkQueueLeaf(trsp, err); errInner != nil { +		return http.StatusInternalServerError, fmt.Errorf("bad QueueLeafResponse: %v", errInner) +	} +	return http.StatusOK, nil +} + +func addCosignature(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { +	glog.V(3).Info("handling add-cosignature request") +	costh, err := i.LogParameters.parseAddCosignatureV1Request(r) +	if err != nil { +		return http.StatusBadRequest, err +	} +	if err := i.SthSource.AddCosignature(ctx, costh); err != nil { +		return http.StatusBadRequest, err +	} +	return http.StatusOK, nil +} + +func getLatestSth(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) { +	glog.V(3).Info("handling get-latest-sth request") +	sth, err := i.SthSource.Latest(ctx) +	if err != nil { +		return http.StatusInternalServerError, fmt.Errorf("Latest: %v", err) +	} +	if err := writeOctetResponse(w, *sth); err != nil { +		return http.StatusInternalServerError, fmt.Errorf("writeOctetResponse: %v", err) +	} +	return http.StatusOK, nil +} + +func getStableSth(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) { +	glog.V(3).Info("handling get-stable-sth request") +	sth, err := i.SthSource.Stable(ctx) +	if err != nil { +		return http.StatusInternalServerError, fmt.Errorf("Latest: %v", err) +	} +	if err := writeOctetResponse(w, *sth); err != nil { +		return http.StatusInternalServerError, fmt.Errorf("writeOctetResponse: %v", err) +	} +	return http.StatusOK, nil +} + +func getCosignedSth(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) { +	glog.V(3).Info("handling get-cosigned-sth request") +	costh, err := i.SthSource.Cosigned(ctx) +	if err != nil { +		return http.StatusInternalServerError, fmt.Errorf("Cosigned: %v", err) +	} +	if err := writeOctetResponse(w, *costh); err != nil { +		return http.StatusInternalServerError, fmt.Errorf("writeOctetResponse: %v", err) +	} +	return http.StatusOK, nil +} + +func getConsistencyProof(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { +	glog.V(3).Info("handling get-consistency-proof request") +	req, err := i.LogParameters.parseGetConsistencyProofV1Request(r) +	if err != nil { +		return http.StatusBadRequest, err +	} + +	trsp, err := i.Client.GetConsistencyProof(ctx, &trillian.GetConsistencyProofRequest{ +		LogId:          i.LogParameters.TreeId, +		FirstTreeSize:  int64(req.First), +		SecondTreeSize: int64(req.Second), +	}) +	if errInner := checkGetConsistencyProof(i.LogParameters, trsp, err); errInner != nil { +		return http.StatusInternalServerError, fmt.Errorf("bad GetConsistencyProofResponse: %v", errInner) +	} + +	if err := writeOctetResponse(w, *types.NewConsistencyProofV1(i.LogParameters.LogId, req.First, req.Second, NewNodePathFromHashPath(trsp.Proof.Hashes))); err != nil { +		return http.StatusInternalServerError, fmt.Errorf("writeOctetResponse: %v", err) +	} +	return http.StatusOK, nil +} + +func getProofByHash(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { +	glog.V(3).Info("handling get-proof-by-hash request") +	req, err := i.LogParameters.parseGetProofByHashV1Request(r) +	if err != nil { +		return http.StatusBadRequest, err +	} + +	trsp, err := i.Client.GetInclusionProofByHash(ctx, &trillian.GetInclusionProofByHashRequest{ +		LogId:           i.LogParameters.TreeId, +		LeafHash:        req.Hash[:], +		TreeSize:        int64(req.TreeSize), +		OrderBySequence: true, +	}) +	if errInner := checkGetInclusionProofByHash(i.LogParameters, trsp, err); errInner != nil { +		return http.StatusInternalServerError, fmt.Errorf("bad GetInclusionProofByHashResponse: %v", errInner) +	} + +	if err := writeOctetResponse(w, *types.NewInclusionProofV1(i.LogParameters.LogId, req.TreeSize, uint64(trsp.Proof[0].LeafIndex), NewNodePathFromHashPath(trsp.Proof[0].Hashes))); err != nil { +		return http.StatusInternalServerError, fmt.Errorf("writeOctetResponse: %v", err) +	} +	return http.StatusOK, nil +} + +func getEntries(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) { +	glog.V(3).Info("handling get-entries request") +	req, err := i.LogParameters.parseGetEntriesV1Request(r) +	if err != nil { +		return http.StatusBadRequest, err +	} + +	trsp, err := i.Client.GetLeavesByRange(ctx, &trillian.GetLeavesByRangeRequest{ +		LogId:      i.LogParameters.TreeId, +		StartIndex: int64(req.Start), +		Count:      int64(req.End-req.Start) + 1, +	}) +	if errInner := checkGetLeavesByRange(req, trsp, err); errInner != nil { +		return http.StatusInternalServerError, fmt.Errorf("checkGetLeavesByRangeResponse: %v", errInner) // there is one StatusBadRequest in here tho.. +	} + +	if rsp, err := NewStItemListFromLeaves(trsp.Leaves); err != nil { +		return http.StatusInternalServerError, fmt.Errorf("NewStItemListFromLeaves: %v", err) // should never happen +	} else if err := writeOctetResponse(w, *rsp); err != nil { +		return http.StatusInternalServerError, fmt.Errorf("writeOctetResponse: %v", err) +	} +	return http.StatusOK, nil +} + +func writeOctetResponse(w http.ResponseWriter, i interface{}) error { +	b, err := types.Marshal(i) +	if err != nil { +		return fmt.Errorf("Marshal: %v", err) +	} +	w.Header().Set("Content-Type", "application/octet-stream") +	if _, err := w.Write(b); err != nil { +		return fmt.Errorf("Write: %v", err) +	} +	return nil +} | 
