mirror of
				https://github.com/owncloud/android-library.git
				synced 2025-10-31 02:17:41 +00:00 
			
		
		
		
	Log http body if it is not binary
This commit is contained in:
		
							parent
							
								
									aa665a7295
								
							
						
					
					
						commit
						c0d2e20bb1
					
				| @ -29,8 +29,9 @@ object LogBuilder { | ||||
|     fun logHttp( | ||||
|         networkPetition: NetworkPetition, | ||||
|         networkNode: NetworkNode, | ||||
|         requestId: String? = "", | ||||
|         description: String | ||||
|     ) = Timber.d("[Network, $networkPetition] [$networkNode] $description") | ||||
|     ) = Timber.d("[Network, $networkPetition] [$networkNode] [$requestId] $description") | ||||
| } | ||||
| 
 | ||||
| enum class NetworkPetition { | ||||
|  | ||||
| @ -23,37 +23,145 @@ | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID | ||||
| import com.owncloud.android.lib.common.http.LogBuilder.logHttp | ||||
| import com.owncloud.android.lib.common.http.NetworkNode.BODY | ||||
| import com.owncloud.android.lib.common.http.NetworkNode.HEADER | ||||
| import com.owncloud.android.lib.common.http.NetworkNode.INFO | ||||
| import com.owncloud.android.lib.common.http.NetworkPetition.REQUEST | ||||
| import com.owncloud.android.lib.common.http.NetworkPetition.RESPONSE | ||||
| import okhttp3.Headers | ||||
| import okhttp3.Interceptor | ||||
| import okhttp3.RequestBody | ||||
| import okhttp3.Response | ||||
| import okhttp3.ResponseBody | ||||
| import okio.Buffer | ||||
| import java.nio.charset.Charset | ||||
| import java.nio.charset.StandardCharsets | ||||
| import kotlin.math.max | ||||
| 
 | ||||
| class LogInterceptor : Interceptor { | ||||
| 
 | ||||
|     override fun intercept(chain: Interceptor.Chain): Response { | ||||
| 
 | ||||
|         val response = chain.proceed(chain.request()) | ||||
|         if (!httpLogsEnabled) { | ||||
|             return chain.proceed(chain.request()) | ||||
|         } | ||||
| 
 | ||||
|         val request = chain.request().also { | ||||
|             val requestId = it.headers[OC_X_REQUEST_ID] | ||||
|             logHttp(REQUEST, INFO, requestId, "Type: ${it.method} URL: ${it.url}") | ||||
|             logHeaders(requestId, it.headers, REQUEST) | ||||
|             logRequestBody(requestId, it.body) | ||||
|         } | ||||
| 
 | ||||
|         val response = chain.proceed(request) | ||||
| 
 | ||||
|         return response.also { | ||||
|             if (httpLogsEnabled) { | ||||
|                 // Log request | ||||
|                 logHttp(REQUEST, INFO, "Type: ${it.request.method} URL: ${it.request.url}") | ||||
|                 it.request.headers.forEach { header -> logHttp(REQUEST, HEADER, header.toString()) } | ||||
|                 logHttp(REQUEST, BODY, it.request.body.toString()) | ||||
|             val requestId = it.request.headers[OC_X_REQUEST_ID] | ||||
|             logHttp( | ||||
|                 RESPONSE, | ||||
|                 INFO, | ||||
|                 requestId, | ||||
|                 "Code: ${it.code}  Message: ${it.message} IsSuccessful: ${it.isSuccessful}" | ||||
|             ) | ||||
|             logHeaders(requestId, it.headers, RESPONSE) | ||||
|             logResponseBody(requestId, it.body) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|                 // Log response | ||||
|                 logHttp(RESPONSE, INFO, "Code: ${it.code}  Message: ${it.message} IsSuccessful: ${it.isSuccessful}") | ||||
|                 it.headers.forEach { header -> logHttp(RESPONSE, HEADER, header.toString()) } | ||||
|                 logHttp(RESPONSE, BODY, it.body.toString()) | ||||
|     private fun logHeaders(requestId: String?, headers: Headers, networkPetition: NetworkPetition) { | ||||
|         headers.forEach { header -> | ||||
|             logHttp(networkPetition, HEADER, requestId, "${header.first}: ${header.second}") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun logRequestBody(requestId: String?, requestBodyParam: RequestBody?) { | ||||
|         requestBodyParam?.let { requestBody -> | ||||
| 
 | ||||
|             if (requestBody.isOneShot()) { | ||||
|                 logHttp(REQUEST, BODY, requestId, "One shot body -- Omitted") | ||||
|                 return@let | ||||
|             } | ||||
| 
 | ||||
|             if (requestBody.isDuplex()) { | ||||
|                 logHttp(REQUEST, BODY, requestId, "Duplex body -- Omitted") | ||||
|                 return@let | ||||
|             } | ||||
| 
 | ||||
|             val buffer = Buffer() | ||||
|             requestBody.writeTo(buffer) | ||||
| 
 | ||||
|             val contentType = requestBody.contentType() | ||||
|             val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8 | ||||
| 
 | ||||
|             logHttp(REQUEST, BODY, requestId, "Length: ${requestBody.contentLength()} byte body") | ||||
|             logHttp(REQUEST, BODY, requestId, "Type: ${requestBody.contentType()}") | ||||
|             logHttp(REQUEST, BODY, requestId, "--> Body start for request") | ||||
| 
 | ||||
|             if (buffer.isProbablyUtf8()) { | ||||
|                 if (requestBody.contentLength() < LIMIT_BODY_LOG) { | ||||
|                     logHttp(REQUEST, BODY, requestId, buffer.readString(charset)) | ||||
|                 } else { | ||||
|                     logHttp(REQUEST, BODY, requestId, buffer.readString(LIMIT_BODY_LOG, charset)) | ||||
|                 } | ||||
|                 logHttp( | ||||
|                     REQUEST, | ||||
|                     BODY, | ||||
|                     requestId, | ||||
|                     "<-- Body end for request -- Omitted: ${max(0, requestBody.contentLength() - LIMIT_BODY_LOG)} bytes" | ||||
|                 ) | ||||
|             } else { | ||||
|                 logHttp( | ||||
|                     REQUEST, | ||||
|                     BODY, | ||||
|                     requestId, | ||||
|                     "<-- Body end for request -- Binary -- Omitted: ${requestBody.contentLength()} bytes" | ||||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|         } ?: logHttp(REQUEST, BODY, requestId, "Empty body") | ||||
|     } | ||||
| 
 | ||||
|     private fun logResponseBody(requestId: String?, responseBodyParam: ResponseBody?) { | ||||
|         responseBodyParam?.let { responseBody -> | ||||
| 
 | ||||
|             val contentType = responseBody.contentType() | ||||
|             val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8 | ||||
| 
 | ||||
|             logHttp(RESPONSE, BODY, requestId, "Length: ${responseBody.contentLength()} byte body") | ||||
|             logHttp(RESPONSE, BODY, requestId, "Type: ${responseBody.contentType()}") | ||||
|             logHttp(RESPONSE, BODY, requestId, "--> Body start for request") | ||||
| 
 | ||||
|             val source = responseBody.source() | ||||
|             source.request(LIMIT_BODY_LOG) | ||||
|             val buffer = source.buffer | ||||
| 
 | ||||
|             if (!buffer.isProbablyUtf8()) { | ||||
|                 logHttp( | ||||
|                     REQUEST, | ||||
|                     BODY, | ||||
|                     requestId, | ||||
|                     "<-- Body end for request -- Binary -- Omitted: ${responseBody.contentLength()} bytes" | ||||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|             if (responseBody.contentLength() < LIMIT_BODY_LOG) { | ||||
|                 logHttp(REQUEST, BODY, requestId, buffer.clone().readString(charset)) | ||||
|             } else { | ||||
|                 logHttp(REQUEST, BODY, requestId, buffer.clone().readString(LIMIT_BODY_LOG, charset)) | ||||
|             } | ||||
|             logHttp( | ||||
|                 REQUEST, | ||||
|                 BODY, | ||||
|                 requestId, | ||||
|                 "<-- Body end for request -- Omitted: ${max(0, responseBody.contentLength() - LIMIT_BODY_LOG)} bytes" | ||||
|             ) | ||||
|         } ?: logHttp(RESPONSE, BODY, requestId, "Empty body") | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         var httpLogsEnabled: Boolean = false | ||||
|         private const val LIMIT_BODY_LOG: Long = 1024 | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * Copyright (C) 2015 Square, Inc. | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http | ||||
| 
 | ||||
| import java.io.EOFException | ||||
| import okio.Buffer | ||||
| 
 | ||||
| /** | ||||
|  * Returns true if the body in question probably contains human readable text. Uses a small | ||||
|  * sample of code points to detect unicode control characters commonly used in binary file | ||||
|  * signatures. | ||||
|  */ | ||||
| internal fun Buffer.isProbablyUtf8(): Boolean { | ||||
|     try { | ||||
|         val prefix = Buffer() | ||||
|         val byteCount = size.coerceAtMost(64) | ||||
|         copyTo(prefix, 0, byteCount) | ||||
|         for (i in 0 until 16) { | ||||
|             if (prefix.exhausted()) { | ||||
|                 break | ||||
|             } | ||||
|             val codePoint = prefix.readUtf8CodePoint() | ||||
|             if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) { | ||||
|                 return false | ||||
|             } | ||||
|         } | ||||
|         return true | ||||
|     } catch (_: EOFException) { | ||||
|         return false // Truncated UTF-8 sequence. | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user