<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <sstream>
#include <string>

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "chrome/browser/net/url_request_failed_dns_job.h"
#include "chrome/browser/net/url_request_mock_http_job.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"
#include "net/test/test_server.h"

namespace {

class ResourceDispatcherTest : public UITest {
 public:
  void CheckTitleTest(const std::wstring& file,
                      const std::wstring& expected_title,
                      int expected_navigations) {
    NavigateToURLBlockUntilNavigationsComplete(
        URLRequestMockHTTPJob::GetMockUrl(FilePath::FromWStringHack(file)),
        expected_navigations);
    EXPECT_EQ(expected_title, GetActiveTabTitle());
  }

 protected:
  ResourceDispatcherTest() : UITest() {
    dom_automation_enabled_ = true;
  }
};

TEST_F(ResourceDispatcherTest, SniffHTMLWithNoContentType) {
  CheckTitleTest(L"content-sniffer-test0.html",
                 L"Content Sniffer Test 0", 1);
}

TEST_F(ResourceDispatcherTest, RespectNoSniffDirective) {
  CheckTitleTest(L"nosniff-test.html", L"", 1);
}

TEST_F(ResourceDispatcherTest, DoNotSniffHTMLFromTextPlain) {
  CheckTitleTest(L"content-sniffer-test1.html", L"", 1);
}

TEST_F(ResourceDispatcherTest, DoNotSniffHTMLFromImageGIF) {
  CheckTitleTest(L"content-sniffer-test2.html", L"", 1);
}

TEST_F(ResourceDispatcherTest, SniffNoContentTypeNoData) {
  CheckTitleTest(L"content-sniffer-test3.html",
                 L"Content Sniffer Test 3", 1);
  EXPECT_EQ(1, GetTabCount());

  // Make sure the download shelf is not showing.
  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());
  bool visible = false;
  ASSERT_TRUE(browser->IsShelfVisible(&visible));
  EXPECT_FALSE(visible);
}

TEST_F(ResourceDispatcherTest, ContentDispositionEmpty) {
  CheckTitleTest(L"content-disposition-empty.html", L"success", 1);
}

TEST_F(ResourceDispatcherTest, ContentDispositionInline) {
  CheckTitleTest(L"content-disposition-inline.html", L"success", 1);
}

// Test for bug #1091358.
TEST_F(ResourceDispatcherTest, SyncXMLHttpRequest) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP,
                              FilePath(FILE_PATH_LITERAL("chrome/test/data")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(test_server.GetURL(
                "files/sync_xmlhttprequest.html")));

  // Let's check the XMLHttpRequest ran successfully.
  bool success = false;
  EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
      L"window.domAutomationController.send(DidSyncRequestSucceed());",
      &success));
  EXPECT_TRUE(success);
}

TEST_F(ResourceDispatcherTest, SyncXMLHttpRequest_Disallowed) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP,
                              FilePath(FILE_PATH_LITERAL("chrome/test/data")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(test_server.GetURL(
                "files/sync_xmlhttprequest_disallowed.html")));

  // Let's check the XMLHttpRequest ran successfully.
  bool success = false;
  EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
      L"window.domAutomationController.send(DidSucceed());",
      &success));
  EXPECT_TRUE(success);
}

// Test for bug #1159553 -- A synchronous xhr (whose content-type is
// downloadable) would trigger download and hang the renderer process,
// if executed while navigating to a new page.
TEST_F(ResourceDispatcherTest, SyncXMLHttpRequest_DuringUnload) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP,
                              FilePath(FILE_PATH_LITERAL("chrome/test/data")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(test_server.GetURL(
                "files/sync_xmlhttprequest_during_unload.html")));

  // Confirm that the page has loaded (since it changes its title during load).
  std::wstring tab_title;
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"sync xhr on unload", tab_title);

  // Navigate to a new page, to dispatch unload event and trigger xhr.
  // (the bug would make this step hang the renderer).
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(test_server.GetURL("files/title2.html")));

  // Check that the new page got loaded, and that no download was triggered.
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"Title Of Awesomeness", tab_title);

  bool shelf_is_visible = false;
  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());
  EXPECT_TRUE(browser->IsShelfVisible(&shelf_is_visible));
  EXPECT_FALSE(shelf_is_visible);
}

// Tests that onunload is run for cross-site requests.  (Bug 1114994)
TEST_F(ResourceDispatcherTest, CrossSiteOnunloadCookie) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP,
                              FilePath(FILE_PATH_LITERAL("chrome/test/data")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  GURL url(test_server.GetURL("files/onunload_cookie.html"));
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(url));

  // Confirm that the page has loaded (since it changes its title during load).
  std::wstring tab_title;
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"set cookie on unload", tab_title);

  // Navigate to a new cross-site page, to dispatch unload event and set the
  // cookie.
  CheckTitleTest(L"content-sniffer-test0.html",
                 L"Content Sniffer Test 0", 1);

  // Check that the cookie was set.
  std::string value_result;
  ASSERT_TRUE(tab->GetCookieByName(url, "onunloadCookie", &value_result));
  ASSERT_FALSE(value_result.empty());
  ASSERT_STREQ("foo", value_result.c_str());
}

#if !defined(OS_MACOSX)
#if defined(OS_WIN)
// http://crbug.com/32048
#define CrossSiteAfterCrash FLAKY_CrossSiteAfterCrash
#endif
// Tests that the onbeforeunload and onunload logic is shortcutted if the old
// renderer is gone.  In that case, we don't want to wait for the old renderer
// to run the handlers.
// We need to disable this on Mac because the crash causes the OS CrashReporter
// process to kick in to analyze the poor dead renderer.  Unfortunately, if the
// app isn't stripped of debug symbols, this takes about five minutes to
// complete and isn't conducive to quick turnarounds. As we don't currently
// strip the app on the build bots, this is bad times.
TEST_F(ResourceDispatcherTest, CrossSiteAfterCrash) {
  // This test only works in multi-process mode
  if (in_process_renderer())
    return;

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  // Cause the renderer to crash.
#if defined(OS_WIN) || defined(USE_LINUX_BREAKPAD)
  expected_crashes_ = 1;
#endif
  ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kAboutCrashURL)));
  // Wait for browser to notice the renderer crash.
  PlatformThread::Sleep(sleep_timeout_ms());

  // Navigate to a new cross-site page.  The browser should not wait around for
  // the old renderer's on{before}unload handlers to run.
  CheckTitleTest(L"content-sniffer-test0.html",
                 L"Content Sniffer Test 0", 1);
}
#endif  // !defined(OS_MACOSX)

// Tests that cross-site navigations work when the new page does not go through
// the BufferedEventHandler (e.g., non-http{s} URLs).  (Bug 1225872)
TEST_F(ResourceDispatcherTest, CrossSiteNavigationNonBuffered) {
  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  // Start with an HTTP page.
  CheckTitleTest(L"content-sniffer-test0.html",
                 L"Content Sniffer Test 0", 1);

  // Now load a file:// page, which does not use the BufferedEventHandler.
  // Make sure that the page loads and displays a title, and doesn't get stuck.
  FilePath test_file(test_data_directory_);
  test_file = test_file.AppendASCII("title2.html");
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(net::FilePathToFileURL(test_file)));
  EXPECT_EQ(L"Title Of Awesomeness", GetActiveTabTitle());
}

// Tests that a cross-site navigation to an error page (resulting in the link
// doctor page) still runs the onunload handler and can support navigations
// away from the link doctor page.  (Bug 1235537)
TEST_F(ResourceDispatcherTest, CrossSiteNavigationErrorPage) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP,
                              FilePath(FILE_PATH_LITERAL("chrome/test/data")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  GURL url(test_server.GetURL("files/onunload_cookie.html"));
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(url));

  // Confirm that the page has loaded (since it changes its title during load).
  std::wstring tab_title;
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"set cookie on unload", tab_title);

  // Navigate to a new cross-site URL that results in an error page.
  // TODO(creis): If this causes crashes or hangs, it might be for the same
  // reason as ErrorPageTest::DNSError.  See bug 1199491 and
  // http://crbug.com/22877.
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURLBlockUntilNavigationsComplete(
                GURL(URLRequestFailedDnsJob::kTestUrl), 2));
  EXPECT_NE(L"set cookie on unload", GetActiveTabTitle());

  // Check that the cookie was set, meaning that the onunload handler ran.
  std::string value_result;
  EXPECT_TRUE(tab->GetCookieByName(url, "onunloadCookie", &value_result));
  EXPECT_FALSE(value_result.empty());
  EXPECT_STREQ("foo", value_result.c_str());

  // Check that renderer-initiated navigations still work.  In a previous bug,
  // the ResourceDispatcherHost would think that such navigations were
  // cross-site, because we didn't clean up from the previous request.  Since
  // TabContents was in the NORMAL state, it would ignore the attempt to run
  // the onunload handler, and the navigation would fail.
  // (Test by redirecting to javascript:window.location='someURL'.)
  GURL test_url(test_server.GetURL("files/title2.html"));
  std::string redirect_url = "javascript:window.location='" +
      test_url.possibly_invalid_spec() + "'";
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(GURL(redirect_url)));
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"Title Of Awesomeness", tab_title);
}

TEST_F(ResourceDispatcherTest, CrossOriginRedirectBlocked) {
  // We expect the following URL requests from this test:
  // 1-  http://mock.http/cross-origin-redirect-blocked.html
  // 2-  http://mock.http/redirect-to-title2.html
  // 3-  http://mock.http/title2.html
  //
  // If the redirect in #2 were not blocked, we'd also see a request
  // for http://mock.http:4000/title2.html, and the title would be different.
  CheckTitleTest(L"cross-origin-redirect-blocked.html",
                 L"Title Of More Awesomeness", 2);
}

// Tests that ResourceDispatcherHostRequestInfo is updated correctly on failed
// requests, to prevent calling Read on a request that has already failed.
// See bug 40250.
TEST_F(ResourceDispatcherTest, CrossSiteFailedRequest) {
  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  // Visit another URL first to trigger a cross-site navigation.
  GURL url(chrome::kChromeUINewTabURL);
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(url));

  // Visit a URL that fails without calling ResourceDispatcherHost::Read.
  GURL broken_url("chrome://theme");
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(broken_url));

  // Make sure the navigation finishes.
  std::wstring tab_title;
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"chrome://theme/ is not available", tab_title);
}

}  // namespace
<commit_msg>Disable ResourceDispatcherTest.SyncXMLHttpRequest_DuringUnload.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <sstream>
#include <string>

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "chrome/browser/net/url_request_failed_dns_job.h"
#include "chrome/browser/net/url_request_mock_http_job.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"
#include "net/test/test_server.h"

