{"action":"created","comment":{"url":"https://api.github.com/repos/http4s/http4s/pulls/comments/48978026","id":48978026,"diff_hunk":"@@ -1,48 +1,45 @@\n-package org.http4s.client.blaze\n+package org.http4s\n+package client\n+package blaze\n \n import org.http4s.blaze.pipeline.Command\n-import org.http4s.client.Client\n-import org.http4s.{Request, Response}\n \n import scala.concurrent.duration.Duration\n import scalaz.concurrent.Task\n-import scalaz.stream.Process.eval_\n import scalaz.{-\\/, \\/-}\n \n /** Blaze client implementation */\n-final class BlazeClient(manager: ConnectionManager, idleTimeout: Duration, requestTimeout: Duration) extends Client {\n-\n-  /** Shutdown this client, closing any open connections and freeing resources */\n-  override def shutdown(): Task[Unit] = manager.shutdown()\n-\n-  override def prepare(req: Request): Task[Response] = Task.suspend {\n-    def tryClient(client: BlazeClientStage, freshClient: Boolean, flushPrelude: Boolean): Task[Response] = {\n-      // Add the timeout stage to the pipeline\n-      val ts = new ClientTimeoutStage(idleTimeout, requestTimeout, bits.ClientTickWheel)\n-      client.spliceBefore(ts)\n-      ts.initialize()\n-\n-      client.runRequest(req, flushPrelude).attempt.flatMap {\n-        case \\/-(r)    =>\n-          val recycleProcess = eval_(Task.delay {\n-            if (!client.isClosed()) {\n-              ts.removeStage\n-              manager.recycleClient(req, client)\n+object BlazeClient {\n+  def apply(manager: ConnectionManager, idleTimeout: Duration, requestTimeout: Duration): Client = {\n+    Client(Service.lift { req =>\n+      def tryClient(client: BlazeClientStage, freshClient: Boolean, flushPrelude: Boolean): Task[DisposableResponse] = {\n+        // Add the timeout stage to the pipeline\n+        val ts = new ClientTimeoutStage(idleTimeout, requestTimeout, bits.ClientTickWheel)\n+        client.spliceBefore(ts)\n+        ts.initialize()\n+\n+        client.runRequest(req, flushPrelude).attempt.flatMap {\n+          case \\/-(r)    =>\n+            val dispose = Task.delay {\n+              if (!client.isClosed()) {\n+                ts.removeStage\n+                manager.recycleClient(req, client)\n+              }\n             }\n-          })\n-          Task.now(r.copy(body = r.body.onComplete(recycleProcess)))\n+            Task.now(DisposableResponse(r, dispose))\n \n-        case -\\/(Command.EOF) if !freshClient =>\n-          manager.getClient(req, freshClient = true).flatMap(tryClient(_, true, flushPrelude))\n+          case -\\/(Command.EOF) if !freshClient =>\n+            manager.getClient(req, freshClient = true).flatMap(tryClient(_, true, flushPrelude))\n \n-        case -\\/(e) =>\n-          if (!client.isClosed()) client.shutdown()\n-          Task.fail(e)\n+          case -\\/(e) =>\n+            if (!client.isClosed()) client.shutdown()\n+            Task.fail(e)\n+        }\n       }\n-    }\n \n-    val flushPrelude = !req.body.isHalt\n-    manager.getClient(req, false).flatMap(tryClient(_, false, flushPrelude))\n+      val flushPrelude = !req.body.isHalt\n+      manager.getClient(req, false).flatMap(tryClient(_, false, flushPrelude))\n+    }, manager.shutdown())","path":"blaze-client/src/main/scala/org/http4s/client/blaze/BlazeClient.scala","position":74,"original_position":74,"commit_id":"ed27bab421095c69affef19a7f4248e2df13fe4e","original_commit_id":"ed27bab421095c69affef19a7f4248e2df13fe4e","user":{"login":"bryce-anderson","id":2948506,"avatar_url":"https://avatars.githubusercontent.com/u/2948506?v=3","gravatar_id":"","url":"https://api.github.com/users/bryce-anderson","html_url":"https://github.com/bryce-anderson","followers_url":"https://api.github.com/users/bryce-anderson/followers","following_url":"https://api.github.com/users/bryce-anderson/following{/other_user}","gists_url":"https://api.github.com/users/bryce-anderson/gists{/gist_id}","starred_url":"https://api.github.com/users/bryce-anderson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bryce-anderson/subscriptions","organizations_url":"https://api.github.com/users/bryce-anderson/orgs","repos_url":"https://api.github.com/users/bryce-anderson/repos","events_url":"https://api.github.com/users/bryce-anderson/events{/privacy}","received_events_url":"https://api.github.com/users/bryce-anderson/received_events","type":"User","site_admin":false},"body":"I think the exposure is the exact same, I'm just pointing it out since we are refactoring and all.\r\n\r\nI think the only alternative to an explicit shutdown is to let the GC handle it. In this case I don't think its the worst idea (in fact probably a good backup idea) but probably not good form for the only method of cleanup.","created_at":"2016-01-06T16:41:53Z","updated_at":"2016-01-06T16:41:53Z","html_url":"https://github.com/http4s/http4s/pull/496#discussion_r48978026","pull_request_url":"https://api.github.com/repos/http4s/http4s/pulls/496","_links":{"self":{"href":"https://api.github.com/repos/http4s/http4s/pulls/comments/48978026"},"html":{"href":"https://github.com/http4s/http4s/pull/496#discussion_r48978026"},"pull_request":{"href":"https://api.github.com/repos/http4s/http4s/pulls/496"}}},"pull_request":{"url":"https://api.github.com/repos/http4s/http4s/pulls/496","id":55179875,"html_url":"https://github.com/http4s/http4s/pull/496","diff_url":"https://github.com/http4s/http4s/pull/496.diff","patch_url":"https://github.com/http4s/http4s/pull/496.patch","issue_url":"https://api.github.com/repos/http4s/http4s/issues/496","number":496,"state":"open","locked":false,"title":"Refactor the client for resource safety, take 2","user":{"login":"rossabaker","id":142698,"avatar_url":"https://avatars.githubusercontent.com/u/142698?v=3","gravatar_id":"","url":"https://api.github.com/users/rossabaker","html_url":"https://github.com/rossabaker","followers_url":"https://api.github.com/users/rossabaker/followers","following_url":"https://api.github.com/users/rossabaker/following{/other_user}","gists_url":"https://api.github.com/users/rossabaker/gists{/gist_id}","starred_url":"https://api.github.com/users/rossabaker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rossabaker/subscriptions","organizations_url":"https://api.github.com/users/rossabaker/orgs","repos_url":"https://api.github.com/users/rossabaker/repos","events_url":"https://api.github.com/users/rossabaker/events{/privacy}","received_events_url":"https://api.github.com/users/rossabaker/received_events","type":"User","site_admin":false},"body":"A client is a case class that pairs an open service and a shutdown task.  The open service returns a `DisposableResponse`, which pairs the response a dispose Task that must be called to free the HTTP connection.  This encourages safer usage that sets the table for a better connection pool than currently exists (see #475).\r\n\r\nBecause this refactor makes explicit the connection between an `HttpService` and a `Client`, `Service` and related types are pulled into core.\r\n\r\nA convenient set of methods based on `Request`, `Task[Request]`, and `Uri` is provided for intuitive syntax on single requests.\r\n\r\nA `toService` method is provided for Kleisli composition by partially applying the response callback.\r\n\r\nFinally, a `streaming` method is provided to support a proxy use case within an HttpService.  This method also supports deprecated forms of the prior syntax to gently introduce this change.\r\n","created_at":"2016-01-06T06:00:33Z","updated_at":"2016-01-06T16:41:53Z","closed_at":null,"merged_at":null,"merge_commit_sha":"fc467908a48ae0896b237af35d6593aae5e06dc1","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/http4s/http4s/pulls/496/commits","review_comments_url":"https://api.github.com/repos/http4s/http4s/pulls/496/comments","review_comment_url":"https://api.github.com/repos/http4s/http4s/pulls/comments{/number}","comments_url":"https://api.github.com/repos/http4s/http4s/issues/496/comments","statuses_url":"https://api.github.com/repos/http4s/http4s/statuses/ed27bab421095c69affef19a7f4248e2df13fe4e","head":{"label":"http4s:topic/client-refactor-service","ref":"topic/client-refactor-service","sha":"ed27bab421095c69affef19a7f4248e2df13fe4e","user":{"login":"http4s","id":1527492,"avatar_url":"https://avatars.githubusercontent.com/u/1527492?v=3","gravatar_id":"","url":"https://api.github.com/users/http4s","html_url":"https://github.com/http4s","followers_url":"https://api.github.com/users/http4s/followers","following_url":"https://api.github.com/users/http4s/following{/other_user}","gists_url":"https://api.github.com/users/http4s/gists{/gist_id}","starred_url":"https://api.github.com/users/http4s/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/http4s/subscriptions","organizations_url":"https://api.github.com/users/http4s/orgs","repos_url":"https://api.github.com/users/http4s/repos","events_url":"https://api.github.com/users/http4s/events{/privacy}","received_events_url":"https://api.github.com/users/http4s/received_events","type":"Organization","site_admin":false},"repo":{"id":3692188,"name":"http4s","full_name":"http4s/http4s","owner":{"login":"http4s","id":1527492,"avatar_url":"https://avatars.githubusercontent.com/u/1527492?v=3","gravatar_id":"","url":"https://api.github.com/users/http4s","html_url":"https://github.com/http4s","followers_url":"https://api.github.com/users/http4s/followers","following_url":"https://api.github.com/users/http4s/following{/other_user}","gists_url":"https://api.github.com/users/http4s/gists{/gist_id}","starred_url":"https://api.github.com/users/http4s/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/http4s/subscriptions","organizations_url":"https://api.github.com/users/http4s/orgs","repos_url":"https://api.github.com/users/http4s/repos","events_url":"https://api.github.com/users/http4s/events{/privacy}","received_events_url":"https://api.github.com/users/http4s/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/http4s/http4s","description":"A minimal, idiomatic Scala interface for HTTP","fork":false,"url":"https://api.github.com/repos/http4s/http4s","forks_url":"https://api.github.com/repos/http4s/http4s/forks","keys_url":"https://api.github.com/repos/http4s/http4s/keys{/key_id}","collaborators_url":"https://api.github.com/repos/http4s/http4s/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/http4s/http4s/teams","hooks_url":"https://api.github.com/repos/http4s/http4s/hooks","issue_events_url":"https://api.github.com/repos/http4s/http4s/issues/events{/number}","events_url":"https://api.github.com/repos/http4s/http4s/events","assignees_url":"https://api.github.com/repos/http4s/http4s/assignees{/user}","branches_url":"https://api.github.com/repos/http4s/http4s/branches{/branch}","tags_url":"https://api.github.com/repos/http4s/http4s/tags","blobs_url":"https://api.github.com/repos/http4s/http4s/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/http4s/http4s/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/http4s/http4s/git/refs{/sha}","trees_url":"https://api.github.com/repos/http4s/http4s/git/trees{/sha}","statuses_url":"https://api.github.com/repos/http4s/http4s/statuses/{sha}","languages_url":"https://api.github.com/repos/http4s/http4s/languages","stargazers_url":"https://api.github.com/repos/http4s/http4s/stargazers","contributors_url":"https://api.github.com/repos/http4s/http4s/contributors","subscribers_url":"https://api.github.com/repos/http4s/http4s/subscribers","subscription_url":"https://api.github.com/repos/http4s/http4s/subscription","commits_url":"https://api.github.com/repos/http4s/http4s/commits{/sha}","git_commits_url":"https://api.github.com/repos/http4s/http4s/git/commits{/sha}","comments_url":"https://api.github.com/repos/http4s/http4s/comments{/number}","issue_comment_url":"https://api.github.com/repos/http4s/http4s/issues/comments{/number}","contents_url":"https://api.github.com/repos/http4s/http4s/contents/{+path}","compare_url":"https://api.github.com/repos/http4s/http4s/compare/{base}...{head}","merges_url":"https://api.github.com/repos/http4s/http4s/merges","archive_url":"https://api.github.com/repos/http4s/http4s/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/http4s/http4s/downloads","issues_url":"https://api.github.com/repos/http4s/http4s/issues{/number}","pulls_url":"https://api.github.com/repos/http4s/http4s/pulls{/number}","milestones_url":"https://api.github.com/repos/http4s/http4s/milestones{/number}","notifications_url":"https://api.github.com/repos/http4s/http4s/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/http4s/http4s/labels{/name}","releases_url":"https://api.github.com/repos/http4s/http4s/releases{/id}","created_at":"2012-03-12T04:41:20Z","updated_at":"2016-01-05T11:32:30Z","pushed_at":"2016-01-06T06:00:33Z","git_url":"git://github.com/http4s/http4s.git","ssh_url":"git@github.com:http4s/http4s.git","clone_url":"https://github.com/http4s/http4s.git","svn_url":"https://github.com/http4s/http4s","homepage":"http://http4s.org/","size":14642,"stargazers_count":369,"watchers_count":369,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":76,"mirror_url":null,"open_issues_count":50,"forks":76,"open_issues":50,"watchers":369,"default_branch":"master"}},"base":{"label":"http4s:master","ref":"master","sha":"13fe074b7dbdf1464fbcfddb9b5e216c20cf76c3","user":{"login":"http4s","id":1527492,"avatar_url":"https://avatars.githubusercontent.com/u/1527492?v=3","gravatar_id":"","url":"https://api.github.com/users/http4s","html_url":"https://github.com/http4s","followers_url":"https://api.github.com/users/http4s/followers","following_url":"https://api.github.com/users/http4s/following{/other_user}","gists_url":"https://api.github.com/users/http4s/gists{/gist_id}","starred_url":"https://api.github.com/users/http4s/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/http4s/subscriptions","organizations_url":"https://api.github.com/users/http4s/orgs","repos_url":"https://api.github.com/users/http4s/repos","events_url":"https://api.github.com/users/http4s/events{/privacy}","received_events_url":"https://api.github.com/users/http4s/received_events","type":"Organization","site_admin":false},"repo":{"id":3692188,"name":"http4s","full_name":"http4s/http4s","owner":{"login":"http4s","id":1527492,"avatar_url":"https://avatars.githubusercontent.com/u/1527492?v=3","gravatar_id":"","url":"https://api.github.com/users/http4s","html_url":"https://github.com/http4s","followers_url":"https://api.github.com/users/http4s/followers","following_url":"https://api.github.com/users/http4s/following{/other_user}","gists_url":"https://api.github.com/users/http4s/gists{/gist_id}","starred_url":"https://api.github.com/users/http4s/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/http4s/subscriptions","organizations_url":"https://api.github.com/users/http4s/orgs","repos_url":"https://api.github.com/users/http4s/repos","events_url":"https://api.github.com/users/http4s/events{/privacy}","received_events_url":"https://api.github.com/users/http4s/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/http4s/http4s","description":"A minimal, idiomatic Scala interface for HTTP","fork":false,"url":"https://api.github.com/repos/http4s/http4s","forks_url":"https://api.github.com/repos/http4s/http4s/forks","keys_url":"https://api.github.com/repos/http4s/http4s/keys{/key_id}","collaborators_url":"https://api.github.com/repos/http4s/http4s/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/http4s/http4s/teams","hooks_url":"https://api.github.com/repos/http4s/http4s/hooks","issue_events_url":"https://api.github.com/repos/http4s/http4s/issues/events{/number}","events_url":"https://api.github.com/repos/http4s/http4s/events","assignees_url":"https://api.github.com/repos/http4s/http4s/assignees{/user}","branches_url":"https://api.github.com/repos/http4s/http4s/branches{/branch}","tags_url":"https://api.github.com/repos/http4s/http4s/tags","blobs_url":"https://api.github.com/repos/http4s/http4s/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/http4s/http4s/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/http4s/http4s/git/refs{/sha}","trees_url":"https://api.github.com/repos/http4s/http4s/git/trees{/sha}","statuses_url":"https://api.github.com/repos/http4s/http4s/statuses/{sha}","languages_url":"https://api.github.com/repos/http4s/http4s/languages","stargazers_url":"https://api.github.com/repos/http4s/http4s/stargazers","contributors_url":"https://api.github.com/repos/http4s/http4s/contributors","subscribers_url":"https://api.github.com/repos/http4s/http4s/subscribers","subscription_url":"https://api.github.com/repos/http4s/http4s/subscription","commits_url":"https://api.github.com/repos/http4s/http4s/commits{/sha}","git_commits_url":"https://api.github.com/repos/http4s/http4s/git/commits{/sha}","comments_url":"https://api.github.com/repos/http4s/http4s/comments{/number}","issue_comment_url":"https://api.github.com/repos/http4s/http4s/issues/comments{/number}","contents_url":"https://api.github.com/repos/http4s/http4s/contents/{+path}","compare_url":"https://api.github.com/repos/http4s/http4s/compare/{base}...{head}","merges_url":"https://api.github.com/repos/http4s/http4s/merges","archive_url":"https://api.github.com/repos/http4s/http4s/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/http4s/http4s/downloads","issues_url":"https://api.github.com/repos/http4s/http4s/issues{/number}","pulls_url":"https://api.github.com/repos/http4s/http4s/pulls{/number}","milestones_url":"https://api.github.com/repos/http4s/http4s/milestones{/number}","notifications_url":"https://api.github.com/repos/http4s/http4s/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/http4s/http4s/labels{/name}","releases_url":"https://api.github.com/repos/http4s/http4s/releases{/id}","created_at":"2012-03-12T04:41:20Z","updated_at":"2016-01-05T11:32:30Z","pushed_at":"2016-01-06T06:00:33Z","git_url":"git://github.com/http4s/http4s.git","ssh_url":"git@github.com:http4s/http4s.git","clone_url":"https://github.com/http4s/http4s.git","svn_url":"https://github.com/http4s/http4s","homepage":"http://http4s.org/","size":14642,"stargazers_count":369,"watchers_count":369,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":76,"mirror_url":null,"open_issues_count":50,"forks":76,"open_issues":50,"watchers":369,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/http4s/http4s/pulls/496"},"html":{"href":"https://github.com/http4s/http4s/pull/496"},"issue":{"href":"https://api.github.com/repos/http4s/http4s/issues/496"},"comments":{"href":"https://api.github.com/repos/http4s/http4s/issues/496/comments"},"review_comments":{"href":"https://api.github.com/repos/http4s/http4s/pulls/496/comments"},"review_comment":{"href":"https://api.github.com/repos/http4s/http4s/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/http4s/http4s/pulls/496/commits"},"statuses":{"href":"https://api.github.com/repos/http4s/http4s/statuses/ed27bab421095c69affef19a7f4248e2df13fe4e"}}}}