-
-
Notifications
You must be signed in to change notification settings - Fork 436
Description
Activity
Have you send this email as requested?
Not necessary. Since today 11:00 AM, I got a dozen or so reports with the same stack trace.
de.westnordost.streetcomplete.data.ApiClientException: Client request(POST https://streetcomplete.app/photo-upload/upload.php) invalid: 415 Unsupported Media Type. Text: "{"error":"File type not allowed"}"
at de.westnordost.streetcomplete.data.osmnotes.PhotoServiceApiClientImpl.upload(SourceFile:42)
at de.westnordost.streetcomplete.data.osmnotes.PhotoServiceApiClientImpl$upload$1.invokeSuspend(SourceFile:12)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(SourceFile:34)
at kotlinx.coroutines.DispatchedTask.run(SourceFile:100)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(SourceFile:124)
at kotlinx.coroutines.scheduling.TaskImpl.run(SourceFile:89)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(SourceFile:586)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(SourceFile:798)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(SourceFile:717)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(SourceFile:704)
Caused by: io.ktor.client.plugins.ClientRequestException: Client request(POST https://streetcomplete.app/photo-upload/upload.php) invalid: 415 Unsupported Media Type. Text: "{"error":"File type not allowed"}"
at io.ktor.client.plugins.DefaultResponseValidationKt$addDefaultResponseValidation$1$1.invokeSuspend(SourceFile:54)
at io.ktor.client.plugins.DefaultResponseValidationKt$addDefaultResponseValidation$1$1.invoke(SourceFile:0)
at io.ktor.client.plugins.DefaultResponseValidationKt$addDefaultResponseValidation$1$1.invoke(SourceFile:0)
at io.ktor.client.plugins.HttpCallValidatorKt.HttpCallValidator$lambda$0$validateResponse(SourceFile:125)
at io.ktor.client.plugins.HttpCallValidatorKt.access$HttpCallValidator$lambda$0$validateResponse(SourceFile:1)
at io.ktor.client.plugins.HttpCallValidatorKt$HttpCallValidator$2$2.invokeSuspend(SourceFile:144)
... 8 more
I.e. it happens when attaching a photo to a note.
Nothing changed in the app, but apparently something changed with my hosting? Will investigate.
Hmm, the file type is application/x-empty.
Also reproducible with v63.1. So, it is very likely that it has not been caused by any change made for the app.
Server-side, the script that checks for the content type is
$photo = file_get_contents('php://input', false, null, 0, $max_content_length);
$finfo = new finfo(FILEINFO_MIME_TYPE);
$file_type = $finfo->buffer($photo);$file_type is application/x-empty since today for StreetComplete uploads.
Testing if maybe something in PHP is broken, I executed this script (placing a picture.jpg into the same directory)
$photo = file_get_contents('picture.jpg');
$finfo = new finfo(FILEINFO_MIME_TYPE);
$file_type = $finfo->buffer($photo);
Here, $file_type is image/jpeg as expected.
🤔
Now I have this PHP script:
<?php
$photo = file_get_contents('php://input', false, null, 0, 5000 * 1000);
$finfo = new finfo(FILEINFO_MIME_TYPE);
$file_type = $finfo->buffer($photo);
print($file_type);
?>and when I test via
curl -X POST 'https://www.westnordost.de/misc/test.php' --data-binary '@picture.jpg'
(having a JPEG file named picture.jpg in the same directory), it also returns image/jpeg correctly. What...
$file_type is application/x-empty since today for StreetComplete uploads.
are files themselves blank/empty/mangled or are these containing image data as they should be? Or are partially corrupt? Or fully fine just loading them is broken?
(sorry if my rubber ducking attempt was unhelpful)
I appreciate the quak
Yes, I see the file is empty (from the view of the server). But on the client, it is not empty. So, the issue must be in the HTTP client on Android.
Yes, I see the file is empty (from the view of the server). But on the client, it is not empty. So, the issue must be in the HTTP client on Android.
Or, perhaps, the tmp directory that PHP upload process uses is full on the server? (or it has lost correct permissions? or PHP got upgraded and get different defaults?)
in any case, since the problem is same with old clients which worked perfectly and whose code has not changed since it worked, to me, it would point to some operational change that happened at the server.
I.e. it happens when attaching a photo to a note.
I did post a photo in a note. Then the message kept appearing in both apps.
@mnalis I too expected that, for the reasons you state.
But seems to be not the case.
The code in question on the client side is
Lines 37 to 42 in eb9ae60
| val response = httpClient.post(baseUrl + "upload.php") { | |
| contentType(ContentType.defaultForFilePath(file.toString())) | |
| header("Content-Transfer-Encoding", "binary") | |
| setBody(ByteReadChannel(fileSystem.source(file).buffered())) | |
| expectSuccess = true | |
| } |
What's conspicuous is that the Content-Length header is not set, which is required as far as I know. This worked before. Ktor (the HTTP client library) should calculate that automatically.
(Also, a LLM chatbot tells me that the "Content-Transfer-Encoding" header is not used in HTTP but in email and thus superfluous/incorrect, so I removed it.)
I found a fix:
val response = httpClient.post(baseUrl + "upload.php") {
contentType(ContentType.defaultForFilePath(file.toString()))
- setBody(ByteReadChannel(fileSystem.source(file).buffered()))
+ setBody(fileSystem.source(file).buffered().readByteArray())
expectSuccess = true
} i.e., read the whole file into a byte array first, rather than stream it into the HTTP request. Then, Ktor has no problem to determine and set the Content-Length automatically.
The Ktor API reference mentions that for streamed body content, where the content length is unknown, Transfer-Encoding: chunked is used. However, I read that this transfer encoding is only supported in HTTP 1.1, not in HTTP/2. Now, this might explain why it started to fail today even though nothing in the client nor server was changed: If Ktor uses HTTP/2 (don't know how to check that) and also uses chunked transfer encoding anyway, it never should have worked, so a minor update to e.g. the (HTTP dependency of the) PHP runtime on the server could have broken this. Now, this is all conjecture. But I think I have at least gathered enough information to file a bug report at KTOR while in parallel I will deploy the fix to a new minor version of StreetComplete.
How to Reproduce
reinstall SC/SCEE completely. Login via OAuth. disable all quests. Instant error message. Changeset via Vespucci etc is possible.Expected Behavior
should not produce error after fresh installAdditional Information
Android 16 / SC v63.1 / SCEE v63.21