commit 42e16021c49b59433450c8f5b5a54e449f9dc522 Author: Cecylia Bocovich cohosh@torproject.org Date: Mon Nov 25 14:00:54 2019 -0500
Add tests to check for data race in broker
We had some data races in the broker that occur when proxies and clients modify the heap/snowflake map at the same time. This test has a client and proxy access the broker simultaneously to check for data races. --- broker/snowflake-broker_test.go | 144 +++++++++++++++++++++++++++------------- 1 file changed, 97 insertions(+), 47 deletions(-)
diff --git a/broker/snowflake-broker_test.go b/broker/snowflake-broker_test.go index b23e688..18b83dd 100644 --- a/broker/snowflake-broker_test.go +++ b/broker/snowflake-broker_test.go @@ -191,58 +191,108 @@ func TestBroker(t *testing.T) { })
}) + })
Convey("End-To-End", t, func() { - done := make(chan bool) - polled := make(chan bool) ctx := NewBrokerContext(NullLogger())
- // Proxy polls with its ID first... - dataP := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`)) - wP := httptest.NewRecorder() - rP, err := http.NewRequest("POST", "snowflake.broker/proxy", dataP) - So(err, ShouldBeNil) - go func() { - proxyPolls(ctx, wP, rP) - polled <- true - }() - - // Manually do the Broker goroutine action here for full control. - p := <-ctx.proxyPolls - So(p.id, ShouldEqual, "ymbcCMto7KHNGYlp") - s := ctx.AddSnowflake(p.id, "") - go func() { - offer := <-s.offerChannel - p.offerChannel <- offer - }() - So(ctx.idToSnowflake["ymbcCMto7KHNGYlp"], ShouldNotBeNil) - - // Client request blocks until proxy answer arrives. - dataC := bytes.NewReader([]byte("fake offer")) - wC := httptest.NewRecorder() - rC, err := http.NewRequest("POST", "snowflake.broker/client", dataC) - So(err, ShouldBeNil) - go func() { - clientOffers(ctx, wC, rC) - done <- true - }() - - <-polled - So(wP.Code, ShouldEqual, http.StatusOK) - So(wP.Body.String(), ShouldResemble, `{"Status":"client match","Offer":"fake offer"}`) - So(ctx.idToSnowflake["ymbcCMto7KHNGYlp"], ShouldNotBeNil) - // Follow up with the answer request afterwards - wA := httptest.NewRecorder() - dataA := bytes.NewReader([]byte(`{"Version":"1.0","Sid":"ymbcCMto7KHNGYlp","Answer":"test"}`)) - rA, err := http.NewRequest("POST", "snowflake.broker/answer", dataA) - So(err, ShouldBeNil) - proxyAnswers(ctx, wA, rA) - So(wA.Code, ShouldEqual, http.StatusOK) - - <-done - So(wC.Code, ShouldEqual, http.StatusOK) - So(wC.Body.String(), ShouldEqual, "test") + Convey("Check for client/proxy data race", func() { + proxy_done := make(chan bool) + client_done := make(chan bool) + + go ctx.Broker() + + // Make proxy poll + wp := httptest.NewRecorder() + datap := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`)) + rp, err := http.NewRequest("POST", "snowflake.broker/proxy", datap) + So(err, ShouldBeNil) + + go func(ctx *BrokerContext) { + proxyPolls(ctx, wp, rp) + proxy_done <- true + }(ctx) + + // Client offer + wc := httptest.NewRecorder() + datac := bytes.NewReader([]byte("test")) + rc, err := http.NewRequest("POST", "snowflake.broker/client", datac) + So(err, ShouldBeNil) + + go func() { + clientOffers(ctx, wc, rc) + client_done <- true + }() + + <-proxy_done + So(wp.Code, ShouldEqual, http.StatusOK) + + // Proxy answers + wp = httptest.NewRecorder() + datap = bytes.NewReader([]byte(`{"Version":"1.0","Sid":"ymbcCMto7KHNGYlp","Answer":"test"}`)) + rp, err = http.NewRequest("POST", "snowflake.broker/answer", datap) + So(err, ShouldBeNil) + go func(ctx *BrokerContext) { + proxyAnswers(ctx, wp, rp) + proxy_done <- true + }(ctx) + + <-proxy_done + <-client_done + + }) + + Convey("Ensure correct snowflake brokering", func() { + done := make(chan bool) + polled := make(chan bool) + + // Proxy polls with its ID first... + dataP := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.0"}`)) + wP := httptest.NewRecorder() + rP, err := http.NewRequest("POST", "snowflake.broker/proxy", dataP) + So(err, ShouldBeNil) + go func() { + proxyPolls(ctx, wP, rP) + polled <- true + }() + + // Manually do the Broker goroutine action here for full control. + p := <-ctx.proxyPolls + So(p.id, ShouldEqual, "ymbcCMto7KHNGYlp") + s := ctx.AddSnowflake(p.id, "") + go func() { + offer := <-s.offerChannel + p.offerChannel <- offer + }() + So(ctx.idToSnowflake["ymbcCMto7KHNGYlp"], ShouldNotBeNil) + + // Client request blocks until proxy answer arrives. + dataC := bytes.NewReader([]byte("fake offer")) + wC := httptest.NewRecorder() + rC, err := http.NewRequest("POST", "snowflake.broker/client", dataC) + So(err, ShouldBeNil) + go func() { + clientOffers(ctx, wC, rC) + done <- true + }() + + <-polled + So(wP.Code, ShouldEqual, http.StatusOK) + So(wP.Body.String(), ShouldResemble, `{"Status":"client match","Offer":"fake offer"}`) + So(ctx.idToSnowflake["ymbcCMto7KHNGYlp"], ShouldNotBeNil) + // Follow up with the answer request afterwards + wA := httptest.NewRecorder() + dataA := bytes.NewReader([]byte(`{"Version":"1.0","Sid":"ymbcCMto7KHNGYlp","Answer":"test"}`)) + rA, err := http.NewRequest("POST", "snowflake.broker/answer", dataA) + So(err, ShouldBeNil) + proxyAnswers(ctx, wA, rA) + So(wA.Code, ShouldEqual, http.StatusOK) + + <-done + So(wC.Code, ShouldEqual, http.StatusOK) + So(wC.Body.String(), ShouldEqual, "test") + }) }) }