{"action":"created","comment":{"url":"https://api.github.com/repos/http4s/http4s/pulls/comments/57572625","id":57572625,"diff_hunk":"@@ -0,0 +1,166 @@\n+package org.http4s\n+package multipart\n+\n+import scala.language.postfixOps\n+\n+import scodec.bits.ByteVector\n+\n+import scalaz.{-\\/, \\/-, \\/}\n+import scalaz.stream.{process1, Process, Process1, Writer1}\n+\n+/** A low-level multipart-parsing pipe.  Most end users will prefer EntityDecoder[Multipart]. */\n+object MultipartParser {\n+  import Process._\n+\n+  private val logger = org.log4s.getLogger\n+\n+  private val CRLF = ByteVector('\\r', '\\n')\n+  private val DASHDASH = ByteVector('-', '-')\n+\n+  def parse(boundary: Boundary): Writer1[Headers, ByteVector, ByteVector] = {\n+    val boundaryBytes = boundary.toByteVector\n+    val startLine = DASHDASH ++ boundaryBytes\n+    val endLine = startLine ++ DASHDASH\n+\n+    def receiveLine(leading: Option[ByteVector]): Writer1[ByteVector, ByteVector, (ByteVector, Option[ByteVector])] = {\n+      def handle(bv: ByteVector): Writer1[ByteVector, ByteVector, (ByteVector, Option[ByteVector])] = {\n+        val index = bv indexOfSlice CRLF\n+\n+        if (index >= 0) {\n+          val (head, tailPre) = bv splitAt index\n+          val tail = tailPre drop 2       // remove the line feed characters\n+          val tailM = Some(tail) filter { !_.isEmpty }\n+          emit(\\/-((head, tailM)))\n+        }\n+        else if (bv.nonEmpty && bv.get(bv.length - 1) == '\\r') {\n+          // The CRLF may have split across chunks.\n+          val (head, cr) = bv.splitAt(bv.length - 1)\n+          emit(-\\/(head)) ++ receive1(next => handle(cr ++ next))\n+        }\n+        else {\n+          emit(-\\/(bv)) ++ receiveLine(None)\n+        }\n+      }\n+\n+      leading map handle getOrElse receive1(handle)\n+    }\n+\n+    def receiveCollapsedLine(leading: Option[ByteVector]): Process1[ByteVector, (ByteVector, Option[ByteVector])] = {\n+      receiveLine(leading) pipe process1.fold((ByteVector.empty, None: Option[ByteVector])) {\n+        case ((acc, tail), -\\/(left)) => (acc ++ left, tail)\n+        case ((acc, _), \\/-((term, tail))) => (acc ++ term, tail)\n+      }\n+    }\n+\n+    lazy val start: Writer1[Headers, ByteVector, ByteVector] = {","path":"core/src/main/scala/org/http4s/multipart/MultipartParser.scala","position":55,"original_position":55,"commit_id":"af5b5f2d8261394398dbbbd61d6b68a908cf9f84","original_commit_id":"af5b5f2d8261394398dbbbd61d6b68a908cf9f84","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":"I don't see why.  It came to us this way.","created_at":"2016-03-28T13:47:39Z","updated_at":"2016-03-28T13:47:39Z","html_url":"https://github.com/http4s/http4s/pull/589#discussion_r57572625","pull_request_url":"https://api.github.com/repos/http4s/http4s/pulls/589","_links":{"self":{"href":"https://api.github.com/repos/http4s/http4s/pulls/comments/57572625"},"html":{"href":"https://github.com/http4s/http4s/pull/589#discussion_r57572625"},"pull_request":{"href":"https://api.github.com/repos/http4s/http4s/pulls/589"}}},"pull_request":{"url":"https://api.github.com/repos/http4s/http4s/pulls/589","id":64288911,"html_url":"https://github.com/http4s/http4s/pull/589","diff_url":"https://github.com/http4s/http4s/pull/589.diff","patch_url":"https://github.com/http4s/http4s/pull/589.patch","issue_url":"https://api.github.com/repos/http4s/http4s/issues/589","number":589,"state":"open","locked":false,"title":"Experimental multipart support","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":"The outstanding atrocities are documented in the [package object](https://github.com/http4s/http4s/blob/topic/multipart-4/core/src/main/scala/org/http4s/multipart/package.scala).  This provides us a basic, if memory hungry, implementation of multipart.\r\n\r\nRelated issues:\r\n- Incomplete progress toward #179.\r\n- Extends and revives #309.","created_at":"2016-03-28T04:44:53Z","updated_at":"2016-03-28T13:47:39Z","closed_at":null,"merged_at":null,"merge_commit_sha":"dee1ec86fbfcc4f2bc7c5b86d76fac3b6a08255f","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/http4s/http4s/pulls/589/commits","review_comments_url":"https://api.github.com/repos/http4s/http4s/pulls/589/comments","review_comment_url":"https://api.github.com/repos/http4s/http4s/pulls/comments{/number}","comments_url":"https://api.github.com/repos/http4s/http4s/issues/589/comments","statuses_url":"https://api.github.com/repos/http4s/http4s/statuses/af5b5f2d8261394398dbbbd61d6b68a908cf9f84","head":{"label":"http4s:topic/multipart-4","ref":"topic/multipart-4","sha":"af5b5f2d8261394398dbbbd61d6b68a908cf9f84","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}","deployments_url":"https://api.github.com/repos/http4s/http4s/deployments","created_at":"2012-03-12T04:41:20Z","updated_at":"2016-03-27T20:28:07Z","pushed_at":"2016-03-28T04:44:54Z","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":19206,"stargazers_count":418,"watchers_count":418,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":83,"mirror_url":null,"open_issues_count":53,"forks":83,"open_issues":53,"watchers":418,"default_branch":"master"}},"base":{"label":"http4s:master","ref":"master","sha":"e6c57f3058b5700951c1871329a9db1aaa997bf5","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}","deployments_url":"https://api.github.com/repos/http4s/http4s/deployments","created_at":"2012-03-12T04:41:20Z","updated_at":"2016-03-27T20:28:07Z","pushed_at":"2016-03-28T04:44:54Z","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":19206,"stargazers_count":418,"watchers_count":418,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":83,"mirror_url":null,"open_issues_count":53,"forks":83,"open_issues":53,"watchers":418,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/http4s/http4s/pulls/589"},"html":{"href":"https://github.com/http4s/http4s/pull/589"},"issue":{"href":"https://api.github.com/repos/http4s/http4s/issues/589"},"comments":{"href":"https://api.github.com/repos/http4s/http4s/issues/589/comments"},"review_comments":{"href":"https://api.github.com/repos/http4s/http4s/pulls/589/comments"},"review_comment":{"href":"https://api.github.com/repos/http4s/http4s/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/http4s/http4s/pulls/589/commits"},"statuses":{"href":"https://api.github.com/repos/http4s/http4s/statuses/af5b5f2d8261394398dbbbd61d6b68a908cf9f84"}}}}