Skip to content

Commit e2d910c

Browse files
committed
implement binary parsing
1 parent 3cdb942 commit e2d910c

File tree

3 files changed

+38
-12
lines changed

3 files changed

+38
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
22
* jdbc: PooledDataSource.queryTimeout can be configured separately from pool connection timeout
3+
* server: FormDataParser now is able to parse binary files
34

45
# 1.7.0
56
* core: TSIDGenerator can now be used with custom classes outside TSID companion

server/src/klite/FormDataParser.kt

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package klite
22

3+
import java.io.ByteArrayOutputStream
34
import java.io.InputStream
5+
import java.util.*
46
import kotlin.reflect.KType
5-
import kotlin.text.Charsets.ISO_8859_1
67

78
@Deprecated("Use FormDataParser")
89
typealias MultipartFormDataParser = FormDataParser
@@ -12,29 +13,30 @@ class FormDataParser: BodyParser {
1213

1314
@Suppress("UNCHECKED_CAST")
1415
override fun <T: Any> parse(input: InputStream, type: KType): T {
15-
val reader = input.bufferedReader(ISO_8859_1)
16-
val boundary = reader.readLine()
16+
val boundary = input.readLine()!!.trimEnd()
1717
val result = mutableMapOf<String, Any?>()
1818
var state = State()
1919
while (true) {
20-
val line = reader.readLine() ?: break
20+
val line = input.readLine() ?: break
2121
if (state.readingHeaders) {
22-
val lowerLine = line.lowercase()
22+
val lineStr = line.toString(MimeTypes.textCharset).trimEnd()
23+
val lowerLine = lineStr.lowercase()
2324
if (lowerLine.isEmpty()) state.readingHeaders = false
2425
else if (lowerLine.startsWith("content-disposition:")) {
25-
val disposition = line.substring("content-disposition:".length).trim()
26+
val disposition = lineStr.substring("content-disposition:".length).trim()
2627
val params = disposition.split(';').associate(::keyValue)
2728
state.name = params["name"]
2829
state.fileName = params["filename"]
2930
}
3031
else if (lowerLine.startsWith("content-type:")) {
31-
state.contentType = line.substring("content-type:".length).trim()
32+
state.contentType = lineStr.substring("content-type:".length).trim()
3233
}
3334
} else {
3435
if (line.startsWith(boundary)) {
3536
if (state.name != null) result[state.name!!] = state.fileName?.let {
36-
FileUpload(it, state.contentType, state.content.removeSuffix("\n").toString().byteInputStream(ISO_8859_1))
37-
} ?: state.content.toString().trim()
37+
// TODO: remove last newline from content
38+
FileUpload(it, state.contentType, state.content.toByteArray().trimEnd().inputStream())
39+
} ?: state.content.toString(MimeTypes.textCharset).trim()
3840
state = State()
3941
}
4042
else state.append(line)
@@ -43,11 +45,34 @@ class FormDataParser: BodyParser {
4345
return result as T
4446
}
4547

48+
private fun InputStream.readLine(): ByteArray? {
49+
val buf = ByteArrayOutputStream(128)
50+
var b = read()
51+
while (b >= 0) {
52+
buf.write(b)
53+
if (b == '\n'.code) break
54+
b = read()
55+
}
56+
if (b == -1 && buf.size() == 0) return null
57+
return buf.toByteArray()
58+
}
59+
60+
private fun ByteArray.trimEnd(): ByteArray {
61+
var newLen = size
62+
if (this[newLen - 1] == 10.toByte()) newLen--
63+
if (this[newLen - 1] == 13.toByte()) newLen--
64+
return copyOf(newLen)
65+
}
66+
67+
private fun ByteArray.startsWith(prefix: ByteArray): Boolean {
68+
return prefix.size <= size && Arrays.equals(this, 0, prefix.size, prefix, 0, prefix.size)
69+
}
70+
4671
private fun keyValue(s: String) = s.split('=', limit = 2).let { it[0].trim() to it.getOrNull(1)?.trim('"') }
4772

4873
private class State(var readingHeaders: Boolean = true, var name: String? = null, var fileName: String? = null, var contentType: String? = null) {
49-
val content = StringBuilder()
50-
fun append(line: String) = content.append(line).append('\n')
74+
val content = ByteArrayOutputStream(4096)
75+
fun append(line: ByteArray) = content.write(line)
5176
}
5277
}
5378

server/test/klite/FormDataParserTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class FormDataParserTest {
4545
}
4646

4747
@Test fun `parse binary`() {
48-
val boundary = "---------------------------9051914041544843365972754266"
48+
val boundary = "---XXX"
4949
val data = (-128..127).map { it.toByte() }.toByteArray()
5050
val body = (
5151
"$boundary\r\n"

0 commit comments

Comments
 (0)