diff options
| -rw-r--r-- | pkg/instance/endpoint_test.go | 588 | ||||
| -rw-r--r-- | pkg/instance/instance.go | 69 | ||||
| -rw-r--r-- | pkg/instance/instance_test.go | 155 | ||||
| -rw-r--r-- | pkg/instance/metric.go | 10 | ||||
| -rw-r--r-- | pkg/instance/request.go | 77 | ||||
| -rw-r--r-- | pkg/instance/request_test.go | 318 | 
6 files changed, 345 insertions, 872 deletions
| diff --git a/pkg/instance/endpoint_test.go b/pkg/instance/endpoint_test.go index 8511b8d..efcd4c0 100644 --- a/pkg/instance/endpoint_test.go +++ b/pkg/instance/endpoint_test.go @@ -2,479 +2,431 @@ package stfe  import (  	"bytes" -	"context" +	"encoding/hex"  	"fmt" +	"io"  	"net/http"  	"net/http/httptest" -	"reflect"  	"testing"  	"github.com/golang/mock/gomock" -	cttestdata "github.com/google/certificate-transparency-go/trillian/testdata" -	"github.com/google/trillian" -	"github.com/system-transparency/stfe/pkg/testdata" +	"github.com/system-transparency/stfe/pkg/mocks"  	"github.com/system-transparency/stfe/pkg/types"  ) -func TestEndpointAddEntry(t *testing.T) { -	for _, table := range []struct { -		description string -		breq        *bytes.Buffer -		trsp        *trillian.QueueLeafResponse -		terr        error -		wantCode    int -	}{ -		{ -			description: "invalid: bad request: empty", -			breq:        bytes.NewBuffer(nil), -			wantCode:    http.StatusBadRequest, +var ( +	testWitVK  = [types.VerificationKeySize]byte{} +	testConfig = Config{ +		LogID:    hex.EncodeToString(types.Hash([]byte("logid"))[:]), +		TreeID:   0, +		Prefix:   "testonly", +		MaxRange: 3, +		Deadline: 10, +		Interval: 10, +		Witnesses: map[[types.HashSize]byte][types.VerificationKeySize]byte{ +			*types.Hash(testWitVK[:]): testWitVK,  		}, -		{ -			description: "invalid: bad Trillian response: error", -			breq:        testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter, testdata.Ed25519VkSubmitter), -			terr:        fmt.Errorf("backend failure"), -			wantCode:    http.StatusInternalServerError, +	} +	testSTH = &types.SignedTreeHead{ +		TreeHead: types.TreeHead{ +			Timestamp: 0, +			TreeSize:  0, +			RootHash:  types.Hash(nil),  		}, -		{ -			description: "valid", -			breq:        testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter, testdata.Ed25519VkSubmitter), -			trsp:        testdata.DefaultTQlr(t, false), -			wantCode:    http.StatusOK, +		SigIdent: []*types.SigIdent{ +			&types.SigIdent{ +				Signature: &[types.SignatureSize]byte{}, +				KeyHash:   &[types.HashSize]byte{}, +			},  		}, -	} { -		func() { // run deferred functions at the end of each iteration -			ti := newTestInstance(t, nil) -			defer ti.ctrl.Finish() - -			url := EndpointAddEntry.Path("http://example.com", ti.instance.LogParameters.Prefix) -			req, err := http.NewRequest("POST", url, table.breq) -			if err != nil { -				t.Fatalf("must create http request: %v", err) -			} -			req.Header.Set("Content-Type", "application/octet-stream") -			if table.trsp != nil || table.terr != nil { -				ti.client.EXPECT().QueueLeaf(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr) -			} +	} +) -			w := httptest.NewRecorder() -			ti.postHandler(t, EndpointAddEntry).ServeHTTP(w, req) -			if got, want := w.Code, table.wantCode; got != want { -				t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description) -			} -		}() +func mustHandle(t *testing.T, i Instance, e types.Endpoint) Handler { +	for _, handler := range i.Handlers() { +		if handler.Endpoint == e { +			return handler +		}  	} +	t.Fatalf("must handle endpoint: %v", e) +	return Handler{}  } -func TestEndpointAddCosignature(t *testing.T) { +func TestAddLeaf(t *testing.T) { +	buf := func() io.Reader { +		// A valid leaf request that was created manually +		return bytes.NewBufferString(fmt.Sprintf( +			"%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s", +			types.ShardHint, types.Delim, "0", types.EOL, +			types.Checksum, types.Delim, "0000000000000000000000000000000000000000000000000000000000000000", types.EOL, +			types.SignatureOverMessage, types.Delim, "4cb410a4d48f52f761a7c01abcc28fd71811b84ded5403caed5e21b374f6aac9637cecd36828f17529fd503413d30ab66d7bb37a31dbf09a90d23b9241c45009", types.EOL, +			types.VerificationKey, types.Delim, "f2b7a00b625469d32502e06e8b7fad1ef258d4ad0c6cd87b846142ab681957d5", types.EOL, +			types.DomainHint, types.Delim, "example.com", types.EOL, +		)) +	}  	for _, table := range []struct {  		description string -		breq        *bytes.Buffer -		wantCode    int +		ascii       io.Reader // buffer used to populate HTTP request +		expect      bool      // set if a mock answer is expected +		err         error     // error from Trillian client +		wantCode    int       // HTTP status ok  	}{  		{ -			description: "invalid: bad request: empty", -			breq:        bytes.NewBuffer(nil), +			description: "invalid: bad request (parser error)", +			ascii:       bytes.NewBufferString("key=value\n"),  			wantCode:    http.StatusBadRequest,  		},  		{ -			description: "invalid: signed wrong sth", // newLogParameters() use testdata.Ed25519VkLog as default -			breq:        testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog2), &testdata.Ed25519SkWitness, &testdata.Ed25519VkWitness), -			wantCode:    http.StatusBadRequest, +			description: "invalid: bad request (signature error)", +			ascii: bytes.NewBufferString(fmt.Sprintf( +				"%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s", +				types.ShardHint, types.Delim, "1", types.EOL, +				types.Checksum, types.Delim, "1111111111111111111111111111111111111111111111111111111111111111", types.EOL, +				types.SignatureOverMessage, types.Delim, "4cb410a4d48f52f761a7c01abcc28fd71811b84ded5403caed5e21b374f6aac9637cecd36828f17529fd503413d30ab66d7bb37a31dbf09a90d23b9241c45009", types.EOL, +				types.VerificationKey, types.Delim, "f2b7a00b625469d32502e06e8b7fad1ef258d4ad0c6cd87b846142ab681957d5", types.EOL, +				types.DomainHint, types.Delim, "example.com", types.EOL, +			)), +			wantCode: http.StatusBadRequest, +		}, +		{ +			description: "invalid: backend failure", +			ascii:       buf(), +			expect:      true, +			err:         fmt.Errorf("something went wrong"), +			wantCode:    http.StatusInternalServerError,  		},  		{  			description: "valid", -			breq:        testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness, &testdata.Ed25519VkWitness), +			ascii:       buf(), +			expect:      true,  			wantCode:    http.StatusOK,  		},  	} { -		func() { // run deferred functions at the end of each iteration -			ti := newTestInstance(t, nil) -			defer ti.ctrl.Finish() - -			url := EndpointAddCosignature.Path("http://example.com", ti.instance.LogParameters.Prefix) -			req, err := http.NewRequest("POST", url, table.breq) +		// Run deferred functions at the end of each iteration +		func() { +			ctrl := gomock.NewController(t) +			defer ctrl.Finish() +			client := mocks.NewMockClient(ctrl) +			if table.expect { +				client.EXPECT().AddLeaf(gomock.Any(), gomock.Any()).Return(table.err) +			} +			i := Instance{ +				Config: testConfig, +				Client: client, +			} + +			// Create HTTP request +			url := types.EndpointAddLeaf.Path("http://example.com", i.Prefix) +			req, err := http.NewRequest("POST", url, table.ascii)  			if err != nil {  				t.Fatalf("must create http request: %v", err)  			} -			req.Header.Set("Content-Type", "application/octet-stream") +			// Run HTTP request  			w := httptest.NewRecorder() -			ti.postHandler(t, EndpointAddCosignature).ServeHTTP(w, req) +			mustHandle(t, i, types.EndpointAddLeaf).ServeHTTP(w, req)  			if got, want := w.Code, table.wantCode; got != want { -				t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description) +				t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)  			}  		}()  	}  } -func TestEndpointGetLatestSth(t *testing.T) { +func TestAddCosignature(t *testing.T) { +	buf := func() io.Reader { +		return bytes.NewBufferString(fmt.Sprintf( +			"%s%s%x%s"+"%s%s%x%s", +			types.Signature, types.Delim, make([]byte, types.SignatureSize), types.EOL, +			types.KeyHash, types.Delim, *types.Hash(testWitVK[:]), types.EOL, +		)) +	}  	for _, table := range []struct {  		description string -		trsp        *trillian.GetLatestSignedLogRootResponse -		terr        error -		wantCode    int -		wantItem    *types.StItem +		ascii       io.Reader // buffer used to populate HTTP request +		expect      bool      // set if a mock answer is expected +		err         error     // error from Trillian client +		wantCode    int       // HTTP status ok  	}{  		{ -			description: "backend failure", -			terr:        fmt.Errorf("backend failure"), -			wantCode:    http.StatusInternalServerError, +			description: "invalid: bad request (parser error)", +			ascii:       bytes.NewBufferString("key=value\n"), +			wantCode:    http.StatusBadRequest,  		},  		{ -			description: "valid", -			trsp:        testdata.DefaultTSlr(t), -			wantCode:    http.StatusOK, -			wantItem:    testdata.DefaultSth(t, testdata.Ed25519VkLog), +			description: "invalid: bad request (unknown witness)", +			ascii: bytes.NewBufferString(fmt.Sprintf( +				"%s%s%x%s"+"%s%s%x%s", +				types.Signature, types.Delim, make([]byte, types.SignatureSize), types.EOL, +				types.KeyHash, types.Delim, *types.Hash(testWitVK[1:]), types.EOL, +			)), +			wantCode: http.StatusBadRequest,  		}, -	} { -		func() { // run deferred functions at the end of each iteration -			ti := newTestInstance(t, cttestdata.NewSignerWithFixedSig(nil, testdata.Signature)) -			ti.ctrl.Finish() - -			// Setup and run client query -			url := EndpointGetLatestSth.Path("http://example.com", ti.instance.LogParameters.Prefix) -			req, err := http.NewRequest("GET", url, nil) -			if err != nil { -				t.Fatalf("must create http request: %v", err) -			} -			if table.trsp != nil || table.terr != nil { -				ti.client.EXPECT().GetLatestSignedLogRoot(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr) -			} - -			w := httptest.NewRecorder() -			ti.getHandler(t, EndpointGetLatestSth).ServeHTTP(w, req) -			if got, want := w.Code, table.wantCode; got != want { -				t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description) -			} -			if w.Code != http.StatusOK { -				return -			} - -			var item types.StItem -			if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil { -				t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err) -			} -			if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) { -				t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) -			} -		}() -	} -} - -func TestEndpointGetStableSth(t *testing.T) { -	for _, table := range []struct { -		description  string -		useBadSource bool -		wantCode     int -		wantItem     *types.StItem -	}{  		{ -			description:  "invalid: sth source failure", -			useBadSource: true, -			wantCode:     http.StatusInternalServerError, +			description: "invalid: backend failure", +			ascii:       buf(), +			expect:      true, +			err:         fmt.Errorf("something went wrong"), +			wantCode:    http.StatusBadRequest,  		},  		{  			description: "valid", +			ascii:       buf(), +			expect:      true,  			wantCode:    http.StatusOK, -			wantItem:    testdata.DefaultSth(t, testdata.Ed25519VkLog),  		},  	} { -		func() { // run deferred functions at the end of each iteration -			ti := newTestInstance(t, nil) -			ti.ctrl.Finish() -			if table.useBadSource { -				ti.instance.SthSource = &ActiveSthSource{} -			} - -			// Setup and run client query -			url := EndpointGetStableSth.Path("http://example.com", ti.instance.LogParameters.Prefix) -			req, err := http.NewRequest("GET", url, nil) +		// Run deferred functions at the end of each iteration +		func() { +			ctrl := gomock.NewController(t) +			defer ctrl.Finish() +			stateman := mocks.NewMockStateManager(ctrl) +			if table.expect { +				stateman.EXPECT().AddCosignature(gomock.Any(), gomock.Any(), gomock.Any()).Return(table.err) +			} +			i := Instance{ +				Config:   testConfig, +				Stateman: stateman, +			} + +			// Create HTTP request +			url := types.EndpointAddCosignature.Path("http://example.com", i.Prefix) +			req, err := http.NewRequest("POST", url, table.ascii)  			if err != nil {  				t.Fatalf("must create http request: %v", err)  			} +			// Run HTTP request  			w := httptest.NewRecorder() -			ti.getHandler(t, EndpointGetStableSth).ServeHTTP(w, req) +			mustHandle(t, i, types.EndpointAddCosignature).ServeHTTP(w, req)  			if got, want := w.Code, table.wantCode; got != want { -				t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description) -			} -			if w.Code != http.StatusOK { -				return -			} - -			var item types.StItem -			if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil { -				t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err) -			} -			if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) { -				t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) +				t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)  			}  		}()  	}  } -func TestEndpointGetCosignedSth(t *testing.T) { +func TestGetTreeHeadLatest(t *testing.T) {  	for _, table := range []struct { -		description  string -		useBadSource bool -		wantCode     int -		wantItem     *types.StItem +		description string +		expect      bool                  // set if a mock answer is expected +		rsp         *types.SignedTreeHead // signed tree head from Trillian client +		err         error                 // error from Trillian client +		wantCode    int                   // HTTP status ok  	}{  		{ -			description:  "invalid: sth source failure", -			useBadSource: true, -			wantCode:     http.StatusInternalServerError, +			description: "invalid: backend failure", +			expect:      true, +			err:         fmt.Errorf("something went wrong"), +			wantCode:    http.StatusInternalServerError,  		},  		{  			description: "valid", +			expect:      true, +			rsp:         testSTH,  			wantCode:    http.StatusOK, -			wantItem:    testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness}),  		},  	} { -		func() { // run deferred functions at the end of each iteration -			ti := newTestInstance(t, nil) -			ti.ctrl.Finish() -			if table.useBadSource { -				ti.instance.SthSource = &ActiveSthSource{} +		// Run deferred functions at the end of each iteration +		func() { +			ctrl := gomock.NewController(t) +			defer ctrl.Finish() +			stateman := mocks.NewMockStateManager(ctrl) +			if table.expect { +				stateman.EXPECT().Latest(gomock.Any()).Return(table.rsp, table.err) +			} +			i := Instance{ +				Config:   testConfig, +				Stateman: stateman,  			} -			// Setup and run client query -			url := EndpointGetCosignedSth.Path("http://example.com", ti.instance.LogParameters.Prefix) +			// Create HTTP request +			url := types.EndpointGetTreeHeadLatest.Path("http://example.com", i.Prefix)  			req, err := http.NewRequest("GET", url, nil)  			if err != nil {  				t.Fatalf("must create http request: %v", err)  			} +			// Run HTTP request  			w := httptest.NewRecorder() -			ti.getHandler(t, EndpointGetCosignedSth).ServeHTTP(w, req) +			mustHandle(t, i, types.EndpointGetTreeHeadLatest).ServeHTTP(w, req)  			if got, want := w.Code, table.wantCode; got != want { -				t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description) -			} -			if w.Code != http.StatusOK { -				return -			} - -			var item types.StItem -			if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil { -				t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err) -			} -			if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) { -				t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) +				t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)  			}  		}()  	}  } -func TestEndpointGetProofByHash(t *testing.T) { +func TestGetTreeToSign(t *testing.T) {  	for _, table := range []struct {  		description string -		breq        *bytes.Buffer -		trsp        *trillian.GetInclusionProofByHashResponse -		terr        error -		wantCode    int -		wantItem    *types.StItem +		expect      bool                  // set if a mock answer is expected +		rsp         *types.SignedTreeHead // signed tree head from Trillian client +		err         error                 // error from Trillian client +		wantCode    int                   // HTTP status ok  	}{  		{ -			description: "invalid: bad request: empty", -			breq:        bytes.NewBuffer(nil), -			wantCode:    http.StatusBadRequest, -		}, -		{ -			description: "invalid: bad Trillian response: error", -			breq:        bytes.NewBuffer(marshal(t, types.GetProofByHashV1{TreeSize: 1, Hash: testdata.LeafHash})), -			terr:        fmt.Errorf("backend failure"), +			description: "invalid: backend failure", +			expect:      true, +			err:         fmt.Errorf("something went wrong"),  			wantCode:    http.StatusInternalServerError,  		},  		{  			description: "valid", -			breq:        bytes.NewBuffer(marshal(t, types.GetProofByHashV1{TreeSize: 1, Hash: testdata.LeafHash})), -			trsp:        testdata.DefaultTGipbhr(t), +			expect:      true, +			rsp:         testSTH,  			wantCode:    http.StatusOK, -			wantItem:    testdata.DefaultInclusionProof(t, 1),  		},  	} { -		func() { // run deferred functions at the end of each iteration -			ti := newTestInstance(t, nil) -			defer ti.ctrl.Finish() +		// Run deferred functions at the end of each iteration +		func() { +			ctrl := gomock.NewController(t) +			defer ctrl.Finish() +			stateman := mocks.NewMockStateManager(ctrl) +			if table.expect { +				stateman.EXPECT().ToSign(gomock.Any()).Return(table.rsp, table.err) +			} +			i := Instance{ +				Config:   testConfig, +				Stateman: stateman, +			} -			url := EndpointGetProofByHash.Path("http://example.com", ti.instance.LogParameters.Prefix) -			req, err := http.NewRequest("POST", url, table.breq) +			// Create HTTP request +			url := types.EndpointGetTreeHeadToSign.Path("http://example.com", i.Prefix) +			req, err := http.NewRequest("GET", url, nil)  			if err != nil {  				t.Fatalf("must create http request: %v", err)  			} -			req.Header.Set("Content-Type", "application/octet-stream") -			if table.trsp != nil || table.terr != nil { -				ti.client.EXPECT().GetInclusionProofByHash(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr) -			} +			// Run HTTP request  			w := httptest.NewRecorder() -			ti.postHandler(t, EndpointGetProofByHash).ServeHTTP(w, req) +			mustHandle(t, i, types.EndpointGetTreeHeadToSign).ServeHTTP(w, req)  			if got, want := w.Code, table.wantCode; got != want { -				t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description) -			} -			if w.Code != http.StatusOK { -				return -			} - -			var item types.StItem -			if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil { -				t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err) -			} -			if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) { -				t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) +				t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)  			}  		}()  	}  } -func TestEndpointGetConsistencyProof(t *testing.T) { +func TestGetTreeCosigned(t *testing.T) {  	for _, table := range []struct {  		description string -		breq        *bytes.Buffer -		trsp        *trillian.GetConsistencyProofResponse -		terr        error -		wantCode    int -		wantItem    *types.StItem +		expect      bool                  // set if a mock answer is expected +		rsp         *types.SignedTreeHead // signed tree head from Trillian client +		err         error                 // error from Trillian client +		wantCode    int                   // HTTP status ok  	}{  		{ -			description: "invalid: bad request: empty", -			breq:        bytes.NewBuffer(nil), -			wantCode:    http.StatusBadRequest, -		}, -		{ -			description: "invalid: bad Trillian response: error", -			breq:        bytes.NewBuffer(marshal(t, types.GetConsistencyProofV1{First: 1, Second: 2})), -			terr:        fmt.Errorf("backend failure"), +			description: "invalid: backend failure", +			expect:      true, +			err:         fmt.Errorf("something went wrong"),  			wantCode:    http.StatusInternalServerError,  		},  		{  			description: "valid", -			breq:        bytes.NewBuffer(marshal(t, types.GetConsistencyProofV1{First: 1, Second: 2})), -			trsp:        testdata.DefaultTGcpr(t), +			expect:      true, +			rsp:         testSTH,  			wantCode:    http.StatusOK, -			wantItem:    testdata.DefaultConsistencyProof(t, 1, 2),  		},  	} { -		func() { // run deferred functions at the end of each iteration -			ti := newTestInstance(t, nil) -			defer ti.ctrl.Finish() +		// Run deferred functions at the end of each iteration +		func() { +			ctrl := gomock.NewController(t) +			defer ctrl.Finish() +			stateman := mocks.NewMockStateManager(ctrl) +			if table.expect { +				stateman.EXPECT().Cosigned(gomock.Any()).Return(table.rsp, table.err) +			} +			i := Instance{ +				Config:   testConfig, +				Stateman: stateman, +			} -			url := EndpointGetConsistencyProof.Path("http://example.com", ti.instance.LogParameters.Prefix) -			req, err := http.NewRequest("POST", url, table.breq) +			// Create HTTP request +			url := types.EndpointGetTreeHeadCosigned.Path("http://example.com", i.Prefix) +			req, err := http.NewRequest("GET", url, nil)  			if err != nil {  				t.Fatalf("must create http request: %v", err)  			} -			req.Header.Set("Content-Type", "application/octet-stream") -			if table.trsp != nil || table.terr != nil { -				ti.client.EXPECT().GetConsistencyProof(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr) -			} +			// Run HTTP request  			w := httptest.NewRecorder() -			ti.postHandler(t, EndpointGetConsistencyProof).ServeHTTP(w, req) +			mustHandle(t, i, types.EndpointGetTreeHeadCosigned).ServeHTTP(w, req)  			if got, want := w.Code, table.wantCode; got != want { -				t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description) -			} -			if w.Code != http.StatusOK { -				return -			} - -			var item types.StItem -			if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil { -				t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err) -			} -			if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) { -				t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) +				t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)  			}  		}()  	}  } -func TestEndpointGetEntriesV1(t *testing.T) { +func TestGetConsistencyProof(t *testing.T) { +	buf := func(oldSize, newSize int) io.Reader { +		return bytes.NewBufferString(fmt.Sprintf( +			"%s%s%d%s"+"%s%s%d%s", +			types.OldSize, types.Delim, oldSize, types.EOL, +			types.NewSize, types.Delim, newSize, types.EOL, +		)) +	} +	// values in testProof are not relevant for the test, just need a path +	testProof := &types.ConsistencyProof{ +		OldSize: 1, +		NewSize: 2, +		Path: []*[types.HashSize]byte{ +			types.Hash(nil), +		}, +	}  	for _, table := range []struct {  		description string -		breq        *bytes.Buffer -		trsp        *trillian.GetLeavesByRangeResponse -		terr        error -		wantCode    int -		wantItem    *types.StItemList +		ascii       io.Reader               // buffer used to populate HTTP request +		expect      bool                    // set if a mock answer is expected +		rsp         *types.ConsistencyProof // consistency proof from Trillian client +		err         error                   // error from Trillian client +		wantCode    int                     // HTTP status ok  	}{  		{ -			description: "invalid: bad request: empty", -			breq:        bytes.NewBuffer(nil), +			description: "invalid: bad request (parser error)", +			ascii:       bytes.NewBufferString("key=value\n"),  			wantCode:    http.StatusBadRequest,  		},  		{ -			description: "invalid: bad Trillian response: error", -			breq:        bytes.NewBuffer(marshal(t, types.GetEntriesV1{Start: 0, End: 0})), -			terr:        fmt.Errorf("backend failure"), -			wantCode:    http.StatusInternalServerError, -		}, -		{ -			description: "valid", // remember that newLogParameters() have testdata.MaxRange configured -			breq:        bytes.NewBuffer(marshal(t, types.GetEntriesV1{Start: 0, End: uint64(testdata.MaxRange - 1)})), -			trsp:        testdata.DefaultTGlbrr(t, 0, testdata.MaxRange-1), +			description: "valid", +			ascii:       buf(1, 2), +			expect:      true, +			rsp:         testProof,  			wantCode:    http.StatusOK, -			wantItem:    testdata.DefaultStItemList(t, 0, uint64(testdata.MaxRange)-1),  		},  	} { -		func() { // run deferred functions at the end of each iteration -			ti := newTestInstance(t, nil) -			defer ti.ctrl.Finish() - -			url := EndpointGetEntries.Path("http://example.com", ti.instance.LogParameters.Prefix) -			req, err := http.NewRequest("POST", url, table.breq) +		// Run deferred functions at the end of each iteration +		func() { +			ctrl := gomock.NewController(t) +			defer ctrl.Finish() +			client := mocks.NewMockClient(ctrl) +			if table.expect { +				client.EXPECT().GetConsistencyProof(gomock.Any(), gomock.Any()).Return(table.rsp, table.err) +			} +			i := Instance{ +				Config: testConfig, +				Client: client, +			} + +			// Create HTTP request +			url := types.EndpointGetConsistencyProof.Path("http://example.com", i.Prefix) +			req, err := http.NewRequest("POST", url, table.ascii)  			if err != nil {  				t.Fatalf("must create http request: %v", err)  			} -			req.Header.Set("Content-Type", "application/octet-stream") -			if table.trsp != nil || table.terr != nil { -				ti.client.EXPECT().GetLeavesByRange(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr) -			} +			// Run HTTP request  			w := httptest.NewRecorder() -			ti.postHandler(t, EndpointGetEntries).ServeHTTP(w, req) +			mustHandle(t, i, types.EndpointGetConsistencyProof).ServeHTTP(w, req)  			if got, want := w.Code, table.wantCode; got != want { -				t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description) -			} -			if w.Code != http.StatusOK { -				return -			} - -			var item types.StItemList -			if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil { -				t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err) -			} -			if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) { -				t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description) +				t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)  			}  		}()  	}  } -// TODO: TestWriteOctetResponse -func TestWriteOctetResponse(t *testing.T) { -} - -// deadlineMatcher implements gomock.Matcher, such that an error is raised if -// there is no context.Context deadline set -type deadlineMatcher struct{} - -// newDeadlineMatcher returns a new DeadlineMatcher -func newDeadlineMatcher() gomock.Matcher { -	return &deadlineMatcher{} -} - -// Matches returns true if the passed interface is a context with a deadline -func (dm *deadlineMatcher) Matches(i interface{}) bool { -	ctx, ok := i.(context.Context) -	if !ok { -		return false -	} -	_, ok = ctx.Deadline() -	return ok +func TestGetInclusionProof(t *testing.T) {  } -// String is needed to implement gomock.Matcher -func (dm *deadlineMatcher) String() string { -	return fmt.Sprintf("deadlineMatcher{}") +func TestGetLeaves(t *testing.T) {  } diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index 3441a0a..c2fe8fa 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -3,6 +3,7 @@ package stfe  import (  	"context"  	"crypto" +	"crypto/ed25519"  	"fmt"  	"net/http"  	"time" @@ -88,3 +89,71 @@ func (a Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {  		http.Error(w, fmt.Sprintf("%s%s%s%s", "Error", types.Delim, err.Error(), types.EOL), statusCode)  	}  } + +func (i *Instance) leafRequestFromHTTP(r *http.Request) (*types.LeafRequest, error) { +	var req types.LeafRequest +	if err := req.UnmarshalASCII(r.Body); err != nil { +		return nil, fmt.Errorf("UnmarshalASCII: %v", err) +	} + +	vk := ed25519.PublicKey(req.VerificationKey[:]) +	msg := req.Message.Marshal() +	sig := req.Signature[:] +	if !ed25519.Verify(vk, msg, sig) { +		return nil, fmt.Errorf("invalid signature") +	} +	// TODO: check shard hint +	// TODO: check domain hint +	return &req, nil +} + +func (i *Instance) cosignatureRequestFromHTTP(r *http.Request) (*types.CosignatureRequest, error) { +	var req types.CosignatureRequest +	if err := req.UnmarshalASCII(r.Body); err != nil { +		return nil, fmt.Errorf("unpackOctetPost: %v", err) +	} +	if _, ok := i.Witnesses[*req.KeyHash]; !ok { +		return nil, fmt.Errorf("Unknown witness: %x", req.KeyHash) +	} +	return &req, nil +} + +func (i *Instance) consistencyProofRequestFromHTTP(r *http.Request) (*types.ConsistencyProofRequest, error) { +	var req types.ConsistencyProofRequest +	if err := req.UnmarshalASCII(r.Body); err != nil { +		return nil, fmt.Errorf("UnmarshalASCII: %v", err) +	} +	if req.OldSize < 1 { +		return nil, fmt.Errorf("OldSize(%d) must be larger than zero", req.OldSize) +	} +	if req.NewSize <= req.OldSize { +		return nil, fmt.Errorf("NewSize(%d) must be larger than OldSize(%d)", req.NewSize, req.OldSize) +	} +	return &req, nil +} + +func (i *Instance) inclusionProofRequestFromHTTP(r *http.Request) (*types.InclusionProofRequest, error) { +	var req types.InclusionProofRequest +	if err := req.UnmarshalASCII(r.Body); err != nil { +		return nil, fmt.Errorf("UnmarshalASCII: %v", err) +	} +	if req.TreeSize < 1 { +		return nil, fmt.Errorf("TreeSize(%d) must be larger than zero", req.TreeSize) +	} +	return &req, nil +} + +func (i *Instance) leavesRequestFromHTTP(r *http.Request) (*types.LeavesRequest, error) { +	var req types.LeavesRequest +	if err := req.UnmarshalASCII(r.Body); err != nil { +		return nil, fmt.Errorf("UnmarshalASCII: %v", err) +	} + +	if req.StartSize > req.EndSize { +		return nil, fmt.Errorf("StartSize(%d) must be less than or equal to EndSize(%d)", req.StartSize, req.EndSize) +	} +	if req.EndSize-req.StartSize+1 > uint64(i.MaxRange) { +		req.EndSize = req.StartSize + uint64(i.MaxRange) - 1 +	} +	return &req, nil +} diff --git a/pkg/instance/instance_test.go b/pkg/instance/instance_test.go index a7a3d8a..45a2837 100644 --- a/pkg/instance/instance_test.go +++ b/pkg/instance/instance_test.go @@ -1,158 +1,9 @@  package stfe  import ( -	"crypto" -	"net/http" -	"net/http/httptest"  	"testing" - -	"github.com/golang/mock/gomock" -	"github.com/google/certificate-transparency-go/trillian/mockclient" -	"github.com/system-transparency/stfe/pkg/testdata" -	"github.com/system-transparency/stfe/pkg/types"  ) -type testInstance struct { -	ctrl     *gomock.Controller -	client   *mockclient.MockTrillianLogClient -	instance *Instance -} - -// newTestInstances sets up a test instance that uses default log parameters -// with an optional signer, see newLogParameters() for further details.  The -// SthSource is instantiated with an ActiveSthSource that has (i) the default -// STH as the currently cosigned STH based on testdata.Ed25519VkWitness, and -// (ii) the default STH without any cosignatures as the currently stable STH. -func newTestInstance(t *testing.T, signer crypto.Signer) *testInstance { -	t.Helper() -	ctrl := gomock.NewController(t) -	client := mockclient.NewMockTrillianLogClient(ctrl) -	return &testInstance{ -		ctrl:   ctrl, -		client: client, -		instance: &Instance{ -			Client:        client, -			LogParameters: newLogParameters(t, signer), -			SthSource: &ActiveSthSource{ -				client:          client, -				logParameters:   newLogParameters(t, signer), -				currCosth:       testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness}), -				nextCosth:       testdata.DefaultCosth(t, testdata.Ed25519VkLog, nil), -				cosignatureFrom: make(map[[types.NamespaceFingerprintSize]byte]bool), -			}, -		}, -	} -} - -// getHandlers returns all endpoints that use HTTP GET as a map to handlers -func (ti *testInstance) getHandlers(t *testing.T) map[Endpoint]Handler { -	t.Helper() -	return map[Endpoint]Handler{ -		EndpointGetLatestSth:   Handler{Instance: ti.instance, Handler: getLatestSth, Endpoint: EndpointGetLatestSth, Method: http.MethodGet}, -		EndpointGetStableSth:   Handler{Instance: ti.instance, Handler: getStableSth, Endpoint: EndpointGetStableSth, Method: http.MethodGet}, -		EndpointGetCosignedSth: Handler{Instance: ti.instance, Handler: getCosignedSth, Endpoint: EndpointGetCosignedSth, Method: http.MethodGet}, -	} -} - -// postHandlers returns all endpoints that use HTTP POST as a map to handlers -func (ti *testInstance) postHandlers(t *testing.T) map[Endpoint]Handler { -	t.Helper() -	return map[Endpoint]Handler{ -		EndpointAddEntry:            Handler{Instance: ti.instance, Handler: addEntry, Endpoint: EndpointAddEntry, Method: http.MethodPost}, -		EndpointAddCosignature:      Handler{Instance: ti.instance, Handler: addCosignature, Endpoint: EndpointAddCosignature, Method: http.MethodPost}, -		EndpointGetConsistencyProof: Handler{Instance: ti.instance, Handler: getConsistencyProof, Endpoint: EndpointGetConsistencyProof, Method: http.MethodPost}, -		EndpointGetProofByHash:      Handler{Instance: ti.instance, Handler: getProofByHash, Endpoint: EndpointGetProofByHash, Method: http.MethodPost}, -		EndpointGetEntries:          Handler{Instance: ti.instance, Handler: getEntries, Endpoint: EndpointGetEntries, Method: http.MethodPost}, -	} -} - -// getHandler must return a particular HTTP GET handler -func (ti *testInstance) getHandler(t *testing.T, endpoint Endpoint) Handler { -	t.Helper() -	handler, ok := ti.getHandlers(t)[endpoint] -	if !ok { -		t.Fatalf("must return HTTP GET handler for endpoint: %s", endpoint) -	} -	return handler -} - -// postHandler must return a particular HTTP POST handler -func (ti *testInstance) postHandler(t *testing.T, endpoint Endpoint) Handler { -	t.Helper() -	handler, ok := ti.postHandlers(t)[endpoint] -	if !ok { -		t.Fatalf("must return HTTP POST handler for endpoint: %s", endpoint) -	} -	return handler -} - -// TestHandlers checks that we configured all endpoints and that there are no -// unexpected ones. -func TestHandlers(t *testing.T) { -	endpoints := map[Endpoint]bool{ -		EndpointAddEntry:            false, -		EndpointAddCosignature:      false, -		EndpointGetLatestSth:        false, -		EndpointGetStableSth:        false, -		EndpointGetCosignedSth:      false, -		EndpointGetConsistencyProof: false, -		EndpointGetProofByHash:      false, -		EndpointGetEntries:          false, -	} -	i := &Instance{nil, newLogParameters(t, nil), nil} -	for _, handler := range i.Handlers() { -		if _, ok := endpoints[handler.Endpoint]; !ok { -			t.Errorf("got unexpected endpoint: %s", handler.Endpoint) -		} -		endpoints[handler.Endpoint] = true -	} -	for endpoint, ok := range endpoints { -		if !ok { -			t.Errorf("endpoint %s is not configured", endpoint) -		} -	} -} - -// TestGetHandlersRejectPost checks that all get handlers reject post requests -func TestGetHandlersRejectPost(t *testing.T) { -	ti := newTestInstance(t, nil) -	defer ti.ctrl.Finish() - -	for endpoint, handler := range ti.getHandlers(t) { -		t.Run(string(endpoint), func(t *testing.T) { -			s := httptest.NewServer(handler) -			defer s.Close() - -			url := endpoint.Path(s.URL, ti.instance.LogParameters.Prefix) -			if rsp, err := http.Post(url, "application/json", nil); err != nil { -				t.Fatalf("http.Post(%s)=(_,%q), want (_,nil)", url, err) -			} else if rsp.StatusCode != http.StatusMethodNotAllowed { -				t.Errorf("http.Post(%s)=(%d,nil), want (%d, nil)", url, rsp.StatusCode, http.StatusMethodNotAllowed) -			} -		}) -	} -} - -// TestPostHandlersRejectGet checks that all post handlers reject get requests -func TestPostHandlersRejectGet(t *testing.T) { -	ti := newTestInstance(t, nil) -	defer ti.ctrl.Finish() - -	for endpoint, handler := range ti.postHandlers(t) { -		t.Run(string(endpoint), func(t *testing.T) { -			s := httptest.NewServer(handler) -			defer s.Close() - -			url := endpoint.Path(s.URL, ti.instance.LogParameters.Prefix) -			if rsp, err := http.Get(url); err != nil { -				t.Fatalf("http.Get(%s)=(_,%q), want (_,nil)", url, err) -			} else if rsp.StatusCode != http.StatusMethodNotAllowed { -				t.Errorf("http.Get(%s)=(%d,nil), want (%d, nil)", url, rsp.StatusCode, http.StatusMethodNotAllowed) -			} -		}) -	} -} - -// TODO: TestHandlerPath -func TestHandlerPath(t *testing.T) { -} +func TestHandlers(t *testing.T)  {} +func TestPath(t *testing.T)      {} +func TestServeHTTP(t *testing.T) {} diff --git a/pkg/instance/metric.go b/pkg/instance/metric.go index 7e3e8b2..db11bd2 100644 --- a/pkg/instance/metric.go +++ b/pkg/instance/metric.go @@ -6,11 +6,9 @@ import (  )  var ( -	reqcnt           monitoring.Counter   // number of incoming http requests -	rspcnt           monitoring.Counter   // number of valid http responses -	latency          monitoring.Histogram // request-response latency -	lastSthTimestamp monitoring.Gauge     // unix timestamp from the most recent sth -	lastSthSize      monitoring.Gauge     // tree size of most recent sth +	reqcnt  monitoring.Counter   // number of incoming http requests +	rspcnt  monitoring.Counter   // number of valid http responses +	latency monitoring.Histogram // request-response latency  )  func init() { @@ -18,6 +16,4 @@ func init() {  	reqcnt = mf.NewCounter("http_req", "number of http requests", "logid", "endpoint")  	rspcnt = mf.NewCounter("http_rsp", "number of http requests", "logid", "endpoint", "status")  	latency = mf.NewHistogram("http_latency", "http request-response latency", "logid", "endpoint", "status") -	lastSthTimestamp = mf.NewGauge("last_sth_timestamp", "unix timestamp while handling the most recent sth", "logid") -	lastSthSize = mf.NewGauge("last_sth_size", "most recent sth tree size", "logid")  } diff --git a/pkg/instance/request.go b/pkg/instance/request.go deleted file mode 100644 index 7475b26..0000000 --- a/pkg/instance/request.go +++ /dev/null @@ -1,77 +0,0 @@ -package stfe - -import ( -	"crypto/ed25519" -	"fmt" -	"net/http" - -	"github.com/system-transparency/stfe/pkg/types" -) - -func (i *Instance) leafRequestFromHTTP(r *http.Request) (*types.LeafRequest, error) { -	var req types.LeafRequest -	if err := req.UnmarshalASCII(r.Body); err != nil { -		return nil, fmt.Errorf("UnmarshalASCII: %v", err) -	} - -	vk := ed25519.PublicKey(req.VerificationKey[:]) -	msg := req.Message.Marshal() -	sig := req.Signature[:] -	if !ed25519.Verify(vk, msg, sig) { -		return nil, fmt.Errorf("invalid signature") -	} -	// TODO: check shard hint -	// TODO: check domain hint -	return &req, nil -} - -func (i *Instance) cosignatureRequestFromHTTP(r *http.Request) (*types.CosignatureRequest, error) { -	var req types.CosignatureRequest -	if err := req.UnmarshalASCII(r.Body); err != nil { -		return nil, fmt.Errorf("unpackOctetPost: %v", err) -	} -	if _, ok := i.Witnesses[*req.KeyHash]; !ok { -		return nil, fmt.Errorf("Unknown witness: %x", req.KeyHash) -	} -	return &req, nil -} - -func (i *Instance) consistencyProofRequestFromHTTP(r *http.Request) (*types.ConsistencyProofRequest, error) { -	var req types.ConsistencyProofRequest -	if err := req.UnmarshalASCII(r.Body); err != nil { -		return nil, fmt.Errorf("UnmarshalASCII: %v", err) -	} -	if req.OldSize < 1 { -		return nil, fmt.Errorf("OldSize(%d) must be larger than zero", req.OldSize) -	} -	if req.NewSize <= req.OldSize { -		return nil, fmt.Errorf("NewSize(%d) must be larger than OldSize(%d)", req.NewSize, req.OldSize) -	} -	return &req, nil -} - -func (i *Instance) inclusionProofRequestFromHTTP(r *http.Request) (*types.InclusionProofRequest, error) { -	var req types.InclusionProofRequest -	if err := req.UnmarshalASCII(r.Body); err != nil { -		return nil, fmt.Errorf("UnmarshalASCII: %v", err) -	} -	if req.TreeSize < 1 { -		return nil, fmt.Errorf("TreeSize(%d) must be larger than zero", req.TreeSize) -	} -	return &req, nil -} - -func (i *Instance) leavesRequestFromHTTP(r *http.Request) (*types.LeavesRequest, error) { -	var req types.LeavesRequest -	if err := req.UnmarshalASCII(r.Body); err != nil { -		return nil, fmt.Errorf("UnmarshalASCII: %v", err) -	} - -	if req.StartSize > req.EndSize { -		return nil, fmt.Errorf("StartSize(%d) must be less than or equal to EndSize(%d)", req.StartSize, req.EndSize) -	} -	if req.EndSize-req.StartSize+1 > uint64(i.MaxRange) { -		req.EndSize = req.StartSize + uint64(i.MaxRange) - 1 -	} -	return &req, nil -} diff --git a/pkg/instance/request_test.go b/pkg/instance/request_test.go deleted file mode 100644 index 0a5a908..0000000 --- a/pkg/instance/request_test.go +++ /dev/null @@ -1,318 +0,0 @@ -package stfe - -import ( -	"bytes" -	//"fmt" -	"reflect" -	"testing" -	//"testing/iotest" - -	"net/http" - -	"github.com/system-transparency/stfe/pkg/testdata" -	"github.com/system-transparency/stfe/pkg/types" -) - -func TestParseAddEntryV1Request(t *testing.T) { -	lp := newLogParameters(t, nil) -	for _, table := range []struct { -		description string -		breq        *bytes.Buffer -		wantErr     bool -	}{ -		{ -			description: "invalid: nothing to unpack", -			breq:        bytes.NewBuffer(nil), -			wantErr:     true, -		}, -		{ -			description: "invalid: not a signed checksum entry", -			breq:        testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness, &testdata.Ed25519VkWitness), -			wantErr:     true, -		}, -		{ -			description: "invalid: untrusted submitter", // only testdata.Ed25519VkSubmitter is registered by default in newLogParameters() - -			breq:    testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter2, testdata.Ed25519VkSubmitter2), -			wantErr: true, -		}, -		{ -			description: "invalid: signature does not cover message", - -			breq:    testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter2, testdata.Ed25519VkSubmitter), -			wantErr: true, -		}, -		{ -			description: "valid", -			breq:        testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter, testdata.Ed25519VkSubmitter), -		}, // TODO: add test case that disables submitter policy (i.e., unregistered namespaces are accepted) -	} { -		url := EndpointAddEntry.Path("http://example.com", lp.Prefix) -		req, err := http.NewRequest("POST", url, table.breq) -		if err != nil { -			t.Fatalf("failed creating http request: %v", err) -		} -		req.Header.Set("Content-Type", "application/octet-stream") - -		_, err = lp.parseAddEntryV1Request(req) -		if got, want := err != nil, table.wantErr; got != want { -			t.Errorf("got errror %v but wanted %v in test %q: %v", got, want, table.description, err) -		} -	} -} - -func TestParseAddCosignatureV1Request(t *testing.T) { -	lp := newLogParameters(t, nil) -	for _, table := range []struct { -		description string -		breq        *bytes.Buffer -		wantErr     bool -	}{ -		{ -			description: "invalid: nothing to unpack", -			breq:        bytes.NewBuffer(nil), -			wantErr:     true, -		}, -		{ -			description: "invalid: not a cosigned sth", -			breq:        testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter, testdata.Ed25519VkSubmitter), -			wantErr:     true, -		}, -		{ -			description: "invalid: no cosignature", -			breq:        testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness, nil), -			wantErr:     true, -		}, -		{ -			description: "invalid: untrusted witness", // only testdata.Ed25519VkWitness is registered by default in newLogParameters() -			breq:        testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness2, &testdata.Ed25519VkWitness2), -			wantErr:     true, -		}, -		{ -			description: "invalid: signature does not cover message", -			breq:        testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness2, &testdata.Ed25519VkWitness), -			wantErr:     true, -		}, -		{ -			description: "valid", -			breq:        testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness, &testdata.Ed25519VkWitness), -		}, // TODO: add test case that disables witness policy (i.e., unregistered namespaces are accepted) -	} { -		url := EndpointAddCosignature.Path("http://example.com", lp.Prefix) -		req, err := http.NewRequest("POST", url, table.breq) -		if err != nil { -			t.Fatalf("failed creating http request: %v", err) -		} -		req.Header.Set("Content-Type", "application/octet-stream") - -		_, err = lp.parseAddCosignatureV1Request(req) -		if got, want := err != nil, table.wantErr; got != want { -			t.Errorf("got errror %v but wanted %v in test %q: %v", got, want, table.description, err) -		} -	} -} - -func TestNewGetConsistencyProofRequest(t *testing.T) { -	lp := newLogParameters(t, nil) -	for _, table := range []struct { -		description string -		req         *types.GetConsistencyProofV1 -		wantErr     bool -	}{ -		{ -			description: "invalid: nothing to unpack", -			req:         nil, -			wantErr:     true, -		}, -		{ -			description: "invalid: first must be larger than zero", -			req:         &types.GetConsistencyProofV1{First: 0, Second: 0}, -			wantErr:     true, -		}, -		{ -			description: "invalid: second must be larger than first", -			req:         &types.GetConsistencyProofV1{First: 2, Second: 1}, -			wantErr:     true, -		}, -		{ -			description: "valid", -			req:         &types.GetConsistencyProofV1{First: 1, Second: 2}, -		}, -	} { -		var buf *bytes.Buffer -		if table.req == nil { -			buf = bytes.NewBuffer(nil) -		} else { -			buf = bytes.NewBuffer(marshal(t, *table.req)) -		} - -		url := EndpointGetConsistencyProof.Path("http://example.com", lp.Prefix) -		req, err := http.NewRequest("POST", url, buf) -		if err != nil { -			t.Fatalf("failed creating http request: %v", err) -		} -		req.Header.Set("Content-Type", "application/octet-stream") - -		_, err = lp.parseGetConsistencyProofV1Request(req) -		if got, want := err != nil, table.wantErr; got != want { -			t.Errorf("got errror %v but wanted %v in test %q: %v", got, want, table.description, err) -		} -	} -} - -func TestNewGetProofByHashRequest(t *testing.T) { -	lp := newLogParameters(t, nil) -	for _, table := range []struct { -		description string -		req         *types.GetProofByHashV1 -		wantErr     bool -	}{ -		{ -			description: "invalid: nothing to unpack", -			req:         nil, -			wantErr:     true, -		}, -		{ -			description: "invalid: no entry in an empty tree", -			req:         &types.GetProofByHashV1{TreeSize: 0, Hash: testdata.LeafHash}, -			wantErr:     true, -		}, -		{ -			description: "valid", -			req:         &types.GetProofByHashV1{TreeSize: 1, Hash: testdata.LeafHash}, -		}, -	} { -		var buf *bytes.Buffer -		if table.req == nil { -			buf = bytes.NewBuffer(nil) -		} else { -			buf = bytes.NewBuffer(marshal(t, *table.req)) -		} - -		url := EndpointGetProofByHash.Path("http://example.com", lp.Prefix) -		req, err := http.NewRequest("POST", url, buf) -		if err != nil { -			t.Fatalf("failed creating http request: %v", err) -		} -		req.Header.Set("Content-Type", "application/octet-stream") - -		_, err = lp.parseGetProofByHashV1Request(req) -		if got, want := err != nil, table.wantErr; got != want { -			t.Errorf("got errror %v but wanted %v in test %q: %v", got, want, table.description, err) -		} -	} -} - -func TestParseGetEntriesV1Request(t *testing.T) { -	lp := newLogParameters(t, nil) -	for _, table := range []struct { -		description string -		req         *types.GetEntriesV1 -		wantErr     bool -		wantReq     *types.GetEntriesV1 -	}{ -		{ -			description: "invalid: nothing to unpack", -			req:         nil, -			wantErr:     true, -		}, -		{ -			description: "invalid: start must be larger than end", -			req:         &types.GetEntriesV1{Start: 1, End: 0}, -			wantErr:     true, -		}, -		{ -			description: "valid: want truncated range", -			req:         &types.GetEntriesV1{Start: 0, End: uint64(testdata.MaxRange)}, -			wantReq:     &types.GetEntriesV1{Start: 0, End: uint64(testdata.MaxRange) - 1}, -		}, -		{ -			description: "valid", -			req:         &types.GetEntriesV1{Start: 0, End: 0}, -			wantReq:     &types.GetEntriesV1{Start: 0, End: 0}, -		}, -	} { -		var buf *bytes.Buffer -		if table.req == nil { -			buf = bytes.NewBuffer(nil) -		} else { -			buf = bytes.NewBuffer(marshal(t, *table.req)) -		} - -		url := EndpointGetEntries.Path("http://example.com", lp.Prefix) -		req, err := http.NewRequest("POST", url, buf) -		if err != nil { -			t.Fatalf("failed creating http request: %v", err) -		} -		req.Header.Set("Content-Type", "application/octet-stream") - -		output, err := lp.parseGetEntriesV1Request(req) -		if got, want := err != nil, table.wantErr; got != want { -			t.Errorf("got errror %v but wanted %v in test %q: %v", got, want, table.description, err) -		} -		if err != nil { -			continue -		} -		if got, want := output, table.wantReq; !reflect.DeepEqual(got, want) { -			t.Errorf("got request\n%v\n\tbut wanted\n%v\n\t in test %q", got, want, table.description) -		} -	} -} - -func TestUnpackOctetPost(t *testing.T) { -	for _, table := range []struct { -		description string -		req         *http.Request -		out         interface{} -		wantErr     bool -	}{ -		//{ -		//	description: "invalid: cannot read request body", -		//	req: func() *http.Request { -		//		req, err := http.NewRequest(http.MethodPost, "", iotest.ErrReader(fmt.Errorf("bad reader"))) -		//		if err != nil { -		//			t.Fatalf("must make new http request: %v", err) -		//		} -		//		return req -		//	}(), -		//	out:     &types.StItem{}, -		//	wantErr: true, -		//}, // testcase requires Go 1.16 -		{ -			description: "invalid: cannot unmarshal", -			req: func() *http.Request { -				req, err := http.NewRequest(http.MethodPost, "", bytes.NewBuffer(nil)) -				if err != nil { -					t.Fatalf("must make new http request: %v", err) -				} -				return req -			}(), -			out:     &types.StItem{}, -			wantErr: true, -		}, -		{ -			description: "valid", -			req: func() *http.Request { -				req, err := http.NewRequest(http.MethodPost, "", bytes.NewBuffer([]byte{0})) -				if err != nil { -					t.Fatalf("must make new http request: %v", err) -				} -				return req -			}(), -			out: &struct{ SomeUint8 uint8 }{}, -		}, -	} { -		err := unpackOctetPost(table.req, table.out) -		if got, want := err != nil, table.wantErr; got != want { -			t.Errorf("got error %v but wanted %v in test %q", got, want, table.description) -		} -	} -} - -func marshal(t *testing.T, out interface{}) []byte { -	b, err := types.Marshal(out) -	if err != nil { -		t.Fatalf("must marshal: %v", err) -	} -	return b -} | 
