diff --git a/okhttp-logging-interceptor/src/main/java/com/squareup/okhttp/logging/HttpLoggingInterceptor.java b/okhttp-logging-interceptor/src/main/java/com/squareup/okhttp/logging/HttpLoggingInterceptor.java index 4839ac75c..50279926a 100644 --- a/okhttp-logging-interceptor/src/main/java/com/squareup/okhttp/logging/HttpLoggingInterceptor.java +++ b/okhttp-logging-interceptor/src/main/java/com/squareup/okhttp/logging/HttpLoggingInterceptor.java @@ -155,9 +155,24 @@ public final class HttpLoggingInterceptor implements Interceptor { logger.log(requestStartMessage); if (logHeaders) { + if (hasRequestBody) { + // Request body headers are only present when installed as a network interceptor. Force + // them to be included (when available) so there values are known. + if (requestBody.contentType() != null) { + logger.log("Content-Type: " + requestBody.contentType()); + } + if (requestBody.contentLength() != -1) { + logger.log("Content-Length: " + requestBody.contentLength()); + } + } + Headers headers = request.headers(); for (int i = 0, count = headers.size(); i < count; i++) { - logger.log(headers.name(i) + ": " + headers.value(i)); + String name = headers.name(i); + // Skip headers from the request body as they are explicitly logged above. + if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) { + logger.log(name + ": " + headers.value(i)); + } } String endMessage = "--> END " + request.method(); diff --git a/okhttp-logging-interceptor/src/test/java/com/squareup/okhttp/logging/HttpLoggingInterceptorTest.java b/okhttp-logging-interceptor/src/test/java/com/squareup/okhttp/logging/HttpLoggingInterceptorTest.java index e0f2edd6e..c2c960602 100644 --- a/okhttp-logging-interceptor/src/test/java/com/squareup/okhttp/logging/HttpLoggingInterceptorTest.java +++ b/okhttp-logging-interceptor/src/test/java/com/squareup/okhttp/logging/HttpLoggingInterceptorTest.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; +import okio.BufferedSink; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -181,7 +182,8 @@ public final class HttpLoggingInterceptorTest { applicationLogs .assertLogEqual("--> POST / HTTP/1.1") - // TODO force content-type and content-length to show here + .assertLogEqual("Content-Type: text/plain; charset=utf-8") + .assertLogEqual("Content-Length: 3") .assertLogEqual("--> END POST") .assertLogMatch("<-- HTTP/1\\.1 200 OK \\(\\d+ms\\)") .assertLogEqual("Content-Length: 0") @@ -209,6 +211,86 @@ public final class HttpLoggingInterceptorTest { .assertNoMoreLogs(); } + @Test public void headersPostNoContentType() throws IOException { + setLevel(Level.HEADERS); + + server.enqueue(new MockResponse()); + client.newCall(request().post(RequestBody.create(null, "Hi?")).build()).execute(); + + applicationLogs + .assertLogEqual("--> POST / HTTP/1.1") + .assertLogEqual("Content-Length: 3") + .assertLogEqual("--> END POST") + .assertLogMatch("<-- HTTP/1\\.1 200 OK \\(\\d+ms\\)") + .assertLogEqual("Content-Length: 0") + .assertLogEqual("OkHttp-Selected-Protocol: http/1.1") + .assertLogMatch("OkHttp-Sent-Millis: \\d+") + .assertLogMatch("OkHttp-Received-Millis: \\d+") + .assertLogEqual("<-- END HTTP") + .assertNoMoreLogs(); + + networkLogs + .assertLogEqual("--> POST / HTTP/1.1") + .assertLogEqual("Content-Length: 3") + .assertLogEqual("Host: " + host) + .assertLogEqual("Connection: Keep-Alive") + .assertLogEqual("Accept-Encoding: gzip") + .assertLogMatch("User-Agent: okhttp/.+") + .assertLogEqual("--> END POST") + .assertLogMatch("<-- HTTP/1\\.1 200 OK \\(\\d+ms\\)") + .assertLogEqual("Content-Length: 0") + .assertLogEqual("OkHttp-Selected-Protocol: http/1.1") + .assertLogMatch("OkHttp-Sent-Millis: \\d+") + .assertLogMatch("OkHttp-Received-Millis: \\d+") + .assertLogEqual("<-- END HTTP") + .assertNoMoreLogs(); + } + + @Test public void headersPostNoLength() throws IOException { + setLevel(Level.HEADERS); + + server.enqueue(new MockResponse()); + RequestBody body = new RequestBody() { + @Override public MediaType contentType() { + return PLAIN; + } + + @Override public void writeTo(BufferedSink sink) throws IOException { + sink.writeUtf8("Hi!"); + } + }; + client.newCall(request().post(body).build()).execute(); + + applicationLogs + .assertLogEqual("--> POST / HTTP/1.1") + .assertLogEqual("Content-Type: text/plain; charset=utf-8") + .assertLogEqual("--> END POST") + .assertLogMatch("<-- HTTP/1\\.1 200 OK \\(\\d+ms\\)") + .assertLogEqual("Content-Length: 0") + .assertLogEqual("OkHttp-Selected-Protocol: http/1.1") + .assertLogMatch("OkHttp-Sent-Millis: \\d+") + .assertLogMatch("OkHttp-Received-Millis: \\d+") + .assertLogEqual("<-- END HTTP") + .assertNoMoreLogs(); + + networkLogs + .assertLogEqual("--> POST / HTTP/1.1") + .assertLogEqual("Content-Type: text/plain; charset=utf-8") + .assertLogEqual("Transfer-Encoding: chunked") + .assertLogEqual("Host: " + host) + .assertLogEqual("Connection: Keep-Alive") + .assertLogEqual("Accept-Encoding: gzip") + .assertLogMatch("User-Agent: okhttp/.+") + .assertLogEqual("--> END POST") + .assertLogMatch("<-- HTTP/1\\.1 200 OK \\(\\d+ms\\)") + .assertLogEqual("Content-Length: 0") + .assertLogEqual("OkHttp-Selected-Protocol: http/1.1") + .assertLogMatch("OkHttp-Sent-Millis: \\d+") + .assertLogMatch("OkHttp-Received-Millis: \\d+") + .assertLogEqual("<-- END HTTP") + .assertNoMoreLogs(); + } + @Test public void headersResponseBody() throws IOException { setLevel(Level.HEADERS); @@ -329,7 +411,8 @@ public final class HttpLoggingInterceptorTest { applicationLogs .assertLogEqual("--> POST / HTTP/1.1") - // TODO force content-type and content-length to show here + .assertLogEqual("Content-Type: text/plain; charset=utf-8") + .assertLogEqual("Content-Length: 3") .assertLogEqual("") .assertLogEqual("Hi?") .assertLogEqual("--> END POST (3-byte body)")