Skip to content

Commit 709cec0

Browse files
rghetiaBogdan Drutu
authored andcommitted
Add a common Http Handler for Http Client and Http Server. (census-instrumentation#1556)
* Add a common Http Handler for Http Client and Http Server. * Renamed HttpHandler to AbstractHttpHandler. - fixed few other comments. * Fixed review comment.
1 parent e1beb41 commit 709cec0

4 files changed

Lines changed: 323 additions & 1 deletion

File tree

‎buildscripts/import-control.xml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,13 @@ General guidelines on imports:
8888
<allow pkg="io.opencensus.stats"/>
8989
<allow pkg="io.opencensus.tags"/>
9090
</subpackage>
91-
<subpackage name="http.util">
91+
<subpackage name="http">
92+
<allow pkg="io.opencensus.contrib.http"/>
9293
<allow pkg="io.opencensus.contrib.http.util"/>
9394
<allow pkg="io.opencensus.stats"/>
9495
<allow pkg="io.opencensus.tags"/>
9596
<allow pkg="io.opencensus.trace"/>
97+
<allow pkg="io.opencensus.trace.propagation"/>
9698
</subpackage>
9799
<subpackage name="logcorrelation.log4j2">
98100
<allow pkg="io.opencensus.contrib.logcorrelation.log4j2"/>
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2018, OpenCensus Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.opencensus.contrib.http;
18+
19+
import static com.google.common.base.Preconditions.checkNotNull;
20+
21+
import com.google.common.annotations.VisibleForTesting;
22+
import io.opencensus.contrib.http.util.HttpTraceUtil;
23+
import io.opencensus.trace.AttributeValue;
24+
import io.opencensus.trace.MessageEvent;
25+
import io.opencensus.trace.MessageEvent.Type;
26+
import io.opencensus.trace.Span;
27+
import javax.annotation.Nullable;
28+
29+
/** Base class for handling request on http client and server. */
30+
abstract class AbstractHttpHandler<Q, P> {
31+
32+
/** The {@link HttpExtractor} used to extract information from request/response. */
33+
@VisibleForTesting final HttpExtractor<Q, P> extractor;
34+
35+
/** Constructor to allow access from same package subclasses only. */
36+
AbstractHttpHandler(HttpExtractor<Q, P> extractor) {
37+
checkNotNull(extractor, "extractor");
38+
this.extractor = extractor;
39+
}
40+
41+
/**
42+
* A convenience to record a {@link MessageEvent} with given parameters.
43+
*
44+
* @param span the span which this {@code MessageEvent} will be added to.
45+
* @param id the id of the event.
46+
* @param type the {@code MessageEvent.Type} of the event.
47+
* @param uncompressedMessageSize size of the message before compressed (optional).
48+
* @param compressedMessageSize size of the message after compressed (optional).
49+
* @since 0.18
50+
*/
51+
static void recordMessageEvent(
52+
Span span, long id, Type type, long uncompressedMessageSize, long compressedMessageSize) {
53+
MessageEvent messageEvent =
54+
MessageEvent.builder(type, id)
55+
.setUncompressedMessageSize(uncompressedMessageSize)
56+
.setCompressedMessageSize(compressedMessageSize)
57+
.build();
58+
span.addMessageEvent(messageEvent);
59+
}
60+
61+
/**
62+
* Instrument an HTTP span after a message is sent.
63+
*
64+
* @param span the span.
65+
* @param messageId an id for the message.
66+
* @param messageSize the size of the message.
67+
* @since 0.18
68+
*/
69+
public final void handleMessageSent(Span span, long messageId, long messageSize) {
70+
checkNotNull(span, "span");
71+
// record compressed size
72+
recordMessageEvent(span, messageId, Type.SENT, messageSize, 0L);
73+
}
74+
75+
/**
76+
* Instrument an HTTP span after a message is received.
77+
*
78+
* @param span the span.
79+
* @param messageId an id for the message.
80+
* @param messageSize the size of the message.
81+
* @since 0.18
82+
*/
83+
public final void handleMessageReceived(Span span, long messageId, long messageSize) {
84+
checkNotNull(span, "span");
85+
// record compressed size
86+
recordMessageEvent(span, messageId, Type.RECEIVED, messageSize, 0L);
87+
}
88+
89+
/**
90+
* Close an HTTP span.
91+
*
92+
* <p>This method will set status of the span and end it.
93+
*
94+
* @param response the HTTP response entity. {@code null} means invalid response.
95+
* @param error the error occurs when processing the response.
96+
* @param span the span.
97+
* @since 0.18
98+
*/
99+
public void handleEnd(Span span, @Nullable P response, @Nullable Throwable error) {
100+
checkNotNull(span, "span");
101+
int statusCode = extractor.getStatusCode(response);
102+
span.putAttribute("http.status_code", AttributeValue.longAttributeValue(statusCode));
103+
span.setStatus(HttpTraceUtil.parseResponseStatus(statusCode, error));
104+
span.end();
105+
}
106+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2018, OpenCensus Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.opencensus.contrib.http;
18+
19+
import io.opencensus.common.ExperimentalApi;
20+
import javax.annotation.Nullable;
21+
22+
/**
23+
* An adaptor to extract information from request and response.
24+
*
25+
* <p>This class provides no-op implementations by default.
26+
*
27+
* <p>Please refer to this <a
28+
* href="https://github.com/census-instrumentation/opencensus-specs/blob/master/trace/HTTP.md">document</a>
29+
* for more information about the HTTP attributes recorded in Open Census.
30+
*
31+
* @param <Q> the HTTP request entity.
32+
* @param <P> the HTTP response entity.
33+
* @since 0.18
34+
*/
35+
@ExperimentalApi
36+
public abstract class HttpExtractor<Q, P> {
37+
38+
/**
39+
* Returns the request route.
40+
*
41+
* @param request the HTTP request.
42+
* @return the request route.
43+
* @since 0.18
44+
*/
45+
@Nullable
46+
public abstract String getRoute(Q request);
47+
48+
/**
49+
* Returns the request URL.
50+
*
51+
* @param request the HTTP request.
52+
* @return the request URL.
53+
* @since 0.18
54+
*/
55+
@Nullable
56+
public abstract String getUrl(Q request);
57+
58+
/**
59+
* Returns the request URL host.
60+
*
61+
* @param request the HTTP request.
62+
* @return the request URL host.
63+
* @since 0.18
64+
*/
65+
@Nullable
66+
public abstract String getHost(Q request);
67+
68+
/**
69+
* Returns the request method.
70+
*
71+
* @param request the HTTP request.
72+
* @return the request method.
73+
* @since 0.18
74+
*/
75+
@Nullable
76+
public abstract String getMethod(Q request);
77+
78+
/**
79+
* Returns the request URL path.
80+
*
81+
* @param request the HTTP request.
82+
* @return the request URL path.
83+
* @since 0.18
84+
*/
85+
@Nullable
86+
public abstract String getPath(Q request);
87+
88+
/**
89+
* Returns the request user agent.
90+
*
91+
* @param request the HTTP request.
92+
* @return the request user agent.
93+
* @since 0.18
94+
*/
95+
@Nullable
96+
public abstract String getUserAgent(Q request);
97+
98+
/**
99+
* Returns the response status code. If the response is null, this method should return {@code 0}.
100+
*
101+
* @param response the HTTP response.
102+
* @return the response status code.
103+
* @since 0.18
104+
*/
105+
public abstract int getStatusCode(@Nullable P response);
106+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2018, OpenCensus Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.opencensus.contrib.http;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
import static org.mockito.Matchers.any;
21+
import static org.mockito.Mockito.verify;
22+
23+
import io.opencensus.trace.EndSpanOptions;
24+
import io.opencensus.trace.MessageEvent;
25+
import io.opencensus.trace.MessageEvent.Type;
26+
import io.opencensus.trace.Span;
27+
import org.junit.Before;
28+
import org.junit.Rule;
29+
import org.junit.Test;
30+
import org.junit.rules.ExpectedException;
31+
import org.junit.runner.RunWith;
32+
import org.junit.runners.JUnit4;
33+
import org.mockito.ArgumentCaptor;
34+
import org.mockito.Captor;
35+
import org.mockito.Mock;
36+
import org.mockito.MockitoAnnotations;
37+
38+
/** Unit tests for {@link AbstractHttpHandler}. */
39+
@RunWith(JUnit4.class)
40+
public class AbstractHttpHandlerTest {
41+
42+
@Rule public final ExpectedException thrown = ExpectedException.none();
43+
private final Object response = new Object();
44+
private final Exception error = new Exception("test");
45+
@Mock private Span span;
46+
@Mock private HttpExtractor<Object, Object> extractor;
47+
private AbstractHttpHandler<Object, Object> handler;
48+
@Captor private ArgumentCaptor<MessageEvent> captor;
49+
50+
@Before
51+
public void setUp() {
52+
MockitoAnnotations.initMocks(this);
53+
handler = new AbstractHttpHandler<Object, Object>(extractor) {};
54+
}
55+
56+
@Test
57+
public void constructorDisallowNullExtractor() {
58+
thrown.expect(NullPointerException.class);
59+
new AbstractHttpHandler<Object, Object>(null) {};
60+
}
61+
62+
@Test
63+
public void handleMessageSent() {
64+
Type type = Type.SENT;
65+
long id = 123L;
66+
long uncompressed = 456L;
67+
handler.handleMessageSent(span, id, uncompressed);
68+
verify(span).addMessageEvent(captor.capture());
69+
70+
MessageEvent messageEvent = captor.getValue();
71+
assertThat(messageEvent.getType()).isEqualTo(type);
72+
assertThat(messageEvent.getMessageId()).isEqualTo(id);
73+
assertThat(messageEvent.getUncompressedMessageSize()).isEqualTo(uncompressed);
74+
assertThat(messageEvent.getCompressedMessageSize()).isEqualTo(0);
75+
}
76+
77+
@Test
78+
public void handleMessageReceived() {
79+
Type type = Type.RECEIVED;
80+
long id = 123L;
81+
long uncompressed = 456L;
82+
handler.handleMessageReceived(span, id, uncompressed);
83+
verify(span).addMessageEvent(captor.capture());
84+
85+
MessageEvent messageEvent = captor.getValue();
86+
assertThat(messageEvent.getType()).isEqualTo(type);
87+
assertThat(messageEvent.getMessageId()).isEqualTo(id);
88+
assertThat(messageEvent.getUncompressedMessageSize()).isEqualTo(uncompressed);
89+
assertThat(messageEvent.getCompressedMessageSize()).isEqualTo(0);
90+
}
91+
92+
@Test
93+
public void handleEndDisallowNullSpan() {
94+
thrown.expect(NullPointerException.class);
95+
handler.handleEnd(null, response, error);
96+
}
97+
98+
@Test
99+
public void handleEndAllowNullResponseAndError() {
100+
handler.handleEnd(span, /*response=*/ null, /*error=*/ null);
101+
}
102+
103+
@Test
104+
public void handleEndShouldEndSpan() {
105+
handler.handleEnd(span, response, error);
106+
verify(span).end(any(EndSpanOptions.class));
107+
}
108+
}

0 commit comments

Comments
 (0)