Hi all, we built an API that allows the upload of...
# ktor
a
Hi all, we built an API that allows the upload of JSON data. We would like to limit the request body size to a fixed number of MB. we didn't find any such feature in ktor, nor in the underlying Jetty engine (not for json content type). So we implemented our own function that would parse the input stream and stop once it is too many bytes As this is using blocking API, we are not sure whether this will cause problems down the road? We wonder how ktor itself receives json in a non-blocking way?
Copy code
suspend fun PipelineContext<Unit, ApplicationCall>.receiveCarefully(): String? =
        @Suppress("BlockingMethodInNonBlockingContext")
        withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
            val bodyBuilder = StringBuilder()
            val stream = call.receiveStream()
            stream.reader().use { reader ->
                do {
                    val chars = CharArray(8192)
                    val numRead = reader.read(chars)
                    if (numRead > 0) {
                        bodyBuilder.append(String(chars, 0, numRead))
                        if (bodyBuilder.length > AppConfig.maxUploadChars) {
                            return@withContext null
                        }
                    }
                } while (numRead > -1)
                bodyBuilder.toString()
            }
        }
j
Keep in mind that how you deploy this will impact the result as well. For example, if you use Nginx as a reverse proxy, you will need to configure at Nginx level the max_body_size you will allow. Maybe this can solve your problems? https://stackoverflow.com/questions/26717013/how-to-edit-nginx-conf-to-increase-file-size-upload
a
We use an AWS application load balancer. That one seems to have unlimited upload and we couldn't find any feature to limit the upload size on the load balancer
The concern is that our service might run OOM if clients perform very huge uploads that were supposed to be split into many smaller requests
So the code above will not load all that into memory but stop if it is too large. Our load testing shows that this works quite okay for our own requirements. We wonder however if there is a way in kotin / ktor to read from the input stream using non-blocking way. ktor itself must also have some place where a request body is parsed into text (coudn't find it by browsing the code). What API would ktor itself use for that? Does ktor itself maybe also use IO dispatcher for that purpose?
j
AFAIK, Ktor uses Async pipelines, so each request should be run async. I'm not a Jetbrains developer/supporter, so just don't simply rely on my answer. The scenario you describe seems like can be achieved using Multipart. Did you check if this could work for you? You need to change how you make the request, but at least you will have control over the buffer and so on.. https://ktor.io/docs/uploads.html#receiving-files-using-multipart
a
Unfortunately, our API was not designed with multi part therefore we cannot change it anymore
But in the example from the link, there is also the function "copyToSuspend" that uses the IO dispatcher like we do ...