{"action":"created","comment":{"url":"https://api.github.com/repos/akka/akka/pulls/comments/46612634","id":46612634,"diff_hunk":"@@ -0,0 +1,265 @@\n+/*\n+ * Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>\n+ */\n+\n+package akka.http.scaladsl.server\n+\n+import akka.http.scaladsl.model\n+import akka.http.scaladsl.model._\n+import akka.http.scaladsl.model.headers._\n+import HttpCharsets.`UTF-8`\n+\n+final class MediaTypeNegotiator(requestHeaders: Seq[HttpHeader]) {\n+\n+  /**\n+   * The media-ranges accepted by the client according to the given request headers, sorted by\n+   * 1. increasing generality (i.e. most specific first)\n+   * 2. decreasing q-value (only for ranges targeting a single MediaType)\n+   * 3. order of appearance in the `Accept` header(s)\n+   */\n+  val acceptedMediaRanges: List[MediaRange] =\n+    (for {\n+      Accept(mediaRanges) ← requestHeaders\n+      range ← mediaRanges\n+    } yield range).sortBy { // `sortBy` is stable, i.e. upholds the original order on identical keys\n+      case x if x.isWildcard     ⇒ 2f // most general, needs to come last\n+      case MediaRange.One(_, qv) ⇒ -qv // most specific, needs to come first\n+      case _                     ⇒ 1f // simple range like `image/*`\n+    }.toList\n+\n+  /**\n+   * Returns the q-value that the client (implicitly or explicitly) attaches to the given media-type.\n+   * See http://tools.ietf.org/html/rfc7231#section-5.3.1 for details.\n+   */\n+  def qValueFor(mediaType: MediaType): Float =\n+    acceptedMediaRanges match {\n+      case Nil ⇒ 1.0f\n+      case x   ⇒ x collectFirst { case r if r matches mediaType ⇒ r.qValue } getOrElse 0f\n+    }\n+\n+  /**\n+   * Determines whether the given [[MediaType]] is accepted by the client.\n+   */\n+  def isAccepted(mediaType: MediaType): Boolean = qValueFor(mediaType) > 0f\n+}\n+\n+final class CharsetNegotiator(requestHeaders: Seq[HttpHeader]) {\n+\n+  /**\n+   * The charset-ranges accepted by the client according to given request headers, sorted by\n+   * 1. increasing generality (i.e. most specific first)\n+   * 2. decreasing q-value (only for ranges targeting a single HttpCharset)\n+   * 3. order of appearance in the `Accept-Charset` header(s)\n+   */\n+  val acceptedCharsetRanges: List[HttpCharsetRange] =\n+    (for {\n+      `Accept-Charset`(charsetRanges) ← requestHeaders\n+      range ← charsetRanges\n+    } yield range).sortBy { // `sortBy` is stable, i.e. upholds the original order on identical keys\n+      case _: HttpCharsetRange.`*` ⇒ 1f // most general, needs to come last\n+      case x                       ⇒ -x.qValue // all others come first\n+    }.toList\n+\n+  /**\n+   * Returns the q-value that the client (implicitly or explicitly) attaches to the given charset.\n+   * See http://tools.ietf.org/html/rfc7231#section-5.3.1 for details.\n+   */\n+  def qValueFor(charset: HttpCharset): Float =\n+    acceptedCharsetRanges match {\n+      case Nil ⇒ 1.0f\n+      case x   ⇒ x collectFirst { case r if r matches charset ⇒ r.qValue } getOrElse 0f\n+    }\n+\n+  /**\n+   * Determines whether the given charset is accepted by the client.\n+   */\n+  def isAccepted(charset: HttpCharset): Boolean = qValueFor(charset) > 0f\n+\n+  /**\n+   * Picks the charset that is most preferred by the client with a bias towards UTF-8,\n+   * i.e. if the client accepts all charsets with equal preference then UTF-8 is picked.\n+   * If the client doesn't accept any charsets the method returns `None`.\n+   *\n+   * See also: http://tools.ietf.org/html/rfc7231#section-5.3.3\n+   */\n+  def pickBest: Option[HttpCharset] =\n+    acceptedCharsetRanges match {\n+      case Nil                                      ⇒ Some(`UTF-8`)\n+      case HttpCharsetRange.One(cs, _) :: _         ⇒ Some(cs)\n+      case HttpCharsetRange.`*`(qv) :: _ if qv > 0f ⇒ Some(`UTF-8`)\n+      case _                                        ⇒ None\n+    }\n+}\n+\n+final class ContentNegotiator(requestHeaders: Seq[HttpHeader]) {\n+  import ContentNegotiator.Alternative\n+\n+  val mtn = new MediaTypeNegotiator(requestHeaders)\n+  val csn = new CharsetNegotiator(requestHeaders)\n+\n+  def qValueFor(alternative: Alternative): Float =\n+    alternative match {\n+      case Alternative.ContentType(ct: ContentType.NonBinary) ⇒\n+        math.min(mtn.qValueFor(ct.mediaType), csn.qValueFor(ct.charset))\n+      case x ⇒ mtn.qValueFor(x.mediaType)\n+    }\n+\n+  /**\n+   * Picks the best of the given content alternatives given the preferences\n+   * the client indicated in the request's `Accept` and `Accept-Charset` headers.\n+   * See http://tools.ietf.org/html/rfc7231#section-5.3.2 ff for details on the negotiation logic.\n+   *\n+   * If there are several best alternatives that the client has equal preference for\n+   * the order of the arguments is used as a tie breaker (first one wins).\n+   *\n+   * If none of the given alternatives is acceptable to the client the methods return `None`.\n+   */\n+  def pickContentType(alternatives: List[Alternative]): Option[ContentType] =\n+    if (alternatives.nonEmpty) {\n+      val (bestAlternative, bestQVneg) = alternatives.map(alt ⇒ (alt, -qValueFor(alt))).sortBy(_._2).head\n+      if (bestQVneg != 0f) {\n+        bestAlternative match {\n+          case Alternative.ContentType(ct) ⇒ Some(ct)\n+          case Alternative.MediaType(mt)   ⇒ csn.pickBest.map(mt.withCharset)\n+        }\n+      } else None\n+    } else None\n+}\n+\n+object ContentNegotiator {\n+  sealed trait Alternative {\n+    def mediaType: MediaType\n+    def format: String\n+  }\n+  object Alternative {\n+    implicit def apply(contentType: model.ContentType): ContentType = ContentType(contentType)\n+    implicit def apply(mediaType: model.MediaType): Alternative =\n+      mediaType match {\n+        case x: model.MediaType.Binary           ⇒ ContentType(x)\n+        case x: model.MediaType.WithFixedCharset ⇒ ContentType(x)\n+        case x: model.MediaType.WithOpenCharset  ⇒ MediaType(x)\n+      }\n+\n+    case class ContentType(contentType: model.ContentType) extends Alternative {\n+      def mediaType = contentType.mediaType\n+      def format = contentType.toString\n+    }\n+    case class MediaType(mediaType: model.MediaType.WithOpenCharset) extends Alternative {\n+      def format = mediaType.toString\n+    }\n+  }\n+\n+  def apply(requestHeaders: Seq[HttpHeader]) = new ContentNegotiator(requestHeaders)\n+}\n+\n+final class EncodingNegotiator(requestHeaders: Seq[HttpHeader]) {\n+\n+  /**\n+   * The encoding-ranges accepted by the client according to given request headers, sorted by\n+   * 1. increasing generality (i.e. most specific first)\n+   * 2. decreasing q-value (only for ranges targeting a single HttpEncoding)\n+   * 3. order of appearance in the `Accept-Encoding` header(s)\n+   */\n+  val acceptedEncodingRanges: List[HttpEncodingRange] =\n+    (for {\n+      `Accept-Encoding`(encodingRanges) ← requestHeaders\n+      range ← encodingRanges\n+    } yield range).sortBy { // `sortBy` is stable, i.e. upholds the original order on identical keys\n+      case _: HttpEncodingRange.`*` ⇒ 1f // most general, needs to come last\n+      case x                        ⇒ -x.qValue // all others come first\n+    }.toList\n+\n+  /**\n+   * Returns the q-value that the client (implicitly or explicitly) attaches to the given encoding.\n+   * See http://tools.ietf.org/html/rfc7231#section-5.3.1 for details.\n+   */\n+  def qValueFor(encoding: HttpEncoding): Float =\n+    acceptedEncodingRanges match {\n+      case Nil ⇒ 1.0f\n+      case x   ⇒ x collectFirst { case r if r matches encoding ⇒ r.qValue } getOrElse 0f\n+    }\n+\n+  /**\n+   * Determines whether the given encoding is accepted by the client.\n+   */\n+  def isAccepted(encoding: HttpEncoding): Boolean = qValueFor(encoding) > 0f\n+\n+  /**\n+   * Determines whether the request has an `Accept-Encoding` clause matching the given encoding.\n+   */\n+  def hasMatchingFor(encoding: HttpEncoding): Boolean =\n+    acceptedEncodingRanges.exists(_ matches encoding)\n+\n+  /**\n+   * Picks the best of the given encoding alternatives given the preferences\n+   * the client indicated in the request's `Accept-Encoding` headers.\n+   * See http://tools.ietf.org/html/rfc7231#section-5.3.4 for details on the negotiation logic.\n+   *\n+   * If there are several best encoding alternatives that the client has equal preference for\n+   * the order of the arguments is used as a tie breaker (first one wins).\n+   *\n+   * If none of the given alternatives is acceptable to the client the methods return `None`.\n+   */\n+  def pickEncoding(alternatives: List[HttpEncoding]): Option[HttpEncoding] =\n+    if (alternatives.nonEmpty) {\n+      val (bestAlternative, bestQVneg) = alternatives.map(l ⇒ l → -qValueFor(l)).sortBy(_._2).head\n+      if (bestQVneg != 0f) Some(bestAlternative) else None\n+    } else None","path":"akka-http/src/main/scala/akka/http/scaladsl/server/ContentNegotation.scala","position":207,"original_position":207,"commit_id":"9065151d7d99547c234ebb74b8bab9ff9af394b7","original_commit_id":"9065151d7d99547c234ebb74b8bab9ff9af394b7","user":{"login":"2beaucoup","id":243903,"avatar_url":"https://avatars.githubusercontent.com/u/243903?v=3","gravatar_id":"","url":"https://api.github.com/users/2beaucoup","html_url":"https://github.com/2beaucoup","followers_url":"https://api.github.com/users/2beaucoup/followers","following_url":"https://api.github.com/users/2beaucoup/following{/other_user}","gists_url":"https://api.github.com/users/2beaucoup/gists{/gist_id}","starred_url":"https://api.github.com/users/2beaucoup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/2beaucoup/subscriptions","organizations_url":"https://api.github.com/users/2beaucoup/orgs","repos_url":"https://api.github.com/users/2beaucoup/repos","events_url":"https://api.github.com/users/2beaucoup/events{/privacy}","received_events_url":"https://api.github.com/users/2beaucoup/received_events","type":"User","site_admin":false},"body":"even shorter:\r\n```scala\r\n.map(l ⇒ l → qValueFor(l))\r\n.sortBy(-_._2)\r\n.collectFirst { case (l, q) if q > 0f ⇒ l }\r\n```","created_at":"2015-12-03T21:13:39Z","updated_at":"2015-12-03T21:13:39Z","html_url":"https://github.com/akka/akka/pull/19066#discussion_r46612634","pull_request_url":"https://api.github.com/repos/akka/akka/pulls/19066","_links":{"self":{"href":"https://api.github.com/repos/akka/akka/pulls/comments/46612634"},"html":{"href":"https://github.com/akka/akka/pull/19066#discussion_r46612634"},"pull_request":{"href":"https://api.github.com/repos/akka/akka/pulls/19066"}}},"pull_request":{"url":"https://api.github.com/repos/akka/akka/pulls/19066","id":52325438,"html_url":"https://github.com/akka/akka/pull/19066","diff_url":"https://github.com/akka/akka/pull/19066.diff","patch_url":"https://github.com/akka/akka/pull/19066.patch","issue_url":"https://api.github.com/repos/akka/akka/issues/19066","number":19066,"state":"open","locked":false,"title":"http: refactor MediaType / ContentType model and content negotiation","user":{"login":"sirthias","id":135347,"avatar_url":"https://avatars.githubusercontent.com/u/135347?v=3","gravatar_id":"","url":"https://api.github.com/users/sirthias","html_url":"https://github.com/sirthias","followers_url":"https://api.github.com/users/sirthias/followers","following_url":"https://api.github.com/users/sirthias/following{/other_user}","gists_url":"https://api.github.com/users/sirthias/gists{/gist_id}","starred_url":"https://api.github.com/users/sirthias/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sirthias/subscriptions","organizations_url":"https://api.github.com/users/sirthias/orgs","repos_url":"https://api.github.com/users/sirthias/repos","events_url":"https://api.github.com/users/sirthias/events{/privacy}","received_events_url":"https://api.github.com/users/sirthias/received_events","type":"User","site_admin":false},"body":"Closes #19034.\r\n\r\nFrom an initially seemingly simple fix this thing has turned into quite a major refactoring, cleaning up some of the weaker parts of the HTTP model. The core problem was the `ContentType` type, which has quite a central role but whose exact semantics had become more and more blurry over time, as it was used for more and more related, but not quite identical, things.\r\n\r\nThis PR completely revamps the `MediaType` and `ContentType` design, moving runtime checks into the type system and providing a more structured and safer API.\r\n\r\nUnfortunately, because these two types are quite core, this patch is now somewhat big and probably a little harder to review. Let me know if there is something that I can do to ease the process.\r\n\r\n/cc @jrudolph","created_at":"2015-12-01T22:24:41Z","updated_at":"2015-12-03T21:13:39Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3a22023006c231eee6b504de76994331c1cd6ee2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/akka/akka/pulls/19066/commits","review_comments_url":"https://api.github.com/repos/akka/akka/pulls/19066/comments","review_comment_url":"https://api.github.com/repos/akka/akka/pulls/comments{/number}","comments_url":"https://api.github.com/repos/akka/akka/issues/19066/comments","statuses_url":"https://api.github.com/repos/akka/akka/statuses/9065151d7d99547c234ebb74b8bab9ff9af394b7","head":{"label":"spray:wip-19034-mathias","ref":"wip-19034-mathias","sha":"9065151d7d99547c234ebb74b8bab9ff9af394b7","user":{"login":"spray","id":688516,"avatar_url":"https://avatars.githubusercontent.com/u/688516?v=3","gravatar_id":"","url":"https://api.github.com/users/spray","html_url":"https://github.com/spray","followers_url":"https://api.github.com/users/spray/followers","following_url":"https://api.github.com/users/spray/following{/other_user}","gists_url":"https://api.github.com/users/spray/gists{/gist_id}","starred_url":"https://api.github.com/users/spray/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spray/subscriptions","organizations_url":"https://api.github.com/users/spray/orgs","repos_url":"https://api.github.com/users/spray/repos","events_url":"https://api.github.com/users/spray/events{/privacy}","received_events_url":"https://api.github.com/users/spray/received_events","type":"Organization","site_admin":false},"repo":{"id":3809985,"name":"akka","full_name":"spray/akka","owner":{"login":"spray","id":688516,"avatar_url":"https://avatars.githubusercontent.com/u/688516?v=3","gravatar_id":"","url":"https://api.github.com/users/spray","html_url":"https://github.com/spray","followers_url":"https://api.github.com/users/spray/followers","following_url":"https://api.github.com/users/spray/following{/other_user}","gists_url":"https://api.github.com/users/spray/gists{/gist_id}","starred_url":"https://api.github.com/users/spray/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spray/subscriptions","organizations_url":"https://api.github.com/users/spray/orgs","repos_url":"https://api.github.com/users/spray/repos","events_url":"https://api.github.com/users/spray/events{/privacy}","received_events_url":"https://api.github.com/users/spray/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/spray/akka","description":"Akka Project","fork":true,"url":"https://api.github.com/repos/spray/akka","forks_url":"https://api.github.com/repos/spray/akka/forks","keys_url":"https://api.github.com/repos/spray/akka/keys{/key_id}","collaborators_url":"https://api.github.com/repos/spray/akka/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/spray/akka/teams","hooks_url":"https://api.github.com/repos/spray/akka/hooks","issue_events_url":"https://api.github.com/repos/spray/akka/issues/events{/number}","events_url":"https://api.github.com/repos/spray/akka/events","assignees_url":"https://api.github.com/repos/spray/akka/assignees{/user}","branches_url":"https://api.github.com/repos/spray/akka/branches{/branch}","tags_url":"https://api.github.com/repos/spray/akka/tags","blobs_url":"https://api.github.com/repos/spray/akka/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/spray/akka/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/spray/akka/git/refs{/sha}","trees_url":"https://api.github.com/repos/spray/akka/git/trees{/sha}","statuses_url":"https://api.github.com/repos/spray/akka/statuses/{sha}","languages_url":"https://api.github.com/repos/spray/akka/languages","stargazers_url":"https://api.github.com/repos/spray/akka/stargazers","contributors_url":"https://api.github.com/repos/spray/akka/contributors","subscribers_url":"https://api.github.com/repos/spray/akka/subscribers","subscription_url":"https://api.github.com/repos/spray/akka/subscription","commits_url":"https://api.github.com/repos/spray/akka/commits{/sha}","git_commits_url":"https://api.github.com/repos/spray/akka/git/commits{/sha}","comments_url":"https://api.github.com/repos/spray/akka/comments{/number}","issue_comment_url":"https://api.github.com/repos/spray/akka/issues/comments{/number}","contents_url":"https://api.github.com/repos/spray/akka/contents/{+path}","compare_url":"https://api.github.com/repos/spray/akka/compare/{base}...{head}","merges_url":"https://api.github.com/repos/spray/akka/merges","archive_url":"https://api.github.com/repos/spray/akka/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/spray/akka/downloads","issues_url":"https://api.github.com/repos/spray/akka/issues{/number}","pulls_url":"https://api.github.com/repos/spray/akka/pulls{/number}","milestones_url":"https://api.github.com/repos/spray/akka/milestones{/number}","notifications_url":"https://api.github.com/repos/spray/akka/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/spray/akka/labels{/name}","releases_url":"https://api.github.com/repos/spray/akka/releases{/id}","created_at":"2012-03-23T15:57:31Z","updated_at":"2015-11-20T02:40:48Z","pushed_at":"2015-12-03T08:45:28Z","git_url":"git://github.com/spray/akka.git","ssh_url":"git@github.com:spray/akka.git","clone_url":"https://github.com/spray/akka.git","svn_url":"https://github.com/spray/akka","homepage":"http://akka.io","size":167899,"stargazers_count":9,"watchers_count":9,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":4,"mirror_url":null,"open_issues_count":0,"forks":4,"open_issues":0,"watchers":9,"default_branch":"master"}},"base":{"label":"akka:release-2.3-dev","ref":"release-2.3-dev","sha":"9922001263a2b82da8f366d59bb8ea913c508d5a","user":{"login":"akka","id":1496237,"avatar_url":"https://avatars.githubusercontent.com/u/1496237?v=3","gravatar_id":"","url":"https://api.github.com/users/akka","html_url":"https://github.com/akka","followers_url":"https://api.github.com/users/akka/followers","following_url":"https://api.github.com/users/akka/following{/other_user}","gists_url":"https://api.github.com/users/akka/gists{/gist_id}","starred_url":"https://api.github.com/users/akka/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/akka/subscriptions","organizations_url":"https://api.github.com/users/akka/orgs","repos_url":"https://api.github.com/users/akka/repos","events_url":"https://api.github.com/users/akka/events{/privacy}","received_events_url":"https://api.github.com/users/akka/received_events","type":"Organization","site_admin":false},"repo":{"id":130013,"name":"akka","full_name":"akka/akka","owner":{"login":"akka","id":1496237,"avatar_url":"https://avatars.githubusercontent.com/u/1496237?v=3","gravatar_id":"","url":"https://api.github.com/users/akka","html_url":"https://github.com/akka","followers_url":"https://api.github.com/users/akka/followers","following_url":"https://api.github.com/users/akka/following{/other_user}","gists_url":"https://api.github.com/users/akka/gists{/gist_id}","starred_url":"https://api.github.com/users/akka/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/akka/subscriptions","organizations_url":"https://api.github.com/users/akka/orgs","repos_url":"https://api.github.com/users/akka/repos","events_url":"https://api.github.com/users/akka/events{/privacy}","received_events_url":"https://api.github.com/users/akka/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/akka/akka","description":"Akka Project","fork":false,"url":"https://api.github.com/repos/akka/akka","forks_url":"https://api.github.com/repos/akka/akka/forks","keys_url":"https://api.github.com/repos/akka/akka/keys{/key_id}","collaborators_url":"https://api.github.com/repos/akka/akka/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/akka/akka/teams","hooks_url":"https://api.github.com/repos/akka/akka/hooks","issue_events_url":"https://api.github.com/repos/akka/akka/issues/events{/number}","events_url":"https://api.github.com/repos/akka/akka/events","assignees_url":"https://api.github.com/repos/akka/akka/assignees{/user}","branches_url":"https://api.github.com/repos/akka/akka/branches{/branch}","tags_url":"https://api.github.com/repos/akka/akka/tags","blobs_url":"https://api.github.com/repos/akka/akka/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/akka/akka/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/akka/akka/git/refs{/sha}","trees_url":"https://api.github.com/repos/akka/akka/git/trees{/sha}","statuses_url":"https://api.github.com/repos/akka/akka/statuses/{sha}","languages_url":"https://api.github.com/repos/akka/akka/languages","stargazers_url":"https://api.github.com/repos/akka/akka/stargazers","contributors_url":"https://api.github.com/repos/akka/akka/contributors","subscribers_url":"https://api.github.com/repos/akka/akka/subscribers","subscription_url":"https://api.github.com/repos/akka/akka/subscription","commits_url":"https://api.github.com/repos/akka/akka/commits{/sha}","git_commits_url":"https://api.github.com/repos/akka/akka/git/commits{/sha}","comments_url":"https://api.github.com/repos/akka/akka/comments{/number}","issue_comment_url":"https://api.github.com/repos/akka/akka/issues/comments{/number}","contents_url":"https://api.github.com/repos/akka/akka/contents/{+path}","compare_url":"https://api.github.com/repos/akka/akka/compare/{base}...{head}","merges_url":"https://api.github.com/repos/akka/akka/merges","archive_url":"https://api.github.com/repos/akka/akka/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/akka/akka/downloads","issues_url":"https://api.github.com/repos/akka/akka/issues{/number}","pulls_url":"https://api.github.com/repos/akka/akka/pulls{/number}","milestones_url":"https://api.github.com/repos/akka/akka/milestones{/number}","notifications_url":"https://api.github.com/repos/akka/akka/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/akka/akka/labels{/name}","releases_url":"https://api.github.com/repos/akka/akka/releases{/id}","created_at":"2009-02-16T12:51:54Z","updated_at":"2015-12-03T20:37:27Z","pushed_at":"2015-12-03T20:23:16Z","git_url":"git://github.com/akka/akka.git","ssh_url":"git@github.com:akka/akka.git","clone_url":"https://github.com/akka/akka.git","svn_url":"https://github.com/akka/akka","homepage":"http://akka.io","size":178046,"stargazers_count":4780,"watchers_count":4780,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1436,"mirror_url":null,"open_issues_count":791,"forks":1436,"open_issues":791,"watchers":4780,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/akka/akka/pulls/19066"},"html":{"href":"https://github.com/akka/akka/pull/19066"},"issue":{"href":"https://api.github.com/repos/akka/akka/issues/19066"},"comments":{"href":"https://api.github.com/repos/akka/akka/issues/19066/comments"},"review_comments":{"href":"https://api.github.com/repos/akka/akka/pulls/19066/comments"},"review_comment":{"href":"https://api.github.com/repos/akka/akka/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/akka/akka/pulls/19066/commits"},"statuses":{"href":"https://api.github.com/repos/akka/akka/statuses/9065151d7d99547c234ebb74b8bab9ff9af394b7"}}}}