diff --git a/zio-http/jvm/src/test/scala/zio/http/ConformanceSpec.scala b/zio-http/jvm/src/test/scala/zio/http/ConformanceSpec.scala index d430df4649..109f9cfcb8 100644 --- a/zio-http/jvm/src/test/scala/zio/http/ConformanceSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/ConformanceSpec.scala @@ -293,97 +293,178 @@ object ConformanceSpec extends ZIOHttpSpec { ) }, suite("sts")( - suite("sts_directives_only_allowed_once")( - test("should return valid if each directive appears only once in STS header") { - val app = Routes( - Method.GET / "secure" -> Handler.fromResponse( - Response.ok.addHeader(Header.StrictTransportSecurity("max-age=31536000; includeSubDomains")), - ), - ) - - val request = Request.get("/secure").withSecure(true) - - for { - response <- app.runZIO(request) - } yield assertTrue(response.status == Status.Ok) && - assertTrue(response.headers.contains(Header.StrictTransportSecurity.name)) - }, - test("should return invalid if STS header contains duplicate directives") { - val app = Routes( - Method.GET / "secure" -> Handler.fromResponse( - Response.ok.addHeader(Header.StrictTransportSecurity("max-age=31536000; max-age=31536000")), - ), - ) - - val request = Request.get("/secure").withSecure(true) - - for { - response <- app.runZIO(request) - } yield assertTrue(response.status == Status.Ok) && - assertTrue(response.headers.contains(Header.StrictTransportSecurity.name)) - }, - ), - suite("only_one_sts_header_allowed")( - test("should return valid if only one STS header is present") { - val app = Routes( - Method.GET / "secure" -> Handler.fromResponse( - Response.ok.addHeader(Header.StrictTransportSecurity("max-age=31536000")), - ), - ) - - val request = Request.get("/secure").withSecure(true) - - for { - response <- app.runZIO(request) - } yield assertTrue(response.status == Status.Ok) && - assertTrue(response.headers.count(Header.StrictTransportSecurity.name) == 1) - }, - test("should return invalid if more than one STS header is present") { - val app = Routes( - Method.GET / "secure" -> Handler.fromResponse( - Response.ok - .addHeader(Header.StrictTransportSecurity("max-age=31536000")) - .addHeader(Header.StrictTransportSecurity("max-age=31536000")), - ), - ) - - val request = Request.get("/secure").withSecure(true) - - for { - response <- app.runZIO(request) - } yield assertTrue(response.status == Status.Ok) && - assertTrue(response.headers.count(Header.StrictTransportSecurity.name) > 1) - }, - ), - suite("sts_header_http")( - test("should not include STS header in HTTP response") { - val app = Routes( - Method.GET / "non-secure" -> Handler.fromResponse( - Response.ok.addHeader(Header.StrictTransportSecurity("max-age=31536000")), - ), - ) - - val request = Request.get("/non-secure").withSecure(false) - - for { - response <- app.runZIO(request) - } yield assertTrue(response.status == Status.Ok) && - assertTrue(!response.headers.contains(Header.StrictTransportSecurity.name)) - }, - test("should include STS header in HTTPS response") { - val app = Routes( - Method.GET / "secure" -> Handler.fromResponse( - Response.ok.addHeader(Header.StrictTransportSecurity("max-age=31536000")), - ), - ) - - val request = Request.get("/secure").withSecure(true) - - for { - response <- app.runZIO(request) - } yield assertTrue(response.status == Status.Ok) && - assertTrue(response.headers.contains(Header.StrictTransportSecurity.name)) - }, + // TODO: Strict-Transport-Security Header to be Added + // suite("sts_directives_only_allowed_once")( + // test("should return valid if each directive appears only once in STS header") { + // val app = Routes( + // Method.GET / "secure" -> Handler.fromResponse( + // Response.ok.addHeader(Header.StrictTransportSecurity("max-age=31536000; includeSubDomains")), + // ), + // ) + + // // val request = Request.get("/secure").withSecure(true) + // val request = Request.get(URL.decode("https://localhost/secure").toOption.get) + + // for { + // response <- app.runZIO(request) + // } yield assertTrue(response.status == Status.Ok) && + // assertTrue(response.headers.contains(Header.StrictTransportSecurity.name)) + // }, + // test("should return invalid if STS header contains duplicate directives") { + // val app = Routes( + // Method.GET / "secure" -> Handler.fromResponse( + // Response.ok.addHeader(Header.StrictTransportSecurity("max-age=31536000; max-age=31536000")), + // ), + // ) + // val request = Request.get(URL.decode("http://localhost/non-secure").toOption.get) + + // // val decodedUrl = URL.decode("https://example.com:443") + + // for { + // response <- app.runZIO(request) + // } yield assertTrue(response.status == Status.Ok) && + // assertTrue(response.headers.contains(Header.StrictTransportSecurity.name)) + // }, + // ), + // suite("only_one_sts_header_allowed")( + // test("should return valid if only one STS header is present") { + // val app = Routes( + // Method.GET / "secure" -> Handler.fromResponse( + // Response.ok.addHeader(Header.StrictTransportSecurity("max-age=31536000")), + // ), + // ) + + // val request = Request.get(URL.decode("https://localhost/secure").toOption.get) + + // for { + // response <- app.runZIO(request) + // } yield assertTrue(response.status == Status.Ok) && + // assertTrue(response.headers.count(Header.StrictTransportSecurity.name) == 1) + // }, + // test("should return invalid if more than one STS header is present") { + // val app = Routes( + // Method.GET / "secure" -> Handler.fromResponse( + // Response.ok + // .addHeader(Header.StrictTransportSecurity("max-age=31536000")) + // .addHeader(Header.StrictTransportSecurity("max-age=31536000")), + // ), + // ) + + // val request = Request.get(URL.decode("https://localhost/secure").toOption.get) + // // val request = Request.get("/secure").withSecure(true) + + // for { + // response <- app.runZIO(request) + // } yield assertTrue(response.status == Status.Ok) && + // assertTrue(response.headers.count(Header.StrictTransportSecurity.name) > 1) + // }, + // ), + // suite("sts_header_http")( + // test("should not include STS header in HTTP response") { + // val app = Routes( + // Method.GET / "non-secure" -> Handler.fromResponse( + // Response.ok.addHeader(Header.StrictTransportSecurity("max-age=31536000")), + // ), + // ) + + // // val request = Request.get("/non-secure").withSecure(false) + // val request = Request.get(URL.decode("http://localhost/non-secure").toOption.get) + + // for { + // response <- app.runZIO(request) + // } yield assertTrue(response.status == Status.Ok) && + // assertTrue(!response.headers.contains(Header.StrictTransportSecurity.name)) + // }, + // test("should include STS header in HTTPS response") { + // val app = Routes( + // Method.GET / "secure" -> Handler.fromResponse( + // Response.ok.addHeader(Header.StrictTransportSecurity("max-age=31536000")), + // ), + // ) + + // val request = Request.get(URL.decode("https://localhost/secure").toOption.get) + // // val request = Request.get("/secure").withSecure(true) + + // for { + // response <- app.runZIO(request) + // } yield assertTrue(response.status == Status.Ok) && + // assertTrue(response.headers.contains(Header.StrictTransportSecurity.name)) + // }, + // ), + // suite("sts-maxage")( + // ), + suite("Transfer-Encoding")( + suite("no_transfer_encoding_1xx_204")( + test("should return valid when Transfer-Encoding is not present for 1xx or 204 status") { + val app = Routes( + Method.GET / "no-content" -> Handler.fromResponse( + Response.status(Status.NoContent), + ), + Method.GET / "continue" -> Handler.fromResponse( + Response.status(Status.Continue), + ), + ) + for { + responseNoContent <- app.runZIO(Request.get("/no-content")) + responseContinue <- app.runZIO(Request.get("/continue")) + } yield assertTrue(responseNoContent.status == Status.NoContent) && + assertTrue(!responseNoContent.headers.contains(Header.TransferEncoding.name)) && + assertTrue(responseContinue.status == Status.Continue) && + assertTrue(!responseContinue.headers.contains(Header.TransferEncoding.name)) + }, + test("should return invalid when Transfer-Encoding is present for 1xx or 204 status") { + val app = Routes( + Method.GET / "no-content" -> Handler.fromResponse( + Response.status(Status.NoContent).addHeader(Header.TransferEncoding.Chunked), + ), + Method.GET / "continue" -> Handler.fromResponse( + Response.status(Status.Continue).addHeader(Header.TransferEncoding.Chunked), + ), + ) + + for { + responseNoContent <- app.runZIO(Request.get("/no-content")) + responseContinue <- app.runZIO(Request.get("/continue")) + } yield assertTrue(responseNoContent.status == Status.NoContent) && + assertTrue(responseNoContent.headers.contains(Header.TransferEncoding.name)) && + assertTrue(responseContinue.status == Status.Continue) && + assertTrue(responseContinue.headers.contains(Header.TransferEncoding.name)) + }, + ), + suite("transfer_encoding_http11")( + test("should not send Transfer-Encoding in response if request HTTP version is below 1.1") { + val app = Routes( + Method.GET / "test" -> Handler.fromResponse( + Response.ok.addHeader(Header.TransferEncoding.Chunked), + ), + ) + + val request = Request.get("/test").copy(version = Version.`HTTP/1.0`) + + for { + response <- app.runZIO(request) + } yield assertTrue( + response.status == Status.Ok, + !response.headers.contains(Header.TransferEncoding.name), + ) + }, + test("should send Transfer-Encoding in response if request HTTP version is 1.1 or higher") { + val app = Routes( + Method.GET / "test" -> Handler.fromResponse( + Response.ok.addHeader(Header.TransferEncoding.Chunked), + ), + ) + + val request = Request.get("/test").copy(version = Version.`HTTP/1.1`) + + for { + response <- app.runZIO(request) + } yield assertTrue( + response.status == Status.Ok, + response.headers.contains(Header.TransferEncoding.name), + ) + }, + ), ), ), ),