{"action":"created","comment":{"url":"https://api.github.com/repos/reactphp/http/pulls/comments/25563569","id":25563569,"diff_hunk":"@@ -0,0 +1,193 @@\n+<?php\n+\n+namespace React\\Http;\n+\n+/**\n+ * Parse a multipart body\n+ *\n+ * Original source is from https://gist.github.com/jas-/5c3fdc26fedd11cb9fb5\n+ *\n+ * @author jason.gerfen@gmail.com\n+ * @author stephane.goetz@onigoetz.ch\n+ * @license http://www.gnu.org/licenses/gpl.html GPL License 3\n+ */\n+class MultipartParser\n+{\n+    /**\n+     * @var string\n+     */\n+    protected $input;\n+\n+    /**\n+     * @var string\n+     */\n+    protected $boundary;\n+\n+    /**\n+     * Contains the resolved posts\n+     *\n+     * @var array\n+     */\n+    protected $post = [];\n+\n+    /**\n+     * Contains the resolved files\n+     *\n+     * @var array\n+     */\n+    protected $files = [];\n+\n+    /**\n+     * @param $input\n+     * @param $boundary\n+     */\n+    public function __construct($input, $boundary)\n+    {\n+        $this->input = $input;\n+        $this->boundary = $boundary;\n+    }\n+\n+    /**\n+     * @return array\n+     */\n+    public function getPost()\n+    {\n+        return $this->post;\n+    }\n+\n+    /**\n+     * @return array\n+     */\n+    public function getFiles()\n+    {\n+        return $this->files;\n+    }\n+\n+    /**\n+     * Do the actual parsing\n+     */\n+    public function parse()\n+    {\n+        $blocks = $this->split($this->boundary);\n+\n+        foreach ($blocks as $value) {\n+            if (empty($value)) {\n+                continue;\n+            }\n+\n+            $this->parseBlock($value);\n+        }\n+    }\n+\n+    /**\n+     * @param $boundary string\n+     * @returns Array\n+     */\n+    protected function split($boundary)\n+    {\n+        $boundary = preg_quote($boundary);\n+        $result = preg_split(\"/\\\\-+$boundary/\", $this->input);\n+        array_pop($result);\n+        return $result;\n+    }\n+\n+    /**\n+     * Decide if we handle a file, post value or octet stream\n+     *\n+     * @param $string string\n+     * @returns void\n+     */\n+    protected function parseBlock($string)\n+    {\n+        if (strpos($string, 'filename') !== false) {\n+            $this->file($string);\n+            return;\n+        }\n+\n+        // This may never be called, if an octet stream\n+        // has a filename it is catched by the previous\n+        // condition already.\n+        if (strpos($string, 'application/octet-stream') !== false) {\n+            $this->octetStream($string);\n+            return;\n+        }\n+\n+        $this->post($string);\n+    }\n+\n+    /**\n+     * Parse a raw octet stream\n+     *\n+     * @param $string\n+     * @return array\n+     */\n+    protected function octetStream($string)\n+    {\n+        preg_match('/name=\\\"([^\\\"]*)\\\".*stream[\\n|\\r]+([^\\n\\r].*)?$/s', $string, $match);\n+\n+        $this->addResolved('post', $match[1], $match[2]);\n+    }\n+\n+    /**\n+     * Parse a file\n+     *\n+     * @param $string\n+     * @return array\n+     */\n+    protected function file($string)\n+    {\n+        preg_match('/name=\\\"([^\\\"]*)\\\"; filename=\\\"([^\\\"]*)\\\"[\\n|\\r]+([^\\n\\r].*)?\\r$/s', $string, $match);\n+        preg_match('/Content-Type: (.*)?/', $match[3], $mime);\n+\n+        $content = preg_replace('/Content-Type: (.*)[^\\n\\r]/', '', $match[3]);\n+        $content = ltrim($content, \"\\r\\n\");\n+\n+        $path = tempnam(sys_get_temp_dir(), \"php\");\n+        $err = file_put_contents($path, $content);","path":"src/MultipartParser.php","position":146,"original_position":146,"commit_id":"244543041d3c1e39b76cd948aa231e2d41b3b252","original_commit_id":"244543041d3c1e39b76cd948aa231e2d41b3b252","user":{"login":"nazar-pc","id":928965,"avatar_url":"https://avatars.githubusercontent.com/u/928965?v=3","gravatar_id":"","url":"https://api.github.com/users/nazar-pc","html_url":"https://github.com/nazar-pc","followers_url":"https://api.github.com/users/nazar-pc/followers","following_url":"https://api.github.com/users/nazar-pc/following{/other_user}","gists_url":"https://api.github.com/users/nazar-pc/gists{/gist_id}","starred_url":"https://api.github.com/users/nazar-pc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nazar-pc/subscriptions","organizations_url":"https://api.github.com/users/nazar-pc/orgs","repos_url":"https://api.github.com/users/nazar-pc/repos","events_url":"https://api.github.com/users/nazar-pc/events{/privacy}","received_events_url":"https://api.github.com/users/nazar-pc/received_events","type":"User","site_admin":false},"body":"I have suggestion about how to do that:\r\n* add one more argument (callable or factory) for handling file uploads to server constructor\r\n* then when it comes to file handling we just call callable or factory (with arguments like file name, mime type) and pass everything to method of object it returned (if there was no callable or factory - just ignore uploaded file, it is too expensive to store it in memory)\r\n* after whole request parsed we'll have access to array with all objects we've created for uploaded files\r\n\r\nWhat you think about such approach?","created_at":"2015-03-01T03:02:16Z","updated_at":"2015-03-01T03:02:16Z","html_url":"https://github.com/reactphp/http/pull/13#discussion_r25563569","pull_request_url":"https://api.github.com/repos/reactphp/http/pulls/13","_links":{"self":{"href":"https://api.github.com/repos/reactphp/http/pulls/comments/25563569"},"html":{"href":"https://github.com/reactphp/http/pull/13#discussion_r25563569"},"pull_request":{"href":"https://api.github.com/repos/reactphp/http/pulls/13"}}},"pull_request":{"url":"https://api.github.com/repos/reactphp/http/pulls/13","id":22477754,"html_url":"https://github.com/reactphp/http/pull/13","diff_url":"https://github.com/reactphp/http/pull/13.diff","patch_url":"https://github.com/reactphp/http/pull/13.patch","issue_url":"https://api.github.com/repos/reactphp/http/issues/13","number":13,"state":"open","locked":false,"title":"Multipart handling","user":{"login":"onigoetz","id":309594,"avatar_url":"https://avatars.githubusercontent.com/u/309594?v=3","gravatar_id":"","url":"https://api.github.com/users/onigoetz","html_url":"https://github.com/onigoetz","followers_url":"https://api.github.com/users/onigoetz/followers","following_url":"https://api.github.com/users/onigoetz/following{/other_user}","gists_url":"https://api.github.com/users/onigoetz/gists{/gist_id}","starred_url":"https://api.github.com/users/onigoetz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/onigoetz/subscriptions","organizations_url":"https://api.github.com/users/onigoetz/orgs","repos_url":"https://api.github.com/users/onigoetz/repos","events_url":"https://api.github.com/users/onigoetz/events{/privacy}","received_events_url":"https://api.github.com/users/onigoetz/received_events","type":"User","site_admin":false},"body":"Hello,\r\n\r\nI added basic support for multipart for files and POST requests\r\n\r\nI also wrote the unit tests for this part and improved the existing ones.\r\n\r\nOne thing that could be done better is that now, it waits for the whole body before it parses it, we could implement a system where it feeds the content to the parser and the files are created on the fly and we don't use the whole memory for file uploads.","created_at":"2014-10-09T08:23:32Z","updated_at":"2015-03-01T03:02:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"cea9a8e99b5b996be4db3f13e576e93d77c07e3f","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/reactphp/http/pulls/13/commits","review_comments_url":"https://api.github.com/repos/reactphp/http/pulls/13/comments","review_comment_url":"https://api.github.com/repos/reactphp/http/pulls/comments{/number}","comments_url":"https://api.github.com/repos/reactphp/http/issues/13/comments","statuses_url":"https://api.github.com/repos/reactphp/http/statuses/244543041d3c1e39b76cd948aa231e2d41b3b252","head":{"label":"onigoetz:master","ref":"master","sha":"244543041d3c1e39b76cd948aa231e2d41b3b252","user":{"login":"onigoetz","id":309594,"avatar_url":"https://avatars.githubusercontent.com/u/309594?v=3","gravatar_id":"","url":"https://api.github.com/users/onigoetz","html_url":"https://github.com/onigoetz","followers_url":"https://api.github.com/users/onigoetz/followers","following_url":"https://api.github.com/users/onigoetz/following{/other_user}","gists_url":"https://api.github.com/users/onigoetz/gists{/gist_id}","starred_url":"https://api.github.com/users/onigoetz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/onigoetz/subscriptions","organizations_url":"https://api.github.com/users/onigoetz/orgs","repos_url":"https://api.github.com/users/onigoetz/repos","events_url":"https://api.github.com/users/onigoetz/events{/privacy}","received_events_url":"https://api.github.com/users/onigoetz/received_events","type":"User","site_admin":false},"repo":{"id":24960645,"name":"http","full_name":"onigoetz/http","owner":{"login":"onigoetz","id":309594,"avatar_url":"https://avatars.githubusercontent.com/u/309594?v=3","gravatar_id":"","url":"https://api.github.com/users/onigoetz","html_url":"https://github.com/onigoetz","followers_url":"https://api.github.com/users/onigoetz/followers","following_url":"https://api.github.com/users/onigoetz/following{/other_user}","gists_url":"https://api.github.com/users/onigoetz/gists{/gist_id}","starred_url":"https://api.github.com/users/onigoetz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/onigoetz/subscriptions","organizations_url":"https://api.github.com/users/onigoetz/orgs","repos_url":"https://api.github.com/users/onigoetz/repos","events_url":"https://api.github.com/users/onigoetz/events{/privacy}","received_events_url":"https://api.github.com/users/onigoetz/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/onigoetz/http","description":"React HTTP server (unstable)","fork":true,"url":"https://api.github.com/repos/onigoetz/http","forks_url":"https://api.github.com/repos/onigoetz/http/forks","keys_url":"https://api.github.com/repos/onigoetz/http/keys{/key_id}","collaborators_url":"https://api.github.com/repos/onigoetz/http/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/onigoetz/http/teams","hooks_url":"https://api.github.com/repos/onigoetz/http/hooks","issue_events_url":"https://api.github.com/repos/onigoetz/http/issues/events{/number}","events_url":"https://api.github.com/repos/onigoetz/http/events","assignees_url":"https://api.github.com/repos/onigoetz/http/assignees{/user}","branches_url":"https://api.github.com/repos/onigoetz/http/branches{/branch}","tags_url":"https://api.github.com/repos/onigoetz/http/tags","blobs_url":"https://api.github.com/repos/onigoetz/http/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/onigoetz/http/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/onigoetz/http/git/refs{/sha}","trees_url":"https://api.github.com/repos/onigoetz/http/git/trees{/sha}","statuses_url":"https://api.github.com/repos/onigoetz/http/statuses/{sha}","languages_url":"https://api.github.com/repos/onigoetz/http/languages","stargazers_url":"https://api.github.com/repos/onigoetz/http/stargazers","contributors_url":"https://api.github.com/repos/onigoetz/http/contributors","subscribers_url":"https://api.github.com/repos/onigoetz/http/subscribers","subscription_url":"https://api.github.com/repos/onigoetz/http/subscription","commits_url":"https://api.github.com/repos/onigoetz/http/commits{/sha}","git_commits_url":"https://api.github.com/repos/onigoetz/http/git/commits{/sha}","comments_url":"https://api.github.com/repos/onigoetz/http/comments{/number}","issue_comment_url":"https://api.github.com/repos/onigoetz/http/issues/comments{/number}","contents_url":"https://api.github.com/repos/onigoetz/http/contents/{+path}","compare_url":"https://api.github.com/repos/onigoetz/http/compare/{base}...{head}","merges_url":"https://api.github.com/repos/onigoetz/http/merges","archive_url":"https://api.github.com/repos/onigoetz/http/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/onigoetz/http/downloads","issues_url":"https://api.github.com/repos/onigoetz/http/issues{/number}","pulls_url":"https://api.github.com/repos/onigoetz/http/pulls{/number}","milestones_url":"https://api.github.com/repos/onigoetz/http/milestones{/number}","notifications_url":"https://api.github.com/repos/onigoetz/http/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/onigoetz/http/labels{/name}","releases_url":"https://api.github.com/repos/onigoetz/http/releases{/id}","created_at":"2014-10-08T21:00:03Z","updated_at":"2015-01-02T10:58:08Z","pushed_at":"2014-10-08T21:01:02Z","git_url":"git://github.com/onigoetz/http.git","ssh_url":"git@github.com:onigoetz/http.git","clone_url":"https://github.com/onigoetz/http.git","svn_url":"https://github.com/onigoetz/http","homepage":"","size":154,"stargazers_count":1,"watchers_count":1,"language":"PHP","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"reactphp:master","ref":"master","sha":"e11296d3f58d9335d23efbaaaa9c73f2afd628e8","user":{"login":"reactphp","id":1696866,"avatar_url":"https://avatars.githubusercontent.com/u/1696866?v=3","gravatar_id":"","url":"https://api.github.com/users/reactphp","html_url":"https://github.com/reactphp","followers_url":"https://api.github.com/users/reactphp/followers","following_url":"https://api.github.com/users/reactphp/following{/other_user}","gists_url":"https://api.github.com/users/reactphp/gists{/gist_id}","starred_url":"https://api.github.com/users/reactphp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/reactphp/subscriptions","organizations_url":"https://api.github.com/users/reactphp/orgs","repos_url":"https://api.github.com/users/reactphp/repos","events_url":"https://api.github.com/users/reactphp/events{/privacy}","received_events_url":"https://api.github.com/users/reactphp/received_events","type":"Organization","site_admin":false},"repo":{"id":4287609,"name":"http","full_name":"reactphp/http","owner":{"login":"reactphp","id":1696866,"avatar_url":"https://avatars.githubusercontent.com/u/1696866?v=3","gravatar_id":"","url":"https://api.github.com/users/reactphp","html_url":"https://github.com/reactphp","followers_url":"https://api.github.com/users/reactphp/followers","following_url":"https://api.github.com/users/reactphp/following{/other_user}","gists_url":"https://api.github.com/users/reactphp/gists{/gist_id}","starred_url":"https://api.github.com/users/reactphp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/reactphp/subscriptions","organizations_url":"https://api.github.com/users/reactphp/orgs","repos_url":"https://api.github.com/users/reactphp/repos","events_url":"https://api.github.com/users/reactphp/events{/privacy}","received_events_url":"https://api.github.com/users/reactphp/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/reactphp/http","description":"React HTTP server (unstable)","fork":false,"url":"https://api.github.com/repos/reactphp/http","forks_url":"https://api.github.com/repos/reactphp/http/forks","keys_url":"https://api.github.com/repos/reactphp/http/keys{/key_id}","collaborators_url":"https://api.github.com/repos/reactphp/http/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/reactphp/http/teams","hooks_url":"https://api.github.com/repos/reactphp/http/hooks","issue_events_url":"https://api.github.com/repos/reactphp/http/issues/events{/number}","events_url":"https://api.github.com/repos/reactphp/http/events","assignees_url":"https://api.github.com/repos/reactphp/http/assignees{/user}","branches_url":"https://api.github.com/repos/reactphp/http/branches{/branch}","tags_url":"https://api.github.com/repos/reactphp/http/tags","blobs_url":"https://api.github.com/repos/reactphp/http/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/reactphp/http/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/reactphp/http/git/refs{/sha}","trees_url":"https://api.github.com/repos/reactphp/http/git/trees{/sha}","statuses_url":"https://api.github.com/repos/reactphp/http/statuses/{sha}","languages_url":"https://api.github.com/repos/reactphp/http/languages","stargazers_url":"https://api.github.com/repos/reactphp/http/stargazers","contributors_url":"https://api.github.com/repos/reactphp/http/contributors","subscribers_url":"https://api.github.com/repos/reactphp/http/subscribers","subscription_url":"https://api.github.com/repos/reactphp/http/subscription","commits_url":"https://api.github.com/repos/reactphp/http/commits{/sha}","git_commits_url":"https://api.github.com/repos/reactphp/http/git/commits{/sha}","comments_url":"https://api.github.com/repos/reactphp/http/comments{/number}","issue_comment_url":"https://api.github.com/repos/reactphp/http/issues/comments{/number}","contents_url":"https://api.github.com/repos/reactphp/http/contents/{+path}","compare_url":"https://api.github.com/repos/reactphp/http/compare/{base}...{head}","merges_url":"https://api.github.com/repos/reactphp/http/merges","archive_url":"https://api.github.com/repos/reactphp/http/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/reactphp/http/downloads","issues_url":"https://api.github.com/repos/reactphp/http/issues{/number}","pulls_url":"https://api.github.com/repos/reactphp/http/pulls{/number}","milestones_url":"https://api.github.com/repos/reactphp/http/milestones{/number}","notifications_url":"https://api.github.com/repos/reactphp/http/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/reactphp/http/labels{/name}","releases_url":"https://api.github.com/repos/reactphp/http/releases{/id}","created_at":"2012-05-10T17:31:28Z","updated_at":"2015-02-13T23:30:14Z","pushed_at":"2014-12-04T12:54:50Z","git_url":"git://github.com/reactphp/http.git","ssh_url":"git@github.com:reactphp/http.git","clone_url":"https://github.com/reactphp/http.git","svn_url":"https://github.com/reactphp/http","homepage":"","size":360,"stargazers_count":26,"watchers_count":26,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":19,"mirror_url":null,"open_issues_count":10,"forks":19,"open_issues":10,"watchers":26,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/reactphp/http/pulls/13"},"html":{"href":"https://github.com/reactphp/http/pull/13"},"issue":{"href":"https://api.github.com/repos/reactphp/http/issues/13"},"comments":{"href":"https://api.github.com/repos/reactphp/http/issues/13/comments"},"review_comments":{"href":"https://api.github.com/repos/reactphp/http/pulls/13/comments"},"review_comment":{"href":"https://api.github.com/repos/reactphp/http/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/reactphp/http/pulls/13/commits"},"statuses":{"href":"https://api.github.com/repos/reactphp/http/statuses/244543041d3c1e39b76cd948aa231e2d41b3b252"}}}}