diff --git a/dom/src/main/scala/org/http4s/dom/FetchClient.scala b/dom/src/main/scala/org/http4s/dom/FetchClient.scala index f887613c..9f74143f 100644 --- a/dom/src/main/scala/org/http4s/dom/FetchClient.scala +++ b/dom/src/main/scala/org/http4s/dom/FetchClient.scala @@ -17,7 +17,6 @@ package org.http4s package dom -import cats.data.OptionT import cats.effect.Async import cats.effect.Poll import cats.effect.Resource @@ -99,23 +98,19 @@ private[dom] object FetchClient { mergedOptions.referrerPolicy.foreach(init.referrerPolicy = _) val fetch = - poll(F.fromPromise(F.delay(Fetch.fetch(req.uri.renderString, init)))) + F.fromPromise(F.delay(Fetch.fetch(req.uri.renderString, init))) .onCancel(F.delay(abortController.abort())) - - requestTimeout match { - case d: FiniteDuration => - fetch.timeoutTo( - d, + .timeoutTo( + requestTimeout, F.raiseError[FetchResponse](new TimeoutException( - s"Request to ${req.uri.renderString} timed out after ${d.toMillis} ms")) + s"Request to ${req.uri.renderString} timed out after ${requestTimeout.toMillis} ms")) ) - case _ => - fetch - } + + poll(fetch) } } { case (r, exitCase) => - OptionT.fromOption(Option(r.body)).foreachF(cancelReadableStream(_, exitCase)) + Option(r.body).traverse_(cancelReadableStream(_, exitCase)) } .evalMap(fromDomResponse[F]) diff --git a/testsBrowser/src/test/scala/org/http4s/dom/FetchServiceWorkerSuite.scala b/testsBrowser/src/test/scala/org/http4s/dom/FetchServiceWorkerSuite.scala index 0173ca0c..3057edcd 100644 --- a/testsBrowser/src/test/scala/org/http4s/dom/FetchServiceWorkerSuite.scala +++ b/testsBrowser/src/test/scala/org/http4s/dom/FetchServiceWorkerSuite.scala @@ -110,6 +110,17 @@ class FetchServiceWorkerSuite extends CatsEffectSuite { } } + test("Cancel an in-flight request") { + client + .expect[String](GET(baseUrl / "delayed")) + .timeoutTo(100.millis, IO.unit) + .timed + .flatMap { + case (duration, _) => + IO(assert(clue(duration) < 500.millis)) + } + } + GetRoutes.getPaths.toList.foreach { case (path, expected) => test(s"Execute GET $path") { diff --git a/testsNodeJS/src/test/scala/org/http4s/dom/NodeJSFetchSuite.scala b/testsNodeJS/src/test/scala/org/http4s/dom/NodeJSFetchSuite.scala index bdea5225..e095df62 100644 --- a/testsNodeJS/src/test/scala/org/http4s/dom/NodeJSFetchSuite.scala +++ b/testsNodeJS/src/test/scala/org/http4s/dom/NodeJSFetchSuite.scala @@ -19,9 +19,35 @@ package dom import cats.effect.IO import cats.effect.Resource +import fs2.Stream +import org.http4s.Method._ import org.http4s.client.Client import org.http4s.client.testkit.ClientRouteTestBattery +import scala.concurrent.duration._ + class NodeJSFetchSuite extends ClientRouteTestBattery("FetchClient") { def clientResource: Resource[IO, Client[IO]] = FetchClientBuilder[IO].resource + + test("POST a chunked body with streaming requests") { + val address = server().addresses.head + val baseUrl = Uri.fromString(s"http://$address/").toOption.get + FetchClientBuilder[IO] + .withStreamingRequests + .create + .expect[String](POST(Stream("This is chunked.").covary[IO], baseUrl / "echo")) + .assertEquals("This is chunked.") + } + + test("Cancel an in-flight request") { + val address = server().addresses.head + client() + .expect[String](s"http://$address/delayed") + .timeoutTo(100.millis, IO.unit) + .timed + .flatMap { + case (duration, _) => + IO(assert(clue(duration) < 500.millis)) + } + } }