com.browserup.bup.util.HttpMessageContents Maven / Gradle / Ivy
/*
* Modifications Copyright (c) 2019 BrowserUp, Inc.
*/
package com.browserup.bup.util;
import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import com.browserup.bup.exception.UnsupportedCharsetException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.Charset;
/**
* Helper class to wrap the contents of an {@link io.netty.handler.codec.http.HttpMessage}. Contains convenience methods to extract and
* manipulate the contents of the wrapped {@link io.netty.handler.codec.http.HttpMessage}.
*
* TODO: Currently this class only wraps FullHttpMessages, since it must modify the Content-Length header; determine if this may be applied to chunked messages as well
*/
public class HttpMessageContents {
private static final Logger log = LoggerFactory.getLogger(HttpMessageContents.class);
private final FullHttpMessage httpMessage;
// caches for contents, to avoid repeated re-extraction of data
private volatile String textContents;
private volatile byte[] binaryContents;
public HttpMessageContents(FullHttpMessage httpMessage) {
this.httpMessage = httpMessage;
}
/**
* Replaces the contents of the wrapped HttpMessage with the specified text contents, encoding them in the character set specified by the
* message's Content-Type header. Note that this method does not update the Content-Type header, so if the content type will change as a
* result of this call, the Content-Type header should be updated before calling this method.
*
* @param newContents new message contents
*/
public void setTextContents(String newContents) {
HttpObjectUtil.replaceTextHttpEntityBody(httpMessage, newContents);
// replaced the contents, so clear the local cache
textContents = null;
binaryContents = null;
}
/**
* Replaces the contents of the wrapped HttpMessage with the specified binary contents. Note that this method does not update the
* Content-Type header, so if the content type will change as a result of this call, the Content-Type header should be updated before
* calling this method.
*
* @param newBinaryContents new message contents
*/
public void setBinaryContents(byte[] newBinaryContents) {
HttpObjectUtil.replaceBinaryHttpEntityBody(httpMessage, newBinaryContents);
// replaced the contents, so clear the local cache
binaryContents = null;
textContents = null;
}
/**
* Retrieves the contents of this message as a String, decoded according to the message's Content-Type header. This method caches
* the contents, so repeated calls to this method should not incur a penalty; however, modifications to the message contents
* outside of this class will result in stale data returned from this method.
*
* @return String representation of the entity body
* @throws java.nio.charset.UnsupportedCharsetException if the character set declared in the message is not supported on this platform
*/
public String getTextContents() throws java.nio.charset.UnsupportedCharsetException {
// avoid re-extracting the contents if this method is called repeatedly
if (textContents == null) {
textContents = HttpObjectUtil.extractHttpEntityBody(httpMessage);
}
return textContents;
}
/**
* Retrieves the binary contents of this message. This method caches the contents, so repeated calls to this method should not incur a
* penalty; however, modifications to the message contents outside of this class will result in stale data returned from this method.
*
* @return binary contents of the entity body
*/
public byte[] getBinaryContents() {
// avoid re-extracting the contents if this method is called repeatedly
if (binaryContents == null) {
binaryContents = HttpObjectUtil.extractBinaryHttpEntityBody(httpMessage);
}
return binaryContents;
}
/**
* Retrieves the Content-Type header of this message. If no Content-Type is present, returns the assumed default Content-Type (see
* {@link BrowserUpHttpUtil#UNKNOWN_CONTENT_TYPE}).
*
* @return the message's content type
*/
public String getContentType() {
String contentTypeHeader = HttpHeaders.getHeader(httpMessage, HttpHeaderNames.CONTENT_TYPE);
if (contentTypeHeader == null || contentTypeHeader.isEmpty()) {
return BrowserUpHttpUtil.UNKNOWN_CONTENT_TYPE;
} else {
return contentTypeHeader;
}
}
/**
* Retrieves the character set of the entity body. If the Content-Type is not a textual type, this value is meaningless.
* If no character set is specified, this method will return the default ISO-8859-1 character set. If the Content-Type
* specifies a character set, but the character set is not supported on this platform, this method throws an
* {@link java.nio.charset.UnsupportedCharsetException}.
*
* @return the entity body's character set
* @throws java.nio.charset.UnsupportedCharsetException if the character set declared in the message is not supported on this platform
*/
public Charset getCharset() throws java.nio.charset.UnsupportedCharsetException {
String contentTypeHeader = getContentType();
Charset charset = null;
try {
charset = BrowserUpHttpUtil.readCharsetInContentTypeHeader(contentTypeHeader);
} catch (UnsupportedCharsetException e) {
java.nio.charset.UnsupportedCharsetException cause = e.getUnsupportedCharsetExceptionCause();
log.error("Character set specified in Content-Type header is not supported on this platform. Content-Type header: {}", contentTypeHeader, cause);
throw cause;
}
if (charset == null) {
return BrowserUpHttpUtil.DEFAULT_HTTP_CHARSET;
}
return charset;
}
/**
* Returns true if this message's Content-Type header indicates that it contains a textual data type. See {@link BrowserUpHttpUtil#hasTextualContent(String)}.
*
* @return true if the Content-Type header is a textual type, otherwise false
*/
public boolean isText() {
return BrowserUpHttpUtil.hasTextualContent(getContentType());
}
}