Send Content-Length if required

This commit is contained in:
Jonas Lochmann 2023-02-13 01:00:00 +01:00
parent 80b58e134d
commit dac0ce74fe
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
3 changed files with 78 additions and 11 deletions

View file

@ -1,5 +1,5 @@
/* /*
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann * TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -31,7 +31,7 @@ val httpClient: OkHttpClient by lazy {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
builder.addInterceptor (HttpLoggingInterceptor { builder.addInterceptor (HttpLoggingInterceptor {
Log.d("HttpClient", it) Log.d("HttpClient", it)
}) }.apply { level = HttpLoggingInterceptor.Level.HEADERS })
} }
builder.build() builder.build()

View file

@ -23,12 +23,15 @@ import io.timelimit.android.async.Threads
import io.timelimit.android.coroutines.executeAndWait import io.timelimit.android.coroutines.executeAndWait
import io.timelimit.android.coroutines.waitForResponse import io.timelimit.android.coroutines.waitForResponse
import io.timelimit.android.sync.network.* import io.timelimit.android.sync.network.*
import io.timelimit.android.util.okio.LengthSink
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.Response import okhttp3.Response
import okhttp3.internal.closeQuietly
import okio.BufferedSink import okio.BufferedSink
import okio.GzipSink import okio.GzipSink
import okio.Sink
import okio.buffer import okio.buffer
import java.io.OutputStreamWriter import java.io.OutputStreamWriter
@ -63,24 +66,37 @@ class HttpServerApi(private val endpointWithoutSlashAtEnd: String): ServerApi {
private val JSON = "application/json; charset=utf-8".toMediaTypeOrNull() private val JSON = "application/json; charset=utf-8".toMediaTypeOrNull()
private fun createJsonRequestBody(serialize: (writer: JsonWriter) -> Unit) = object: RequestBody() { private fun createJsonRequestBody(
override fun contentType() = JSON serialize: (writer: JsonWriter) -> Unit,
override fun writeTo(sink: BufferedSink) { measureContentLength: Boolean
): RequestBody {
fun write(sink: Sink) {
val writer = JsonWriter( val writer = JsonWriter(
OutputStreamWriter( OutputStreamWriter(
GzipSink(sink) GzipSink(sink)
.buffer().outputStream() .buffer().outputStream()
) )
) )
serialize(writer) serialize(writer)
writer.flush()
writer.close() writer.close()
} }
val length =
if (measureContentLength) LengthSink().also { write(it) }.length
else null
return object: RequestBody() {
override fun contentType() = JSON
override fun writeTo(sink: BufferedSink) = write(sink)
override fun contentLength(): Long = length ?: -1
}
} }
} }
private var sendContentLength = false
override suspend fun getTimeInMillis(): Long { override suspend fun getTimeInMillis(): Long {
httpClient.newCall( httpClient.newCall(
Request.Builder() Request.Builder()
@ -595,10 +611,30 @@ class HttpServerApi(private val endpointWithoutSlashAtEnd: String): ServerApi {
path: String, path: String,
requestBody: (writer: JsonWriter) -> Unit requestBody: (writer: JsonWriter) -> Unit
): Response { ): Response {
if (!sendContentLength) {
val response = postJsonRequest(path, requestBody, transmitContentLength = false)
if (response.code != 411) return response
Threads.network.executeAndWait { response.closeQuietly() }
sendContentLength = true
}
return postJsonRequest(path, requestBody, transmitContentLength = true)
}
private suspend fun postJsonRequest(
path: String,
requestBody: (writer: JsonWriter) -> Unit,
transmitContentLength: Boolean
): Response {
val body = createJsonRequestBody(requestBody, transmitContentLength)
return httpClient.newCall( return httpClient.newCall(
Request.Builder() Request.Builder()
.url("$endpointWithoutSlashAtEnd/$path") .url("$endpointWithoutSlashAtEnd/$path")
.post(createJsonRequestBody(requestBody)) .post(body)
.header("Content-Encoding", "gzip") .header("Content-Encoding", "gzip")
.build() .build()
).waitForResponse() ).waitForResponse()

View file

@ -0,0 +1,31 @@
/*
* TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.timelimit.android.util.okio
import okio.Buffer
import okio.Sink
import okio.Timeout
class LengthSink: Sink {
private var lengthInternal = 0L
val length get() = lengthInternal
override fun write(source: Buffer, byteCount: Long) { lengthInternal += byteCount }
override fun timeout(): Timeout = Timeout.NONE
override fun flush() = Unit
override fun close() = Unit
}