package de.jodamob.android.tracing; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.List; import de.jodamob.android.logging.Log; import retrofit.client.Client; import retrofit.client.Header; import retrofit.client.Request; import retrofit.client.Response; import retrofit.mime.MimeUtil; import retrofit.mime.TypedByteArray; import retrofit.mime.TypedInput; /** * use this when you want to trace with body */ public class TracedRetrofitClient implements Client{ private static final int BUFFER_SIZE = 0x1000; private static final long MAX_BODY_TO_LOG_SIZE = 20000L; private final Client client; private final Tracer tracer; public TracedRetrofitClient(Client client, Tracer tracer) { this.client = client; this.tracer = tracer; } @Override public Response execute(Request request) throws IOException { long startTime = getTime(); try { return executeTraced(request, startTime); } catch (IOException e) { trace(request, e, startTime, getTime()); throw e; } } private Response executeTraced(Request request, long startTime) throws IOException { Response response = client.execute(request); trace(request, response, startTime); return response; } private void trace(Request request, IOException e, long startTime, long endTime) { tracer.trace(request.getUrl(), startTime, endTime, e); } private void trace(Request request, Response response, long startTime) throws IOException { long bytesReceived = getBytesReceived(response); int status = getStatus(response); if (status >= 400) { tracer.trace(request.getUrl(), status, startTime, getTime(), 0, bytesReceived, getBodyString(response)); } else { tracer.trace(request.getUrl(), status, startTime, getTime(), 0, bytesReceived); } } private int getStatus(Response response) { return response != null ? response.getStatus() : -1; } private long getBytesReceived(Response response) { return response != null ? getResponseSize(response.getHeaders()) : 0; } private long getResponseSize(List<Header> headers) { for (Header header : headers) { if (isContentLength((header.getName()))) { return Long.parseLong(header.getValue()); } } return 0L; } private boolean isContentLength(String name) { return "Content-Length".equalsIgnoreCase(name); } private long getTime() { return System.currentTimeMillis(); } private static String getBodyString(Response response) throws IOException { try { TypedInput body = response.getBody(); if (body != null && body.length() < MAX_BODY_TO_LOG_SIZE) { return convertToString(response, body); } } catch(Exception e) { Log.wtf(e); } return ""; } private static String convertToString(Response response, TypedInput body) throws IOException { if (!(body instanceof TypedByteArray)) { return convertToString(readBodyToBytesIfNecessary(response).getBody()); } return convertToString(body); } private static String convertToString(TypedInput body) throws UnsupportedEncodingException { byte[] bodyBytes = ((TypedByteArray) body).getBytes(); return new String(bodyBytes, MimeUtil.parseCharset(body.mimeType())); } private static Response readBodyToBytesIfNecessary (Response response) throws IOException { TypedInput body = response.getBody(); if (body == null || body instanceof TypedByteArray) { return response; } return replaceResponseBody(response, new TypedByteArray(body.mimeType(), stream(body.in()))); } private static Response replaceResponseBody(Response response, TypedInput body) { return new Response(response.getStatus(), response.getReason(), response.getHeaders(), body); } private static byte[] stream(InputStream stream) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); if (stream != null) { stream(stream, outputStream); } return outputStream.toByteArray(); } private static void stream(InputStream stream, ByteArrayOutputStream outputStream) throws IOException { byte[] buf = new byte[BUFFER_SIZE]; int r; while ((r = stream.read(buf)) != -1) { outputStream.write(buf, 0, r); } } }