summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2022-01-16 13:57:22 +0000
committerJuan J. Martinez <jjm@usebox.net>2022-01-16 13:57:22 +0000
commit66deff10063ff49bcf0443ca3cc2b7f4639857cc (patch)
treecad8ef7e765af573552b530c847da1e761872204 /server
parentada8e769780fbaa3294e36a732c8711790155ae3 (diff)
downloadspacebeans-66deff10063ff49bcf0443ca3cc2b7f4639857cc.tar.gz
spacebeans-66deff10063ff49bcf0443ca3cc2b7f4639857cc.zip
Move tests to the right spec
Diffstat (limited to 'server')
-rw-r--r--server/test/src/ServerSpec.scala562
-rw-r--r--server/test/src/handlers/GeminiHandlerSpec.scala528
2 files changed, 542 insertions, 548 deletions
diff --git a/server/test/src/ServerSpec.scala b/server/test/src/ServerSpec.scala
index a7a2db0..cfb613a 100644
--- a/server/test/src/ServerSpec.scala
+++ b/server/test/src/ServerSpec.scala
@@ -24,49 +24,6 @@ class ServerSpec extends AnyFlatSpec with Matchers {
behavior of "handleReq"
- it should "return bad request on URLs with no scheme" in {
- Server(TestData.conf)
- .handleReq("//localhost/", "127.0.0.1") should be(a[BadRequest])
- }
-
- it should "return proxy request refused on port mismatch" in {
- Server(TestData.conf)
- .handleReq("gemini://localhost:8080/", "127.0.0.1") should be(
- a[ProxyRequestRefused]
- )
- }
-
- it should "return proxy request refused when port not provided and configured port is not default" in {
- Server(TestData.conf.copy(port = 8080))
- .handleReq("gemini://localhost/", "127.0.0.1") should be(
- a[ProxyRequestRefused]
- )
- }
-
- it should "return success when port is provided and matches configured port (not default)" in {
- Server(TestData.conf.copy(port = 8080))
- .handleReq("gemini://localhost:8080/", "127.0.0.1") should be(a[Success])
- }
-
- it should "handle host case insensitive" in {
- Server(TestData.conf)
- .handleReq("gemini://LOCALHOST/", "127.0.0.1") should be(a[Success])
- }
-
- it should "return proxy request refused when the vhost is not found" in {
- Server(TestData.conf)
- .handleReq("gemini://otherhost/", "127.0.0.1") should be(
- a[ProxyRequestRefused]
- )
- }
-
- it should "return bad request when user info is present" in {
- Server(TestData.conf)
- .handleReq("gemini://user@localhost/", "127.0.0.1") should be(
- a[BadRequest]
- )
- }
-
it should "return bad request on empty URLs" in {
Server(TestData.conf)
.handleReq("", "127.0.0.1") should be(
@@ -74,66 +31,35 @@ class ServerSpec extends AnyFlatSpec with Matchers {
)
}
- it should "return bad request when the path is out of root dir" in {
+ it should "return bad request for invalid URLs" in {
Server(TestData.conf)
- .handleReq("gemini://localhost/../../", "127.0.0.1") should be(
+ .handleReq("gemini://localhost/ invalid", "127.0.0.1") should be(
a[BadRequest]
)
}
- it should "return bad request for invalid URLs" in {
+ it should "return bad request on URLs with no scheme" in {
Server(TestData.conf)
- .handleReq(
- "gemini://localhost/ invalid",
- "127.0.0.1"
- ) should be(a[BadRequest])
+ .handleReq("//localhost/", "127.0.0.1") should be(a[BadRequest])
}
- it should "redirect to normalize the URL" in {
+ it should "return proxy request refused on port mismatch" in {
Server(TestData.conf)
- .handleReq("gemini://localhost/./", "127.0.0.1") should be(
- a[PermanentRedirect]
+ .handleReq("gemini://localhost:8080/", "127.0.0.1") should be(
+ a[ProxyRequestRefused]
)
}
- it should "return not found if the path doesn't exist" in {
- Server(TestData.conf)
- .handleReq(
- "gemini://localhost/doesnotexist",
- "127.0.0.1"
- ) should be(a[NotFound])
- }
-
- it should "return not found if a dot file" in {
- Server(TestData.conf)
- .handleReq(
- "gemini://localhost/.dotfile",
- "127.0.0.1"
- ) should be(a[NotFound])
- }
-
- it should "return success on reading file" in {
- Server(TestData.conf)
- .handleReq(
- "gemini://localhost/index.gmi",
- "127.0.0.1"
- ) should matchPattern {
- case Success(_, "text/gemini", Some(_), 25L) =>
- }
- }
-
- it should "redirect and normalize request on a directory" in {
- Server(TestData.conf)
- .handleReq("gemini://localhost/dir", "127.0.0.1") should be(
- a[PermanentRedirect]
+ it should "return proxy request refused when port not provided and configured port is not default" in {
+ Server(TestData.conf.copy(port = 8080))
+ .handleReq("gemini://localhost/", "127.0.0.1") should be(
+ a[ProxyRequestRefused]
)
}
- it should "return an existing index file when requesting a directory" in {
- Server(TestData.conf)
- .handleReq("gemini://localhost/", "127.0.0.1") should matchPattern {
- case Success(_, "text/gemini", Some(_), 25L) =>
- }
+ it should "return success when port is provided and matches configured port (not default)" in {
+ Server(TestData.conf.copy(port = 8080))
+ .handleReq("gemini://localhost:8080/", "127.0.0.1") should be(a[Success])
}
it should "return proxy request refused for non gemini schemes" in {
@@ -142,464 +68,4 @@ class ServerSpec extends AnyFlatSpec with Matchers {
a[ProxyRequestRefused]
)
}
-
- it should "include gemini params for gemini MIME type" in {
- Server(
- TestData.conf.copy(virtualHosts =
- List(TestData.conf.virtualHosts(0).copy(geminiParams = Some("test")))
- )
- ).handleReq(
- "gemini://localhost/index.gmi",
- "127.0.0.1"
- ) should matchPattern {
- case Success(_, "text/gemini; test", Some(_), 25L) =>
- }
- }
-
- behavior of "handleReq, directory listings"
-
- it should "return a directory listing if is enabled and no index" in {
- Server(TestData.conf)
- .handleReq("gemini://localhost/dir/", "127.0.0.1") should be(
- a[DirListing]
- )
- }
-
- it should "return a directory listing, directory listing flags: vhost flag false, directories flag true" in {
- Server(
- ServiceConf.initConf(
- TestData.conf.copy(virtualHosts =
- List(
- TestData.conf
- .virtualHosts(0)
- .copy(
- directoryListing = false,
- directories = List(
- Directory(
- "dir/",
- directoryListing = Some(true),
- allowCgi = None
- )
- )
- )
- )
- )
- )
- ).handleReq("gemini://localhost/dir/", "127.0.0.1") should be(a[DirListing])
- }
-
- it should "return not found with no index, directory listing flags: vhost flag true, directories flag false" in {
- Server(
- ServiceConf.initConf(
- TestData.conf.copy(virtualHosts =
- List(
- TestData.conf
- .virtualHosts(0)
- .copy(
- directoryListing = true,
- directories = List(
- Directory(
- "dir/",
- directoryListing = Some(false),
- allowCgi = None
- )
- )
- )
- )
- )
- )
- ).handleReq("gemini://localhost/dir/", "127.0.0.1") should be(a[NotFound])
- }
-
- it should "not apply directory listing override to subdirectories" in {
- Server(
- ServiceConf.initConf(
- TestData.conf.copy(virtualHosts =
- List(
- TestData.conf
- .virtualHosts(0)
- .copy(
- directoryListing = false,
- directories = List(
- Directory(
- "dir/",
- directoryListing = Some(true),
- allowCgi = None
- )
- )
- )
- )
- )
- )
- ).handleReq("gemini://localhost/dir/sub/", "127.0.0.1") should be(
- a[NotFound]
- )
- }
-
- it should "return not found if directory listing is not enabled and no index" in {
- Server(
- TestData.conf.copy(virtualHosts =
- List(TestData.conf.virtualHosts(0).copy(directoryListing = false))
- )
- ).handleReq("gemini://localhost/dir/", "127.0.0.1") should be(a[NotFound])
- }
-
- behavior of "handleReq, user directories"
-
- it should "return success on reading file" in {
- Server(TestData.confUserDir).handleReq(
- "gemini://localhost/~username/index.gmi",
- "127.0.0.1"
- ) should matchPattern {
- case Success(_, "text/gemini", Some(_), 38L) =>
- }
- }
-
- it should "return redirect accessing the user directory without ending slash" in {
- Server(TestData.confUserDir).handleReq(
- "gemini://localhost/~username",
- "127.0.0.1"
- ) should be(a[PermanentRedirect])
- }
-
- it should "return success accessing the user directory index" in {
- Server(TestData.confUserDir).handleReq(
- "gemini://localhost/~username/",
- "127.0.0.1"
- ) should matchPattern {
- case Success(_, "text/gemini", Some(_), 38L) =>
- }
- }
-
- it should "return bad request trying to exit the root directory" in {
- Server(TestData.confUserDir).handleReq(
- "gemini://localhost/~username/../../",
- "127.0.0.1"
- ) should be(a[BadRequest])
- }
-
- it should "return redirect to the virtual host root when leaving the user dir" in {
- Server(TestData.confUserDir).handleReq(
- "gemini://localhost/~username/../",
- "127.0.0.1"
- ) should be(a[PermanentRedirect])
- }
-
- it should "not translate root if used an invalid user pattern" in {
- Server(TestData.confUserDir).handleReq(
- "gemini://localhost/~username../",
- "127.0.0.1"
- ) should be(a[NotFound])
-
- Server(TestData.confUserDir).handleReq(
- "gemini://localhost/~0invalid/",
- "127.0.0.1"
- ) should be(a[NotFound])
- }
-
- it should "not translate root if no user directory path was provided" in {
- Server(
- TestData.conf.copy(virtualHosts =
- List(
- TestData.conf
- .virtualHosts(0)
- .copy(userDirectories = true)
- )
- )
- ).handleReq(
- "gemini://localhost/~username/",
- "127.0.0.1"
- ) should be(a[NotFound])
- }
-
- it should "not execute a CGI if the target resource is not executable" in {
- Server(TestData.cgiConf).handleReq(
- "gemini://localhost/dir/file.txt",
- "127.0.0.1"
- ) should matchPattern {
- case Success(_, "text/plain", Some(_), 5L) =>
- }
- }
-
- it should "not execute a CGI if the target resource is a directory" in {
- Server(TestData.cgiConf).handleReq(
- "gemini://localhost/dir/sub/",
- "127.0.0.1"
- ) should be(a[DirListing])
- }
-
- it should "not apply allow CGI to subdirectories" in {
- Server(TestData.cgiConf).handleReq(
- "gemini://localhost/dir/sub/cgi",
- "127.0.0.1"
- ) should matchPattern {
- case Success(_, "text/plain", Some(_), 72) =>
- }
- }
-
- it should "execute a CGI" in {
- val cgi = Server(TestData.cgiConf)
- .handleReq(
- "gemini://localhost/dir/cgi",
- "127.0.0.1"
- )
- .asInstanceOf[Cgi]
-
- cgi.status should be(20)
- cgi.meta should be("text/gemini")
- cgi.body should include("GATEWAY_INTERFACE=CGI/1.1")
- }
-
- it should "execute a CGI: empty parameters, host and port" in {
- Server(TestData.cgiConf).handleReq(
- "gemini://localhost/dir/cgi",
- "127.0.0.1"
- ) should matchPattern {
- case Cgi(
- _,
- _,
- "",
- "",
- "cgi",
- TestData.host,
- TestData.portStr,
- _,
- _
- ) =>
- }
- }
-
- it should "execute a CGI: host is case-insensitive and the value in the conf is used" in {
- Server(TestData.cgiConf).handleReq(
- "gemini://LOCALHOST/dir/cgi",
- "127.0.0.1"
- ) should matchPattern {
- case Cgi(
- _,
- _,
- "",
- "",
- "cgi",
- TestData.host,
- TestData.portStr,
- _,
- _
- ) =>
- }
- }
-
- it should "execute a CGI: query string" in {
- Server(TestData.cgiConf).handleReq(
- "gemini://localhost/dir/cgi?query&string",
- "127.0.0.1"
- ) should matchPattern {
- case Cgi(
- _,
- _,
- "query&string",
- "",
- "cgi",
- TestData.host,
- TestData.portStr,
- _,
- _
- ) =>
- }
- }
-
- it should "execute a CGI: path info" in {
- Server(TestData.cgiConf).handleReq(
- "gemini://localhost/dir/cgi/path/info",
- "127.0.0.1"
- ) should matchPattern {
- case Cgi(
- _,
- _,
- "",
- "/path/info",
- "cgi",
- TestData.host,
- TestData.portStr,
- _,
- _
- ) =>
- }
- }
-
- it should "execute a CGI: query string and path info" in {
- Server(TestData.cgiConf).handleReq(
- "gemini://localhost/dir/cgi/path/info?query=string",
- "127.0.0.1"
- ) should matchPattern {
- case Cgi(
- _,
- _,
- "query=string",
- "/path/info",
- "cgi",
- TestData.host,
- TestData.portStr,
- _,
- _
- ) =>
- }
- }
-
- it should "not execute an executable if allow CGI is off" in {
- Server(TestData.conf)
- .handleReq(
- "gemini://localhost/dir/cgi",
- "127.0.0.1"
- ) should matchPattern {
- case Success(_, "text/plain", Some(_), _) =>
- }
- }
-
- it should "response with an error if the CGI exits with non 0" in {
- val bad = Server(TestData.cgiConf)
- .handleReq(
- "gemini://localhost/dir/bad-cgi",
- "127.0.0.1"
- )
- .asInstanceOf[Cgi]
-
- val meta = "Error executing CGI"
- bad.status should be(50)
- bad.meta should be(meta)
- bad.body should include(meta)
- }
-
- it should "return a response with an error if the CGI exits with non 0" in {
- val bad = Server(TestData.cgiConf)
- .handleReq(
- "gemini://localhost/dir/bad-cgi",
- "127.0.0.1"
- )
- .asInstanceOf[Cgi]
-
- val meta = "Error executing CGI"
- bad.status should be(50)
- bad.meta should be(meta)
- bad.body should include(meta)
- }
-
- it should "return a response with an error if the CGI response is invalid" in {
- val bad = Server(TestData.cgiConf)
- .handleReq(
- "gemini://localhost/dir/bad-response",
- "127.0.0.1"
- )
- .asInstanceOf[Cgi]
-
- val meta = "Invalid response from CGI"
- bad.status should be(40)
- bad.meta should be(meta)
- bad.body should include(meta)
- }
-
- it should "environment variables are optional" in {
- Server(TestData.cgiConf).handleReq(
- "gemini://localhost/dir/cgi/",
- "127.0.0.1"
- ) should matchPattern {
- case Cgi(
- _,
- _,
- _,
- _,
- "cgi",
- TestData.host,
- TestData.portStr,
- _,
- m
- ) if m == Map() =>
- }
- }
-
- it should "pass environment variables to the CGI" in {
- Server(TestData.cgiEnvConf).handleReq(
- "gemini://localhost/dir/cgi/",
- "127.0.0.1"
- ) should matchPattern {
- case Cgi(
- _,
- _,
- _,
- _,
- "cgi",
- TestData.host,
- TestData.portStr,
- _,
- m
- ) if m == Map("env1" -> "value") =>
- }
- }
-
- it should "execute a CGI with the environment variables" in {
- val cgi = Server(TestData.cgiEnvConf)
- .handleReq(
- "gemini://localhost/dir/cgi",
- "127.0.0.1"
- )
- .asInstanceOf[Cgi]
-
- cgi.status should be(20)
- cgi.meta should be("text/gemini")
- cgi.body should include("env1=value")
- }
-
- it should "execute a CGI when it is the index document" in {
- val cgi = Server(TestData.cgiIndexConf)
- .handleReq(
- "gemini://localhost/dir/",
- "127.0.0.1"
- )
- .asInstanceOf[Cgi]
-
- cgi.status should be(20)
- cgi.meta should be("text/gemini")
- cgi.body should include("GATEWAY_INTERFACE=CGI/1.1")
- }
-
- it should "execute a CGI when it is the index document (full name)" in {
- val cgi = Server(TestData.cgiIndexConf)
- .handleReq(
- "gemini://localhost/dir/cgi",
- "127.0.0.1"
- )
- .asInstanceOf[Cgi]
-
- cgi.status should be(20)
- cgi.meta should be("text/gemini")
- cgi.body should include("GATEWAY_INTERFACE=CGI/1.1")
- }
-
- it should "execute a CGI when it is the index document (full name, path info)" in {
- val cgi = Server(TestData.cgiIndexConf)
- .handleReq(
- "gemini://localhost/dir/cgi/path/info",
- "127.0.0.1"
- )
- .asInstanceOf[Cgi]
-
- cgi.status should be(20)
- cgi.meta should be("text/gemini")
- cgi.body should include("GATEWAY_INTERFACE=CGI/1.1")
- cgi.body should include("PATH_INFO=/path/info")
- }
-
- it should "resolve CGI directories from more to less specific" in {
- // issue: https://gitlab.com/reidrac/spacebeans/-/issues/2
- val cgi = Server(TestData.cgiPrefConf)
- .handleReq(
- "gemini://localhost/dir/sub/cgiOk/path/info",
- "127.0.0.1"
- )
- .asInstanceOf[Cgi]
-
- cgi.status should be(20)
- cgi.meta should be("text/gemini")
- cgi.body should include("GATEWAY_INTERFACE=CGI/1.1")
- cgi.body should include("PATH_INFO=/path/info")
- }
}
diff --git a/server/test/src/handlers/GeminiHandlerSpec.scala b/server/test/src/handlers/GeminiHandlerSpec.scala
new file mode 100644
index 0000000..6b177f8
--- /dev/null
+++ b/server/test/src/handlers/GeminiHandlerSpec.scala
@@ -0,0 +1,528 @@
+package net.usebox.gemini.server.handlers
+
+import java.net.URI
+
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.matchers.should.Matchers
+
+import net.usebox.gemini.server._
+
+class GeminiHandlerSpec extends AnyFlatSpec with Matchers {
+
+ behavior of "handle"
+
+ val handler = new GeminiHandler(TestData.conf)
+ val handlerUserDir = new GeminiHandler(TestData.confUserDir)
+ val handlerCgi = new GeminiHandler(TestData.cgiConf)
+
+ def handleWith(
+ geminiHandler: GeminiHandler,
+ req: String,
+ remoteAddr: String = "127.0.0.1"
+ ): Response =
+ geminiHandler.handle(req, URI.create(req), remoteAddr)
+
+ it should "handle host case insensitive" in {
+ handleWith(handler, "gemini://LOCALHOST/") should be(a[Success])
+ }
+
+ it should "return proxy request refused when the vhost is not found" in {
+ handleWith(handler, "gemini://otherhost/") should be(
+ a[ProxyRequestRefused]
+ )
+ }
+
+ it should "return bad request when user info is present" in {
+ handleWith(handler, "gemini://user@localhost/") should be(
+ a[BadRequest]
+ )
+ }
+
+ it should "return bad request when the path is out of root dir" in {
+ handleWith(handler, "gemini://localhost/../../") should be(
+ a[BadRequest]
+ )
+ }
+
+ it should "redirect to normalize the URL" in {
+ handleWith(handler, "gemini://localhost/./") should be(
+ a[PermanentRedirect]
+ )
+ }
+
+ it should "return not found if the path doesn't exist" in {
+ handleWith(
+ handler,
+ "gemini://localhost/doesnotexist"
+ ) should be(a[NotFound])
+ }
+
+ it should "return not found if a dot file" in {
+ handleWith(handler, "gemini://localhost/.dotfile") should be(
+ a[NotFound]
+ )
+ }
+
+ it should "return success on reading file" in {
+ handleWith(
+ handler,
+ "gemini://localhost/index.gmi"
+ ) should matchPattern {
+ case Success(_, "text/gemini", Some(_), 25L) =>
+ }
+ }
+
+ it should "redirect and normalize request on a directory" in {
+ handleWith(handler, "gemini://localhost/dir") should be(
+ a[PermanentRedirect]
+ )
+ }
+
+ it should "return an existing index file when requesting a directory" in {
+ handleWith(handler, "gemini://localhost/") should matchPattern {
+ case Success(_, "text/gemini", Some(_), 25L) =>
+ }
+ }
+
+ it should "include gemini params for gemini MIME type" in {
+ handleWith(
+ new GeminiHandler(
+ TestData.conf.copy(virtualHosts =
+ List(TestData.conf.virtualHosts(0).copy(geminiParams = Some("test")))
+ )
+ ),
+ "gemini://localhost/index.gmi"
+ ) should matchPattern {
+ case Success(_, "text/gemini; test", Some(_), 25L) =>
+ }
+ }
+
+ behavior of "handler, directory listings"
+
+ it should "return a directory listing if is enabled and no index" in {
+ handleWith(handler, "gemini://localhost/dir/") should be(
+ a[DirListing]
+ )
+ }
+
+ it should "return a directory listing, directory listing flags: vhost flag false, directories flag true" in {
+ handleWith(
+ new GeminiHandler(
+ ServiceConf.initConf(
+ TestData.conf.copy(virtualHosts =
+ List(
+ TestData.conf
+ .virtualHosts(0)
+ .copy(
+ directoryListing = false,
+ directories = List(
+ Directory(
+ "dir/",
+ directoryListing = Some(true),
+ allowCgi = None
+ )
+ )
+ )
+ )
+ )
+ )
+ ),
+ "gemini://localhost/dir/"
+ ) should be(a[DirListing])
+ }
+
+ it should "return not found with no index, directory listing flags: vhost flag true, directories flag false" in {
+ handleWith(
+ new GeminiHandler(
+ ServiceConf.initConf(
+ TestData.conf.copy(virtualHosts =
+ List(
+ TestData.conf
+ .virtualHosts(0)
+ .copy(
+ directoryListing = true,
+ directories = List(
+ Directory(
+ "dir/",
+ directoryListing = Some(false),
+ allowCgi = None
+ )
+ )
+ )
+ )
+ )
+ )
+ ),
+ "gemini://localhost/dir/"
+ ) should be(a[NotFound])
+ }
+
+ it should "not apply directory listing override to subdirectories" in {
+ handleWith(
+ new GeminiHandler(
+ ServiceConf.initConf(
+ TestData.conf.copy(virtualHosts =
+ List(
+ TestData.conf
+ .virtualHosts(0)
+ .copy(
+ directoryListing = false,
+ directories = List(
+ Directory(
+ "dir/",
+ directoryListing = Some(true),
+ allowCgi = None
+ )
+ )
+ )
+ )
+ )
+ )
+ ),
+ "gemini://localhost/dir/sub/"
+ ) should be(
+ a[NotFound]
+ )
+ }
+
+ it should "return not found if directory listing is not enabled and no index" in {
+ handleWith(
+ new GeminiHandler(
+ TestData.conf.copy(virtualHosts =
+ List(TestData.conf.virtualHosts(0).copy(directoryListing = false))
+ )
+ ),
+ "gemini://localhost/dir/"
+ ) should be(a[NotFound])
+ }
+
+ behavior of "handler, user directories"
+
+ it should "return success on reading file" in {
+ handleWith(
+ handlerUserDir,
+ "gemini://localhost/~username/index.gmi"
+ ) should matchPattern {
+ case Success(_, "text/gemini", Some(_), 38L) =>
+ }
+ }
+
+ it should "return redirect accessing the user directory without ending slash" in {
+ handleWith(handlerUserDir, "gemini://localhost/~username") should be(
+ a[PermanentRedirect]
+ )
+ }
+
+ it should "return success accessing the user directory index" in {
+ handleWith(
+ handlerUserDir,
+ "gemini://localhost/~username/"
+ ) should matchPattern {
+ case Success(_, "text/gemini", Some(_), 38L) =>
+ }
+ }
+
+ it should "return bad request trying to exit the root directory" in {
+ handleWith(handlerUserDir, "gemini://localhost/~username/../../") should be(
+ a[BadRequest]
+ )
+ }
+
+ it should "return redirect to the virtual host root when leaving the user dir" in {
+ handleWith(handlerUserDir, "gemini://localhost/~username/../") should be(
+ a[PermanentRedirect]
+ )
+ }
+
+ it should "not translate root if used an invalid user pattern" in {
+ handleWith(handlerUserDir, "gemini://localhost/~username../") should be(
+ a[NotFound]
+ )
+
+ handleWith(handlerUserDir, "gemini://localhost/~0invalid/") should be(
+ a[NotFound]
+ )
+ }
+
+ it should "not translate root if no user directory path was provided" in {
+ handleWith(
+ new GeminiHandler(
+ TestData.conf.copy(virtualHosts =
+ List(
+ TestData.conf
+ .virtualHosts(0)
+ .copy(userDirectories = true)
+ )
+ )
+ ),
+ "gemini://localhost/~username/",
+ "127.0.0.1"
+ ) should be(a[NotFound])
+ }
+
+ it should "not execute a CGI if the target resource is not executable" in {
+ handleWith(
+ handlerCgi,
+ "gemini://localhost/dir/file.txt"
+ ) should matchPattern {
+ case Success(_, "text/plain", Some(_), 5L) =>
+ }
+ }
+
+ it should "not execute a CGI if the target resource is a directory" in {
+ handleWith(handlerCgi, "gemini://localhost/dir/sub/") should be(
+ a[DirListing]
+ )
+ }
+
+ it should "not apply allow CGI to subdirectories" in {
+ handleWith(
+ handlerCgi,
+ "gemini://localhost/dir/sub/cgi"
+ ) should matchPattern {
+ case Success(_, "text/plain", Some(_), 72) =>
+ }
+ }
+
+ it should "execute a CGI" in {
+ val cgi = handleWith(handlerCgi, "gemini://localhost/dir/cgi")
+ .asInstanceOf[Cgi]
+
+ cgi.status should be(20)
+ cgi.meta should be("text/gemini")
+ cgi.body should include("GATEWAY_INTERFACE=CGI/1.1")
+ }
+
+ it should "execute a CGI: empty parameters, host and port" in {
+ handleWith(
+ handlerCgi,
+ "gemini://localhost/dir/cgi"
+ ) should matchPattern {
+ case Cgi(
+ _,
+ _,
+ "",
+ "",
+ "cgi",
+ TestData.host,
+ TestData.portStr,
+ _,
+ _
+ ) =>
+ }
+ }
+
+ it should "execute a CGI: host is case-insensitive and the value in the conf is used" in {
+ handleWith(
+ handlerCgi,
+ "gemini://LOCALHOST/dir/cgi"
+ ) should matchPattern {
+ case Cgi(
+ _,
+ _,
+ "",
+ "",
+ "cgi",
+ TestData.host,
+ TestData.portStr,
+ _,
+ _
+ ) =>
+ }
+ }
+
+ it should "execute a CGI: query string" in {
+ handleWith(
+ handlerCgi,
+ "gemini://localhost/dir/cgi?query&string"
+ ) should matchPattern {
+ case Cgi(
+ _,
+ _,
+ "query&string",
+ "",
+ "cgi",
+ TestData.host,
+ TestData.portStr,
+ _,
+ _
+ ) =>
+ }
+ }
+
+ it should "execute a CGI: path info" in {
+ handleWith(
+ handlerCgi,
+ "gemini://localhost/dir/cgi/path/info"
+ ) should matchPattern {
+ case Cgi(
+ _,
+ _,
+ "",
+ "/path/info",
+ "cgi",
+ TestData.host,
+ TestData.portStr,
+ _,
+ _
+ ) =>
+ }
+ }
+
+ it should "execute a CGI: query string and path info" in {
+ handleWith(
+ handlerCgi,
+ "gemini://localhost/dir/cgi/path/info?query=string"
+ ) should matchPattern {
+ case Cgi(
+ _,
+ _,
+ "query=string",
+ "/path/info",
+ "cgi",
+ TestData.host,
+ TestData.portStr,
+ _,
+ _
+ ) =>
+ }
+ }
+
+ it should "not execute an executable if allow CGI is off" in {
+ handleWith(
+ handler,
+ "gemini://localhost/dir/cgi"
+ ) should matchPattern {
+ case Success(_, "text/plain", Some(_), _) =>
+ }
+ }
+
+ it should "response with an error if the CGI exits with non 0" in {
+ val bad =
+ handleWith(handlerCgi, "gemini://localhost/dir/bad-cgi")
+ .asInstanceOf[Cgi]
+
+ val meta = "Error executing CGI"
+ bad.status should be(50)
+ bad.meta should be(meta)
+ bad.body should include(meta)
+ }
+
+ it should "return a response with an error if the CGI exits with non 0" in {
+ val bad =
+ handleWith(handlerCgi, "gemini://localhost/dir/bad-cgi")
+ .asInstanceOf[Cgi]
+
+ val meta = "Error executing CGI"
+ bad.status should be(50)
+ bad.meta should be(meta)
+ bad.body should include(meta)
+ }
+
+ it should "return a response with an error if the CGI response is invalid" in {
+ val bad =
+ handleWith(handlerCgi, "gemini://localhost/dir/bad-response")
+ .asInstanceOf[Cgi]
+
+ val meta = "Invalid response from CGI"
+ bad.status should be(40)
+ bad.meta should be(meta)
+ bad.body should include(meta)
+ }
+
+ it should "environment variables are optional" in {
+ handleWith(
+ handlerCgi,
+ "gemini://localhost/dir/cgi/"
+ ) should matchPattern {
+ case Cgi(
+ _,
+ _,
+ _,
+ _,
+ "cgi",
+ TestData.host,
+ TestData.portStr,
+ _,
+ m
+ ) if m == Map() =>
+ }
+ }
+
+ it should "pass environment variables to the CGI" in {
+ handleWith(
+ new GeminiHandler(TestData.cgiEnvConf),
+ "gemini://localhost/dir/cgi/"
+ ) should matchPattern {
+ case Cgi(
+ _,
+ _,
+ _,
+ _,
+ "cgi",
+ TestData.host,
+ TestData.portStr,
+ _,
+ m
+ ) if m == Map("env1" -> "value") =>
+ }
+ }
+
+ it should "execute a CGI with the environment variables" in {
+ val cgi = handleWith(
+ new GeminiHandler(TestData.cgiEnvConf),
+ "gemini://localhost/dir/cgi"
+ ).asInstanceOf[Cgi]
+
+ cgi.status should be(20)
+ cgi.meta should be("text/gemini")
+ cgi.body should include("env1=value")
+ }
+
+ it should "execute a CGI when it is the index document" in {
+ val cgi = handleWith(
+ new GeminiHandler(TestData.cgiIndexConf),
+ "gemini://localhost/dir/"
+ ).asInstanceOf[Cgi]
+
+ cgi.status should be(20)
+ cgi.meta should be("text/gemini")
+ cgi.body should include("GATEWAY_INTERFACE=CGI/1.1")
+ }
+
+ it should "execute a CGI when it is the index document (full name)" in {
+ val cgi = handleWith(
+ new GeminiHandler(TestData.cgiIndexConf),
+ "gemini://localhost/dir/cgi"
+ ).asInstanceOf[Cgi]
+
+ cgi.status should be(20)
+ cgi.meta should be("text/gemini")
+ cgi.body should include("GATEWAY_INTERFACE=CGI/1.1")
+ }
+
+ it should "execute a CGI when it is the index document (full name, path info)" in {
+ val cgi = handleWith(
+ new GeminiHandler(TestData.cgiIndexConf),
+ "gemini://localhost/dir/cgi/path/info"
+ ).asInstanceOf[Cgi]
+
+ cgi.status should be(20)
+ cgi.meta should be("text/gemini")
+ cgi.body should include("GATEWAY_INTERFACE=CGI/1.1")
+ cgi.body should include("PATH_INFO=/path/info")
+ }
+
+ it should "resolve CGI directories from more to less specific" in {
+ // issue: https://gitlab.com/reidrac/spacebeans/-/issues/2
+ val cgi = handleWith(
+ new GeminiHandler(TestData.cgiPrefConf),
+ "gemini://localhost/dir/sub/cgiOk/path/info"
+ ).asInstanceOf[Cgi]
+
+ cgi.status should be(20)
+ cgi.meta should be("text/gemini")
+ cgi.body should include("GATEWAY_INTERFACE=CGI/1.1")
+ cgi.body should include("PATH_INFO=/path/info")
+ }
+}