Skip to content

Commit 3dfdd92

Browse files
authored
Bring parity with cli to header command (#405)
- Do not include stderr. - Allow no output.
1 parent 1d96051 commit 3dfdd92

File tree

2 files changed

+45
-24
lines changed

2 files changed

+45
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package com.coder.gateway.util
22

3-
import java.net.URL
43
import org.zeroturnaround.exec.ProcessExecutor
4+
import java.io.OutputStream
5+
import java.net.URL
56

67
private val newlineRegex = "\r?\n".toRegex()
78
private val endingNewlineRegex = "\r?\n$".toRegex()
@@ -14,24 +15,36 @@ fun getHeaders(url: URL, headerCommand: String?): Map<String, String> {
1415
OS.WINDOWS -> Pair("cmd.exe", "/c")
1516
else -> Pair("sh", "-c")
1617
}
17-
return ProcessExecutor()
18+
val output = ProcessExecutor()
1819
.command(shell, caller, headerCommand)
1920
.environment("CODER_URL", url.toString())
21+
// By default stderr is in the output, but we want to ignore it. stderr
22+
// will still be included in the exception if something goes wrong.
23+
.redirectError(OutputStream.nullOutputStream())
2024
.exitValues(0)
2125
.readOutput(true)
2226
.execute()
2327
.outputUTF8()
28+
29+
// The Coder CLI will allow no output, but not blank lines. Possibly we
30+
// should skip blank lines, but it is better to have parity so commands will
31+
// not sometimes work in one context and not another.
32+
return if (output == "") mapOf() else output
2433
.replaceFirst(endingNewlineRegex, "")
2534
.split(newlineRegex)
2635
.associate {
2736
// Header names cannot be blank or contain whitespace and the Coder
28-
// CLI requires that there be an equals sign (the value can be blank
29-
// though). The second case is taken care of by the destructure
30-
// here, as it will throw if there are not enough parts.
31-
val (name, value) = it.split("=", limit=2)
32-
if (name.contains(" ") || name == "") {
33-
throw Exception("\"$name\" is not a valid header name")
37+
// CLI requires there be an equals sign (the value can be blank).
38+
val parts = it.split("=", limit=2)
39+
if (it.isBlank()) {
40+
throw Exception("Blank lines are not allowed")
41+
} else if (parts.size != 2) {
42+
throw Exception("Header \"$it\" does not have two parts")
43+
} else if (parts[0].isBlank()) {
44+
throw Exception("Header name is missing in \"$it\"")
45+
} else if (parts[0].contains(" ")) {
46+
throw Exception("Header name cannot contain spaces, got \"${parts[0]}\"")
3447
}
35-
name to value
48+
parts[0] to parts[1]
3649
}
3750
}

src/test/kotlin/com/coder/gateway/util/HeadersTest.kt

+23-15
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package com.coder.gateway.util
22

3+
import java.net.URL
34
import kotlin.test.Test
5+
import kotlin.test.assertContains
46
import kotlin.test.assertEquals
57
import kotlin.test.assertFailsWith
68

7-
import java.net.URL
8-
99
internal class HeadersTest {
1010
@Test
1111
fun testGetHeadersOK() {
@@ -19,6 +19,11 @@ internal class HeadersTest {
1919
"printf 'foo=bar='" to mapOf("foo" to "bar="),
2020
"printf 'foo=bar=baz'" to mapOf("foo" to "bar=baz"),
2121
"printf 'foo='" to mapOf("foo" to ""),
22+
"printf 'foo=bar '" to mapOf("foo" to "bar "),
23+
"exit 0" to mapOf(),
24+
"printf ''" to mapOf(),
25+
"printf 'ignore me' >&2" to mapOf(),
26+
"printf 'foo=bar' && printf 'ignore me' >&2" to mapOf("foo" to "bar"),
2227
)
2328
tests.forEach{
2429
assertEquals(
@@ -30,22 +35,25 @@ internal class HeadersTest {
3035

3136
@Test
3237
fun testGetHeadersFail() {
33-
val tests = listOf(
34-
"printf 'foo=bar\\r\\n\\r\\n'",
35-
"printf '\\r\\nfoo=bar'",
36-
"printf '=foo'",
37-
"printf 'foo'",
38-
"printf ' =foo'",
39-
"printf 'foo =bar'",
40-
"printf 'foo foo=bar'",
41-
"printf ''",
42-
"exit 0",
43-
"exit 1",
38+
val tests = mapOf(
39+
"printf '=foo'" to "Header name is missing in \"=foo\"",
40+
"printf 'foo'" to "Header \"foo\" does not have two parts",
41+
"printf ' =foo'" to "Header name is missing in \" =foo\"",
42+
"printf 'foo =bar'" to "Header name cannot contain spaces, got \"foo \"",
43+
"printf 'foo foo=bar'" to "Header name cannot contain spaces, got \"foo foo\"",
44+
"printf ' foo=bar '" to "Header name cannot contain spaces, got \" foo\"",
45+
"exit 1" to "Unexpected exit value: 1",
46+
"printf 'foobar' >&2 && exit 1" to "foobar",
47+
"printf 'foo=bar\\r\\n\\r\\n'" to "Blank lines are not allowed",
48+
"printf '\\r\\nfoo=bar'" to "Blank lines are not allowed",
49+
"printf '\\r\\n'" to "Blank lines are not allowed",
50+
"printf 'f=b\\r\\n\\r\\nb=q'" to "Blank lines are not allowed"
4451
)
4552
tests.forEach{
46-
assertFailsWith(
53+
val ex = assertFailsWith(
4754
exceptionClass = Exception::class,
48-
block = { getHeaders(URL("http://localhost"), it) })
55+
block = { getHeaders(URL("http://localhost"), it.key) })
56+
assertContains(ex.message.toString(), it.value)
4957
}
5058
}
5159

0 commit comments

Comments
 (0)