I have python flask back-end and I don't know how to configure it to have a delay between accepting a connection and reading data, or perform any other back-end workaround, so it receives an empty request body all the time.
Likely a minimal example:
package com.acidbro.test.megabyte0.httplib
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.os.HandlerCompat
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import java.util.concurrent.*
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
}
class MyHttpLib(
private val responseParser: MyHttpResponseParser,
private val executor: Executor//,
//private val resultHandler: Handler
) {
// Function that makes the network request, blocking the current thread
fun makeRequest(
url: String,
method: String,
jsonBody: String,
headers: Map<String,String>,
resultHandler: Handler?,
callback: (Result<MyHttpResponse>) -> Unit
) {
executor.execute {
try {
val response = makeSynchronousRequest(url,method, jsonBody, headers)
if (resultHandler!==null) {
resultHandler.post { callback(response) }
} else {
callback(response)
}
} catch (e: Exception) {
val errorResult = Result.Error(e)
if (resultHandler!==null) {
resultHandler.post { callback(errorResult) }
} else {
callback(errorResult)
}
e.printStackTrace()
}
}
}
private fun makeSynchronousRequest(
resourceUrl: String,
method: String,
body: String,
headers: Map<String,String>
): Result<MyHttpResponse> {
val url = URL(resourceUrl)
val requestHeaders = hashMapOf<String,String>()
requestHeaders.putAll(headers)
(url.openConnection() as? HttpURLConnection)?.run {
Log.d(TAG,"run")
//https://stackoverflow.com/a/26030149
instanceFollowRedirects = false
Log.d(TAG,"instanceFollowRedirects")
requestMethod = method
Log.d(TAG,"requestMethod")
doOutput = method==="POST"
Log.d(TAG,"doOutput = ${method==="POST"}")
for ((key,value) in requestHeaders) {
setRequestProperty(key, value)
}
Log.d(TAG,"setRequestProperty")
if (method==="POST") {
val bytes = body.toByteArray()
setFixedLengthStreamingMode(bytes.size)
//connect()
outputStream.run {
write(bytes)
Log.d(TAG, "outputStream.write")
flush()
Log.d(TAG, "outputStream.flush()")
close()
Log.d(TAG,"outputStream.close()")
}
}
val myInputStream: InputStream? = (
try {inputStream}
catch (e: Exception) {null}
)
Log.d(TAG,"$responseCode $headerFields $myInputStream")
return Result.Success(
responseParser.parse(
responseCode,
headerFields,
myInputStream
)
)
}
return Result.Error(Exception("Cannot open HttpURLConnection"))
}
companion object {
val TAG = MyHttpLib::class.simpleName
val mainThreadHandler: Handler = HandlerCompat.createAsync(Looper.getMainLooper())
/*
* Gets the number of available cores
* (not always the same as the maximum number of cores)
*/
private val NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors()
// Instantiates the queue of Runnables as a LinkedBlockingQueue
private val workQueue: BlockingQueue<Runnable> =
LinkedBlockingQueue<Runnable>()
// Sets the amount of time an idle thread waits before terminating
private val KEEP_ALIVE_TIME = 5L
// Sets the Time Unit to seconds
private val KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS
// Creates a thread pool manager
val threadPoolExecutor: ThreadPoolExecutor = ThreadPoolExecutor(
NUMBER_OF_CORES, // Initial pool size
NUMBER_OF_CORES, // Max pool size
KEEP_ALIVE_TIME,
KEEP_ALIVE_TIME_UNIT,
workQueue
)
@Volatile
private var INSTANCE: MyHttpLib? = null
fun getInstance(): MyHttpLib {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: MyHttpLib(
MyHttpResponseParser(),
threadPoolExecutor//,
//mainThreadHandler
)
.also { INSTANCE = it }
}
}
}
}
package com.acidbro.test.megabyte0.httplib
import org.json.JSONException
import org.json.JSONObject
import java.io.InputStream
class MyHttpResponseParser {
fun parse(
status: Int,
headers: Map<String,List<String>>,
inputStream: InputStream?
): MyHttpResponse {
val data = (
if (inputStream!==null)
String(inputStream.readBytes())
else ""
)
return MyHttpResponse(status, headers,
data,
try {
JSONObject(data)
} catch (e: JSONException) {
null
}
)
}
}
data class MyHttpResponse(
val status: Int,
val headers: Map<String,List<String>>,
val data: String,
val json: JSONObject?
) {
}
And the python simple socket server back-end. When I uncomment sleep(0.1)
it receives all the data in the first request.recv
, if not it receives only the headers and all the body goes in the second request.recv
, so does flask, except that flask doesn't get the request body.
import socketserver
import time
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The request handler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def handle(self):
t0 = time.monotonic()
#time.sleep(0.02)
# self.request is the TCP socket connected to the client
self.data = [self.request.recv(2**16)]#.strip()
t1 = time.monotonic()
# just send back the same data, but upper-cased
# self.request.sendall(self.data.upper())
self.request.sendall(
rb'''HTTP/1.1 400 BAD REQUEST
Content-Length: 0
Connection: Close
'''.replace(b'\n',b'\r\n'))
t2 = time.monotonic()
self.data.append(self.request.recv(2**16))
t3 = time.monotonic()
print("{} wrote:".format(self.client_address[0]))
print([t-t0 for t in [t1,t2,t3]])
print(self.data)
print()
if __name__ == "__main__":
HOST, PORT = "192.168.1.108", 8000
# Create the server, binding to localhost on port 9999
with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
The question is -- is there any android front-end android workaround to force the flask server to receive the request body? Thanks.
EDIT:
responding to comments, deleted setFixedLengthStreamingMode
, it's the same story, the thing was added to try to speed things up. The same with connect()
, flush()
and close()
and/or wrapping outputStream
into a buffered stream.
The exact values of the delays are like [0.010252761996525805, 0.010276289998728316, 0.010734848998254165]
when using setFixedLengthStreamingMode
and like [0.04281934200116666, 0.04284564500267152, 0.043687549004971515]
when not using it. Either way request is read by the server in two parts, as mentioned above.
Thanks in advance for your time and effort.