/* Wraps the RoundTrip function of the given RoundTripper. Will provide accounting metrics for the http.Request / http.Response life-cycle Callers of RoundTrip are responsible for setting the ‘X-CF-RequestID’ field in the request header if they have one. Callers are also responsible for setting the ‘X-CF-ApplicationID’ and ‘X-CF-InstanceIndex’ fields in the request header if they are known. */ func (irt *instrumentedRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { requestId, err := GenerateUuid() if err != nil { log.Printf("failed to generated request ID: %v\n", err) requestId = &uuid.UUID{} } startTime := time.Now() parentRequestId := req.Header.Get("X-CF-RequestID") req.Header.Set("X-CF-RequestID", requestId.String()) resp, roundTripErr := irt.roundTripper.RoundTrip(req) var statusCode int var contentLength int64 if roundTripErr == nil { statusCode = resp.StatusCode contentLength = resp.ContentLength } httpStartStop := factories.NewHttpStartStop(req, statusCode, contentLength, events.PeerType_Client, requestId) if parentRequestId != "" { if id, err := uuid.ParseHex(parentRequestId); err == nil { httpStartStop.ParentRequestId = factories.NewUUID(id) } } httpStartStop.StartTimestamp = proto.Int64(startTime.UnixNano()) err = irt.emitter.Emit(httpStartStop) if err != nil { log.Printf("failed to emit startstop event: %v\n", err) } return resp, roundTripErr }
func basicHTTPStopEventEnvelope() *events.Envelope { uuid, _ := uuid.ParseHex("9f45fa9d-dbf8-463e-425f-a79d30e1b56f") return &events.Envelope{ Origin: proto.String("fake-origin-2"), EventType: events.Envelope_HttpStop.Enum(), HttpStop: &events.HttpStop{ Timestamp: proto.Int64(12), Uri: proto.String("some uri"), RequestId: factories.NewUUID(uuid), PeerType: events.PeerType_Client.Enum(), StatusCode: proto.Int32(404), ContentLength: proto.Int64(98475189), }, } }
func basicHTTPStopMessageEnvelope(requestId string) *events.Envelope { uuid, _ := uuid.ParseHex(requestId) return &events.Envelope{ Origin: proto.String("fake-origin-2"), EventType: events.Envelope_HttpStop.Enum(), HttpStop: &events.HttpStop{ Timestamp: proto.Int64(12), Uri: proto.String("some uri"), RequestId: factories.NewUUID(uuid), PeerType: events.PeerType_Client.Enum(), StatusCode: proto.Int32(404), ContentLength: proto.Int64(98475189), }, } }
func basicHTTPStartEventEnvelope() *events.Envelope { uuid, _ := uuid.ParseHex("9f45fa9d-dbf8-463e-425f-a79d30e1b56f") return &events.Envelope{ Origin: proto.String("fake-origin-2"), EventType: events.Envelope_HttpStart.Enum(), HttpStart: &events.HttpStart{ Timestamp: proto.Int64(12), RequestId: factories.NewUUID(uuid), PeerType: events.PeerType_Client.Enum(), Method: events.Method_GET.Enum(), Uri: proto.String("some uri"), RemoteAddress: proto.String("some address"), UserAgent: proto.String("some user agent"), }, } }
/* Wraps the RoundTrip function of the given RoundTripper. Will provide accounting metrics for the http.Request / http.Response life-cycle Callers of RoundTrip are responsible for setting the ‘X-CF-RequestID’ field in the request header if they have one. Callers are also responsible for setting the ‘X-CF-ApplicationID’ and ‘X-CF-InstanceIndex’ fields in the request header if they are known. */ func (irt *instrumentedRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { requestId, err := GenerateUuid() if err != nil { log.Printf("failed to generated request ID: %v\n", err) requestId = &uuid.UUID{} } httpStart := factories.NewHttpStart(req, events.PeerType_Client, requestId) parentRequestId, err := uuid.ParseHex(req.Header.Get("X-CF-RequestID")) if err == nil { httpStart.ParentRequestId = factories.NewUUID(parentRequestId) } req.Header.Set("X-CF-RequestID", requestId.String()) err = irt.emitter.Emit(httpStart) if err != nil { log.Printf("failed to emit start event: %v\n", err) } resp, roundTripErr := irt.roundTripper.RoundTrip(req) var httpStop *events.HttpStop if roundTripErr != nil { httpStop = factories.NewHttpStop(req, 0, 0, events.PeerType_Client, requestId) } else { httpStop = factories.NewHttpStop(req, resp.StatusCode, resp.ContentLength, events.PeerType_Client, requestId) } err = irt.emitter.Emit(httpStop) if err != nil { log.Printf("failed to emit stop event: %v\n", err) } return resp, roundTripErr }
req.Header.Set("X-Vcap-Request-Id", requestId.String()) conn.WriteRequest(req) findStartStopEvent := func() *events.HttpStartStop { for _, event := range fakeEmitter.GetEvents() { startStopEvent, ok := event.(*events.HttpStartStop) if ok { return startStopEvent } } return nil } Eventually(findStartStopEvent).ShouldNot(BeNil()) Expect(findStartStopEvent().GetParentRequestId()).To(Equal(factories.NewUUID(requestId))) conn.ReadResponse() }) It("X-CF-InstanceID header is added with host:port information if NOT present in the routing endpoint", func() { done := make(chan string) ln := registerHandler(r, "app", func(conn *test_util.HttpConn) { req, err := http.ReadRequest(conn.Reader) Expect(err).NotTo(HaveOccurred()) resp := test_util.NewResponse(http.StatusOK) conn.WriteResponse(resp) conn.Close()
var origin string BeforeEach(func() { origin = "testEventFormatter/42" }) It("works with dropsonde status (Heartbeat) events", func() { statusEvent := &events.Heartbeat{SentCount: proto.Uint64(1), ErrorCount: proto.Uint64(0)} envelope, _ := emitter.Wrap(statusEvent, origin) Expect(envelope.GetEventType()).To(Equal(events.Envelope_Heartbeat)) Expect(envelope.GetHeartbeat()).To(Equal(statusEvent)) }) It("works with HttpStart events", func() { id, _ := uuid.NewV4() testEvent := &events.HttpStart{RequestId: factories.NewUUID(id)} envelope, _ := emitter.Wrap(testEvent, origin) Expect(envelope.GetEventType()).To(Equal(events.Envelope_HttpStart)) Expect(envelope.GetHttpStart()).To(Equal(testEvent)) }) It("works with HttpStop events", func() { id, _ := uuid.NewV4() testEvent := &events.HttpStop{RequestId: factories.NewUUID(id)} envelope, _ := emitter.Wrap(testEvent, origin) Expect(envelope.GetEventType()).To(Equal(events.Envelope_HttpStop)) Expect(envelope.GetHttpStop()).To(Equal(testEvent)) })
conn.WriteRequest(req) findStartStopEvent := func() *events.HttpStartStop { for _, event := range fakeEmitter.GetEvents() { startStopEvent, ok := event.(*events.HttpStartStop) if ok { return startStopEvent } } return nil } Eventually(findStartStopEvent).ShouldNot(BeNil()) Expect(findStartStopEvent().RequestId).To(Equal(factories.NewUUID(requestId))) conn.ReadResponse() }) It("X-CF-InstanceID header is added with host:port information if NOT present in the routing endpoint", func() { done := make(chan string) ln := registerHandler(r, "app", func(conn *test_util.HttpConn) { req, err := http.ReadRequest(conn.Reader) Expect(err).NotTo(HaveOccurred()) resp := test_util.NewResponse(http.StatusOK) conn.WriteResponse(resp) conn.Close() done <- req.Header.Get(router_http.CfInstanceIdHeader)
Expect(fakeEmitter.Messages[0].Event).To(BeAssignableToTypeOf(new(events.HttpStart))) Expect(fakeEmitter.Messages[0].Origin).To(Equal("testRoundtripper/42")) }) Context("if request ID already exists", func() { var existingRequestId *uuid.UUID BeforeEach(func() { existingRequestId, _ = uuid.NewV4() req.Header.Set("X-CF-RequestID", existingRequestId.String()) }) It("should emit the existing request ID as the parent request ID", func() { rt.RoundTrip(req) startEvent := fakeEmitter.Messages[0].Event.(*events.HttpStart) Expect(startEvent.GetParentRequestId()).To(Equal(factories.NewUUID(existingRequestId))) }) }) Context("if round tripper returns an error", func() { It("should emit a stop event with blank response fields", func() { fakeRoundTripper.FakeError = errors.New("fakeEmitter error") rt.RoundTrip(req) Expect(fakeEmitter.Messages[1].Event).To(BeAssignableToTypeOf(new(events.HttpStop))) stopEvent := fakeEmitter.Messages[1].Event.(*events.HttpStop) Expect(stopEvent.GetStatusCode()).To(BeNumerically("==", 0)) Expect(stopEvent.GetContentLength()).To(BeNumerically("==", 0)) }) })
BeforeEach(func() { applicationId, _ = uuid.NewV4() requestId, _ = uuid.NewV4() req, _ = http.NewRequest("GET", "http://foo.example.com/", nil) req.RemoteAddr = "127.0.0.1" req.Header.Set("User-Agent", "our-testing-client") }) Describe("NewHttpStart", func() { Context("without an application ID or instanceIndex", func() { It("should set appropriate fields", func() { expectedStartEvent := &events.HttpStart{ RequestId: factories.NewUUID(requestId), PeerType: events.PeerType_Server.Enum(), Method: events.Method_GET.Enum(), Uri: proto.String("foo.example.com/"), RemoteAddress: proto.String("127.0.0.1"), UserAgent: proto.String("our-testing-client"), } startEvent := factories.NewHttpStart(req, events.PeerType_Server, requestId) Expect(startEvent.GetTimestamp()).ToNot(BeZero()) startEvent.Timestamp = nil Expect(startEvent).To(Equal(expectedStartEvent)) }) })