-
-
Notifications
You must be signed in to change notification settings - Fork 32
Fix: Refactor HttpClient to prevent potential memory leak #215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
4b2f758
Fix: Refactor HttpClient to prevent potential memory leak
google-labs-jules[bot] f4ce447
Fix: Ensure correct HttpClient instantiation in Server.scala
google-labs-jules[bot] 0eef36d
Fix: Address PR comments and attempt to resolve build failure
google-labs-jules[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| package http | ||
|
|
||
| import cats.effect.{IO, Resource} | ||
| import cats.syntax.all._ | ||
| import com.comcast.ip4s._ | ||
| import io.circe.Json | ||
| import org.http4s._ | ||
| import org.http4s.circe._ | ||
| import org.http4s.client.Client | ||
| import org.http4s.dsl.io._ | ||
| import org.http4s.ember.client.EmberClientBuilder | ||
| import org.http4s.ember.server.EmberServerBuilder | ||
| import org.http4s.server.Server | ||
| // import org.scalatest.wordspec.AsyncWordSpec // No longer needed | ||
| import cats.effect.testing.scalatest.AsyncIOSpec | ||
| import org.scalatest.matchers.should.Matchers // Ensure Matchers is imported and used | ||
|
|
||
| class HttpClientSpec extends AsyncIOSpec with Matchers { | ||
|
|
||
| // Define the service for the test server | ||
| val testService: HttpRoutes[IO] = HttpRoutes.of[IO] { | ||
| case GET -> Root / "test" / id => | ||
| Ok(Json.obj("id" -> Json.fromString(id), "message" -> Json.fromString("Ok"))) | ||
| } | ||
|
|
||
| // Resource for the test server | ||
| val serverResource: Resource[IO, Server] = EmberServerBuilder | ||
| .default[IO] | ||
| .withHost(ipv4"0.0.0.0") | ||
| .withPort(port"0") // Use port 0 to let the system pick an available port | ||
| .withHttpApp(testService.orNotFound) | ||
| .build | ||
|
|
||
| // Resource for the http4s client | ||
| val clientResource: Resource[IO, Client[IO]] = EmberClientBuilder | ||
| .default[IO] | ||
| .build | ||
|
|
||
| "HttpClient" should { | ||
| "stress test HttpClient with multiple requests" in { | ||
| val numRequests = 500 | ||
| val uniqueRequests = numRequests / 2 | ||
|
|
||
| (serverResource, clientResource).tupled.use { case (server, liveHttpClient) => | ||
| val httpClient = new HttpClient(liveHttpClient) // Use the live client from the resource | ||
|
|
||
| val baseUri = s"http://${server.address.getHostString}:${server.address.getPort}/test" | ||
|
|
||
| // Generate 250 unique URIs and 250 duplicates | ||
| val uris = (0 until uniqueRequests).map(i => Uri.unsafeFromString(s"$baseUri/$i")).toList ++ | ||
| (0 until uniqueRequests).map(i => Uri.unsafeFromString(s"$baseUri/$i")).toList | ||
|
|
||
| uris.length shouldBe numRequests // Quick check | ||
|
|
||
| val resultsIO: IO[List[Either[Throwable, Json]]] = | ||
| Traverse[List].traverse(uris)(uri => httpClient.httpRequest(Method.GET, uri)) | ||
|
|
||
| resultsIO.asserting { results => | ||
| results.foreach { result => | ||
| result shouldBe a[Right[_, _]] // Uses Matchers | ||
| result.foreach { json => | ||
| json.hcursor.downField("message").as[String] shouldBe Right("Ok") // Uses Matchers | ||
| } | ||
| } | ||
| results.length shouldBe numRequests // Uses Matchers | ||
| results.count(_.isRight) shouldBe numRequests // Uses Matchers | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify direct client usage aligns with resource lifecycle.
The direct usage of the injected
httpClientis correct and eliminates the internal resource management that was causing memory leaks. However, ensure that the external resource management (in Server.scala) properly handles the client lifecycle.🏁 Script executed:
Length of output: 1287
Let’s verify that the HTTP client is being managed as a Resource in your server bootstrap. We’ll locate the
Server.scalafile and check for anyEmberClientBuilderusage as well asResourceand.usecalls:🏁 Script executed:
Length of output: 1436
Remove unused EmberClientBuilder and FollowRedirect imports in HttpClient.scala
The direct use of the injected
httpClientinHttpClient.scalais correct, but there are leftover imports that should be cleaned up:• In
src/main/scala/http/HttpClient.scala, remove:–
import org.http4s.ember.client.EmberClientBuilder–
import org.http4s.client.middleware.FollowRedirectNo other references to
EmberClientBuilderorFollowRedirectremain in that file, and your calls tohttpClient.expect[Json](…)are already using the injected client.External resource management in
Server.scalais correctly handled:• Builds the client as a
Resource:val httpClientResource = EmberClientBuilder.default[IO].build.map(FollowRedirect(5))• Invokes
.use { implicit resolvedHttpClient => … }🤖 Prompt for AI Agents