namespace {

class ResourceDispatcherTest : public UITest {
 public:
  void CheckTitleTest(const std::wstring& file,
                      const std::wstring& expected_title,
                      int expected_navigations) {
    NavigateToURLBlockUntilNavigationsComplete(
        URLRequestMockHTTPJob::GetMockUrl(FilePath::FromWStringHack(file)),
        expected_navigations);
    EXPECT_EQ(expected_title, GetActiveTabTitle());
  }

 protected:
  ResourceDispatcherTest() : UITest() {
    dom_automation_enabled_ = true;
  }
};

TEST_F(ResourceDispatcherTest, SniffHTMLWithNoContentType) {
  CheckTitleTest(L"content-sniffer-test0.html",
                 L"Content Sniffer Test 0", 1);
}

TEST_F(ResourceDispatcherTest, RespectNoSniffDirective) {
  CheckTitleTest(L"nosniff-test.html", L"", 1);
}

TEST_F(ResourceDispatcherTest, DoNotSniffHTMLFromTextPlain) {
  CheckTitleTest(L"content-sniffer-test1.html", L"", 1);
}

TEST_F(ResourceDispatcherTest, DoNotSniffHTMLFromImageGIF) {
  CheckTitleTest(L"content-sniffer-test2.html", L"", 1);
}

TEST_F(ResourceDispatcherTest, SniffNoContentTypeNoData) {
  CheckTitleTest(L"content-sniffer-test3.html",
                 L"Content Sniffer Test 3", 1);
  EXPECT_EQ(1, GetTabCount());

  // Make sure the download shelf is not showing.
  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());
  bool visible = false;
  ASSERT_TRUE(browser->IsShelfVisible(&visible));
  EXPECT_FALSE(visible);
}

TEST_F(ResourceDispatcherTest, ContentDispositionEmpty) {
  CheckTitleTest(L"content-disposition-empty.html", L"success", 1);
}

TEST_F(ResourceDispatcherTest, ContentDispositionInline) {
  CheckTitleTest(L"content-disposition-inline.html", L"success", 1);
}

// Test for bug #1091358.
TEST_F(ResourceDispatcherTest, SyncXMLHttpRequest) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP,
                              FilePath(FILE_PATH_LITERAL("chrome/test/data")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(test_server.GetURL(
                "files/sync_xmlhttprequest.html")));

  // Let's check the XMLHttpRequest ran successfully.
  bool success = false;
  EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
      L"window.domAutomationController.send(DidSyncRequestSucceed());",
      &success));
  EXPECT_TRUE(success);
}

TEST_F(ResourceDispatcherTest, SyncXMLHttpRequest_Disallowed) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP,
                              FilePath(FILE_PATH_LITERAL("chrome/test/data")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(test_server.GetURL(
                "files/sync_xmlhttprequest_disallowed.html")));

  // Let's check the XMLHttpRequest ran successfully.
  bool success = false;
  EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
      L"window.domAutomationController.send(DidSucceed());",
      &success));
  EXPECT_TRUE(success);
}

// Test for bug #1159553 -- A synchronous xhr (whose content-type is
// downloadable) would trigger download and hang the renderer process,
// if executed while navigating to a new page.
// Disabled -- http://code.google.com/p/chromium/issues/detail?id=56264
TEST_F(ResourceDispatcherTest, DISABLED_SyncXMLHttpRequest_DuringUnload) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP,
                              FilePath(FILE_PATH_LITERAL("chrome/test/data")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(test_server.GetURL(
                "files/sync_xmlhttprequest_during_unload.html")));

  // Confirm that the page has loaded (since it changes its title during load).
  std::wstring tab_title;
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"sync xhr on unload", tab_title);

  // Navigate to a new page, to dispatch unload event and trigger xhr.
  // (the bug would make this step hang the renderer).
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(test_server.GetURL("files/title2.html")));

  // Check that the new page got loaded, and that no download was triggered.
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"Title Of Awesomeness", tab_title);

  bool shelf_is_visible = false;
  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());
  EXPECT_TRUE(browser->IsShelfVisible(&shelf_is_visible));
  EXPECT_FALSE(shelf_is_visible);
}

// Tests that onunload is run for cross-site requests.  (Bug 1114994)
TEST_F(ResourceDispatcherTest, CrossSiteOnunloadCookie) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP,
                              FilePath(FILE_PATH_LITERAL("chrome/test/data")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  GURL url(test_server.GetURL("files/onunload_cookie.html"));
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(url));

  // Confirm that the page has loaded (since it changes its title during load).
  std::wstring tab_title;
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"set cookie on unload", tab_title);

  // Navigate to a new cross-site page, to dispatch unload event and set the
  // cookie.
  CheckTitleTest(L"content-sniffer-test0.html",
                 L"Content Sniffer Test 0", 1);

  // Check that the cookie was set.
  std::string value_result;
  ASSERT_TRUE(tab->GetCookieByName(url, "onunloadCookie", &value_result));
  ASSERT_FALSE(value_result.empty());
  ASSERT_STREQ("foo", value_result.c_str());
}

#if !defined(OS_MACOSX)
#if defined(OS_WIN)
// http://crbug.com/32048
#define CrossSiteAfterCrash FLAKY_CrossSiteAfterCrash
#endif
// Tests that the onbeforeunload and onunload logic is shortcutted if the old
// renderer is gone.  In that case, we don't want to wait for the old renderer
// to run the handlers.
// We need to disable this on Mac because the crash causes the OS CrashReporter
// process to kick in to analyze the poor dead renderer.  Unfortunately, if the
// app isn't stripped of debug symbols, this takes about five minutes to
// complete and isn't conducive to quick turnarounds. As we don't currently
// strip the app on the build bots, this is bad times.
TEST_F(ResourceDispatcherTest, CrossSiteAfterCrash) {
  // This test only works in multi-process mode
  if (in_process_renderer())
    return;

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  // Cause the renderer to crash.
#if defined(OS_WIN) || defined(USE_LINUX_BREAKPAD)
  expected_crashes_ = 1;
#endif
  ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kAboutCrashURL)));
  // Wait for browser to notice the renderer crash.
  PlatformThread::Sleep(sleep_timeout_ms());

  // Navigate to a new cross-site page.  The browser should not wait around for
  // the old renderer's on{before}unload handlers to run.
  CheckTitleTest(L"content-sniffer-test0.html",
                 L"Content Sniffer Test 0", 1);
}
#endif  // !defined(OS_MACOSX)

// Tests that cross-site navigations work when the new page does not go through
// the BufferedEventHandler (e.g., non-http{s} URLs).  (Bug 1225872)
TEST_F(ResourceDispatcherTest, CrossSiteNavigationNonBuffered) {
  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  // Start with an HTTP page.
  CheckTitleTest(L"content-sniffer-test0.html",
                 L"Content Sniffer Test 0", 1);

  // Now load a file:// page, which does not use the BufferedEventHandler.
  // Make sure that the page loads and displays a title, and doesn't get stuck.
  FilePath test_file(test_data_directory_);
  test_file = test_file.AppendASCII("title2.html");
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(net::FilePathToFileURL(test_file)));
  EXPECT_EQ(L"Title Of Awesomeness", GetActiveTabTitle());
}

// Tests that a cross-site navigation to an error page (resulting in the link
// doctor page) still runs the onunload handler and can support navigations
// away from the link doctor page.  (Bug 1235537)
TEST_F(ResourceDispatcherTest, CrossSiteNavigationErrorPage) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP,
                              FilePath(FILE_PATH_LITERAL("chrome/test/data")));
  ASSERT_TRUE(test_server.Start());

  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  GURL url(test_server.GetURL("files/onunload_cookie.html"));
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(url));

  // Confirm that the page has loaded (since it changes its title during load).
  std::wstring tab_title;
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"set cookie on unload", tab_title);

  // Navigate to a new cross-site URL that results in an error page.
  // TODO(creis): If this causes crashes or hangs, it might be for the same
  // reason as ErrorPageTest::DNSError.  See bug 1199491 and
  // http://crbug.com/22877.
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURLBlockUntilNavigationsComplete(
                GURL(URLRequestFailedDnsJob::kTestUrl), 2));
  EXPECT_NE(L"set cookie on unload", GetActiveTabTitle());

  // Check that the cookie was set, meaning that the onunload handler ran.
  std::string value_result;
  EXPECT_TRUE(tab->GetCookieByName(url, "onunloadCookie", &value_result));
  EXPECT_FALSE(value_result.empty());
  EXPECT_STREQ("foo", value_result.c_str());

  // Check that renderer-initiated navigations still work.  In a previous bug,
  // the ResourceDispatcherHost would think that such navigations were
  // cross-site, because we didn't clean up from the previous request.  Since
  // TabContents was in the NORMAL state, it would ignore the attempt to run
  // the onunload handler, and the navigation would fail.
  // (Test by redirecting to javascript:window.location='someURL'.)
  GURL test_url(test_server.GetURL("files/title2.html"));
  std::string redirect_url = "javascript:window.location='" +
      test_url.possibly_invalid_spec() + "'";
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
            tab->NavigateToURL(GURL(redirect_url)));
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"Title Of Awesomeness", tab_title);
}

TEST_F(ResourceDispatcherTest, CrossOriginRedirectBlocked) {
  // We expect the following URL requests from this test:
  // 1-  http://mock.http/cross-origin-redirect-blocked.html
  // 2-  http://mock.http/redirect-to-title2.html
  // 3-  http://mock.http/title2.html
  //
  // If the redirect in #2 were not blocked, we'd also see a request
  // for http://mock.http:4000/title2.html, and the title would be different.
  CheckTitleTest(L"cross-origin-redirect-blocked.html",
                 L"Title Of More Awesomeness", 2);
}

// Tests that ResourceDispatcherHostRequestInfo is updated correctly on failed
// requests, to prevent calling Read on a request that has already failed.
// See bug 40250.
TEST_F(ResourceDispatcherTest, CrossSiteFailedRequest) {
  scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser_proxy.get());
  scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
  ASSERT_TRUE(tab.get());

  // Visit another URL first to trigger a cross-site navigation.
  GURL url(chrome::kChromeUINewTabURL);
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(url));

  // Visit a URL that fails without calling ResourceDispatcherHost::Read.
  GURL broken_url("chrome://theme");
  ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(broken_url));

  // Make sure the navigation finishes.
  std::wstring tab_title;
  EXPECT_TRUE(tab->GetTabTitle(&tab_title));
  EXPECT_EQ(L"chrome://theme/ is not available", tab_title);
}

}  // namespace
<|endoftext|>


