HomeyKrogerSage
HomeyKrogerSage

Reputation: 11

Downloading APK file from diy rust server to android app results in End of Stream error

As the title says I'm trying, for both functionality and learning purposes, to use a rust server to "host" the newest version of my android app apk and download the apk to my phone when a newer version is available.

As far as I can tell the rust server is correctly writing the data (I'm just using std::fs::read and TcpStream::write_all). The logcat is showing that I'm picking up chunks. What doesn't seem to be happening is that the stream gets interrupted every time just a few kilobytes shy. The file gets created and has most of the data written to it. I'm convinced it must be the rust server for whatever reason is closing the connection before the app is finished receiving.

Here's my rust code:

fn handle_get_apk(stream: TcpStream) {

    HttpResponseBuilder { 
        status : "200 OK".to_string(),
        headers : vec![],
        body : String::new()
    }.build_with_bytes(match fs::read(apk::get_apk_path()) {
        Ok(k_) => k_,
        Err(e_) => {
            error_response(stream, hkslib::error::format_error("handle_get_apk", e_.to_string()));

            return;
    }}).write_all(&stream);

}

fn build_with_bytes (&self, body_bytes: Vec<u8>) -> HttpStream {
        let mut res = format!("HTTP/1.1 {}\r\nAccept-Ranges: bytes\r\n", self.status);

        for header in &self.headers {
            res = res + &header + "\r\n";
        }

        let length = body_bytes.len();
        res = res + &format!("Content-Length: {}\r\n\r\n", length);

        let mut response = res.as_bytes().to_vec();
        response.extend_from_slice(&body_bytes);

        HttpStream {
            bytes : response
        }
    }
}

Here's the android kotlin code:

 fun getFile(destination: String, client: HttpClient, headers: ArrayList<Pair<String, String>>?) {

        GlobalScope.launch(Dispatchers.IO) {

            log("sending request to $destination")

                    var localfile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "app.apk")

            runBlocking {
                client.prepareGet(destination, block = {

                    headers?.forEach { (key, value) ->
                        header(key, value)
                    }

                }).execute { response: HttpResponse ->
                    if(response.status.value >= 400) {
                        println(response.toString())
                        return@execute
                    }
                    val channel = response.bodyAsChannel()
                    while(!channel.isClosedForRead) {
                        try {
                            val packet = channel.readRemaining(4096) // I've tried using different values, no effect
                            while (!packet.exhausted()) {
                                val bytes = packet.readByteArray()
                                localFile.appendBytes(bytes)
                                _log("Received ${localFile.length()} bytes from ${response.contentLength()}")
                            }
                        } catch (e: Exception) {
                            _log("Failed to complete capture... $e")
                            e.printStackTrace()
                            break
                        }
                    }
                }
            }

        }

Some other notes about the general architecture, the rust server is running on 4 threads with workers handling each request. This is not going to become a commercial app so I'm skipping a lot of the usual scoped storage restrictions for convenience.

Thoughts?

I've tried different headers, timeout settings, sending as chunks server side (couldn't get it to work tbh).

I haven't found anything on stackoverflow or github that gives me a solution

I'm expecting the file to be created and appended with all the data, the file is being created and appended with ~95% of the data

Upvotes: 1

Views: 34

Answers (0)

Related Questions