Reputation: 2574
I want use http4s as proxy(like nginx), how to forward all data from my http4s server to another http server?
What I really want do is append a verify function on every request before do forward function. Hopefully like this:
HttpService[IO] {
case request =>
val httpClient: Client[IO] = Http1Client[IO]().unsafeRunSync
if(verifySuccess(request)) { // forward all http data to host2 and
// get a http response.
val result = httpClient.forward(request, "http://host2")
result
} else {
Forbidden //403
}
}
How to do this with http4s and it's client?
Thanks
with the help of @TheInnerLight, I give it a try with the snippet code:
val httpClient = Http1Client[IO]()
val service: HttpService[IO] = HttpService[IO] {
case req =>
if(true) {
for {
client <- httpClient
newAuthority = req.uri.authority.map(_.copy(host = RegName("scala-lang.org"), port = Some(80)))
proxiedReq = req.withUri(req.uri.copy(authority = newAuthority))
response <- client.fetch(proxiedReq)(IO.pure(_))
} yield response
} else {
Forbidden("Some forbidden message...")
}
}
With a request: http://localhost:28080
(http4s server listen at 28080):
but occurred a error:
[ERROR] org.http4s.client.PoolManager:102 - Error establishing client connection for key RequestKey(Scheme(http),localhost)
java.net.ConnectException: Connection refused
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finishConnect(UnixAsynchronousSocketChannelImpl.java:252)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finish(UnixAsynchronousSocketChannelImpl.java:198)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.onEvent(UnixAsynchronousSocketChannelImpl.java:213)
at sun.nio.ch.KQueuePort$EventHandlerTask.run(KQueuePort.java:301)
at java.lang.Thread.run(Thread.java:748)
[ERROR] org.http4s.server.service-errors:88 - Error servicing request: GET / from 0:0:0:0:0:0:0:1
java.net.ConnectException: Connection refused
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finishConnect(UnixAsynchronousSocketChannelImpl.java:252)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finish(UnixAsynchronousSocketChannelImpl.java:198)
at sun.nio.ch.UnixAsynchronousSocketChannelImpl.onEvent(UnixAsynchronousSocketChannelImpl.java:213)
at sun.nio.ch.KQueuePort$EventHandlerTask.run(KQueuePort.java:301)
at java.lang.Thread.run(Thread.java:748)
val httpClient: IO[Client[IO]] = Http1Client[IO]()
override val service: HttpService[IO] = HttpService[IO] {
case req =>
val hostName = "scala-lang.org"
val myPort = 80
if(true) {
val newHeaders = {
val filterHeader = req.headers.filterNot{h =>
h.name == CaseInsensitiveString("Connection") ||
h.name == CaseInsensitiveString("Keep-Alive") ||
h.name == CaseInsensitiveString("Proxy-Authenticate") ||
h.name == CaseInsensitiveString("Proxy-Authorization") ||
h.name == CaseInsensitiveString("TE") ||
h.name == CaseInsensitiveString("Trailer") ||
h.name == CaseInsensitiveString("Transfer-Encoding") ||
h.name == CaseInsensitiveString("Upgrade")
}
filterHeader.put(Header("host", hostName))
}
for {
client <- httpClient
newAuthority = req.uri.authority
.map(_.copy(host = RegName(hostName), port = Some(myPort)))
.getOrElse( Authority(host = RegName(hostName), port = Some(myPort)))
proxiedReq = req.withUri(req.uri.copy(authority = Some(newAuthority)))
.withHeaders(newHeaders)
response <- client.fetch(proxiedReq)(x => IO.pure(x))
} yield {
val rst = response
rst
}
} else {
Forbidden("Some forbidden message...")
}
}
It works fine enough for my REST API web server.
There are some error when proxy scala-lang.org
for test:
[ERROR] org.http4s.blaze.pipeline.Stage:226 - Error writing body
org.http4s.InvalidBodyException: Received premature EOF.
Upvotes: 4
Views: 2919
Reputation: 12184
How about something like this:
HttpService[IO] {
case req =>
if(verifyRequest(req)) {
for {
client <- Http1Client[IO]()
newHost = "host2"
newAuthority = Authority(host = RegName("host2"), port = Some(80))
proxiedReq =
req.withUri(req.uri.copy(authority = Some(newAuthority)))
.withHeaders(req.headers.put(Header("host", newHost)))
response <- client.fetch(proxiedReq)(IO.pure(_))
} yield response
} else {
Forbidden("Some forbidden message...")
}
}
Note that you should definitely avoid littering your code with calls tounsafeRunSync
. You should generally be using it at most once in your program (in Main
). In other circumstances, you should focus on lifting the effects into the monad you're working in.
Upvotes: 1