<commit_before>#ifndef INCLUDED_SROOK_MPL_CONSTANT_SEQUENCE_ALGORITHM_MERGE_HPP
#define INCLUDED_SROOK_MPL_CONSTANT_SEQUENCE_ALGORITHM_MERGE_HPP
#include<srook/mpl/constant_sequence/algorithm/sort.hpp>
#include<srook/mpl/constant_sequence/algorithm/first.hpp>
#include<srook/mpl/constant_sequence/algorithm/concat.hpp>

namespace srook{
inline namespace mpl{
namespace constant_sequence{
inline namespace v1{

template<bool,class,class,template<std::size_t,std::size_t>class>
struct merge;

template<
	std::size_t head1,std::size_t head_next1,std::size_t... tail1,
	std::size_t head2,std::size_t... tail2,
	template<std::size_t,std::size_t>class comp
>
struct merge<true,std::index_sequence<head1,head_next1,tail1...>,std::index_sequence<head2,tail2...>,comp>{
	using type=
		concat_t<
			std::index_sequence<head1>,
			typename merge<
				comp<head_next1,head2>::value,
				std::index_sequence<head_next1,tail1...>,
				std::index_sequence<head2,tail2...>,
				comp
			>::type
		>;
};

template<
	std::size_t head1,std::size_t... tail1,
	std::size_t head2,std::size_t head_next2,std::size_t... tail2,
	template<std::size_t,std::size_t>class comp
>
struct merge<false,std::index_sequence<head1,tail1...>,std::index_sequence<head2,head_next2,tail2...>,comp>{
	using type=
		concat_t<
			std::index_sequence<head2>,
			typename merge<
				comp<head1,head_next2>::value,
				std::index_sequence<head1,tail1...>,
				std::index_sequence<head_next2,tail2...>,
				comp
			>::type
		>;
};

template<
	std::size_t last1,
	std::size_t prev_last,std::size_t... last2,
	template<std::size_t,std::size_t>class comp
>
struct merge<true,std::index_sequence<last1>,std::index_sequence<prev_last,last2...>,comp>{
	using type=std::index_sequence<last1,prev_last,last2...>;
};

template<
	std::size_t last1,
	std::size_t prev_last,std::size_t prev_next_last2,std::size_t... last2,
	template<std::size_t,std::size_t>class comp
>
struct merge<false,std::index_sequence<last1>,std::index_sequence<prev_last,prev_next_last2,last2...>,comp>{
	using type=
		concat_t<
			std::index_sequence<prev_last>,
			typename merge<
				comp<last1,prev_next_last2>::value,
				std::index_sequence<last1>,
				std::index_sequence<prev_next_last2,last2...>,
				comp
			>::type
		>;
};

/*template<std::size_t last1,std::size_t last2,template<std::size_t,std::size_t>class comp>
struct merge<true,std::index_sequence<last1>,std::index_sequence<last2>,comp>{
	using type=std::index_sequence<last1,last2>;
};

template<std::size_t last1,std::size_t last2,template<std::size_t,std::size_t>class comp>
struct merge<false,std::index_sequence<last1>,std::index_sequence<last2>,comp>{
	using type=std::index_sequence<last2,last1>;
};*/

template<bool b,std::size_t last,template<std::size_t,std::size_t>class comp>
struct merge<b,std::index_sequence<last>,std::index_sequence<>,comp>{
	using type=std::index_sequence<last>;
};

template<std::size_t prev_last,std::size_t next_last,std::size_t... last1,std::size_t last2,template<std::size_t,std::size_t>class comp>
struct merge<true,std::index_sequence<prev_last,next_last,last1...>,std::index_sequence<last2>,comp>{
	using type=
		concat_t<
			std::index_sequence<prev_last>,
			typename merge<
				comp<next_last,last2>::value,
				std::index_sequence<next_last,last1...>,
				std::index_sequence<last2>,
				comp
			>::type
		>;
};

template<std::size_t... last1,std::size_t last2,template<std::size_t,std::size_t>class comp>
struct merge<false,std::index_sequence<last1...>,std::index_sequence<last2>,comp>{
	using type=std::index_sequence<last2,last1...>;
};

template<class Seq1,class Seq2,template<std::size_t,std::size_t>class comp=less>
using merge_t=
	typename merge<
		(Seq1::size()<Seq2::size())?comp<first_v<Seq1>,first_v<Seq2>>::value:comp<first_v<Seq2>,first_v<Seq1>>::value,
		std::conditional_t<(Seq1::size()<Seq2::size()),Seq1,Seq2>,
		std::conditional_t<(Seq1::size()<Seq2::size()),Seq2,Seq1>,
		comp
	>::type;

} // inline namespace v1
} // inline namespace constant_sequence
} // inline namespace mpl
} // namespace srook
#endif
<commit_msg>remove comment<commit_after>#ifndef INCLUDED_SROOK_MPL_CONSTANT_SEQUENCE_ALGORITHM_MERGE_HPP
#define INCLUDED_SROOK_MPL_CONSTANT_SEQUENCE_ALGORITHM_MERGE_HPP
#include<srook/mpl/constant_sequence/algorithm/sort.hpp>
#include<srook/mpl/constant_sequence/algorithm/first.hpp>
#include<srook/mpl/constant_sequence/algorithm/concat.hpp>

namespace srook{
inline namespace mpl{
namespace constant_sequence{
inline namespace v1{

template<bool,class,class,template<std::size_t,std::size_t>class>
struct merge;

template<
	std::size_t head1,std::size_t head_next1,std::size_t... tail1,
	std::size_t head2,std::size_t... tail2,
	template<std::size_t,std::size_t>class comp
>
struct merge<true,std::index_sequence<head1,head_next1,tail1...>,std::index_sequence<head2,tail2...>,comp>{
	using type=
		concat_t<
			std::index_sequence<head1>,
			typename merge<
				comp<head_next1,head2>::value,
				std::index_sequence<head_next1,tail1...>,
				std::index_sequence<head2,tail2...>,
				comp
			>::type
		>;
};

template<
	std::size_t head1,std::size_t... tail1,
	std::size_t head2,std::size_t head_next2,std::size_t... tail2,
	template<std::size_t,std::size_t>class comp
>
struct merge<false,std::index_sequence<head1,tail1...>,std::index_sequence<head2,head_next2,tail2...>,comp>{
	using type=
		concat_t<
			std::index_sequence<head2>,
			typename merge<
				comp<head1,head_next2>::value,
				std::index_sequence<head1,tail1...>,
				std::index_sequence<head_next2,tail2...>,
				comp
			>::type
		>;
};

template<
	std::size_t last1,
	std::size_t prev_last,std::size_t... last2,
	template<std::size_t,std::size_t>class comp
>
struct merge<true,std::index_sequence<last1>,std::index_sequence<prev_last,last2...>,comp>{
	using type=std::index_sequence<last1,prev_last,last2...>;
};

template<
	std::size_t last1,
	std::size_t prev_last,std::size_t prev_next_last2,std::size_t... last2,
	template<std::size_t,std::size_t>class comp
>
struct merge<false,std::index_sequence<last1>,std::index_sequence<prev_last,prev_next_last2,last2...>,comp>{
	using type=
		concat_t<
			std::index_sequence<prev_last>,
			typename merge<
				comp<last1,prev_next_last2>::value,
				std::index_sequence<last1>,
				std::index_sequence<prev_next_last2,last2...>,
				comp
			>::type
		>;
};

template<bool b,std::size_t last,template<std::size_t,std::size_t>class comp>
struct merge<b,std::index_sequence<last>,std::index_sequence<>,comp>{
	using type=std::index_sequence<last>;
};

template<std::size_t prev_last,std::size_t next_last,std::size_t... last1,std::size_t last2,template<std::size_t,std::size_t>class comp>
struct merge<true,std::index_sequence<prev_last,next_last,last1...>,std::index_sequence<last2>,comp>{
	using type=
		concat_t<
			std::index_sequence<prev_last>,
			typename merge<
				comp<next_last,last2>::value,
				std::index_sequence<next_last,last1...>,
				std::index_sequence<last2>,
				comp
			>::type
		>;
};

template<std::size_t... last1,std::size_t last2,template<std::size_t,std::size_t>class comp>
struct merge<false,std::index_sequence<last1...>,std::index_sequence<last2>,comp>{
	using type=std::index_sequence<last2,last1...>;
};

template<class Seq1,class Seq2,template<std::size_t,std::size_t>class comp=less>
using merge_t=
	typename merge<
		(Seq1::size()<Seq2::size())?comp<first_v<Seq1>,first_v<Seq2>>::value:comp<first_v<Seq2>,first_v<Seq1>>::value,
		std::conditional_t<(Seq1::size()<Seq2::size()),Seq1,Seq2>,
		std::conditional_t<(Seq1::size()<Seq2::size()),Seq2,Seq1>,
		comp
	>::type;

} // inline namespace v1
} // inline namespace constant_sequence
} // inline namespace mpl
} // namespace srook
#endif
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 */

#include "base/loader/raw_object.hh"
#include "base/loader/symtab.hh"
#include "base/trace.hh"

ObjectFile *
RawObject::tryFile(const std::string &fname, int fd, size_t len, uint8_t *data)
{
    return new RawObject(fname, fd, len, data, ObjectFile::UnknownArch,
            ObjectFile::UnknownOpSys);
}

RawObject::RawObject(const std::string &_filename, int _fd, size_t _len,
        uint8_t *_data, Arch _arch, OpSys _opSys)
    : ObjectFile(_filename, _fd, _len, _data, _arch, _opSys)
{
    text.baseAddr = 0;
    text.size = len;
    text.fileImage = fileData;

    data.baseAddr = 0;
    data.size = 0;
    data.fileImage = NULL;

    bss.baseAddr = 0;
    bss.size = 0;
    bss.fileImage = NULL;

    DPRINTFR(Loader, "text: 0x%x %d\ndata: 0x%x %d\nbss: 0x%x %d\n",
             text.baseAddr, text.size, data.baseAddr, data.size,
             bss.baseAddr, bss.size);
}

bool
RawObject::loadGlobalSymbols(SymbolTable *symtab, Addr addrMask)
{
    int fnameStart = filename.rfind('/',filename.size()) + 1;
    int extStart = filename.rfind('.',filename.size());
    symtab->insert(text.baseAddr & addrMask, filename.substr(fnameStart,
                extStart-fnameStart) + "_start");
    return true;
}

bool
RawObject::loadLocalSymbols(SymbolTable *symtab, Addr addrMask)
{
    int fnameStart = filename.rfind('/',filename.size()) + 1;
    int extStart = filename.rfind('.',filename.size());
    symtab->insert(text.baseAddr & addrMask, filename.substr(fnameStart,
                extStart-fnameStart) + "_start");
    return true;
}
<commit_msg>Don't add symbols for loaded files to symbol table since they are pretty much meaningless with all the copying that goes on<commit_after>/*
 * Copyright (c) 2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 */

