// proxyMessages forwards a set of pubsub messages to the endpoint proxy. func (a *application) proxyMessages(ctx context.Context, msgs []*pubsub.Message) error { log.Fields{ "size": len(msgs), }.Debugf(ctx, "Sending messages to Proxy.") // TODO: Batch messages together into larger pushes. // TODO: Validate messages. err := parallel.FanOutIn(func(c chan<- func() error) { for idx, msg := range msgs { msg := msg c <- func() error { ctx := log.SetFields(ctx, log.Fields{ "size": len(msg.Data), "messageID": msg.ID, }) err := a.proxySingleMessage(ctx, msg) // If we hit a transient error, set the message's element to nil, // causing it to not be ACKed. if err != nil { transient := luciErrors.IsTransient(err) log.Fields{ log.ErrorKey: err, "transient": transient, }.Errorf(ctx, "Error when pushing message.") if transient { msgs[idx] = nil } } return err } } }) merr, _ := err.(luciErrors.MultiError) log.Fields{ "errorStatus": err, "count": len(msgs), "errorCount": len(merr), }.Infof(ctx, "Sent messages to endpoint.") return err }
// Next implements the Iterator interface. func (i *TransientOnly) Next(ctx context.Context, err error) time.Duration { if !errors.IsTransient(err) { return Stop } return i.Iterator.Next(ctx, err) }
// TestEndpoint tests the endpoint implementation and API. func TestEndpointService(t *testing.T) { t.Parallel() Convey(`An endpoint service connected to a testing HTTP server`, t, func() { ctx, tc := testclock.UseTime(context.Background(), time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)) // Retry up to ten times without delaying. ctx = context.WithValue(ctx, backoffPolicyKey, func() retry.Iterator { return &retry.Limited{Retries: 10} }) h := &testEndpointServiceHandler{} srv := httptest.NewTLSServer(h) defer srv.Close() tr := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, } client := &http.Client{ Transport: tr, } c := &endpointServiceImpl{ endpointConfig: endpointConfig{url: srv.URL}, client: client, } msg := bytes.Repeat([]byte{0x60, 0x0d, 0xd0, 0x65}, 32) Convey(`Successfully posts a message.`, func() { So(c.send(ctx, msg), ShouldBeNil) So(h.connections, ShouldEqual, 1) So(h.errors, ShouldEqual, 0) So(h.messages, ShouldResemble, [][]byte{msg}) }) Convey(`Retries sending when an error is encountered.`, func() { tc.SetTimerCallback(func(t clock.Timer) { tc.Add(time.Second) }) h.failures = 4 So(c.send(ctx, msg), ShouldBeNil) So(h.connections, ShouldEqual, 5) So(h.errors, ShouldEqual, 4) So(h.messages, ShouldResemble, [][]byte{msg}) }) Convey(`Returns a transient error when a send completely fails.`, func() { h.failures = 11 err := c.send(ctx, msg) So(err, ShouldNotBeNil) So(errors.IsTransient(err), ShouldBeTrue) So(h.connections, ShouldEqual, 11) So(h.errors, ShouldEqual, 11) So(h.messages, ShouldResemble, [][]byte(nil)) }) }) }