com.smartsheet.api.internal.http.RequestAndResponseData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of smartsheet-sdk-java Show documentation
Show all versions of smartsheet-sdk-java Show documentation
Library for connecting to Smartsheet Services
package com.smartsheet.api.internal.http;
/*
* #[license]
* Smartsheet Java SDK
* %%
* Copyright (C) 2014 - 2017 Smartsheet
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* %[license]
*/
import com.smartsheet.api.Trace;
import com.smartsheet.api.internal.util.StreamUtil;
import org.apache.commons.codec.binary.Hex;
import org.apache.http.Header;
import org.apache.http.client.methods.HttpRequestBase;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* a POJO from which is generated JSON from HTTP request/response pairs
*/
public class RequestAndResponseData {
public static abstract class HttpPayloadData {
Map headers;
String body;
public String getBody() {
return body;
}
public boolean hasBody() {
return body != null;
}
public boolean hasHeaders() {
return headers != null;
}
public Map getHeaders() {
return headers;
}
static abstract class Builder {
public void withHeaders() {
// this is seaprate from addHeader in case headers were requested but none found
if (getDataObject().headers == null) {
getDataObject().headers = new TreeMap();
}
}
public Builder addHeader(String key, String val) {
withHeaders();
getDataObject().headers.put(key, val);
return this;
}
public Builder setBody(String body) {
getDataObject().body = body;
return this;
}
public abstract Type build();
public abstract void reset();
protected abstract Type getDataObject();
}
}
public static class RequestData extends HttpPayloadData {
private String command;
public String getCommand() {
return command;
}
public static class Builder extends HttpPayloadData.Builder {
private RequestData dataObject;
@Override
public void reset() {
dataObject = null;
}
@Override
protected RequestData getDataObject() {
if (dataObject == null) {
dataObject = new RequestData();
}
return dataObject;
}
public HttpPayloadData.Builder withCommand(String command) {
getDataObject().command = command;
return this;
}
public RequestData build() {
try {
return dataObject; // if nothing was added then nothing was built (i.e., this can be null)
} finally {
reset();
}
}
}
}
public static class ResponseData extends HttpPayloadData {
private String status;
public String getStatus() {
return status;
}
public static class Builder extends HttpPayloadData.Builder {
private ResponseData dataObject;
@Override
public void reset() {
dataObject = null;
}
@Override
protected ResponseData getDataObject() {
if (dataObject == null) {
dataObject = new ResponseData();
}
return dataObject;
}
public HttpPayloadData.Builder withStatus(String status) {
getDataObject().status = status;
return this;
}
public ResponseData build() {
try {
return dataObject; // if nothing was added then nothing was built (i.e., this can be null)
} finally {
reset();
}
}
}
}
private static int TRUNCATE_LENGTH = Integer.getInteger("Smartsheet.trace.truncateLen", 1024);
public final RequestData request;
public final ResponseData response;
private RequestAndResponseData(RequestData requestData, ResponseData responseData) {
request = requestData;
response = responseData;
}
@Override
public String toString() {
return toString(false);
}
public String toString(boolean pretty) {
final String EOL = pretty ? "\n" : "";
final String INDENT = pretty ? " " : "";
final String INDENT2 = INDENT + INDENT;
final String INDENT3 = INDENT2 + INDENT;
StringBuilder buf = new StringBuilder();
buf.append("{").append(EOL);
buf.append(INDENT).append("request:");
if (request == null) {
buf.append("null,").append(EOL);
} else {
buf.append("{").append(EOL);
buf.append(INDENT2).append("command:'").append(request.getCommand()).append("',").append(EOL);
if (request.hasHeaders()) {
buf.append(INDENT2).append("headers:");
if (request.getHeaders() == null) {
buf.append("null");
} else {
buf.append("{").append(EOL);
for (Map.Entry header : request.headers.entrySet()) {
buf.append(INDENT3).append("'").append(header.getKey()).append("':'").append(header.getValue()).append("',").append(EOL);
}
buf.append(INDENT2).append("},").append(EOL);
}
}
if (request.hasBody()) {
buf.append(INDENT2).append("body:");
if (request.body == null) {
buf.append("null");
} else {
buf.append("'").append(request.body).append("'");
}
buf.append(EOL);
}
buf.append(INDENT).append("},").append(EOL);
}
buf.append(INDENT).append("response:");
if (response == null) {
buf.append("null").append(EOL);
} else {
buf.append("{").append(EOL);
buf.append(INDENT2).append("status:'").append(response.getStatus()).append("',").append(EOL);
if (response.hasHeaders()) {
buf.append(INDENT2).append("headers:");
if (response.getHeaders() == null) {
buf.append("null");
} else {
buf.append("{").append(EOL);
for (Map.Entry header : response.headers.entrySet()) {
buf.append(INDENT3).append("'").append(header.getKey()).append("':'").append(header.getValue()).append("',").append(EOL);
}
buf.append(INDENT2).append("},").append(EOL);
}
}
if (response.hasBody()) {
buf.append(INDENT2).append("body:");
if (response.body == null) {
buf.append("null");
} else {
buf.append("'").append(response.body).append("'");
}
buf.append(EOL);
}
buf.append(INDENT).append("}").append(EOL);
}
buf.append("}");
return buf.toString();
}
/**
* factory method for creating a RequestAndResponseData object from request and response data with the specifid trace fields
*/
public static RequestAndResponseData of(HttpRequestBase request, HttpEntity requestEntity,
HttpResponse response, HttpEntity responseEntity,
Set traces)
throws IOException {
RequestData.Builder requestBuilder = new RequestData.Builder();
ResponseData.Builder responseBuilder = new ResponseData.Builder();
if (request != null) {
requestBuilder.withCommand(request.getMethod() + " " + request.getURI());
boolean binaryBody = false;
if (traces.contains(Trace.RequestHeaders) && request.getAllHeaders() != null) {
requestBuilder.withHeaders();
for (Header header : request.getAllHeaders()) {
String headerName = header.getName();
String headerValue = header.getValue();
if ("Authorization".equals(headerName) && headerValue.length() > 0) {
headerValue = "Bearer ****" + headerValue.substring(Math.max(0, headerValue.length() - 4));
} else if ("Content-Disposition".equals(headerName)) {
binaryBody = true;
}
requestBuilder.addHeader(headerName, headerValue);
}
}
if (requestEntity != null) {
if (traces.contains(Trace.RequestBody)) {
requestBuilder.setBody(binaryBody ? binaryBody(requestEntity) : getContentAsText(requestEntity));
} else if (traces.contains(Trace.RequestBodySummary)) {
requestBuilder.setBody(binaryBody ? binaryBody(requestEntity) : truncateAsNeeded(getContentAsText(requestEntity), TRUNCATE_LENGTH));
}
}
}
if (response != null) {
boolean binaryBody = false;
responseBuilder.withStatus(response.getStatusText());
if (traces.contains(Trace.ResponseHeaders) && response.getHeaders() != null) {
responseBuilder.withHeaders();
for (Map.Entry header : response.getHeaders().entrySet()) {
String headerName = header.getKey();
String headerValue = header.getValue();
if ("Content-Disposition".equals(headerName)) {
binaryBody = true;
}
responseBuilder.addHeader(headerName, headerValue);
}
}
if (responseEntity != null) {
if (traces.contains(Trace.ResponseBody)) {
responseBuilder.setBody(binaryBody ? binaryBody(responseEntity) : getContentAsText(responseEntity));
} else if (traces.contains(Trace.ResponseBodySummary)) {
responseBuilder.setBody(binaryBody ? binaryBody(responseEntity) : truncateAsNeeded(getContentAsText(responseEntity), TRUNCATE_LENGTH));
}
}
}
return new RequestAndResponseData(requestBuilder.build(), responseBuilder.build());
}
static String binaryBody(HttpEntity entity) {
return "**possibly-binary(type:" + entity.getContentType() + ", len:" + entity.getContentLength() + ")**";
}
public static String getContentAsText(HttpEntity entity) throws IOException {
if (entity == null) {
return "";
}
InputStream inputStream = entity.getContent();
if (inputStream == null) {
return "";
}
final boolean markSupported = inputStream.markSupported();
if (markSupported) {
inputStream.mark(10 * 1024 * 1024); // 10MB read buffer; beyond that it probably won't matter
}
byte[] contentBytes = StreamUtil.readBytesFromStream(inputStream);
String contentAsText;
try {
contentAsText = new String(contentBytes, "UTF-8");
} catch (UnsupportedEncodingException badEncodingOrNotText) {
contentAsText = new String(Hex.encodeHex(contentBytes));
}
// since we've consumed the stream we have to reset it (note, this will have real perf impact if the stream
// was to a large file or something else we'd rather not hold entirely in RAM if we can help it)
if (markSupported) {
inputStream.reset();
} else {
// we can't reset the stream so rebuild the stream around the bytes we read
entity.setContent(new ByteArrayInputStream(contentBytes));
}
return contentAsText;
}
public static String truncateAsNeeded(String string, int truncateLen) {
if (truncateLen == -1) {
return string;
}
truncateLen = Math.min(string.length(), truncateLen);
String suffix = truncateLen < string.length() ? "..." : "";
return string.substring(0, truncateLen) + suffix;
}
}