#include "base/loader/raw_object.hh"
#include "base/loader/symtab.hh"
#include "base/trace.hh"

ObjectFile *
RawObject::tryFile(const std::string &fname, int fd, size_t len, uint8_t *data)
{
    return new RawObject(fname, fd, len, data, ObjectFile::UnknownArch,
            ObjectFile::UnknownOpSys);
}

RawObject::RawObject(const std::string &_filename, int _fd, size_t _len,
        uint8_t *_data, Arch _arch, OpSys _opSys)
    : ObjectFile(_filename, _fd, _len, _data, _arch, _opSys)
{
    text.baseAddr = 0;
    text.size = len;
    text.fileImage = fileData;

    data.baseAddr = 0;
    data.size = 0;
    data.fileImage = NULL;

    bss.baseAddr = 0;
    bss.size = 0;
    bss.fileImage = NULL;

    DPRINTFR(Loader, "text: 0x%x %d\ndata: 0x%x %d\nbss: 0x%x %d\n",
             text.baseAddr, text.size, data.baseAddr, data.size,
             bss.baseAddr, bss.size);
}

bool
RawObject::loadGlobalSymbols(SymbolTable *symtab, Addr addrMask)
{
/*    int fnameStart = filename.rfind('/',filename.size()) + 1;
    int extStart = filename.rfind('.',filename.size());
    symtab->insert(text.baseAddr & addrMask, filename.substr(fnameStart,
                extStart-fnameStart) + "_start");*/
    return true;
}

bool
RawObject::loadLocalSymbols(SymbolTable *symtab, Addr addrMask)
{
/*    int fnameStart = filename.rfind('/',filename.size()) + 1;
    int extStart = filename.rfind('.',filename.size());
    symtab->insert(text.baseAddr & addrMask, filename.substr(fnameStart,
                extStart-fnameStart) + "_start");*/
    return true;
}
<|endoftext|>


<commit_before>#include <cstdio>
#include <algorithm>
#include <limits>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <unistd.h>

extern "C" {
#include "stinger_core/stinger.h"
#include "stinger_core/stinger_shared.h"
#include "stinger_utils/stinger_utils.h"
#include "stinger_utils/timer.h"
#include "stinger_core/xmalloc.h"
}

#include "proto/stinger-batch.pb.h"
#include "server.h"
#include "send_rcv.h"

using namespace gt::stinger;

int main(int argc, char *argv[])
{
  /* default global options */
  int port = 10101;
  uint64_t buffer_size = 1ULL << 28ULL;
  char * graph_name = (char *) xmalloc (128*sizeof(char));
  sprintf(graph_name, "/default");
  char * input_file = (char *) xmalloc (1024*sizeof(char));
  input_file[0] = '\0';

  /* parse command line configuration */
  int opt = 0;
  while(-1 != (opt = getopt(argc, argv, "p:b:n:i:"))) {
    switch(opt) {
      case 'p': {
		  port = atoi(optarg);
		} break;

      case 'b': {
		  buffer_size = atol(optarg);
		} break;

      case 'n': {
		  strcpy (graph_name, optarg);
		} break;

      case 'i': {
		  strcpy (input_file, optarg);
		} break;

      case '?':
      case 'h': {
		  printf("Usage:    %s [-p port] [-b buffer_size]\n", argv[0]);
		  printf("Defaults: port: %d buffer_size: %lu\n", port, (unsigned long) buffer_size);
		  exit(0);
		} break;

    }
  }

  /* print configuration to the terminal */
  printf("\tName: %s\n", graph_name);

  /* allocate the graph */
  struct stinger * S = stinger_shared_new(&graph_name);
  //struct stinger * S = stinger_new ();
  size_t graph_sz = S->length + sizeof(struct stinger);

  int64_t nv, ne;
  int64_t *off, *ind, *weight, *graphmem;
  /* load edges from disk (if applicable) */
  if (input_file[0] != '\0')
  {
    snarf_graph (input_file, &nv, &ne, (int64_t**)&off,
	(int64_t**)&ind, (int64_t**)&weight, (int64_t**)&graphmem);
    stinger_set_initial_edges (S, nv, 0, off, ind, weight, NULL, NULL, 0);
    free(graphmem);
  }

  printf("Graph created. Running stats...");
  tic();
  printf("\n\tVertices: %ld\n\tEdges: %ld\n",
      stinger_num_active_vertices(S), stinger_total_edges(S));

  /* consistency check */
  printf("\tConsistency %ld\n", stinger_consistency_check(S, STINGER_MAX_LVERTICES));
  printf("\tDone. %lf seconds\n", toc());

  /* we need a socket that can reply with the shmem name & size of the graph */

  pid_t name_pid, batch_pid;

  /* child will handle name and size requests */
  name_pid = fork ();
  if (name_pid == 0)
  {
    start_udp_graph_name_server (graph_name, graph_sz, port);
    exit (0);
  }

  /* and we need a listener that can receive new edges */
  batch_pid = fork ();
  if (batch_pid == 0)
  {
    start_tcp_batch_server (S, port, buffer_size);
    exit (0);
  }

  printf("Press <return> to shut down the server...\n");
  getchar();

  printf("Shutting down the name server..."); fflush(stdout);
  int status;
  kill(name_pid, SIGTERM);
  waitpid(name_pid, &status, 0);
  printf(" done.\n"); fflush(stdout);

  printf("Shutting down the batch server..."); fflush(stdout);
  kill(batch_pid, SIGTERM);
  waitpid(batch_pid, &status, 0);
  printf(" done.\n"); fflush(stdout);

  /* clean up */
  stinger_shared_free(S, graph_name, graph_sz);
  free(graph_name);
  free(input_file);

  return 0;
}
<commit_msg>Server can now import DIMACS edge list graph files.  Tested with NY road map.<commit_after>#include <cstdio>
#include <algorithm>
#include <limits>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <unistd.h>

extern "C" {
#include "stinger_core/stinger.h"
#include "stinger_core/stinger_shared.h"
#include "stinger_core/xmalloc.h"
#include "stinger_utils/stinger_utils.h"
#include "stinger_utils/timer.h"
#include "stinger_utils/dimacs_support.h"
}

#include "proto/stinger-batch.pb.h"
#include "server.h"
#include "send_rcv.h"

using namespace gt::stinger;

