{"action":"created","comment":{"url":"https://api.github.com/repos/c5t/Current/pulls/comments/49856981","id":49856981,"diff_hunk":"@@ -0,0 +1,229 @@\n+/*******************************************************************************\n+The MIT License (MIT)\n+\n+Copyright (c) 2015 Maxim Zhurovich <zhurovich@gmail.com>\n+\n+Permission is hereby granted, free of charge, to any person obtaining a copy\n+of this software and associated documentation files (the \"Software\"), to deal\n+in the Software without restriction, including without limitation the rights\n+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n+copies of the Software, and to permit persons to whom the Software is\n+furnished to do so, subject to the following conditions:\n+\n+The above copyright notice and this permission notice shall be included in all\n+copies or substantial portions of the Software.\n+\n+THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+SOFTWARE.\n+*******************************************************************************/\n+\n+#ifndef CURRENT_MIDICHLORIANS_SERVER_H\n+#define CURRENT_MIDICHLORIANS_SERVER_H\n+\n+#include \"schema.h\"\n+\n+#include \"../../TypeSystem/Serialization/json.h\"\n+\n+#include \"../../Blocks/HTTP/api.h\"\n+#include \"../../Bricks/strings/strings.h\"\n+#include \"../../Bricks/time/chrono.h\"\n+\n+namespace current {\n+namespace midichlorians {\n+namespace server {\n+\n+template <class LOG_ENTRY_CONSUMER>\n+class MidichloriansHTTPServer {\n+ public:\n+  MidichloriansHTTPServer(int http_port,\n+                          LOG_ENTRY_CONSUMER& log_entry_consumer,\n+                          std::chrono::microseconds tick_interval_us,\n+                          const std::string& route = \"/log\",\n+                          const std::string& response_text = \"OK\\n\")\n+      : http_port_(http_port),\n+        log_entry_consumer_(log_entry_consumer),\n+        route_(route),\n+        response_text_(response_text),\n+        tick_interval_us_(tick_interval_us),\n+        send_ticks_(tick_interval_us_.count() > 0),\n+        last_event_t_(0u),\n+        events_pushed_(0u),\n+        timer_thread_(&MidichloriansHTTPServer::TimerThreadFunction, this),\n+        routes_(HTTP(http_port_).Register(route_, [this](Request r) { ProcessRequest(std::move(r)); })) {}\n+\n+  ~MidichloriansHTTPServer() {\n+    send_ticks_ = false;\n+    timer_thread_.join();\n+  }\n+\n+  void Join() { HTTP(http_port_).Join(); }\n+\n+  size_t EventsPushed() const { return events_pushed_; }\n+\n+ private:\n+  void ProcessRequest(Request r) {\n+    {\n+      std::lock_guard<std::mutex> lock(mutex_);\n+      if (r.method == \"POST\" && !r.body.empty()) {\n+        // POST request could be either iOS or web event.\n+        // In case of iOS there could be a bunch of events in the body.\n+        std::istringstream iss(r.body);\n+        std::string line;\n+        while (std::getline(iss, line)) {\n+          if (!BodyLineAsiOSEvent(line)) {\n+            if (!RequestAsWebEvent(r)) {\n+              LogInvalidRequest(r);\n+            }\n+          }\n+        }\n+      } else if (r.method == \"GET\" || r.method == \"HEAD\") {\n+        // GET and HEAD requests could be only web events.\n+        if (!RequestAsWebEvent(r)) {\n+          LogInvalidRequest(r);\n+        }\n+      } else {\n+        LogInvalidRequest(r);\n+      }\n+    }\n+    r(response_text_);\n+  }\n+\n+  bool BodyLineAsiOSEvent(const std::string& line) {\n+    using namespace current::midichlorians::ios;\n+\n+    Variant<T_IOS_EVENTS> ios_event;\n+    try {\n+      ios_event = ParseJSON<Variant<T_IOS_EVENTS>>(line);\n+    } catch (const InvalidJSONException&) {\n+      return false;\n+    }\n+\n+    PassLogEntryToConsumer(EventLogEntry(current::time::Now(), std::move(ios_event)));\n+    return true;\n+  }\n+\n+  bool RequestAsWebEvent(const Request& r) {\n+    using namespace current::midichlorians::web;\n+\n+    std::map<std::string, std::string> post_q;  // Query parameters extracted from POST body.\n+    const std::map<std::string, std::string>* q;\n+    const std::map<std::string, std::string>* h = &r.headers;\n+\n+    if (r.method == \"GET\" || r.method == \"HEAD\") {\n+      q = &r.url.AllQueryParameters();\n+    } else if (r.method == \"POST\") {\n+      post_q = blocks::impl::URLParametersExtractor(\"?\" + r.body).AllQueryParameters();\n+      for (const auto& cit : r.url.AllQueryParameters()) {\n+        post_q.insert(cit);\n+      }\n+      q = &post_q;\n+    } else {\n+      return false;\n+    }\n+\n+    Variant<T_WEB_EVENTS> web_event;\n+    try {\n+      if (q->at(\"ea\") == \"En\") {\n+        web_event = WebEnterEvent();\n+      } else if (q->at(\"ea\") == \"Ex\") {\n+        web_event = WebExitEvent();\n+      } else if (q->at(\"ea\") == \"Fg\") {\n+        web_event = WebForegroundEvent();\n+      } else if (q->at(\"ea\") == \"Bg\") {\n+        web_event = WebBackgroundEvent();\n+      } else {\n+        web_event = WebGenericEvent(q->at(\"ec\"), q->at(\"ea\"));\n+      }\n+    } catch (const std::out_of_range&) {\n+      return false;\n+    }\n+\n+    if (!ExtractWebBaseEventFields(q, h, web_event)) {\n+      return false;\n+    }\n+\n+    PassLogEntryToConsumer(EventLogEntry(current::time::Now(), std::move(web_event)));\n+    return true;\n+  }\n+\n+  template <typename T_LOG_ENTRY>\n+  void PassLogEntryToConsumer(T_LOG_ENTRY&& entry, bool is_valid_entry = true) {\n+    T_LOG_ENTRY_VARIANT entry_variant(std::move(entry));\n+    entry_variant.Call(log_entry_consumer_);\n+    if (is_valid_entry) {\n+      ++events_pushed_;\n+      last_event_t_ = Value<LogEntryBase>(entry_variant).server_us;\n+    }\n+  }\n+\n+  bool ExtractWebBaseEventFields(const std::map<std::string, std::string>* q,\n+                                 const std::map<std::string, std::string>* h,\n+                                 Variant<T_WEB_EVENTS>& web_event) {\n+    using namespace current::midichlorians::web;\n+\n+    assert(q);","path":"Midichlorians/Server/server.h","position":169,"original_position":169,"commit_id":"38cf7f02db51affef3cf29a5c80f8c8ea61c47e9","original_commit_id":"38cf7f02db51affef3cf29a5c80f8c8ea61c47e9","user":{"login":"mzhurovich","id":11226961,"avatar_url":"https://avatars.githubusercontent.com/u/11226961?v=3","gravatar_id":"","url":"https://api.github.com/users/mzhurovich","html_url":"https://github.com/mzhurovich","followers_url":"https://api.github.com/users/mzhurovich/followers","following_url":"https://api.github.com/users/mzhurovich/following{/other_user}","gists_url":"https://api.github.com/users/mzhurovich/gists{/gist_id}","starred_url":"https://api.github.com/users/mzhurovich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mzhurovich/subscriptions","organizations_url":"https://api.github.com/users/mzhurovich/orgs","repos_url":"https://api.github.com/users/mzhurovich/repos","events_url":"https://api.github.com/users/mzhurovich/events{/privacy}","received_events_url":"https://api.github.com/users/mzhurovich/received_events","type":"User","site_admin":false},"body":":+1: ","created_at":"2016-01-15T14:17:46Z","updated_at":"2016-01-15T14:17:46Z","html_url":"https://github.com/c5t/Current/pull/338#discussion_r49856981","pull_request_url":"https://api.github.com/repos/c5t/Current/pulls/338","_links":{"self":{"href":"https://api.github.com/repos/c5t/Current/pulls/comments/49856981"},"html":{"href":"https://github.com/c5t/Current/pull/338#discussion_r49856981"},"pull_request":{"href":"https://api.github.com/repos/c5t/Current/pulls/338"}}},"pull_request":{"url":"https://api.github.com/repos/c5t/Current/pulls/338","id":55811350,"html_url":"https://github.com/c5t/Current/pull/338","diff_url":"https://github.com/c5t/Current/pull/338.diff","patch_url":"https://github.com/c5t/Current/pull/338.patch","issue_url":"https://api.github.com/repos/c5t/Current/issues/338","number":338,"state":"open","locked":false,"title":"Midichlorians refactoring + event server.","user":{"login":"mzhurovich","id":11226961,"avatar_url":"https://avatars.githubusercontent.com/u/11226961?v=3","gravatar_id":"","url":"https://api.github.com/users/mzhurovich","html_url":"https://github.com/mzhurovich","followers_url":"https://api.github.com/users/mzhurovich/followers","following_url":"https://api.github.com/users/mzhurovich/following{/other_user}","gists_url":"https://api.github.com/users/mzhurovich/gists{/gist_id}","starred_url":"https://api.github.com/users/mzhurovich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mzhurovich/subscriptions","organizations_url":"https://api.github.com/users/mzhurovich/orgs","repos_url":"https://api.github.com/users/mzhurovich/repos","events_url":"https://api.github.com/users/mzhurovich/events{/privacy}","received_events_url":"https://api.github.com/users/mzhurovich/received_events","type":"User","site_admin":false},"body":"@dkorolev PTAL","created_at":"2016-01-12T20:21:15Z","updated_at":"2016-01-15T14:17:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":"44b6d3fb5fac6bd4218e4f5d64edd616c55044c2","assignee":{"login":"dkorolev","id":2159447,"avatar_url":"https://avatars.githubusercontent.com/u/2159447?v=3","gravatar_id":"","url":"https://api.github.com/users/dkorolev","html_url":"https://github.com/dkorolev","followers_url":"https://api.github.com/users/dkorolev/followers","following_url":"https://api.github.com/users/dkorolev/following{/other_user}","gists_url":"https://api.github.com/users/dkorolev/gists{/gist_id}","starred_url":"https://api.github.com/users/dkorolev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dkorolev/subscriptions","organizations_url":"https://api.github.com/users/dkorolev/orgs","repos_url":"https://api.github.com/users/dkorolev/repos","events_url":"https://api.github.com/users/dkorolev/events{/privacy}","received_events_url":"https://api.github.com/users/dkorolev/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/c5t/Current/pulls/338/commits","review_comments_url":"https://api.github.com/repos/c5t/Current/pulls/338/comments","review_comment_url":"https://api.github.com/repos/c5t/Current/pulls/comments{/number}","comments_url":"https://api.github.com/repos/c5t/Current/issues/338/comments","statuses_url":"https://api.github.com/repos/c5t/Current/statuses/38cf7f02db51affef3cf29a5c80f8c8ea61c47e9","head":{"label":"mzhurovich:midichlorians","ref":"midichlorians","sha":"38cf7f02db51affef3cf29a5c80f8c8ea61c47e9","user":{"login":"mzhurovich","id":11226961,"avatar_url":"https://avatars.githubusercontent.com/u/11226961?v=3","gravatar_id":"","url":"https://api.github.com/users/mzhurovich","html_url":"https://github.com/mzhurovich","followers_url":"https://api.github.com/users/mzhurovich/followers","following_url":"https://api.github.com/users/mzhurovich/following{/other_user}","gists_url":"https://api.github.com/users/mzhurovich/gists{/gist_id}","starred_url":"https://api.github.com/users/mzhurovich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mzhurovich/subscriptions","organizations_url":"https://api.github.com/users/mzhurovich/orgs","repos_url":"https://api.github.com/users/mzhurovich/repos","events_url":"https://api.github.com/users/mzhurovich/events{/privacy}","received_events_url":"https://api.github.com/users/mzhurovich/received_events","type":"User","site_admin":false},"repo":{"id":31462826,"name":"Current","full_name":"mzhurovich/Current","owner":{"login":"mzhurovich","id":11226961,"avatar_url":"https://avatars.githubusercontent.com/u/11226961?v=3","gravatar_id":"","url":"https://api.github.com/users/mzhurovich","html_url":"https://github.com/mzhurovich","followers_url":"https://api.github.com/users/mzhurovich/followers","following_url":"https://api.github.com/users/mzhurovich/following{/other_user}","gists_url":"https://api.github.com/users/mzhurovich/gists{/gist_id}","starred_url":"https://api.github.com/users/mzhurovich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mzhurovich/subscriptions","organizations_url":"https://api.github.com/users/mzhurovich/orgs","repos_url":"https://api.github.com/users/mzhurovich/repos","events_url":"https://api.github.com/users/mzhurovich/events{/privacy}","received_events_url":"https://api.github.com/users/mzhurovich/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mzhurovich/Current","description":"Our own core pieces to reuse.","fork":true,"url":"https://api.github.com/repos/mzhurovich/Current","forks_url":"https://api.github.com/repos/mzhurovich/Current/forks","keys_url":"https://api.github.com/repos/mzhurovich/Current/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mzhurovich/Current/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mzhurovich/Current/teams","hooks_url":"https://api.github.com/repos/mzhurovich/Current/hooks","issue_events_url":"https://api.github.com/repos/mzhurovich/Current/issues/events{/number}","events_url":"https://api.github.com/repos/mzhurovich/Current/events","assignees_url":"https://api.github.com/repos/mzhurovich/Current/assignees{/user}","branches_url":"https://api.github.com/repos/mzhurovich/Current/branches{/branch}","tags_url":"https://api.github.com/repos/mzhurovich/Current/tags","blobs_url":"https://api.github.com/repos/mzhurovich/Current/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mzhurovich/Current/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mzhurovich/Current/git/refs{/sha}","trees_url":"https://api.github.com/repos/mzhurovich/Current/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mzhurovich/Current/statuses/{sha}","languages_url":"https://api.github.com/repos/mzhurovich/Current/languages","stargazers_url":"https://api.github.com/repos/mzhurovich/Current/stargazers","contributors_url":"https://api.github.com/repos/mzhurovich/Current/contributors","subscribers_url":"https://api.github.com/repos/mzhurovich/Current/subscribers","subscription_url":"https://api.github.com/repos/mzhurovich/Current/subscription","commits_url":"https://api.github.com/repos/mzhurovich/Current/commits{/sha}","git_commits_url":"https://api.github.com/repos/mzhurovich/Current/git/commits{/sha}","comments_url":"https://api.github.com/repos/mzhurovich/Current/comments{/number}","issue_comment_url":"https://api.github.com/repos/mzhurovich/Current/issues/comments{/number}","contents_url":"https://api.github.com/repos/mzhurovich/Current/contents/{+path}","compare_url":"https://api.github.com/repos/mzhurovich/Current/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mzhurovich/Current/merges","archive_url":"https://api.github.com/repos/mzhurovich/Current/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mzhurovich/Current/downloads","issues_url":"https://api.github.com/repos/mzhurovich/Current/issues{/number}","pulls_url":"https://api.github.com/repos/mzhurovich/Current/pulls{/number}","milestones_url":"https://api.github.com/repos/mzhurovich/Current/milestones{/number}","notifications_url":"https://api.github.com/repos/mzhurovich/Current/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mzhurovich/Current/labels{/name}","releases_url":"https://api.github.com/repos/mzhurovich/Current/releases{/id}","created_at":"2015-02-28T13:03:55Z","updated_at":"2015-05-24T23:39:55Z","pushed_at":"2016-01-12T20:19:43Z","git_url":"git://github.com/mzhurovich/Current.git","ssh_url":"git@github.com:mzhurovich/Current.git","clone_url":"https://github.com/mzhurovich/Current.git","svn_url":"https://github.com/mzhurovich/Current","homepage":null,"size":9579,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"c5t:master","ref":"master","sha":"439dc24a3f5afef6f900a43349f5705dde9bbec5","user":{"login":"c5t","id":12224789,"avatar_url":"https://avatars.githubusercontent.com/u/12224789?v=3","gravatar_id":"","url":"https://api.github.com/users/c5t","html_url":"https://github.com/c5t","followers_url":"https://api.github.com/users/c5t/followers","following_url":"https://api.github.com/users/c5t/following{/other_user}","gists_url":"https://api.github.com/users/c5t/gists{/gist_id}","starred_url":"https://api.github.com/users/c5t/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/c5t/subscriptions","organizations_url":"https://api.github.com/users/c5t/orgs","repos_url":"https://api.github.com/users/c5t/repos","events_url":"https://api.github.com/users/c5t/events{/privacy}","received_events_url":"https://api.github.com/users/c5t/received_events","type":"Organization","site_admin":false},"repo":{"id":27625425,"name":"Current","full_name":"c5t/Current","owner":{"login":"c5t","id":12224789,"avatar_url":"https://avatars.githubusercontent.com/u/12224789?v=3","gravatar_id":"","url":"https://api.github.com/users/c5t","html_url":"https://github.com/c5t","followers_url":"https://api.github.com/users/c5t/followers","following_url":"https://api.github.com/users/c5t/following{/other_user}","gists_url":"https://api.github.com/users/c5t/gists{/gist_id}","starred_url":"https://api.github.com/users/c5t/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/c5t/subscriptions","organizations_url":"https://api.github.com/users/c5t/orgs","repos_url":"https://api.github.com/users/c5t/repos","events_url":"https://api.github.com/users/c5t/events{/privacy}","received_events_url":"https://api.github.com/users/c5t/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/c5t/Current","description":"Data Infrastructure in a Box:","fork":false,"url":"https://api.github.com/repos/c5t/Current","forks_url":"https://api.github.com/repos/c5t/Current/forks","keys_url":"https://api.github.com/repos/c5t/Current/keys{/key_id}","collaborators_url":"https://api.github.com/repos/c5t/Current/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/c5t/Current/teams","hooks_url":"https://api.github.com/repos/c5t/Current/hooks","issue_events_url":"https://api.github.com/repos/c5t/Current/issues/events{/number}","events_url":"https://api.github.com/repos/c5t/Current/events","assignees_url":"https://api.github.com/repos/c5t/Current/assignees{/user}","branches_url":"https://api.github.com/repos/c5t/Current/branches{/branch}","tags_url":"https://api.github.com/repos/c5t/Current/tags","blobs_url":"https://api.github.com/repos/c5t/Current/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/c5t/Current/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/c5t/Current/git/refs{/sha}","trees_url":"https://api.github.com/repos/c5t/Current/git/trees{/sha}","statuses_url":"https://api.github.com/repos/c5t/Current/statuses/{sha}","languages_url":"https://api.github.com/repos/c5t/Current/languages","stargazers_url":"https://api.github.com/repos/c5t/Current/stargazers","contributors_url":"https://api.github.com/repos/c5t/Current/contributors","subscribers_url":"https://api.github.com/repos/c5t/Current/subscribers","subscription_url":"https://api.github.com/repos/c5t/Current/subscription","commits_url":"https://api.github.com/repos/c5t/Current/commits{/sha}","git_commits_url":"https://api.github.com/repos/c5t/Current/git/commits{/sha}","comments_url":"https://api.github.com/repos/c5t/Current/comments{/number}","issue_comment_url":"https://api.github.com/repos/c5t/Current/issues/comments{/number}","contents_url":"https://api.github.com/repos/c5t/Current/contents/{+path}","compare_url":"https://api.github.com/repos/c5t/Current/compare/{base}...{head}","merges_url":"https://api.github.com/repos/c5t/Current/merges","archive_url":"https://api.github.com/repos/c5t/Current/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/c5t/Current/downloads","issues_url":"https://api.github.com/repos/c5t/Current/issues{/number}","pulls_url":"https://api.github.com/repos/c5t/Current/pulls{/number}","milestones_url":"https://api.github.com/repos/c5t/Current/milestones{/number}","notifications_url":"https://api.github.com/repos/c5t/Current/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/c5t/Current/labels{/name}","releases_url":"https://api.github.com/repos/c5t/Current/releases{/id}","created_at":"2014-12-06T06:52:23Z","updated_at":"2016-01-12T20:21:24Z","pushed_at":"2016-01-12T20:21:16Z","git_url":"git://github.com/c5t/Current.git","ssh_url":"git@github.com:c5t/Current.git","clone_url":"https://github.com/c5t/Current.git","svn_url":"https://github.com/c5t/Current","homepage":"https://j.mp/currentintelligence","size":9613,"stargazers_count":23,"watchers_count":23,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":9,"mirror_url":null,"open_issues_count":36,"forks":9,"open_issues":36,"watchers":23,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/c5t/Current/pulls/338"},"html":{"href":"https://github.com/c5t/Current/pull/338"},"issue":{"href":"https://api.github.com/repos/c5t/Current/issues/338"},"comments":{"href":"https://api.github.com/repos/c5t/Current/issues/338/comments"},"review_comments":{"href":"https://api.github.com/repos/c5t/Current/pulls/338/comments"},"review_comment":{"href":"https://api.github.com/repos/c5t/Current/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/c5t/Current/pulls/338/commits"},"statuses":{"href":"https://api.github.com/repos/c5t/Current/statuses/38cf7f02db51affef3cf29a5c80f8c8ea61c47e9"}}}}