Skip to content

Commit

Permalink
Head request empty body - series/0.17 (#1149)
Browse files Browse the repository at this point in the history
* handle head requests with empty body

* remove content length header

* fix comparison
  • Loading branch information
lewisjkl authored Aug 14, 2023
1 parent f001a5e commit 39fb372
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import smithy4s.schema.Alt
import smithy4s.kinds._
import org.http4s.HttpApp
import org.typelevel.vault.Key
import org.typelevel.ci.CIString

/**
* A construct that encapsulates a smithy4s endpoint, and exposes
Expand Down Expand Up @@ -104,6 +105,18 @@ private[http4s] class SmithyHttp4sServerEndpointImpl[F[_], Op[_, _, _, _, _], I,
httpEndpoint.matches(path)
}

private val headRemoveBody: Response[F] => Response[F] =
if (endpoint.hints.get[smithy.api.Http].exists(_.method.value === "HEAD"))
r =>
r.withHeaders(
r.headers.headers.filterNot(h =>
h.name === CIString("Content-Type") || h.name === CIString(
"Content-Length"
)
)
).withBodyStream(fs2.Stream.empty)
else identity

override val httpApp: HttpApp[F] = {
val baseApp = HttpApp[F] { req =>
val pathParams = req.attributes.lookup(pathParamsKey).getOrElse(Map.empty)
Expand All @@ -114,7 +127,7 @@ private[http4s] class SmithyHttp4sServerEndpointImpl[F[_], Op[_, _, _, _, _], I,
output <- (impl(endpoint.wrap(input)): F[O])
} yield output

run.map(successResponse)
run.map(successResponse).map(headRemoveBody)
}
middleware(endpoint)(baseApp).handleErrorWith(error =>
Kleisli.liftF(errorResponse(error))
Expand Down
3 changes: 3 additions & 0 deletions modules/tests/src/smithy4s/tests/PizzaAdminServiceImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,7 @@ class PizzaAdminServiceImpl(ref: Compat.Ref[IO, State])
body: EchoBody,
queryParam: Option[String]
): IO[Unit] = IO.unit

def headRequest(): cats.effect.IO[HeadRequestOutput] =
IO.pure(HeadRequestOutput("test"))
}
21 changes: 21 additions & 0 deletions modules/tests/src/smithy4s/tests/PizzaSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,27 @@ abstract class PizzaSpec
)
}

routerTest("HEAD request should have empty body") { (client, uri, log) =>
for {
res <- client.send[String](
HEAD((uri / "head-request")),
log
)
} yield {
val (code, headers, body) = res
expect.same(code, 200) &&
expect.same(body, "") &&
expect.same(
headers,
HeaderMap(
Map(
CaseInsensitive("Test") -> List("test")
)
)
)
}
}

pureTest("Negative: http no match (bad path)") {
val matchResult = smithy4s.http.httpMatch(
PizzaAdminService.service,
Expand Down
14 changes: 13 additions & 1 deletion sampleSpecs/pizza.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use alloy#simpleRestJson
@simpleRestJson
service PizzaAdminService {
version: "1.0.0",
operations: [AddMenuItem, GetMenu, Version, Health, HeaderEndpoint, RoundTrip, GetEnum, GetIntEnum, CustomCode, Book, Echo]
operations: [AddMenuItem, GetMenu, Version, Health, HeaderEndpoint, RoundTrip, GetEnum, GetIntEnum, CustomCode, Book, Echo, HeadRequest]
}

@http(method: "POST", uri: "/restaurant/{restaurant}/menu/item", code: 201)
Expand Down Expand Up @@ -336,3 +336,15 @@ structure EchoBody {
@length(min: 10)
data: String
}

@http(method: "HEAD", uri: "/head-request")
@readonly
operation HeadRequest {
output: HeadRequestOutput
}

structure HeadRequestOutput {
@httpHeader("Test")
@required
test: String
}

0 comments on commit 39fb372

Please sign in to comment.