int main(int argc, char *argv[])
{
  /* default global options */
  int port = 10101;
  uint64_t buffer_size = 1ULL << 28ULL;
  char * graph_name = (char *) xmalloc (128*sizeof(char));
  sprintf(graph_name, "/default");
  char * input_file = (char *) xmalloc (1024*sizeof(char));
  input_file[0] = '\0';
  char * file_type = (char *) xmalloc (128*sizeof(char));
  file_type[0] = '\0';

  /* parse command line configuration */
  int opt = 0;
  while(-1 != (opt = getopt(argc, argv, "p:b:n:i:t:h?"))) {
    switch(opt) {
      case 'p': {
		  port = atoi(optarg);
		} break;

      case 'b': {
		  buffer_size = atol(optarg);
		} break;

      case 'n': {
		  strcpy (graph_name, optarg);
		} break;

      case 'i': {
		  strcpy (input_file, optarg);
		} break;

      case 't': {
		  strcpy (file_type, optarg);
		} break;

      case '?':
      case 'h': {
		  printf("Usage:    %s [-p port] [-b buffer_size] [-n graph_name] [-i input_file_path [-t file_type]]\n", argv[0]);
		  printf("Defaults:\n\tport: %d\n\tbuffer_size: %lu\n\tgraph_name: %s\n", port, (unsigned long) buffer_size, graph_name);
		  exit(0);
		} break;

    }
  }

  /* print configuration to the terminal */
  printf("\tName: %s\n", graph_name);

  /* allocate the graph */
  struct stinger * S = stinger_shared_new(&graph_name);
  //struct stinger * S = stinger_new ();
  size_t graph_sz = S->length + sizeof(struct stinger);

  /* load edges from disk (if applicable) */
  if (input_file[0] != '\0')
  {
    switch (file_type[0])
    {
      case 'b': {
		  int64_t nv, ne;
		  int64_t *off, *ind, *weight, *graphmem;
		  snarf_graph (input_file, &nv, &ne, (int64_t**)&off,
		      (int64_t**)&ind, (int64_t**)&weight, (int64_t**)&graphmem);
		  stinger_set_initial_edges (S, nv, 0, off, ind, weight, NULL, NULL, 0);
		  free(graphmem);
		} break;  /* STINGER binary */

      case 'c': {
		} break;  /* CSV */

      case 'd': {
		  load_dimacs_graph (S, input_file);
		} break;  /* DIMACS */

      case 'e': {
		} break;  /* Edge list */

      case 'g': {
		} break;  /* GML / GraphML / GEXF -- you pick */

      case 'j': {
		} break;  /* JSON */

      case 'x': {
		} break;  /* XML */

      default:	{
		  printf("Unsupported file type.\n");
		  exit(0);
		} break;
    }
  }

  printf("Graph created. Running stats...");
  tic();
  printf("\n\tVertices: %ld\n\tEdges: %ld\n",
      stinger_num_active_vertices(S), stinger_total_edges(S));

  /* consistency check */
  printf("\tConsistency %ld\n", stinger_consistency_check(S, STINGER_MAX_LVERTICES));
  printf("\tDone. %lf seconds\n", toc());

  /* we need a socket that can reply with the shmem name & size of the graph */
  pid_t name_pid, batch_pid;

  /* child will handle name and size requests */
  name_pid = fork ();
  if (name_pid == 0)
  {
    start_udp_graph_name_server (graph_name, graph_sz, port);
    exit (0);
  }

  /* and we need a listener that can receive new edges */
  batch_pid = fork ();
  if (batch_pid == 0)
  {
    start_tcp_batch_server (S, port, buffer_size);
    exit (0);
  }

  printf("Press <return> to shut down the server...\n");
  getchar();

  printf("Shutting down the name server..."); fflush(stdout);
  int status;
  kill(name_pid, SIGTERM);
  waitpid(name_pid, &status, 0);
  printf(" done.\n"); fflush(stdout);

  printf("Shutting down the batch server..."); fflush(stdout);
  kill(batch_pid, SIGTERM);
  waitpid(batch_pid, &status, 0);
  printf(" done.\n"); fflush(stdout);

  /* clean up */
  stinger_shared_free(S, graph_name, graph_sz);
  free(graph_name);
  free(input_file);

  return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define IN_FRUIT_CPP_FILE

#include <cstdlib>
#include <memory>
#include <vector>
#include <iostream>
#include <algorithm>
#include <fruit/impl/util/type_info.h>

#include <fruit/impl/storage/injector_storage.h>
#include <fruit/impl/storage/component_storage.h>
#include <fruit/impl/data_structures/semistatic_graph.templates.h>
#include <fruit/impl/meta/basics.h>
#include <fruit/impl/storage/normalized_component_storage.h>

using std::cout;
using std::endl;

using namespace fruit::impl;

namespace {

std::string multipleBindingsError(TypeId type) {
  return "Fatal injection error: the type " + type.type_info->name() + " was provided more than once, with different bindings.\n"
        + "This was not caught at compile time because at least one of the involved components bound this type but didn't expose it in the component signature.\n"
        + "If the type has a default constructor or an Inject annotation, this problem may arise even if this type is bound/provided by only one component (and then hidden), if this type is auto-injected in another component.\n"
        + "If the source of the problem is unclear, try exposing this type in all the component signatures where it's bound; if no component hides it this can't happen.\n";
}

auto typeInfoLessThanForMultibindings = [](const std::pair<TypeId, MultibindingData>& x,
                                           const std::pair<TypeId, MultibindingData>& y) {
  return x.first < y.first;
};

} // namespace

namespace fruit {
namespace impl {

std::vector<std::pair<TypeId, BindingData>>
BindingNormalization::normalizeBindings(const std::vector<std::pair<TypeId, BindingData>>& bindings_vector,
                                        FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
                                        std::vector<CompressedBinding>&& compressed_bindings_vector,
                                        const std::vector<std::pair<TypeId, MultibindingData>>& multibindings_vector,
                                        const std::vector<TypeId>& exposed_types,
                                        BindingNormalization::BindingCompressionInfoMap& bindingCompressionInfoMap) {
  HashMap<TypeId, BindingData> binding_data_map = createHashMap<TypeId, BindingData>(bindings_vector.size());

  for (auto& p : bindings_vector) {
    auto itr = binding_data_map.find(p.first);
    if (itr != binding_data_map.end()) {
      if (!(p.second == itr->second)) {
        std::cerr << multipleBindingsError(p.first) << std::endl;
        exit(1);
      }
      // Otherwise ok, duplicate but consistent binding.

    } else {
      // New binding, add it to the map.
      binding_data_map[p.first] = p.second;
    }
  }

  for (const auto& p : bindings_vector) {
    if (p.second.needsAllocation()) {
      fixed_size_allocator_data.addType(p.first);
    } else {
      fixed_size_allocator_data.addExternallyAllocatedType(p.first);
    }
  }

  // Remove duplicates from `compressedBindingsVector'.

  // CtypeId -> (ItypeId, bindingData)
  HashMap<TypeId, std::pair<TypeId, BindingData>> compressed_bindings_map =
      createHashMap<TypeId, std::pair<TypeId, BindingData>>(compressed_bindings_vector.size());

  // This also removes any duplicates. No need to check for multiple I->C, I2->C mappings, will filter these out later when
  // considering deps.
  for (CompressedBinding& compressed_binding : compressed_bindings_vector) {
    compressed_bindings_map[compressed_binding.class_id] = {compressed_binding.interface_id, compressed_binding.binding_data};
  }

  // We can't compress the binding if C is a dep of a multibinding.
  for (auto p : multibindings_vector) {
    const BindingDeps* deps = p.second.deps;
    if (deps != nullptr) {
      for (std::size_t i = 0; i < deps->num_deps; ++i) {
        compressed_bindings_map.erase(deps->deps[i]);
      }
    }
  }

  // We can't compress the binding if C is an exposed type (but I is likely to be exposed instead).
  for (TypeId type : exposed_types) {
    compressed_bindings_map.erase(type);
  }

  // We can't compress the binding if some type X depends on C and X!=I.
  for (auto& p : binding_data_map) {
    TypeId x_id = p.first;
    BindingData binding_data = p.second;
    if (!binding_data.isCreated()) {
      for (std::size_t i = 0; i < binding_data.getDeps()->num_deps; ++i) {
        TypeId c_id = binding_data.getDeps()->deps[i];
        auto itr = compressed_bindings_map.find(c_id);
        if (itr != compressed_bindings_map.end() && itr->second.first != x_id) {
          compressed_bindings_map.erase(itr);
        }
      }
    }
  }

  // Two pairs of compressible bindings (I->C) and (C->X) can not exist (the C of a compressible binding is always bound either
  // using constructor binding or provider binding, it can't be a binding itself). So no need to check for that.

  bindingCompressionInfoMap =
      createHashMap<TypeId, BindingNormalization::BindingCompressionInfo>(compressed_bindings_map.size());

  // Now perform the binding compression.
  for (auto& p : compressed_bindings_map) {
    TypeId c_id = p.first;
    TypeId i_id = p.second.first;
    BindingData binding_data = p.second.second;
    auto i_binding_data = binding_data_map.find(i_id);
    auto c_binding_data = binding_data_map.find(c_id);
    FruitAssert(i_binding_data != binding_data_map.end());
    FruitAssert(c_binding_data != binding_data_map.end());
    bindingCompressionInfoMap[c_id] = BindingCompressionInfo{i_id, i_binding_data->second, c_binding_data->second};
    // Note that even if I is the one that remains, C is the one that will be allocated, not I.
    FruitAssert(!i_binding_data->second.needsAllocation());
    i_binding_data->second = binding_data;
    binding_data_map.erase(c_binding_data);
#ifdef FRUIT_EXTRA_DEBUG
    std::cout << "InjectorStorage: performing binding compression for the edge " << i_id << "->" << c_id << std::endl;
#endif
  }

  // Copy the normalized bindings into the result vector.
  std::vector<std::pair<TypeId, BindingData>> result;
  result.reserve(binding_data_map.size());
  for (auto& p : binding_data_map) {
    result.push_back(p);
  }
  return result;
}

void BindingNormalization::addMultibindings(std::unordered_map<TypeId, NormalizedMultibindingData>& multibindings,
                                            FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
                                            const std::vector<std::pair<TypeId, MultibindingData>>& multibindingsVector) {

  std::vector<std::pair<TypeId, MultibindingData>> sortedMultibindingsVector = multibindingsVector;
  std::sort(sortedMultibindingsVector.begin(), sortedMultibindingsVector.end(),
            typeInfoLessThanForMultibindings);

#ifdef FRUIT_EXTRA_DEBUG
  std::cout << "InjectorStorage: adding multibindings:" << std::endl;
#endif
  // Now we must merge multiple bindings for the same type.
  for (auto i = sortedMultibindingsVector.begin(); i != sortedMultibindingsVector.end(); /* no increment */) {
    const std::pair<TypeId, MultibindingData>& x = *i;
    NormalizedMultibindingData& b = multibindings[x.first];

    // Might be set already, but we need to set it if there was no multibinding for this type.
    b.get_multibindings_vector = x.second.get_multibindings_vector;

#ifdef FRUIT_EXTRA_DEBUG
    std::cout << x.first << " has " << std::distance(i, sortedMultibindingsVector.end()) << " multibindings." << std::endl;
#endif
    // Insert all multibindings for this type (note that x is also inserted here).
    for (; i != sortedMultibindingsVector.end() && i->first == x.first; ++i) {
      b.elems.push_back(NormalizedMultibindingData::Elem(i->second));
      if (i->second.needs_allocation) {
        fixed_size_allocator_data.addType(x.first);
      } else {
        fixed_size_allocator_data.addExternallyAllocatedType(x.first);
      }
    }
#ifdef FRUIT_EXTRA_DEBUG
    std::cout << std::endl;
#endif
  }
}

} // namespace impl
} // namespace fruit
<commit_msg>Add some debug prints (executed only with FRUIT_EXTRA_DEBUG) to help debug compressed bindings.<commit_after>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define IN_FRUIT_CPP_FILE

#include <cstdlib>
#include <memory>
#include <vector>
#include <iostream>
#include <algorithm>
#include <fruit/impl/util/type_info.h>

#include <fruit/impl/storage/injector_storage.h>
#include <fruit/impl/storage/component_storage.h>
#include <fruit/impl/data_structures/semistatic_graph.templates.h>
#include <fruit/impl/meta/basics.h>
#include <fruit/impl/storage/normalized_component_storage.h>

using std::cout;
using std::endl;

using namespace fruit::impl;

namespace {

std::string multipleBindingsError(TypeId type) {
  return "Fatal injection error: the type " + type.type_info->name() + " was provided more than once, with different bindings.\n"
        + "This was not caught at compile time because at least one of the involved components bound this type but didn't expose it in the component signature.\n"
        + "If the type has a default constructor or an Inject annotation, this problem may arise even if this type is bound/provided by only one component (and then hidden), if this type is auto-injected in another component.\n"
        + "If the source of the problem is unclear, try exposing this type in all the component signatures where it's bound; if no component hides it this can't happen.\n";
}

auto typeInfoLessThanForMultibindings = [](const std::pair<TypeId, MultibindingData>& x,
                                           const std::pair<TypeId, MultibindingData>& y) {
  return x.first < y.first;
};

} // namespace

namespace fruit {
namespace impl {

std::vector<std::pair<TypeId, BindingData>>
BindingNormalization::normalizeBindings(const std::vector<std::pair<TypeId, BindingData>>& bindings_vector,
                                        FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
                                        std::vector<CompressedBinding>&& compressed_bindings_vector,
                                        const std::vector<std::pair<TypeId, MultibindingData>>& multibindings_vector,
                                        const std::vector<TypeId>& exposed_types,
                                        BindingNormalization::BindingCompressionInfoMap& bindingCompressionInfoMap) {
  HashMap<TypeId, BindingData> binding_data_map = createHashMap<TypeId, BindingData>(bindings_vector.size());

  for (auto& p : bindings_vector) {
    auto itr = binding_data_map.find(p.first);
    if (itr != binding_data_map.end()) {
      if (!(p.second == itr->second)) {
        std::cerr << multipleBindingsError(p.first) << std::endl;
        exit(1);
      }
      // Otherwise ok, duplicate but consistent binding.

    } else {
      // New binding, add it to the map.
      binding_data_map[p.first] = p.second;
    }
  }

  for (const auto& p : bindings_vector) {
    if (p.second.needsAllocation()) {
      fixed_size_allocator_data.addType(p.first);
    } else {
      fixed_size_allocator_data.addExternallyAllocatedType(p.first);
    }
  }

  // Remove duplicates from `compressedBindingsVector'.

  // CtypeId -> (ItypeId, bindingData)
  HashMap<TypeId, std::pair<TypeId, BindingData>> compressed_bindings_map =
      createHashMap<TypeId, std::pair<TypeId, BindingData>>(compressed_bindings_vector.size());

  // This also removes any duplicates. No need to check for multiple I->C, I2->C mappings, will filter these out later when
  // considering deps.
  for (CompressedBinding& compressed_binding : compressed_bindings_vector) {
    compressed_bindings_map[compressed_binding.class_id] = {compressed_binding.interface_id, compressed_binding.binding_data};
  }

  // We can't compress the binding if C is a dep of a multibinding.
  for (auto p : multibindings_vector) {
    const BindingDeps* deps = p.second.deps;
    if (deps != nullptr) {
      for (std::size_t i = 0; i < deps->num_deps; ++i) {
        compressed_bindings_map.erase(deps->deps[i]);
#ifdef FRUIT_EXTRA_DEBUG
        std::cout << "InjectorStorage: ignoring compressed binding for " << deps->deps[i] << " because it's a dep of a multibinding." << std::endl;
#endif
      }
    }
  }

  // We can't compress the binding if C is an exposed type (but I is likely to be exposed instead).
  for (TypeId type : exposed_types) {
    compressed_bindings_map.erase(type);
#ifdef FRUIT_EXTRA_DEBUG
    std::cout << "InjectorStorage: ignoring compressed binding for " << type << " because it's an exposed type." << std::endl;
#endif
  }

  // We can't compress the binding if some type X depends on C and X!=I.
  for (auto& p : binding_data_map) {
    TypeId x_id = p.first;
    BindingData binding_data = p.second;
    if (!binding_data.isCreated()) {
      for (std::size_t i = 0; i < binding_data.getDeps()->num_deps; ++i) {
        TypeId c_id = binding_data.getDeps()->deps[i];
        auto itr = compressed_bindings_map.find(c_id);
        if (itr != compressed_bindings_map.end() && itr->second.first != x_id) {
          compressed_bindings_map.erase(itr);
#ifdef FRUIT_EXTRA_DEBUG
          std::cout << "InjectorStorage: ignoring compressed binding for " << c_id << " because the type " <<  x_id << " depends on it." << std::endl;
#endif
        }
      }
    }
  }

  // Two pairs of compressible bindings (I->C) and (C->X) can not exist (the C of a compressible binding is always bound either
  // using constructor binding or provider binding, it can't be a binding itself). So no need to check for that.

  bindingCompressionInfoMap =
      createHashMap<TypeId, BindingNormalization::BindingCompressionInfo>(compressed_bindings_map.size());

  // Now perform the binding compression.
  for (auto& p : compressed_bindings_map) {
    TypeId c_id = p.first;
    TypeId i_id = p.second.first;
    BindingData binding_data = p.second.second;
    auto i_binding_data = binding_data_map.find(i_id);
    auto c_binding_data = binding_data_map.find(c_id);
    FruitAssert(i_binding_data != binding_data_map.end());
    FruitAssert(c_binding_data != binding_data_map.end());
    bindingCompressionInfoMap[c_id] = BindingCompressionInfo{i_id, i_binding_data->second, c_binding_data->second};
    // Note that even if I is the one that remains, C is the one that will be allocated, not I.
    FruitAssert(!i_binding_data->second.needsAllocation());
    i_binding_data->second = binding_data;
    binding_data_map.erase(c_binding_data);
#ifdef FRUIT_EXTRA_DEBUG
    std::cout << "InjectorStorage: performing binding compression for the edge " << i_id << "->" << c_id << std::endl;
#endif
  }

  // Copy the normalized bindings into the result vector.
  std::vector<std::pair<TypeId, BindingData>> result;
  result.reserve(binding_data_map.size());
  for (auto& p : binding_data_map) {
    result.push_back(p);
  }
  return result;
}

void BindingNormalization::addMultibindings(std::unordered_map<TypeId, NormalizedMultibindingData>& multibindings,
                                            FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
                                            const std::vector<std::pair<TypeId, MultibindingData>>& multibindingsVector) {

  std::vector<std::pair<TypeId, MultibindingData>> sortedMultibindingsVector = multibindingsVector;
  std::sort(sortedMultibindingsVector.begin(), sortedMultibindingsVector.end(),
            typeInfoLessThanForMultibindings);

#ifdef FRUIT_EXTRA_DEBUG
  std::cout << "InjectorStorage: adding multibindings:" << std::endl;
#endif
  // Now we must merge multiple bindings for the same type.
  for (auto i = sortedMultibindingsVector.begin(); i != sortedMultibindingsVector.end(); /* no increment */) {
    const std::pair<TypeId, MultibindingData>& x = *i;
    NormalizedMultibindingData& b = multibindings[x.first];

    // Might be set already, but we need to set it if there was no multibinding for this type.
    b.get_multibindings_vector = x.second.get_multibindings_vector;

#ifdef FRUIT_EXTRA_DEBUG
    std::cout << x.first << " has " << std::distance(i, sortedMultibindingsVector.end()) << " multibindings." << std::endl;
#endif
    // Insert all multibindings for this type (note that x is also inserted here).
    for (; i != sortedMultibindingsVector.end() && i->first == x.first; ++i) {
      b.elems.push_back(NormalizedMultibindingData::Elem(i->second));
      if (i->second.needs_allocation) {
        fixed_size_allocator_data.addType(x.first);
      } else {
        fixed_size_allocator_data.addExternallyAllocatedType(x.first);
      }
    }
#ifdef FRUIT_EXTRA_DEBUG
    std::cout << std::endl;
#endif
  }
}

} // namespace impl
} // namespace fruit
<|endoftext|>


<commit_before>/* The MIT License:

Copyright (c) 2012-2015 Ivan Gagis <igagis@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */

// Home page: http://morda.googlecode.com

/**
 * @author Ivan Gagis <igagis@gmail.com>
 */

#pragma once

#include <string>
#include <set>

#include <ting/Shared.hpp>
#include <ting/util.hpp>

#include "../util/keycodes.hpp"

#include "../util/Matrix4.hpp"
#include "../util/Vector2.hpp"
#include "../util/Rectangle2.hpp"
#include "../util/LayoutParams.hpp"

#include "../config.hpp"

#include <stob/dom.hpp>

namespace morda{

class Container;

class Widget : virtual public ting::Shared{
	friend class Container;
	friend class App;

public:
	typedef std::list<std::shared_ptr<Widget>> T_ChildrenList;

private:
	Container* parent = nullptr;
	T_ChildrenList::iterator parentIter;

	std::set<unsigned> hovered;

	bool isVisible;

	bool isEnabled;

	morda::Rect2r rect;

	//cached minimal dimensions needed to show widget's contents normally
	mutable morda::Vec2r minDim;
	mutable bool minDimNeedsRecomputing = true;

	//clip widgets contents by widget's border if set to true
	bool clip;
public:
	bool Clip()const NOEXCEPT{
		return this->clip;
	}

	void SetClip(bool clip)NOEXCEPT{
		this->clip = clip;
	}

private:
	//logical ID of the widget
	std::string name;

	bool relayoutNeeded = true;

	std::unique_ptr<stob::Node> layout;

	std::unique_ptr<LayoutParams> layoutParams;
public:
	std::unique_ptr<LayoutParams> ResetLayoutParams(std::unique_ptr<LayoutParams> params = nullptr)NOEXCEPT;

	bool NeedsRelayout()const NOEXCEPT{
		return this->relayoutNeeded;
	}

	const std::string& Name()const NOEXCEPT{
		return this->name;
	}

	const Container* Parent()const NOEXCEPT{
		return this->parent;
	}

	Container* Parent()NOEXCEPT{
		return this->parent;
	}

	//NOTE: if only parent holds Ref then object may be deleted
	void RemoveFromParent();

	/**
	 * @brief Check if widget is hovered by any pointer.
     * @return true if hovered by any pointer.
	 * @return false otherwise.
     */
	bool IsHovered()const NOEXCEPT{
		return this->hovered.size() != 0;
	}

	/**
	 * @brief Check if widget is hovered by given pointer.
     * @param pointerID - pointer ID to check against.
     * @return true if widget is hovered by given pointer ID.
	 * @return false otherwise.
     */
	bool IsHovered(unsigned pointerID)const NOEXCEPT{
		return this->hovered.find(pointerID) != this->hovered.end();
	}

private:
	void SetHovered(bool isHovered, unsigned pointerID){
		if(isHovered){
			if(this->IsHovered(pointerID)){
				return;
			}

			this->hovered.insert(pointerID);
		}else{
			if(!this->IsHovered(pointerID)){
				return;
			}

			this->hovered.erase(pointerID);
		}

		this->OnHoverChanged(pointerID);
	}

	void SetUnhovered(){
		this->hovered.clear();
	}
public:

	const morda::Rect2r& Rect()const NOEXCEPT{
		return this->rect;
	}

	morda::Rect2i ComputeViewportRect(const Matr4r& matrix)const NOEXCEPT;

	void MoveTo(const morda::Vec2r& newPos)NOEXCEPT{
		this->rect.p = newPos;
	}

	void MoveBy(const morda::Vec2r& delta)NOEXCEPT{
		this->rect.p += delta;
	}

	void Resize(const morda::Vec2r& newDims);

	void ResizeBy(const morda::Vec2r& delta){
		this->Resize(this->Rect().d + delta);
	}

	virtual std::shared_ptr<Widget> FindChildByName(const std::string& name)NOEXCEPT;

	template <typename T> std::shared_ptr<T> FindChildByNameAs(const std::string& name)NOEXCEPT{
		return std::dynamic_pointer_cast<T>(this->FindChildByName(name));
	}

public:
	Widget(const stob::Node* chain);

public:

	virtual ~Widget()NOEXCEPT{}

	virtual void Render(const morda::Matr4r& matrix)const{}

private:
	void RenderInternal(const morda::Matr4r& matrix)const;

private:
	void OnKeyInternal(bool isDown, EKey keyCode);

private:
	bool isFocused = false;
public:

	//return true to consume
	virtual bool OnKey(bool isDown, morda::EKey keyCode){
		return false;
	}

	void Focus()NOEXCEPT;

	void Unfocus()NOEXCEPT;

	bool IsFocused()const NOEXCEPT{
		return this->isFocused;
	}

	virtual void OnFocusedChanged(){}

	enum class EMouseButton{
		LEFT,
		RIGHT,
		MIDDLE,
		WHEEL_UP,
		WHEEL_DOWN,

		ENUM_SIZE
	};

	//return true to consume event
	virtual bool OnMouseButton(bool isDown, const morda::Vec2r& pos, EMouseButton button, unsigned pointerID){
		return false;
	}

	//return true to consume event
	virtual bool OnMouseMove(const morda::Vec2r& pos, unsigned pointerID){
		return false;
	}

	virtual void OnHoverChanged(unsigned pointerID){
//		TRACE(<< "Widget::OnHoverChanged(): this->IsHovered() = " << this->IsHovered() << std::endl)
	}

	virtual void OnResize(){
//		TRACE(<< "Widget::OnResize(): invoked" << std::endl)
	}

	morda::Vec2r Measure(const morda::Vec2r& quotum = morda::Vec2r(-1))const{
		if(quotum == morda::Vec2r(-1)){
			if(this->minDimNeedsRecomputing){
				this->minDim = this->ComputeMinDim(quotum);
				this->minDimNeedsRecomputing = false;
			}
			return this->minDim;
		}else{
			return this->ComputeMinDim(quotum);
		}
	}

protected:
	virtual morda::Vec2r ComputeMinDim(const Vec2r& quotum)const{
		return morda::Vec2r(0, 0);
	}

public:

	void SetRelayoutNeeded()NOEXCEPT;

	void SetVisible(bool visible){
		this->isVisible = visible;
		if(!this->isVisible){
			this->SetUnhovered();
		}
	}

	bool IsVisible()const NOEXCEPT{
		return this->isVisible;
	}

	void SetEnabled(bool enable)NOEXCEPT{
		this->isEnabled = enable;
	}

	bool IsEnabled()const NOEXCEPT{
		return this->isEnabled;
	}

	/**
	 * @brief Check if point is within the widget bounds.
     * @param pos - point to check in widget coordinates.
     * @return true if point is inside of the widget boundaries.
	 * @return false otherwise.
     */
	bool Contains(const morda::Vec2r& pos)const NOEXCEPT{
		return morda::Rect2r(morda::Vec2r(0, 0), this->Rect().d).Overlaps(pos);
	}

	virtual void OnTopmostChanged(){}

	bool IsTopmost()const NOEXCEPT;

	void MakeTopmost();
};

}//~namespace
<commit_msg>cached quotum<commit_after>/* The MIT License:

Copyright (c) 2012-2015 Ivan Gagis <igagis@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */

// Home page: http://morda.googlecode.com

/**
 * @author Ivan Gagis <igagis@gmail.com>
 */

#pragma once

#include <string>
#include <set>

#include <ting/Shared.hpp>
#include <ting/util.hpp>

#include "../util/keycodes.hpp"

#include "../util/Matrix4.hpp"
#include "../util/Vector2.hpp"
#include "../util/Rectangle2.hpp"
#include "../util/LayoutParams.hpp"

#include "../config.hpp"

#include <stob/dom.hpp>

namespace morda{

class Container;

class Widget : virtual public ting::Shared{
	friend class Container;
	friend class App;

public:
	typedef std::list<std::shared_ptr<Widget>> T_ChildrenList;

private:
	Container* parent = nullptr;
	T_ChildrenList::iterator parentIter;

	std::set<unsigned> hovered;

	bool isVisible;

	bool isEnabled;

	morda::Rect2r rect;

	//cached minimal dimensions needed to show widget's contents normally
	mutable morda::Vec2r minDim;
	mutable bool minDimNeedsRecomputing = true;
	mutable Vec2r cachedQuotum;

	//clip widgets contents by widget's border if set to true
	bool clip;
public:
	bool Clip()const NOEXCEPT{
		return this->clip;
	}

	void SetClip(bool clip)NOEXCEPT{
		this->clip = clip;
	}

private:
	//logical ID of the widget
	std::string name;

	bool relayoutNeeded = true;

	std::unique_ptr<stob::Node> layout;

	std::unique_ptr<LayoutParams> layoutParams;
public:
	std::unique_ptr<LayoutParams> ResetLayoutParams(std::unique_ptr<LayoutParams> params = nullptr)NOEXCEPT;

	bool NeedsRelayout()const NOEXCEPT{
		return this->relayoutNeeded;
	}

	const std::string& Name()const NOEXCEPT{
		return this->name;
	}

	const Container* Parent()const NOEXCEPT{
		return this->parent;
	}

	Container* Parent()NOEXCEPT{
		return this->parent;
	}

	//NOTE: if only parent holds Ref then object may be deleted
	void RemoveFromParent();

	/**
	 * @brief Check if widget is hovered by any pointer.
     * @return true if hovered by any pointer.
	 * @return false otherwise.
     */
	bool IsHovered()const NOEXCEPT{
		return this->hovered.size() != 0;
	}

	/**
	 * @brief Check if widget is hovered by given pointer.
     * @param pointerID - pointer ID to check against.
     * @return true if widget is hovered by given pointer ID.
	 * @return false otherwise.
     */
	bool IsHovered(unsigned pointerID)const NOEXCEPT{
		return this->hovered.find(pointerID) != this->hovered.end();
	}

private:
	void SetHovered(bool isHovered, unsigned pointerID){
		if(isHovered){
			if(this->IsHovered(pointerID)){
				return;
			}

			this->hovered.insert(pointerID);
		}else{
			if(!this->IsHovered(pointerID)){
				return;
			}

			this->hovered.erase(pointerID);
		}

		this->OnHoverChanged(pointerID);
	}

	void SetUnhovered(){
		this->hovered.clear();
	}
public:

	const morda::Rect2r& Rect()const NOEXCEPT{
		return this->rect;
	}

	morda::Rect2i ComputeViewportRect(const Matr4r& matrix)const NOEXCEPT;

	void MoveTo(const morda::Vec2r& newPos)NOEXCEPT{
		this->rect.p = newPos;
	}

	void MoveBy(const morda::Vec2r& delta)NOEXCEPT{
		this->rect.p += delta;
	}

	void Resize(const morda::Vec2r& newDims);

	void ResizeBy(const morda::Vec2r& delta){
		this->Resize(this->Rect().d + delta);
	}

	virtual std::shared_ptr<Widget> FindChildByName(const std::string& name)NOEXCEPT;

	template <typename T> std::shared_ptr<T> FindChildByNameAs(const std::string& name)NOEXCEPT{
		return std::dynamic_pointer_cast<T>(this->FindChildByName(name));
	}

public:
	Widget(const stob::Node* chain);

public:

	virtual ~Widget()NOEXCEPT{}

	virtual void Render(const morda::Matr4r& matrix)const{}

private:
	void RenderInternal(const morda::Matr4r& matrix)const;

private:
	void OnKeyInternal(bool isDown, EKey keyCode);

private:
	bool isFocused = false;
public:

	//return true to consume
	virtual bool OnKey(bool isDown, morda::EKey keyCode){
		return false;
	}

	void Focus()NOEXCEPT;

	void Unfocus()NOEXCEPT;

	bool IsFocused()const NOEXCEPT{
		return this->isFocused;
	}

	virtual void OnFocusedChanged(){}

	enum class EMouseButton{
		LEFT,
		RIGHT,
		MIDDLE,
		WHEEL_UP,
		WHEEL_DOWN,

		ENUM_SIZE
	};

	//return true to consume event
	virtual bool OnMouseButton(bool isDown, const morda::Vec2r& pos, EMouseButton button, unsigned pointerID){
		return false;
	}

	//return true to consume event
	virtual bool OnMouseMove(const morda::Vec2r& pos, unsigned pointerID){
		return false;
	}

	virtual void OnHoverChanged(unsigned pointerID){
//		TRACE(<< "Widget::OnHoverChanged(): this->IsHovered() = " << this->IsHovered() << std::endl)
	}

	virtual void OnResize(){
//		TRACE(<< "Widget::OnResize(): invoked" << std::endl)
	}

	morda::Vec2r Measure(const morda::Vec2r& quotum = morda::Vec2r(-1))const{
		if(!this->minDimNeedsRecomputing && quotum == this->cachedQuotum){
			return this->minDim;
		}else{
			this->minDim = this->ComputeMinDim(quotum);
			this->minDimNeedsRecomputing = false;
			this->cachedQuotum = quotum;
			return this->minDim;
		}
	}

protected:
	virtual morda::Vec2r ComputeMinDim(const Vec2r& quotum)const{
		return morda::Vec2r(0, 0);
	}

public:

	void SetRelayoutNeeded()NOEXCEPT;

	void SetVisible(bool visible){
		this->isVisible = visible;
		if(!this->isVisible){
			this->SetUnhovered();
		}
	}

	bool IsVisible()const NOEXCEPT{
		return this->isVisible;
	}

	void SetEnabled(bool enable)NOEXCEPT{
		this->isEnabled = enable;
	}

	bool IsEnabled()const NOEXCEPT{
		return this->isEnabled;
	}

	/**
	 * @brief Check if point is within the widget bounds.
     * @param pos - point to check in widget coordinates.
     * @return true if point is inside of the widget boundaries.
	 * @return false otherwise.
     */
	bool Contains(const morda::Vec2r& pos)const NOEXCEPT{
		return morda::Rect2r(morda::Vec2r(0, 0), this->Rect().d).Overlaps(pos);
	}

	virtual void OnTopmostChanged(){}

	bool IsTopmost()const NOEXCEPT;

	void MakeTopmost();
};

}//~namespace
<|endoftext|>


<commit_before>/*
 * Implementation of a ACME client.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "GlueHttpClient.hxx"
#include "balancer.hxx"
#include "tcp_stock.hxx"
#include "stock/MapStock.hxx"
#include "tcp_balancer.hxx"
#include "address_resolver.hxx"
#include "http_request.hxx"
#include "http_response.hxx"
#include "http_address.hxx"
#include "pool.hxx"
#include "filtered_socket.hxx"
#include "ssl/ssl_client.hxx"
#include "event/Loop.hxx"
#include "async.hxx"
#include "istream/Handler.hxx"
#include "istream/Pointer.hxx"
#include "fb_pool.hxx"
#include "thread_pool.hxx"

#include <stdexcept>

#include <string.h>
#include <netdb.h>

gcc_noreturn
static void
ThrowError(GError *error)
{
    std::string msg(error->message);
    g_error_free(error);
    throw std::runtime_error(std::move(msg));
}

static void
CheckThrowError(GError *error)
{
    if (error != nullptr)
        ThrowError(error);
}

static AddressList &
ResolveOrThrow(struct pool &p, const char *host_and_port, int default_port)
{
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;

    GError *error = nullptr;
    auto *al = address_list_resolve_new(&p, host_and_port, default_port,
                                        &hints, &error);
    if (al == nullptr)
        ThrowError(error);

    return *al;
}

GlueHttpServerAddress::GlueHttpServerAddress(struct pool &p, bool _ssl,
                                             const char *_host_and_port,
                                             int default_port)
    :host_and_port(_host_and_port),
     addresses(ResolveOrThrow(p, _host_and_port, default_port)),
     ssl(_ssl) {}

GlueHttpClient::GlueHttpClient(struct pool &p, EventLoop &event_loop)
    :balancer(balancer_new(p, event_loop)),
     tcp_stock(tcp_stock_new(event_loop, 0)),
     tcp_balancer(tcp_balancer_new(*tcp_stock, *balancer))
{
    fb_pool_init(event_loop, false);
    ssl_client_init();
}

GlueHttpClient::~GlueHttpClient()
{
    thread_pool_stop();

    ssl_client_deinit();
    fb_pool_deinit();
    tcp_balancer_free(tcp_balancer);
    delete tcp_stock;
    balancer_free(balancer);

    thread_pool_join();
    thread_pool_deinit();
}

class SslSocketFilterFactory final : public SocketFilterFactory {
    struct pool &pool;
    EventLoop &event_loop;
    const char *const host;

public:
    SslSocketFilterFactory(struct pool &_pool,
                           EventLoop &_event_loop,
                           const char *_host)
        :pool(_pool), event_loop(_event_loop), host(_host) {}

    void *CreateFilter(GError **error_r) override {
        return ssl_client_create(&pool, event_loop, host, error_r);
    }
};

void
GlueHttpClient::Request(struct pool &p, EventLoop &event_loop,
                        GlueHttpServerAddress &server,
                        http_method_t method, const char *uri,
                        HttpHeaders &&headers, Istream *body,
                        const struct http_response_handler &handler,
                        void *handler_ctx,
                        struct async_operation_ref &async_ref)
{
    const SocketFilter *filter = nullptr;
    SocketFilterFactory *filter_factory = nullptr;

    if (server.ssl) {
        filter = &ssl_client_get_filter();
        filter_factory = NewFromPool<SslSocketFilterFactory>(p, p, event_loop,
                                                             /* TODO: only host */
                                                             server.host_and_port);
    }

    auto *address = NewFromPool<HttpAddress>(p,
                                             URI_SCHEME_HTTP, server.ssl,
                                             server.host_and_port, uri);
    address->addresses.CopyFrom(&p, server.addresses);

    http_request(p, event_loop, *tcp_balancer, 0,
                 filter, filter_factory,
                 method, *address,
                 std::move(headers), body,
                 handler, handler_ctx, async_ref);
}

