Skip to content

Commit

Permalink
Merge pull request #2042 from newrelic/netty-http2-exception
Browse files Browse the repository at this point in the history
Protect against Http2Headers methods throwing exceptions
  • Loading branch information
jasonjkeller authored Sep 9, 2024
2 parents 95fc1aa + 08ba607 commit 8308389
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,40 +38,79 @@ public class Http2RequestHeaderWrapper extends ExtendedRequest {
public Http2RequestHeaderWrapper(Http2Headers http2Headers) {
super();
this.http2Headers = http2Headers;
this.method = http2Headers.method();
this.path = http2Headers.path();
this.authority = http2Headers.authority();

Set<Cookie> rawCookies = null;
if (http2Headers.contains(HttpHeaderNames.COOKIE)) {
CharSequence cookie = http2Headers.get(HttpHeaderNames.COOKIE);
try {
if (cookie != null) {
rawCookies = ServerCookieDecoder.STRICT.decode(cookie.toString());
}
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to decode cookie: {0}", cookie);
rawCookies = Collections.emptySet();
}
}
this.cookies = rawCookies;
this.method = getMethodHeader();
this.path = getPathHeader();
this.authority = getAuthorityHeader();
this.cookies = getCookies();
this.parameters = getParameters();
}

private Map<String, List<String>> getParameters() {
Map<String, List<String>> params = null;
CharSequence path = http2Headers.path();
try {
String uri;
if (path != null) {
uri = path.toString();
uri = URL_REPLACEMENT_PATTERN.matcher(uri).replaceAll("%25"); // Escape any percent signs in the URI
// Escape any percent signs in the URI
uri = URL_REPLACEMENT_PATTERN.matcher(uri).replaceAll("%25");
QueryStringDecoder decoderQuery = new QueryStringDecoder(uri);
params = decoderQuery.parameters();

}
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to decode URI: {0}", path);
params = new LinkedHashMap<>();
}
this.parameters = params;
return params;
}

private Set<Cookie> getCookies() {
Set<Cookie> rawCookies = null;
try {
if (http2Headers.contains(HttpHeaderNames.COOKIE)) {
CharSequence cookie = http2Headers.get(HttpHeaderNames.COOKIE);
try {
if (cookie != null) {
rawCookies = ServerCookieDecoder.STRICT.decode(cookie.toString());
}
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to decode cookie: {0}", cookie);
rawCookies = Collections.emptySet();
}
}
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to get Http2Headers cookies: {0}", e.getMessage());
}
return rawCookies;
}

private CharSequence getMethodHeader() {
CharSequence method = null;
try {
method = http2Headers.method();
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to get Http2Headers method: {0}", e.getMessage());
}
return method;
}

private CharSequence getPathHeader() {
CharSequence path = null;
try {
path = http2Headers.path();
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to get Http2Headers path: {0}", e.getMessage());
}
return path;
}

private CharSequence getAuthorityHeader() {
CharSequence authority = null;
try {
authority = http2Headers.authority();
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to get Http2Headers authority: {0}", e.getMessage());
}
return authority;
}

@Override
Expand All @@ -84,14 +123,18 @@ public String getRequestURI() {

@Override
public String getHeader(String name) {
// HTTP/2 only supports lowercase headers
String lowerCaseHeaderName = name.toLowerCase();
if (lowerCaseHeaderName.equals(HttpHeaderNames.HOST.toString())) {
return getHost();
}
try {
// HTTP/2 only supports lowercase headers
String lowerCaseHeaderName = name.toLowerCase();
if (lowerCaseHeaderName.equals(HttpHeaderNames.HOST.toString())) {
return getHost();
}

if (http2Headers.contains(lowerCaseHeaderName)) {
return http2Headers.get(lowerCaseHeaderName).toString();
if (http2Headers.contains(lowerCaseHeaderName)) {
return http2Headers.get(lowerCaseHeaderName).toString();
}
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to get Http2Headers header: {0}", e.getMessage());
}
return null;
}
Expand Down Expand Up @@ -145,10 +188,13 @@ public String getMethod() {
}

public String getHost() {
if (http2Headers.contains(HttpHeaderNames.HOST)) {
return http2Headers.get(HttpHeaderNames.HOST).toString();
try {
if (http2Headers.contains(HttpHeaderNames.HOST)) {
return http2Headers.get(HttpHeaderNames.HOST).toString();
}
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, "host header is not present in Http2Headers");
}

if (authority == null) {
return null;
}
Expand All @@ -157,12 +203,16 @@ public String getHost() {

@Override
public List<String> getHeaders(String name) {
// HTTP/2 only supports lowercase headers
String lowerCaseHeaderName = name.toLowerCase();
List<String> headers = new ArrayList<>();
List<CharSequence> allHeaders = http2Headers.getAll(lowerCaseHeaderName);
for (CharSequence header : allHeaders) {
headers.add(header.toString());
try {
// HTTP/2 only supports lowercase headers
String lowerCaseHeaderName = name.toLowerCase();
List<CharSequence> allHeaders = http2Headers.getAll(lowerCaseHeaderName);
for (CharSequence header : allHeaders) {
headers.add(header.toString());
}
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to get Http2Headers headers: {0}", e.getMessage());
}
return headers;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@

package com.agent.instrumentation.netty4116;

import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.api.agent.ExtendedResponse;
import com.newrelic.api.agent.HeaderType;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http2.Http2Headers;

import java.util.logging.Level;

public class Http2ResponseHeaderWrapper extends ExtendedResponse {
private final Http2Headers http2Headers;

Expand All @@ -26,23 +29,29 @@ public HeaderType getHeaderType() {

@Override
public void setHeader(String name, String value) {
// HTTP/2 only supports lowercase headers
String lowerCaseHeaderName = name.toLowerCase();
try {
// HTTP/2 only supports lowercase headers
String lowerCaseHeaderName = name.toLowerCase();
http2Headers.set(lowerCaseHeaderName, value);
} catch (Exception ignored) {
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to set Http2Headers header: {0}", e.getMessage());
}
}

@Override
public int getStatus() {
CharSequence status = http2Headers.status();
if (status == null) {
return -1;
}
try {
return Integer.parseInt(status.toString());
} catch (NumberFormatException e) {
CharSequence status = http2Headers.status();
if (status == null) {
return -1;
}
try {
return Integer.parseInt(status.toString());
} catch (NumberFormatException e) {
return -1;
}
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to get Http2Headers status: {0}", e.getMessage());
return -1;
}
}
Expand All @@ -55,26 +64,34 @@ public String getStatusMessage() {

@Override
public String getContentType() {
if (http2Headers.contains(HttpHeaderNames.CONTENT_TYPE)) {
CharSequence contentType = http2Headers.get(HttpHeaderNames.CONTENT_TYPE);
if (contentType != null) {
return contentType.toString();
try {
if (http2Headers.contains(HttpHeaderNames.CONTENT_TYPE)) {
CharSequence contentType = http2Headers.get(HttpHeaderNames.CONTENT_TYPE);
if (contentType != null) {
return contentType.toString();
}
}
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to get Http2Headers content-type: {0}", e.getMessage());
}
return null;
}

@Override
public long getContentLength() {
if (http2Headers.contains(HttpHeaderNames.CONTENT_LENGTH)) {
CharSequence contentLength = http2Headers.get(HttpHeaderNames.CONTENT_LENGTH);
if (contentLength != null) {
try {
return Long.parseLong(contentLength.toString());
} catch (NumberFormatException e) {
return -1;
try {
if (http2Headers.contains(HttpHeaderNames.CONTENT_LENGTH)) {
CharSequence contentLength = http2Headers.get(HttpHeaderNames.CONTENT_LENGTH);
if (contentLength != null) {
try {
return Long.parseLong(contentLength.toString());
} catch (NumberFormatException e) {
return -1;
}
}
}
} catch (Exception e) {
AgentBridge.getAgent().getLogger().log(Level.FINER, e, "Unable to get Http2Headers content-length: {0}", e.getMessage());
}
return -1;
}
Expand Down

0 comments on commit 8308389

Please sign in to comment.