5

I tried the following code using http4s v0.19.0:

import cats.effect._

def usingHttp4s(uri: String, bearerToken: String)(implicit cs: ContextShift[IO]): String = {
    import scala.concurrent.ExecutionContext
    import org.http4s.client.dsl.io._
    import org.http4s.headers._
    import org.http4s.Method._
    import org.http4s._
    import org.http4s.client._


    import org.http4s.client.middleware._

    val blockingEC = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(5))

    val middlewares = Seq(
      RequestLogger[IO](logHeaders = true, logBody = true, redactHeadersWhen = _ => false)(_),
      FollowRedirect[IO](maxRedirects = 5)(_)
    )

    val client = middlewares.foldRight(JavaNetClientBuilder(blockingEC).create[IO])(_.apply(_))

    val req = GET(
      Uri.unsafeFromString(uri),
      Authorization(Credentials.Token(AuthScheme.Bearer, bearerToken))
    )
    client.expect[String](req).unsafeRunSync()
  }

I get the following error:

[error] (run-main-0) org.http4s.client.UnexpectedStatus: unexpected HTTP status: 401 Unauthorized
[error] org.http4s.client.UnexpectedStatus: unexpected HTTP status: 401 Unauthorized

Not only that, my program never exited (do I have to close some client!?) and it also never printed the request even though I hooked up a logging middleware

I next tried @li-haoyi's request library and this returned no error:

def usingLiHaoyiRequest(uri: String, bearerToken: String): String =
    requests.get(uri, headers = Iterable("Authorization" -> s"Bearer $bearerToken")).text()

The above code works for the same uri and same baseToken so it can't be that my token is wrong. To be doubly sure, I tried curl:

curl -L -H "Authorization: Bearer ${BEARER}" ${URI}

This problem also happens in http4s v0.18.19 (Even with explicit Json and accept headers):

import io.circle.Json

def usingHttp4s(uri: String, bearerToken: String): Json = {
    import org.http4s.client.dsl.io._
    import org.http4s.headers._
    import org.http4s.Method._
    import org.http4s._
    import org.http4s.client.blaze.Http1Client
    import org.http4s.client.middleware._
    import org.http4s.circe._
    import org.http4s.MediaType._

    val program = for {
      c <- Http1Client[IO]()
      client = FollowRedirect(maxRedirects = 5)(c)
      req = GET(
        Uri.unsafeFromString(uri),
        Authorization(Credentials.Token(AuthScheme.Bearer, bearerToken)),
        Accept(`application/json`)
      )
      res <- client.expect[Json](req)
    } yield res

    program.unsafeRunSync()
  }

So my question is:

  1. How come both requests and curl work but http4s gives me 401 for same request?
  2. Why does my http4s version never exit?
  3. Why does the request logger middleware does not log request?
pathikrit
  • 32,469
  • 37
  • 142
  • 221
  • Just in case, is $bearerToken a value of the form "Bearer xxx" or "xxx" ? It should be the later and is an easily made error ^^ – C4stor Dec 14 '18 at 16:54
  • No, both `usingLihaoyiRequest` and `usingHttp4s` are called with same args – pathikrit Dec 14 '18 at 17:05

1 Answers1

5

As described in the gitter room, the bug is in http4s which does not forward authorization header to redirects but both curl and requests do (as long as forwarding is to same sub-domain).

pathikrit
  • 32,469
  • 37
  • 142
  • 221
  • Did you find any way around this? I am also trying to work with http4s, and it is fine with most APIs around the web, but the one I am trying to query (cex.io) still sends me 403s - even with the currently most recent version 0.21.0-M6. – Aarkon Jan 07 '20 at 20:09
  • There we are, I found something out: I had to compile my client with Java 11, not 8. No idea what's the specific influence of the JDK here though. – Aarkon Jan 07 '20 at 21:46