class GlueHttpRequest final : IstreamHandler {
    http_status_t status;
    struct strmap *headers;
    IstreamPointer body;

    std::string body_string;

    GError *error = nullptr;

    bool done = false;

public:
    GlueHttpRequest():body(nullptr) {}

    ~GlueHttpRequest() {
        if (error != nullptr)
            g_error_free(error);
    }

    bool IsDone() const {
        return done;
    }

    void CheckThrowError() {
        GError *_error = error;
        error = nullptr;
        ::CheckThrowError(_error);
    }

    GlueHttpResponse MoveResponse() {
        return {status, *headers, std::move(body_string)};
    }

private:
    /* virtual methods from class IstreamHandler */

    size_t OnData(const void *data, size_t length) override {
        body_string.append((const char *)data, length);
        return length;
    }

    void OnEof() override {
        done = true;
    }

    void OnError(GError *_error) override {
        error = _error;
        done = true;
    }

    /* virtual methods from struct http_response_handler */

    void OnResponse(http_status_t _status, struct strmap *_headers,
                    Istream *_body) {
        assert(error == nullptr);

        status = _status;
        headers = _headers;

        if (_body != nullptr) {
            body.Set(*_body, *this);
            body.Read();
        } else
            done = true;
    }

    void OnResponseError(GError *_error) {
        assert(error == nullptr);

        error = _error;
        done = true;
    }

    static void OnResponse(http_status_t status, struct strmap *headers,
                           Istream *body, void *ctx) {
        ((GlueHttpRequest *)ctx)->OnResponse(status, headers, body);
    }

    static void OnResponseError(GError *_error, void *ctx) {
        ((GlueHttpRequest *)ctx)->OnResponseError(_error);
    }

public:
    static const struct http_response_handler handler;
};

constexpr struct http_response_handler GlueHttpRequest::handler = {
    OnResponse,
    OnResponseError,
};

GlueHttpResponse
GlueHttpClient::Request(EventLoop &event_loop,
                        struct pool &p, GlueHttpServerAddress &server,
                        http_method_t method, const char *uri,
                        HttpHeaders &&headers, Istream *body)
{
    struct async_operation_ref async_ref;

    GlueHttpRequest request;
    Request(p, event_loop, server, method, uri, std::move(headers), body,
            GlueHttpRequest::handler, &request, async_ref);
    while (!request.IsDone() && event_loop.LoopOnce()) {}

    request.CheckThrowError();

    return request.MoveResponse();
}
<commit_msg>certdb/GlueHttpClient: use AtScopeExit()<commit_after>/*
 * Implementation of a ACME client.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "GlueHttpClient.hxx"
#include "balancer.hxx"
#include "tcp_stock.hxx"
#include "stock/MapStock.hxx"
#include "tcp_balancer.hxx"
#include "address_resolver.hxx"
#include "http_request.hxx"
#include "http_response.hxx"
#include "http_address.hxx"
#include "pool.hxx"
#include "filtered_socket.hxx"
#include "ssl/ssl_client.hxx"
#include "event/Loop.hxx"
#include "async.hxx"
#include "istream/Handler.hxx"
#include "istream/Pointer.hxx"
#include "fb_pool.hxx"
#include "thread_pool.hxx"
#include "util/ScopeExit.hxx"

#include <stdexcept>

#include <string.h>
#include <netdb.h>

gcc_noreturn
static void
ThrowError(GError *error)
{
    AtScopeExit(error) { g_error_free(error); };
    throw std::runtime_error(error->message);
}

static void
CheckThrowError(GError *error)
{
    if (error != nullptr)
        ThrowError(error);
}

static AddressList &
ResolveOrThrow(struct pool &p, const char *host_and_port, int default_port)
{
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;

    GError *error = nullptr;
    auto *al = address_list_resolve_new(&p, host_and_port, default_port,
                                        &hints, &error);
    if (al == nullptr)
        ThrowError(error);

    return *al;
}

GlueHttpServerAddress::GlueHttpServerAddress(struct pool &p, bool _ssl,
                                             const char *_host_and_port,
                                             int default_port)
    :host_and_port(_host_and_port),
     addresses(ResolveOrThrow(p, _host_and_port, default_port)),
     ssl(_ssl) {}

GlueHttpClient::GlueHttpClient(struct pool &p, EventLoop &event_loop)
    :balancer(balancer_new(p, event_loop)),
     tcp_stock(tcp_stock_new(event_loop, 0)),
     tcp_balancer(tcp_balancer_new(*tcp_stock, *balancer))
{
    fb_pool_init(event_loop, false);
    ssl_client_init();
}

GlueHttpClient::~GlueHttpClient()
{
    thread_pool_stop();

    ssl_client_deinit();
    fb_pool_deinit();
    tcp_balancer_free(tcp_balancer);
    delete tcp_stock;
    balancer_free(balancer);

    thread_pool_join();
    thread_pool_deinit();
}

class SslSocketFilterFactory final : public SocketFilterFactory {
    struct pool &pool;
    EventLoop &event_loop;
    const char *const host;

public:
    SslSocketFilterFactory(struct pool &_pool,
                           EventLoop &_event_loop,
                           const char *_host)
        :pool(_pool), event_loop(_event_loop), host(_host) {}

    void *CreateFilter(GError **error_r) override {
        return ssl_client_create(&pool, event_loop, host, error_r);
    }
};

void
GlueHttpClient::Request(struct pool &p, EventLoop &event_loop,
                        GlueHttpServerAddress &server,
                        http_method_t method, const char *uri,
                        HttpHeaders &&headers, Istream *body,
                        const struct http_response_handler &handler,
                        void *handler_ctx,
                        struct async_operation_ref &async_ref)
{
    const SocketFilter *filter = nullptr;
    SocketFilterFactory *filter_factory = nullptr;

    if (server.ssl) {
        filter = &ssl_client_get_filter();
        filter_factory = NewFromPool<SslSocketFilterFactory>(p, p, event_loop,
                                                             /* TODO: only host */
                                                             server.host_and_port);
    }

    auto *address = NewFromPool<HttpAddress>(p,
                                             URI_SCHEME_HTTP, server.ssl,
                                             server.host_and_port, uri);
    address->addresses.CopyFrom(&p, server.addresses);

    http_request(p, event_loop, *tcp_balancer, 0,
                 filter, filter_factory,
                 method, *address,
                 std::move(headers), body,
                 handler, handler_ctx, async_ref);
}

class GlueHttpRequest final : IstreamHandler {
    http_status_t status;
    struct strmap *headers;
    IstreamPointer body;

    std::string body_string;

    GError *error = nullptr;

    bool done = false;

public:
    GlueHttpRequest():body(nullptr) {}

    ~GlueHttpRequest() {
        if (error != nullptr)
            g_error_free(error);
    }

    bool IsDone() const {
        return done;
    }

    void CheckThrowError() {
        GError *_error = error;
        error = nullptr;
        ::CheckThrowError(_error);
    }

    GlueHttpResponse MoveResponse() {
        return {status, *headers, std::move(body_string)};
    }

private:
    /* virtual methods from class IstreamHandler */

    size_t OnData(const void *data, size_t length) override {
        body_string.append((const char *)data, length);
        return length;
    }

    void OnEof() override {
        done = true;
    }

    void OnError(GError *_error) override {
        error = _error;
        done = true;
    }

    /* virtual methods from struct http_response_handler */

    void OnResponse(http_status_t _status, struct strmap *_headers,
                    Istream *_body) {
        assert(error == nullptr);

        status = _status;
        headers = _headers;

        if (_body != nullptr) {
            body.Set(*_body, *this);
            body.Read();
        } else
            done = true;
    }

    void OnResponseError(GError *_error) {
        assert(error == nullptr);

        error = _error;
        done = true;
    }

    static void OnResponse(http_status_t status, struct strmap *headers,
                           Istream *body, void *ctx) {
        ((GlueHttpRequest *)ctx)->OnResponse(status, headers, body);
    }

    static void OnResponseError(GError *_error, void *ctx) {
        ((GlueHttpRequest *)ctx)->OnResponseError(_error);
    }

public:
    static const struct http_response_handler handler;
};

constexpr struct http_response_handler GlueHttpRequest::handler = {
    OnResponse,
    OnResponseError,
};

GlueHttpResponse
GlueHttpClient::Request(EventLoop &event_loop,
                        struct pool &p, GlueHttpServerAddress &server,
                        http_method_t method, const char *uri,
                        HttpHeaders &&headers, Istream *body)
{
    struct async_operation_ref async_ref;

    GlueHttpRequest request;
    Request(p, event_loop, server, method, uri, std::move(headers), body,
            GlueHttpRequest::handler, &request, async_ref);
    while (!request.IsDone() && event_loop.LoopOnce()) {}

    request.CheckThrowError();

    return request.MoveResponse();
}
<|endoftext|>