<commit_before>/*
    knode.cpp

    KNode, the KDE newsreader
    Copyright (c) 1999-2000 the KNode authors.
    See file AUTHORS for details

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, US
*/

#include <qpixmap.h>

#include <kconfig.h>
#include <kglobal.h>
#include <kmessagebox.h>
#include <kkeydialog.h>
#include <kedittoolbar.h>
#include <kstdaction.h>
#include <kaccel.h>
#include <kurl.h>
#include <klocale.h>
#include <kwin.h>
#include <kdebug.h>
#include <kmenubar.h>

#include "knode.h"
#include "knfiltermanager.h"
#include "knodeview.h"
#include "knlistview.h"
#include "utilities.h"
#include "knglobals.h"
#include "knconfigmanager.h"
#include "knaccountmanager.h"
#include "knserverinfo.h"
#include "knarticlewidget.h"
#include "knnetaccess.h"

KNGlobals knGlobals;

//======================================================================================================

KNProgress::KNProgress (int desiredHeight, int minValue, int maxValue, int value, KProgress::Orientation orient, QWidget *parent, const char *name)
 : KProgress(minValue, maxValue, value, orient, parent, name), desHeight(desiredHeight)
{
  setFixedWidth(110);
  setFrameStyle(QFrame::Box | QFrame::Raised);
  setLineWidth(1);
  setBackgroundMode(QWidget::PaletteBackground);
  disableProgressBar();
}

KNProgress::~KNProgress()
{}

// 0% and no text
void KNProgress::disableProgressBar()
{
  setFormat(QString::null);
  setValue(0);
  repaint(false);
}

// manual operation
void KNProgress::setProgressBar(int value,const QString& text)
{
  setFormat(text);
  if (value>1000) {
    setValue(1000);
    update();       // circumvent the optimization of setValue
  } else
    setValue(value);
}

QSize KNProgress::sizeHint() const
{
  return QSize(KProgress::sizeHint().width(),desHeight);
}

//===============================================================================================

KNMainWindow::KNMainWindow() : KMainWindow(0,"mainWindow"), b_lockInput(false)
{
  knGlobals.top=this;
  knGlobals.topWidget=this;
  kapp->setMainWidget(this);  // this makes the external viewer windows close on shutdown...

  //statusbar
  KStatusBar *sb=statusBar();
  p_rogBar=new KNProgress(sb->sizeHint().height()-4,0,1000,0, KProgress::Horizontal,sb );
  knGlobals.progressBar=p_rogBar;
  sb->addWidget(p_rogBar);
  sb->insertItem(QString::null, SB_MAIN,2);
  sb->setItemAlignment (SB_MAIN,AlignLeft | AlignVCenter);
  sb->insertItem(QString::null, SB_FILTER,2);
  sb->setItemAlignment (SB_FILTER,AlignLeft | AlignVCenter);
  sb->insertItem(QString::null,SB_GROUP,3);
  sb->setItemAlignment (SB_GROUP,AlignLeft | AlignVCenter);

  //view
  setCaption(i18n("KDE News Reader"));
  v_iew=new KNodeView(this, "knodeView");
  setCentralWidget(v_iew);
  knGlobals.view=v_iew;

  //actions
  a_ccel=new KAccel(this);
  v_iew->a_ctNavReadThrough->plugAccel(a_ccel);
  v_iew->a_ctArtFilterKeyb->plugAccel(a_ccel);
  v_iew->a_ctArtSortHeadersKeyb->plugAccel(a_ccel);
  v_iew->articleView()->setCharsetKeyboardAction()->plugAccel(a_ccel);

  KStdAction::quit(kapp, SLOT(closeAllWindows()), actionCollection());
  KStdAction::keyBindings(this, SLOT(slotConfKeys()), actionCollection());
  KStdAction::configureToolbars(this, SLOT(slotConfToolbar()), actionCollection());
  KStdAction::preferences(this, SLOT(slotSettings()), actionCollection());

  a_ctWinToggleToolbar   = KStdAction::showToolbar(this, SLOT(slotWinToggleToolbar()), actionCollection());
  a_ctWinToggleStatusbar = KStdAction::showStatusbar(this, SLOT(slotWinToggleStatusbar()), actionCollection());

  createGUI("knodeui.rc");
  v_iew->initPopups(this);

  //apply settings
  resize(787,478);  // default optimized for 800x600
  applyMainWindowSettings(KGlobal::config(),"mainWindow_options");
  a_ctWinToggleToolbar->setChecked(!toolBar()->isHidden());
  a_ctWinToggleStatusbar->setChecked(!statusBar()->isHidden());

  // set the keyboard focus indicator on the first item in the Collection View
  // we do this here because everything needs to be properly setup when we
  // call setActive()
  if(v_iew->collectionView()->firstChild()) {
    QListViewItem *i = v_iew->collectionView()->firstChild();
    bool open = i->isOpen();
    v_iew->collectionView()->setActive(i,true);
    i->setOpen(open);
  }
  v_iew->collectionView()->setFocus();

  setStatusMsg();

  if(firstStart()) {  // open the config dialog on the first start
    show();              // the settings dialog must appear in front of the main window!
    slotSettings();
  }
}

KNMainWindow::~KNMainWindow()
{
  delete a_ccel;
}

//================================== GUI =================================

void KNMainWindow::setStatusMsg(const QString& text, int id)
{
  statusBar()->clear();
  if (text.isEmpty() && (id==SB_MAIN))
    if (knGlobals.netAccess->currentMsg().isEmpty())
      statusBar()->changeItem(i18n(" Ready"),SB_MAIN);
    else
      statusBar()->changeItem(knGlobals.netAccess->currentMsg(), SB_MAIN);   // restore the original message
  else
    statusBar()->changeItem(text, id);
}

void KNMainWindow::setStatusHelpMsg(const QString& text)
{
   statusBar()->message(text, 2000);
}

void KNMainWindow::setCursorBusy(bool b)
{
  if(b) kapp->setOverrideCursor(waitCursor);
  else  kapp->restoreOverrideCursor();
}

void KNMainWindow::blockUI(bool b)
{
  b_lockInput = b;
  menuBar()->setEnabled(!b);
  a_ccel->setEnabled(!b);
  v_iew->blockUI(b);
  setCursorBusy(b);
}

// processEvents with some blocking
void KNMainWindow::secureProcessEvents()
{
  b_lockInput = true;
  menuBar()->setEnabled(false);
  a_ccel->setEnabled(false);
  v_iew->blockUI(true);

  kapp->processEvents();

  b_lockInput = false;
  v_iew->blockUI(false);
  menuBar()->setEnabled(true);
  a_ccel->setEnabled(true);
}

QSize KNMainWindow::sizeHint() const
{
  return QSize(759,478);    // default optimized for 800x600
}

void KNMainWindow::slotWinToggleToolbar()
{
  if(toolBar()->isVisible())
    toolBar()->hide();
  else
    toolBar()->show();
}

void KNMainWindow::slotWinToggleStatusbar()
{
  if (statusBar()->isVisible())
    statusBar()->hide();
  else
    statusBar()->show();
}

void KNMainWindow::slotConfKeys()
{
  KActionCollection coll(*actionCollection());

  coll.take(v_iew->a_ctArtSortHeaders);   // hack, remove actions which cant have a shortcut
  coll.take(v_iew->a_ctArtFilter);
  coll.take(v_iew->articleView()->setCharsetAction());

  KKeyDialog::configureKeys(&coll, xmlFile(), true, this);
}

void KNMainWindow::slotConfToolbar()
{
  saveMainWindowSettings(KGlobal::config(),"mainWindow_options");
  KEditToolbar *dlg = new KEditToolbar(guiFactory(),this);

  if (dlg->exec()) {
    createGUI("knodeui.rc");
    v_iew->initPopups(this);
    applyMainWindowSettings(KGlobal::config(),"mainWindow_options");
  }

  delete dlg;
}

void KNMainWindow::slotSettings()
{
  v_iew->c_fgManager->configure();
}

bool KNMainWindow::firstStart()
{
  KConfig *conf=KGlobal::config();
  conf->setGroup("GENERAL");
  QString ver = conf->readEntry("Version");
  if(!ver.isEmpty())
    return false;

  KConfig emailConf("emaildefaults");

  emailConf.setGroup("Defaults");
  QString group = emailConf.readEntry("Profile","Default");

  emailConf.setGroup(QString("PROFILE_%1").arg(group));
  KNConfig::Identity *id=knGlobals.cfgManager->identity();
  id->setName(emailConf.readEntry("FullName"));
  id->setEmail(emailConf.readEntry("EmailAddress").latin1());
  id->setOrga(emailConf.readEntry("Organization"));
  id->setReplyTo(emailConf.readEntry("ReplyAddr"));
  id->save();

  KNServerInfo *smtp=knGlobals.accManager->smtp();
  smtp->setServer(emailConf.readEntry("OutgoingServer").latin1());
  smtp->setPort(25);
  conf->setGroup("MAILSERVER");
  smtp->saveConf(conf);

  conf->setGroup("GENERAL");
  conf->writeEntry("Version", KNODE_VERSION);

  return true;
}

bool KNMainWindow::queryClose()
{
  if(b_lockInput)
    return false;
  if(!v_iew->requestShutdown())
    return false;

  saveMainWindowSettings(KGlobal::config(),"mainWindow_options");
  v_iew->prepareShutdown();

  return true;
}

void KNMainWindow::fontChange( const QFont & )
{
  p_rogBar->setFont(font());    // should be called automatically?
}

void KNMainWindow::paletteChange( const QPalette & )
{
  p_rogBar->setPalette(palette());    // should be called automatically?
}

//--------------------------------

#include "knode.moc"
<commit_msg>found the source of the duplicated toolbars: -  KEditToolbar *dlg = new KEditToolbar(guiFactory(),this); +  KEditToolbar *dlg = new KEditToolbar(actionCollection(), "knodeui.rc");<commit_after>/*
    knode.cpp

    KNode, the KDE newsreader
    Copyright (c) 1999-2000 the KNode authors.
    See file AUTHORS for details

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, US
*/

#include <qpixmap.h>

#include <kconfig.h>
#include <kglobal.h>
#include <kmessagebox.h>
#include <kkeydialog.h>
#include <kedittoolbar.h>
#include <kstdaction.h>
#include <kaccel.h>
#include <kurl.h>
#include <klocale.h>
#include <kwin.h>
#include <kdebug.h>
#include <kmenubar.h>

#include "knode.h"
#include "knfiltermanager.h"
#include "knodeview.h"
#include "knlistview.h"
#include "utilities.h"
#include "knglobals.h"
#include "knconfigmanager.h"
#include "knaccountmanager.h"
#include "knserverinfo.h"
#include "knarticlewidget.h"
#include "knnetaccess.h"

KNGlobals knGlobals;

//======================================================================================================

KNProgress::KNProgress (int desiredHeight, int minValue, int maxValue, int value, KProgress::Orientation orient, QWidget *parent, const char *name)
 : KProgress(minValue, maxValue, value, orient, parent, name), desHeight(desiredHeight)
{
  setFixedWidth(110);
  setFrameStyle(QFrame::Box | QFrame::Raised);
  setLineWidth(1);
  setBackgroundMode(QWidget::PaletteBackground);
  disableProgressBar();
}

KNProgress::~KNProgress()
{}

// 0% and no text
void KNProgress::disableProgressBar()
{
  setFormat(QString::null);
  setValue(0);
  repaint(false);
}

// manual operation
void KNProgress::setProgressBar(int value,const QString& text)
{
  setFormat(text);
  if (value>1000) {
    setValue(1000);
    update();       // circumvent the optimization of setValue
  } else
    setValue(value);
}

QSize KNProgress::sizeHint() const
{
  return QSize(KProgress::sizeHint().width(),desHeight);
}

//===============================================================================================

KNMainWindow::KNMainWindow() : KMainWindow(0,"mainWindow"), b_lockInput(false)
{
  knGlobals.top=this;
  knGlobals.topWidget=this;
  kapp->setMainWidget(this);  // this makes the external viewer windows close on shutdown...

  //statusbar
  KStatusBar *sb=statusBar();
  p_rogBar=new KNProgress(sb->sizeHint().height()-4,0,1000,0, KProgress::Horizontal,sb );
  knGlobals.progressBar=p_rogBar;
  sb->addWidget(p_rogBar);
  sb->insertItem(QString::null, SB_MAIN,2);
  sb->setItemAlignment (SB_MAIN,AlignLeft | AlignVCenter);
  sb->insertItem(QString::null, SB_FILTER,2);
  sb->setItemAlignment (SB_FILTER,AlignLeft | AlignVCenter);
  sb->insertItem(QString::null,SB_GROUP,3);
  sb->setItemAlignment (SB_GROUP,AlignLeft | AlignVCenter);

  //view
  setCaption(i18n("KDE News Reader"));
  v_iew=new KNodeView(this, "knodeView");
  setCentralWidget(v_iew);
  knGlobals.view=v_iew;

  //actions
  a_ccel=new KAccel(this);
  v_iew->a_ctNavReadThrough->plugAccel(a_ccel);
  v_iew->a_ctArtFilterKeyb->plugAccel(a_ccel);
  v_iew->a_ctArtSortHeadersKeyb->plugAccel(a_ccel);
  v_iew->articleView()->setCharsetKeyboardAction()->plugAccel(a_ccel);

  KStdAction::quit(kapp, SLOT(closeAllWindows()), actionCollection());
  KStdAction::keyBindings(this, SLOT(slotConfKeys()), actionCollection());
  KStdAction::configureToolbars(this, SLOT(slotConfToolbar()), actionCollection());
  KStdAction::preferences(this, SLOT(slotSettings()), actionCollection());

  a_ctWinToggleToolbar   = KStdAction::showToolbar(this, SLOT(slotWinToggleToolbar()), actionCollection());
  a_ctWinToggleStatusbar = KStdAction::showStatusbar(this, SLOT(slotWinToggleStatusbar()), actionCollection());

  createGUI("knodeui.rc");
  v_iew->initPopups(this);

  //apply settings
  resize(787,478);  // default optimized for 800x600
  applyMainWindowSettings(KGlobal::config(),"mainWindow_options");
  a_ctWinToggleToolbar->setChecked(!toolBar()->isHidden());
  a_ctWinToggleStatusbar->setChecked(!statusBar()->isHidden());

  // set the keyboard focus indicator on the first item in the Collection View
  // we do this here because everything needs to be properly setup when we
  // call setActive()
  if(v_iew->collectionView()->firstChild()) {
    QListViewItem *i = v_iew->collectionView()->firstChild();
    bool open = i->isOpen();
    v_iew->collectionView()->setActive(i,true);
    i->setOpen(open);
  }
  v_iew->collectionView()->setFocus();

  setStatusMsg();

  if(firstStart()) {  // open the config dialog on the first start
    show();              // the settings dialog must appear in front of the main window!
    slotSettings();
  }
}

KNMainWindow::~KNMainWindow()
{
  delete a_ccel;
}

//================================== GUI =================================

void KNMainWindow::setStatusMsg(const QString& text, int id)
{
  statusBar()->clear();
  if (text.isEmpty() && (id==SB_MAIN))
    if (knGlobals.netAccess->currentMsg().isEmpty())
      statusBar()->changeItem(i18n(" Ready"),SB_MAIN);
    else
      statusBar()->changeItem(knGlobals.netAccess->currentMsg(), SB_MAIN);   // restore the original message
  else
    statusBar()->changeItem(text, id);
}

void KNMainWindow::setStatusHelpMsg(const QString& text)
{
   statusBar()->message(text, 2000);
}

void KNMainWindow::setCursorBusy(bool b)
{
  if(b) kapp->setOverrideCursor(waitCursor);
  else  kapp->restoreOverrideCursor();
}

void KNMainWindow::blockUI(bool b)
{
  b_lockInput = b;
  menuBar()->setEnabled(!b);
  a_ccel->setEnabled(!b);
  v_iew->blockUI(b);
  setCursorBusy(b);
}

// processEvents with some blocking
void KNMainWindow::secureProcessEvents()
{
  b_lockInput = true;
  menuBar()->setEnabled(false);
  a_ccel->setEnabled(false);
  v_iew->blockUI(true);

  kapp->processEvents();

  b_lockInput = false;
  v_iew->blockUI(false);
  menuBar()->setEnabled(true);
  a_ccel->setEnabled(true);
}

QSize KNMainWindow::sizeHint() const
{
  return QSize(759,478);    // default optimized for 800x600
}

void KNMainWindow::slotWinToggleToolbar()
{
  if(toolBar()->isVisible())
    toolBar()->hide();
  else
    toolBar()->show();
}

void KNMainWindow::slotWinToggleStatusbar()
{
  if (statusBar()->isVisible())
    statusBar()->hide();
  else
    statusBar()->show();
}

void KNMainWindow::slotConfKeys()
{
  KActionCollection coll(*actionCollection());

  coll.take(v_iew->a_ctArtSortHeaders);   // hack, remove actions which cant have a shortcut
  coll.take(v_iew->a_ctArtFilter);
  coll.take(v_iew->articleView()->setCharsetAction());

  KKeyDialog::configureKeys(&coll, xmlFile(), true, this);
}

void KNMainWindow::slotConfToolbar()
{
  saveMainWindowSettings(KGlobal::config(),"mainWindow_options");
  KEditToolbar *dlg = new KEditToolbar(actionCollection(), "knodeui.rc");

  if (dlg->exec()) {
    createGUI("knodeui.rc");
    v_iew->initPopups(this);
    applyMainWindowSettings(KGlobal::config(),"mainWindow_options");
  }

  delete dlg;
}

void KNMainWindow::slotSettings()
{
  v_iew->c_fgManager->configure();
}

bool KNMainWindow::firstStart()
{
  KConfig *conf=KGlobal::config();
  conf->setGroup("GENERAL");
  QString ver = conf->readEntry("Version");
  if(!ver.isEmpty())
    return false;

  KConfig emailConf("emaildefaults");

  emailConf.setGroup("Defaults");
  QString group = emailConf.readEntry("Profile","Default");

  emailConf.setGroup(QString("PROFILE_%1").arg(group));
  KNConfig::Identity *id=knGlobals.cfgManager->identity();
  id->setName(emailConf.readEntry("FullName"));
  id->setEmail(emailConf.readEntry("EmailAddress").latin1());
  id->setOrga(emailConf.readEntry("Organization"));
  id->setReplyTo(emailConf.readEntry("ReplyAddr"));
  id->save();

  KNServerInfo *smtp=knGlobals.accManager->smtp();
  smtp->setServer(emailConf.readEntry("OutgoingServer").latin1());
  smtp->setPort(25);
  conf->setGroup("MAILSERVER");
  smtp->saveConf(conf);

  conf->setGroup("GENERAL");
  conf->writeEntry("Version", KNODE_VERSION);

  return true;
}

bool KNMainWindow::queryClose()
{
  if(b_lockInput)
    return false;
  if(!v_iew->requestShutdown())
    return false;

  saveMainWindowSettings(KGlobal::config(),"mainWindow_options");
  v_iew->prepareShutdown();

  return true;
}

void KNMainWindow::fontChange( const QFont & )
{
  p_rogBar->setFont(font());    // should be called automatically?
}

void KNMainWindow::paletteChange( const QPalette & )
{
  p_rogBar->setPalette(palette());    // should be called automatically?
}

//--------------------------------

#include "knode.moc"
<|endoftext|>


<commit_before><commit_msg>Reworked connect/disconnect detection logic, when windows scans for new devices<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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 "config.h"

#include "base/compiler_specific.h"

MSVC_PUSH_WARNING_LEVEL(0);
#include "EventNames.h"
#include "EventTarget.h"
#include "KeyboardCodes.h"
#include "KeyboardEvent.h"
MSVC_POP_WARNING();

#undef LOG

#include "webkit/glue/editor_client_impl.h"
#include "webkit/glue/event_conversion.h"
#include "webkit/glue/webinputevent.h"
#include "testing/gtest/include/gtest/gtest.h"

using WebCore::PlatformKeyboardEvent;
using WebCore::KeyboardEvent;

class KeyboardTest : public testing::Test {
 public:
  void SetUp() {
    WTF::initializeThreading();
    WebCore::EventNames::init();
  }

  // Pass a WebKeyboardEvent into the EditorClient and get back the string
  // name of which editing event that key causes.
  // E.g., sending in the enter key gives back "InsertNewline".
  const char* InterpretKeyEvent(
      const WebKeyboardEvent& keyboard_event,
      PlatformKeyboardEvent::Type key_type) {
    EditorClientImpl editor_impl(NULL);

    MakePlatformKeyboardEvent evt(keyboard_event);
    evt.SetKeyType(key_type);
    RefPtr<KeyboardEvent> keyboardEvent = KeyboardEvent::create(evt, NULL);
    return editor_impl.interpretKeyEvent(keyboardEvent.get());
  }

  // Set up a WebKeyboardEvent KEY_DOWN event with key code and modifiers.
  void SetupKeyDownEvent(WebKeyboardEvent* keyboard_event,
                         char key_code,
                         int modifiers) {
    keyboard_event->key_code = key_code;
    keyboard_event->modifiers = modifiers;
    keyboard_event->type = WebInputEvent::KEY_DOWN;
#if defined(OS_LINUX)
    keyboard_event->text = key_code;
#endif
  }

  // Like InterpretKeyEvent, but with pressing down ctrl+|key_code|.
  const char* InterpretCtrlKeyPress(char key_code) {
    WebKeyboardEvent keyboard_event;
    SetupKeyDownEvent(&keyboard_event, key_code, WebInputEvent::CTRL_KEY);
    return InterpretKeyEvent(keyboard_event, PlatformKeyboardEvent::RawKeyDown);
  }

  // Like InterpretKeyEvent, but with typing a tab.
  const char* InterpretTab(int modifiers) {
    WebKeyboardEvent keyboard_event;
    SetupKeyDownEvent(&keyboard_event, '\t', modifiers);
    return InterpretKeyEvent(keyboard_event, PlatformKeyboardEvent::Char);
  }

  // Like InterpretKeyEvent, but with typing a newline.
  const char* InterpretNewLine(int modifiers) {
    WebKeyboardEvent keyboard_event;
    SetupKeyDownEvent(&keyboard_event, '\r', modifiers);
    return InterpretKeyEvent(keyboard_event, PlatformKeyboardEvent::Char);
  }

  // A name for "no modifiers set".
  static const int kNoModifiers = 0;
};

TEST_F(KeyboardTest, TestCtrlReturn) {
  EXPECT_STREQ("InsertNewline", InterpretCtrlKeyPress(0xD));
}

TEST_F(KeyboardTest, TestCtrlZ) {
  EXPECT_STREQ("Undo", InterpretCtrlKeyPress('Z'));
}

TEST_F(KeyboardTest, TestCtrlA) {
  EXPECT_STREQ("SelectAll", InterpretCtrlKeyPress('A'));
}

TEST_F(KeyboardTest, TestCtrlX) {
  EXPECT_STREQ("Cut", InterpretCtrlKeyPress('X'));
}

TEST_F(KeyboardTest, TestCtrlC) {
  EXPECT_STREQ("Copy", InterpretCtrlKeyPress('C'));
}

TEST_F(KeyboardTest, TestCtrlV) {
  EXPECT_STREQ("Paste", InterpretCtrlKeyPress('V'));
}

TEST_F(KeyboardTest, TestEscape) {
  WebKeyboardEvent keyboard_event;
  SetupKeyDownEvent(&keyboard_event, WebCore::VKEY_ESCAPE, kNoModifiers);

  const char* result = InterpretKeyEvent(keyboard_event,
                                         PlatformKeyboardEvent::RawKeyDown);
  EXPECT_STREQ("Cancel", result);
}

TEST_F(KeyboardTest, TestRedo) {
  EXPECT_STREQ(InterpretCtrlKeyPress('Y'), "Redo");
}

TEST_F(KeyboardTest, TestInsertTab) {
  EXPECT_STREQ("InsertTab", InterpretTab(kNoModifiers));
}

TEST_F(KeyboardTest, TestInsertBackTab) {
  EXPECT_STREQ("InsertBacktab", InterpretTab(WebInputEvent::SHIFT_KEY));
}

TEST_F(KeyboardTest, TestInsertNewline) {
  EXPECT_STREQ("InsertNewline", InterpretNewLine(kNoModifiers));
}

TEST_F(KeyboardTest, TestInsertNewline2) {
  EXPECT_STREQ("InsertNewline", InterpretNewLine(WebInputEvent::CTRL_KEY));
}

TEST_F(KeyboardTest, TestInsertlinebreak) {
  EXPECT_STREQ("InsertLineBreak", InterpretNewLine(WebInputEvent::SHIFT_KEY));
}

TEST_F(KeyboardTest, TestInsertNewline3) {
  EXPECT_STREQ("InsertNewline", InterpretNewLine(WebInputEvent::ALT_KEY));
}

TEST_F(KeyboardTest, TestInsertNewline4) {
  int modifiers = WebInputEvent::ALT_KEY | WebInputEvent::SHIFT_KEY;
  const char* result = InterpretNewLine(modifiers);
  EXPECT_STREQ(result, "InsertNewline");
}
<commit_msg>Make test_shell_test's KeyboardTest work on the Mac Review URL: http://codereview.chromium.org/14001<commit_after>// Copyright (c) 2006-2008 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 "config.h"

#include "base/compiler_specific.h"

MSVC_PUSH_WARNING_LEVEL(0);
#include "EventNames.h"
#include "EventTarget.h"
#include "KeyboardCodes.h"
#include "KeyboardEvent.h"
MSVC_POP_WARNING();

#undef LOG

#include "webkit/glue/editor_client_impl.h"
#include "webkit/glue/event_conversion.h"
#include "webkit/glue/webinputevent.h"
#include "testing/gtest/include/gtest/gtest.h"

using WebCore::PlatformKeyboardEvent;
using WebCore::KeyboardEvent;

class KeyboardTest : public testing::Test {
 public:
  void SetUp() {
    WTF::initializeThreading();
    WebCore::EventNames::init();
  }

  // Pass a WebKeyboardEvent into the EditorClient and get back the string
  // name of which editing event that key causes.
  // E.g., sending in the enter key gives back "InsertNewline".
  const char* InterpretKeyEvent(
      const WebKeyboardEvent& keyboard_event,
      PlatformKeyboardEvent::Type key_type) {
    EditorClientImpl editor_impl(NULL);

    MakePlatformKeyboardEvent evt(keyboard_event);
    evt.SetKeyType(key_type);
    RefPtr<KeyboardEvent> keyboardEvent = KeyboardEvent::create(evt, NULL);
    return editor_impl.interpretKeyEvent(keyboardEvent.get());
  }

  // Set up a WebKeyboardEvent KEY_DOWN event with key code and modifiers.
  void SetupKeyDownEvent(WebKeyboardEvent* keyboard_event,
                         char key_code,
                         int modifiers) {
    keyboard_event->key_code = key_code;
    keyboard_event->modifiers = modifiers;
    keyboard_event->type = WebInputEvent::KEY_DOWN;
#if defined(OS_LINUX)
    keyboard_event->text = key_code;
#elif defined(OS_MACOSX)
    keyboard_event->text.clear();
    keyboard_event->text.push_back(key_code);
#endif
  }

  // Like InterpretKeyEvent, but with pressing down OSModifier+|key_code|.
  // OSModifier is the platform's standard modifier key: control on most
  // platforms, but meta (command) on Mac.
  const char* InterpretOSModifierKeyPress(char key_code) {
    WebKeyboardEvent keyboard_event;
#if defined(OS_WIN) || defined(OS_LINUX)
    WebInputEvent::Modifiers os_modifier = WebInputEvent::CTRL_KEY;
#elif defined(OS_MACOSX)
    WebInputEvent::Modifiers os_modifier = WebInputEvent::META_KEY;
#endif
    SetupKeyDownEvent(&keyboard_event, key_code, os_modifier);
    return InterpretKeyEvent(keyboard_event, PlatformKeyboardEvent::RawKeyDown);
  }

  // Like InterpretKeyEvent, but with pressing down ctrl+|key_code|.
  const char* InterpretCtrlKeyPress(char key_code) {
    WebKeyboardEvent keyboard_event;
    SetupKeyDownEvent(&keyboard_event, key_code, WebInputEvent::CTRL_KEY);
    return InterpretKeyEvent(keyboard_event, PlatformKeyboardEvent::RawKeyDown);
  }

  // Like InterpretKeyEvent, but with typing a tab.
  const char* InterpretTab(int modifiers) {
    WebKeyboardEvent keyboard_event;
    SetupKeyDownEvent(&keyboard_event, '\t', modifiers);
    return InterpretKeyEvent(keyboard_event, PlatformKeyboardEvent::Char);
  }

  // Like InterpretKeyEvent, but with typing a newline.
  const char* InterpretNewLine(int modifiers) {
    WebKeyboardEvent keyboard_event;
    SetupKeyDownEvent(&keyboard_event, '\r', modifiers);
    return InterpretKeyEvent(keyboard_event, PlatformKeyboardEvent::Char);
  }

  // A name for "no modifiers set".
  static const int kNoModifiers = 0;
};

TEST_F(KeyboardTest, TestCtrlReturn) {
  EXPECT_STREQ("InsertNewline", InterpretCtrlKeyPress(0xD));
}

TEST_F(KeyboardTest, TestOSModifierZ) {
  EXPECT_STREQ("Undo", InterpretOSModifierKeyPress('Z'));
}

TEST_F(KeyboardTest, TestOSModifierY) {
  EXPECT_STREQ("Redo", InterpretOSModifierKeyPress('Y'));
}

TEST_F(KeyboardTest, TestOSModifierA) {
  EXPECT_STREQ("SelectAll", InterpretOSModifierKeyPress('A'));
}

TEST_F(KeyboardTest, TestOSModifierX) {
  EXPECT_STREQ("Cut", InterpretOSModifierKeyPress('X'));
}

TEST_F(KeyboardTest, TestOSModifierC) {
  EXPECT_STREQ("Copy", InterpretOSModifierKeyPress('C'));
}

TEST_F(KeyboardTest, TestOSModifierV) {
  EXPECT_STREQ("Paste", InterpretOSModifierKeyPress('V'));
}

TEST_F(KeyboardTest, TestEscape) {
  WebKeyboardEvent keyboard_event;
  SetupKeyDownEvent(&keyboard_event, WebCore::VKEY_ESCAPE, kNoModifiers);

  const char* result = InterpretKeyEvent(keyboard_event,
                                         PlatformKeyboardEvent::RawKeyDown);
  EXPECT_STREQ("Cancel", result);
}

TEST_F(KeyboardTest, TestInsertTab) {
  EXPECT_STREQ("InsertTab", InterpretTab(kNoModifiers));
}

TEST_F(KeyboardTest, TestInsertBackTab) {
  EXPECT_STREQ("InsertBacktab", InterpretTab(WebInputEvent::SHIFT_KEY));
}

TEST_F(KeyboardTest, TestInsertNewline) {
  EXPECT_STREQ("InsertNewline", InterpretNewLine(kNoModifiers));
}

TEST_F(KeyboardTest, TestInsertNewline2) {
  EXPECT_STREQ("InsertNewline", InterpretNewLine(WebInputEvent::CTRL_KEY));
}

TEST_F(KeyboardTest, TestInsertLineBreak) {
  EXPECT_STREQ("InsertLineBreak", InterpretNewLine(WebInputEvent::SHIFT_KEY));
}

TEST_F(KeyboardTest, TestInsertNewline3) {
  EXPECT_STREQ("InsertNewline", InterpretNewLine(WebInputEvent::ALT_KEY));
}

TEST_F(KeyboardTest, TestInsertNewline4) {
  int modifiers = WebInputEvent::ALT_KEY | WebInputEvent::SHIFT_KEY;
  const char* result = InterpretNewLine(modifiers);
  EXPECT_STREQ("InsertNewline", result);
}
<|endoftext|>


<commit_before>// Copyright 2015 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.

// This file contains the default suppressions for LeakSanitizer.
// You can also pass additional suppressions via LSAN_OPTIONS:
// LSAN_OPTIONS=suppressions=/path/to/suppressions. Please refer to
// http://dev.chromium.org/developers/testing/leaksanitizer for more info.

#if defined(LEAK_SANITIZER)

// Please make sure the code below declares a single string variable
// kLSanDefaultSuppressions which contains LSan suppressions delimited by
// newlines. See http://dev.chromium.org/developers/testing/leaksanitizer
// for the instructions on writing suppressions.
char kLSanDefaultSuppressions[] =
// Intentional leak used as sanity test for Valgrind/memcheck.
"leak:base::ToolsSanityTest_MemoryLeak_Test::TestBody\n"

// ================ Leaks in third-party code ================

// False positives in libfontconfig. http://crbug.com/39050
"leak:libfontconfig\n"

// Leaks in Nvidia's libGL.
"leak:libGL.so\n"

// A small leak in V8. http://crbug.com/46571#c9
"leak:blink::V8GCController::collectGarbage\n"

// TODO(earthdok): revisit NSS suppressions after the switch to BoringSSL
// NSS leaks in CertDatabaseNSSTest tests. http://crbug.com/51988
"leak:net::NSSCertDatabase::ImportFromPKCS12\n"
"leak:net::NSSCertDatabase::ListCerts\n"
"leak:net::NSSCertDatabase::DeleteCertAndKey\n"
"leak:crypto::ScopedTestNSSDB::ScopedTestNSSDB\n"
// Another leak due to not shutting down NSS properly. http://crbug.com/124445
"leak:error_get_my_stack\n"
// The NSS suppressions above will not fire when the fast stack unwinder is
// used, because it can't unwind through NSS libraries. Apply blanket
// suppressions for now.
"leak:libnssutil3\n"
"leak:libnspr4\n"
"leak:libnss3\n"
"leak:libplds4\n"
"leak:libnssckbi\n"

// XRandR has several one time leaks.
"leak:libxrandr\n"

// xrandr leak. http://crbug.com/119677
"leak:XRRFindDisplay\n"

// Suppressions for objects which can be owned by the V8 heap. This is a
// temporary workaround until LeakSanitizer supports the V8 heap.
// Those should only fire in (browser)tests. If you see one of them in Chrome,
// then it's a real leak.
// http://crbug.com/328552
"leak:WTF::StringImpl::createUninitialized\n"
"leak:WTF::StringImpl::create8BitIfPossible\n"
"leak:blink::MouseEvent::create\n"
"leak:blink::*::*GetterCallback\n"
"leak:blink::CSSComputedStyleDeclaration::create\n"
"leak:blink::V8PerIsolateData::ensureDomInJSContext\n"
"leak:gin/object_template_builder.h\n"
"leak:gin::internal::Dispatcher\n"
"leak:blink::LocalDOMWindow::getComputedStyle\n"
// This should really be RemoteDOMWindow::create, but symbolization is
// weird in release builds. https://crbug.com/484760
"leak:blink::RemoteFrame::create\n"
// Likewise, this should really be blink::WindowProxy::initializeIfNeeded.
// https://crbug.com/484760
"leak:blink::WindowProxy::createContext\n"

// http://crbug.com/356785
"leak:content::RenderViewImplTest_DecideNavigationPolicyForWebUI_Test::TestBody\n"

// ================ Leaks in Chromium code ================
// PLEASE DO NOT ADD SUPPRESSIONS FOR NEW LEAKS.
// Instead, commits that introduce memory leaks should be reverted. Suppressing
// the leak is acceptable in some cases when reverting is impossible, i.e. when
// enabling leak detection for the first time for a test target with
// pre-existing leaks.

// Small test-only leak in ppapi_unittests. http://crbug.com/258113
"leak:ppapi::proxy::PPP_Instance_Private_ProxyTest_PPPInstancePrivate_Test\n"

// http://crbug.com/322671
"leak:content::SpeechRecognitionBrowserTest::SetUpOnMainThread\n"

// http://crbug.com/355641
"leak:TrayAccessibilityTest\n"

// http://crbug.com/354644
"leak:CertificateViewerUITest::ShowModalCertificateViewer\n"

// http://crbug.com/356306
"leak:content::SetProcessTitleFromCommandLine\n"

// PLEASE READ ABOVE BEFORE ADDING NEW SUPPRESSIONS.

// End of suppressions.
;  // Please keep this semicolon.

#endif  // LEAK_SANITIZER
<commit_msg>Add a leak sanitizer suppression for blink::ResourceFetcher::garbageCollectDocumentResources<commit_after>// Copyright 2015 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.

// This file contains the default suppressions for LeakSanitizer.
// You can also pass additional suppressions via LSAN_OPTIONS:
// LSAN_OPTIONS=suppressions=/path/to/suppressions. Please refer to
// http://dev.chromium.org/developers/testing/leaksanitizer for more info.

#if defined(LEAK_SANITIZER)

// Please make sure the code below declares a single string variable
// kLSanDefaultSuppressions which contains LSan suppressions delimited by
// newlines. See http://dev.chromium.org/developers/testing/leaksanitizer
// for the instructions on writing suppressions.
char kLSanDefaultSuppressions[] =
// Intentional leak used as sanity test for Valgrind/memcheck.
"leak:base::ToolsSanityTest_MemoryLeak_Test::TestBody\n"

// ================ Leaks in third-party code ================

// False positives in libfontconfig. http://crbug.com/39050
"leak:libfontconfig\n"

// Leaks in Nvidia's libGL.
"leak:libGL.so\n"

// A small leak in V8. http://crbug.com/46571#c9
"leak:blink::V8GCController::collectGarbage\n"

// TODO(earthdok): revisit NSS suppressions after the switch to BoringSSL
// NSS leaks in CertDatabaseNSSTest tests. http://crbug.com/51988
"leak:net::NSSCertDatabase::ImportFromPKCS12\n"
"leak:net::NSSCertDatabase::ListCerts\n"
"leak:net::NSSCertDatabase::DeleteCertAndKey\n"
"leak:crypto::ScopedTestNSSDB::ScopedTestNSSDB\n"
// Another leak due to not shutting down NSS properly. http://crbug.com/124445
"leak:error_get_my_stack\n"
// The NSS suppressions above will not fire when the fast stack unwinder is
// used, because it can't unwind through NSS libraries. Apply blanket
// suppressions for now.
"leak:libnssutil3\n"
"leak:libnspr4\n"
"leak:libnss3\n"
"leak:libplds4\n"
"leak:libnssckbi\n"

// XRandR has several one time leaks.
"leak:libxrandr\n"

// xrandr leak. http://crbug.com/119677
"leak:XRRFindDisplay\n"

// Suppressions for objects which can be owned by the V8 heap. This is a
// temporary workaround until LeakSanitizer supports the V8 heap.
// Those should only fire in (browser)tests. If you see one of them in Chrome,
// then it's a real leak.
// http://crbug.com/328552
"leak:WTF::StringImpl::createUninitialized\n"
"leak:WTF::StringImpl::create8BitIfPossible\n"
"leak:blink::MouseEvent::create\n"
"leak:blink::*::*GetterCallback\n"
"leak:blink::CSSComputedStyleDeclaration::create\n"
"leak:blink::V8PerIsolateData::ensureDomInJSContext\n"
"leak:gin/object_template_builder.h\n"
"leak:gin::internal::Dispatcher\n"
"leak:blink::LocalDOMWindow::getComputedStyle\n"
// This should really be RemoteDOMWindow::create, but symbolization is
// weird in release builds. https://crbug.com/484760
"leak:blink::RemoteFrame::create\n"
// Likewise, this should really be blink::WindowProxy::initializeIfNeeded.
// https://crbug.com/484760
"leak:blink::WindowProxy::createContext\n"

// http://crbug.com/356785
"leak:content::RenderViewImplTest_DecideNavigationPolicyForWebUI_Test::TestBody\n"

// ================ Leaks in Chromium code ================
// PLEASE DO NOT ADD SUPPRESSIONS FOR NEW LEAKS.
// Instead, commits that introduce memory leaks should be reverted. Suppressing
// the leak is acceptable in some cases when reverting is impossible, i.e. when
// enabling leak detection for the first time for a test target with
// pre-existing leaks.

// Small test-only leak in ppapi_unittests. http://crbug.com/258113
"leak:ppapi::proxy::PPP_Instance_Private_ProxyTest_PPPInstancePrivate_Test\n"

// http://crbug.com/322671
"leak:content::SpeechRecognitionBrowserTest::SetUpOnMainThread\n"

// http://crbug.com/355641
"leak:TrayAccessibilityTest\n"

// http://crbug.com/354644
"leak:CertificateViewerUITest::ShowModalCertificateViewer\n"

// http://crbug.com/356306
"leak:content::SetProcessTitleFromCommandLine\n"

// http://crbug.com/506433
"leak:blink::ResourceFetcher::garbageCollectDocumentResources\n"

// PLEASE READ ABOVE BEFORE ADDING NEW SUPPRESSIONS.

// End of suppressions.
;  // Please keep this semicolon.

#endif  // LEAK_SANITIZER
<|endoftext|>


<commit_before>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "StitchedSubgenerators.h"

#include "CastUniquePointer.h"
#include "MooseUtils.h"
#include "FileMeshGenerator.h"

#include "libmesh/replicated_mesh.h"

registerMooseObject("MooseApp", StitchedSubgenerators);

defineLegacyParams(StitchedSubgenerators);

InputParameters
StitchedSubgenerators::validParams()
{
  InputParameters params = StitchedMeshGenerator::validParams();

  params.makeParamNotRequired<std::vector<MeshGeneratorName>>("inputs");

  params.addRequiredParam<std::vector<std::string>>("input_files", "The input mesh filenames");
  return params;
}

StitchedSubgenerators::StitchedSubgenerators(const InputParameters & parameters)
  : StitchedMeshGenerator(parameters),
    _input_filenames(getParam<std::vector<std::string>>("input_files"))
{
  MooseApp &app = this->getMooseApp();
  const std::string sg_name_base = "subgenerator_";

  // InputParameters filemesh_params = FileMeshGenerator::validParams();
  InputParameters filemesh_params = _app.getFactory().getValidParams("FileMeshGenerator");

  // Create and add MeshGenerators for the input meshes
  _mesh_ptrs.reserve(_input_filenames.size());
  int sg_num = 0;
  for (auto & input_filename : _input_filenames)
    {
      filemesh_params.set<MeshFileName>("file") = input_filename;

      const std::string sg_name = sg_name_base + std::to_string(sg_num++);

      app.addMeshGenerator("FileMeshGenerator", sg_name, filemesh_params);

      _mesh_ptrs.push_back(&getMeshByName(sg_name));
    }
}
<commit_msg>Use addMeshSubgenerator in test code<commit_after>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "StitchedSubgenerators.h"

#include "CastUniquePointer.h"
#include "MooseUtils.h"
#include "FileMeshGenerator.h"

#include "libmesh/replicated_mesh.h"

registerMooseObject("MooseApp", StitchedSubgenerators);

defineLegacyParams(StitchedSubgenerators);

InputParameters
StitchedSubgenerators::validParams()
{
  InputParameters params = StitchedMeshGenerator::validParams();

  params.makeParamNotRequired<std::vector<MeshGeneratorName>>("inputs");

  params.addRequiredParam<std::vector<std::string>>("input_files", "The input mesh filenames");
  return params;
}

StitchedSubgenerators::StitchedSubgenerators(const InputParameters & parameters)
  : StitchedMeshGenerator(parameters),
    _input_filenames(getParam<std::vector<std::string>>("input_files"))
{
  const std::string sg_name_base = "subgenerator_";

  // Create and add MeshGenerators for the input meshes
  _mesh_ptrs.reserve(_input_filenames.size());
  int sg_num = 0;
  for (auto & input_filename : _input_filenames)
    _mesh_ptrs.push_back(
      &this->addMeshSubgenerator("FileMeshGenerator",
                                 sg_name_base + std::to_string(sg_num++),
                                 "file", MeshFileName(input_filename)));
}
<|endoftext|>


<commit_before>#ifndef __PROCESS_HTTP_HPP__
#define __PROCESS_HTTP_HPP__

#include <cctype>
#include <cstdlib>
#include <iomanip>
#include <sstream>
#include <string>

#include <limits.h>

#include <process/future.hpp>
#include <process/pid.hpp>

#include <stout/hashmap.hpp>
#include <stout/json.hpp>
#include <stout/option.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>
#include <stout/try.hpp>

namespace process {
namespace http {

struct Request
{
  // TODO(benh): Add major/minor version.
  hashmap<std::string, std::string> headers;
  std::string method;
  std::string path;
  std::string url;
  std::string fragment;
  hashmap<std::string, std::string> query;
  std::string body;
  bool keepAlive;
};

struct Response
{
  Response(const std::string& _body = "")
    : type(BODY),
      body(_body)
  {
    if (!body.empty()) {
      headers["Content-Length"] = stringify(body.size());
    }
  }

  // TODO(benh): Add major/minor version.
  std::string status;
  hashmap<std::string, std::string> headers;

  // TODO(benh): Make body a stream (channel) instead, and allow a
  // response to be returned without forcing the stream to be
  // finished.

  // Either provide a 'body' or an absolute 'path' to a file. If a
  // path is specified then we will attempt to perform a 'sendfile'
  // operation on the file. In either case you are expected to
  // properly specify the 'Content-Type' header, but the
  // 'Content-Length' header will be filled in for you if you specify
  // a path. Distinguish between the two using 'type' below.
  enum {
    BODY,
    PATH
  } type;

  std::string body;
  std::string path;
};

struct OK : Response
{
  OK(const std::string& body = "") : Response(body)
  {
    status = "200 OK";
  }

  OK(const JSON::Value& value, const Option<std::string>& jsonp) : Response()
  {
    status = "200 OK";

    std::ostringstream out;

    if (jsonp.isSome()) {
      out << jsonp.get() << "(";
    }

    JSON::render(out, value);

    if (jsonp.isSome()) {
      out << ");";
      headers["Content-Type"] = "text/javascript";
    } else {
      headers["Content-Type"] = "application/json";
    }

    headers["Content-Length"] = stringify(out.str().size());
    body = out.str().data();
  }
};

struct TemporaryRedirect : Response
{
  TemporaryRedirect(const std::string& url) : Response("")
  {
    status = "307 Temporary Redirect";
    headers["Location"] = url;
  }
};

struct BadRequest : Response
{
  BadRequest(const std::string& body = "") : Response(body)
  {
    status = "400 Bad Request";
  }
};

struct NotFound : Response
{
  NotFound(const std::string& body = "") : Response(body)
  {
    status = "404 Not Found";
  }
};

struct InternalServerError : Response
{
  InternalServerError(const std::string& body = "") : Response(body)
  {
    status = "500 Internal Server Error";
  }
};

struct ServiceUnavailable : Response
{
  ServiceUnavailable(const std::string& body = "") : Response(body)
  {
    status = "503 Service Unavailable";
  }
};

namespace query {

// Parses an HTTP query string into a map. For example:
//
//   parse("foo=1;bar=2;baz;foo=3")
//
// Would return a map with the following:
//   bar: "2"
//   baz: ""
//   foo: "3"
//
// We use the last value for a key for simplicity, since the RFC does not
// specify how to handle duplicate keys:
// http://en.wikipedia.org/wiki/Query_string
// TODO(bmahler): If needed, investigate populating the query map inline
// for better performance.
inline hashmap<std::string, std::string> parse(const std::string& query)
{
  hashmap<std::string, std::string> result;

  const std::vector<std::string>& tokens = strings::tokenize(query, ";&");
  foreach (const std::string& token, tokens) {
    const std::vector<std::string>& pairs = strings::split(token, "=");
    if (pairs.size() == 2) {
      result[pairs[0]] = pairs[1];
    } else if (pairs.size() == 1) {
      result[pairs[0]] = "";
    }
  }

  return result;
}

// Returns a percent-encoded string according to RFC 3986.
// The input string must not already be percent encoded.
inline std::string encode(const std::string& s)
{
  std::ostringstream out;

  foreach (unsigned char c, s) {
    switch (c) {
      // Reserved characters.
      case '$':
      case '&':
      case '+':
      case ',':
      case '/':
      case ':':
      case ';':
      case '=':
      case '?':
      case '@':
      // Unsafe characters.
      case ' ':
      case '"':
      case '<':
      case '>':
      case '#':
      case '%':
      case '{':
      case '}':
      case '|':
      case '\\':
      case '^':
      case '~':
      case '[':
      case ']':
      case '`':
        out << '%' << std::setfill('0') << std::setw(2) << std::hex << c;
        break;
      default:
        // ASCII control characters and non-ASCII characters.
        if (c < 0x20 || c > 0x7F) {
          out << '%' << std::setfill('0') << std::setw(2) << std::hex << c;
        } else {
          out << c;
        }
        break;
    }
  }

  return out.str();
}

// Decodes a percent-encoded string according to RFC 3986.
// The input string must not already be decoded.
// Returns error on the occurrence of a malformed % escape in s.
inline Try<std::string> decode(const std::string& s)
{
  std::ostringstream out;

  for (size_t i = 0; i < s.length(); ++i) {
    if (s[i] != '%') {
      out << s[i];
      continue;
    }

    // We now expect two more characters: % HEXDIG HEXDIG
    if (i + 2 >= s.length() || !isxdigit(s[i+1]) || !isxdigit(s[i+2])) {
      return Try<std::string>::error(
          "Malformed % escape in '" + s + "': '" + s.substr(i, 3) + "'");
    }

    // Convert from HEXDIG HEXDIG to char value.
    std::istringstream in(s.substr(i + 1, 2));
    unsigned long l;
    in >> std::hex >> l;
    CHECK(l >= 0 && l <= UCHAR_MAX);
    out << static_cast<unsigned char>(l);

    i += 2;
  }

  return out.str();
}

}  // namespace query {

// Sends a blocking HTTP GET request to the process with the given upid.
// Returns the HTTP response from the process, read asynchronously.
// The query is the string version of the path and arguments:
// eg. browse.json?path=sandbox
//
// TODO(bmahler): Have the request sent asynchronously as well.
// TODO(bmahler): For efficiency, this should properly use the ResponseDecoder
// on the read stream, rather than parsing the full string response at the end.
Future<Response> get(const PID<>& pid,
                     const std::string& query = "",
                     const std::string& body = "");

// Status code reason strings, from the HTTP1.1 RFC:
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html
extern hashmap<uint16_t, std::string> statuses;

inline void initialize()
{
  statuses[100] = "100 Continue";
  statuses[101] = "101 Switching Protocols";
  statuses[200] = "200 OK";
  statuses[201] = "201 Created";
  statuses[202] = "202 Accepted";
  statuses[203] = "203 Non-Authoritative Information";
  statuses[204] = "204 No Content";
  statuses[205] = "205 Reset Content";
  statuses[206] = "206 Partial Content";
  statuses[300] = "300 Multiple Choices";
  statuses[301] = "301 Moved Permanently";
  statuses[302] = "302 Found";
  statuses[303] = "303 See Other";
  statuses[304] = "304 Not Modified";
  statuses[305] = "305 Use Proxy";
  statuses[307] = "307 Temporary Redirect";
  statuses[400] = "400 Bad Request";
  statuses[401] = "401 Unauthorized";
  statuses[402] = "402 Payment Required";
  statuses[403] = "403 Forbidden";
  statuses[404] = "404 Not Found";
  statuses[405] = "405 Method Not Allowed";
  statuses[406] = "406 Not Acceptable";
  statuses[407] = "407 Proxy Authentication Required";
  statuses[408] = "408 Request Time-out";
  statuses[409] = "409 Conflict";
  statuses[410] = "410 Gone";
  statuses[411] = "411 Length Required";
  statuses[412] = "412 Precondition Failed";
  statuses[413] = "413 Request Entity Too Large";
  statuses[414] = "414 Request-URI Too Large";
  statuses[415] = "415 Unsupported Media Type";
  statuses[416] = "416 Requested range not satisfiable";
  statuses[417] = "417 Expectation Failed";
  statuses[500] = "500 Internal Server Error";
  statuses[501] = "501 Not Implemented";
  statuses[502] = "502 Bad Gateway";
  statuses[503] = "503 Service Unavailable";
  statuses[504] = "504 Gateway Time-out";
  statuses[505] = "505 HTTP Version not supported";
}

} // namespace http {
} // namespace process {

#endif // __PROCESS_HTTP_HPP__
<commit_msg>Added a default value to process::http::OK.<commit_after>#ifndef __PROCESS_HTTP_HPP__
#define __PROCESS_HTTP_HPP__

#include <cctype>
#include <cstdlib>
#include <iomanip>
#include <sstream>
#include <string>

#include <limits.h>

#include <process/future.hpp>
#include <process/pid.hpp>

#include <stout/hashmap.hpp>
#include <stout/json.hpp>
#include <stout/option.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>
#include <stout/try.hpp>

namespace process {
namespace http {

struct Request
{
  // TODO(benh): Add major/minor version.
  hashmap<std::string, std::string> headers;
  std::string method;
  std::string path;
  std::string url;
  std::string fragment;
  hashmap<std::string, std::string> query;
  std::string body;
  bool keepAlive;
};

struct Response
{
  Response(const std::string& _body = "")
    : type(BODY),
      body(_body)
  {
    if (!body.empty()) {
      headers["Content-Length"] = stringify(body.size());
    }
  }

  // TODO(benh): Add major/minor version.
  std::string status;
  hashmap<std::string, std::string> headers;

  // TODO(benh): Make body a stream (channel) instead, and allow a
  // response to be returned without forcing the stream to be
  // finished.

  // Either provide a 'body' or an absolute 'path' to a file. If a
  // path is specified then we will attempt to perform a 'sendfile'
  // operation on the file. In either case you are expected to
  // properly specify the 'Content-Type' header, but the
  // 'Content-Length' header will be filled in for you if you specify
  // a path. Distinguish between the two using 'type' below.
  enum {
    BODY,
    PATH
  } type;

  std::string body;
  std::string path;
};

struct OK : Response
{
  OK(const std::string& body = "")
    : Response(body)
  {
    status = "200 OK";
  }

  OK(const JSON::Value& value,
     const Option<std::string>& jsonp = Option<std::string>::none())
    : Response()
  {
    status = "200 OK";

    std::ostringstream out;

    if (jsonp.isSome()) {
      out << jsonp.get() << "(";
    }

    JSON::render(out, value);

    if (jsonp.isSome()) {
      out << ");";
      headers["Content-Type"] = "text/javascript";
    } else {
      headers["Content-Type"] = "application/json";
    }

    headers["Content-Length"] = stringify(out.str().size());
    body = out.str().data();
  }
};

struct TemporaryRedirect : Response
{
  TemporaryRedirect(const std::string& url) : Response("")
  {
    status = "307 Temporary Redirect";
    headers["Location"] = url;
  }
};

struct BadRequest : Response
{
  BadRequest(const std::string& body = "") : Response(body)
  {
    status = "400 Bad Request";
  }
};

struct NotFound : Response
{
  NotFound(const std::string& body = "") : Response(body)
  {
    status = "404 Not Found";
  }
};

struct InternalServerError : Response
{
  InternalServerError(const std::string& body = "") : Response(body)
  {
    status = "500 Internal Server Error";
  }
};

struct ServiceUnavailable : Response
{
  ServiceUnavailable(const std::string& body = "") : Response(body)
  {
    status = "503 Service Unavailable";
  }
};

namespace query {

// Parses an HTTP query string into a map. For example:
//
//   parse("foo=1;bar=2;baz;foo=3")
//
// Would return a map with the following:
//   bar: "2"
//   baz: ""
//   foo: "3"
//
// We use the last value for a key for simplicity, since the RFC does not
// specify how to handle duplicate keys:
// http://en.wikipedia.org/wiki/Query_string
// TODO(bmahler): If needed, investigate populating the query map inline
// for better performance.
inline hashmap<std::string, std::string> parse(const std::string& query)
{
  hashmap<std::string, std::string> result;

  const std::vector<std::string>& tokens = strings::tokenize(query, ";&");
  foreach (const std::string& token, tokens) {
    const std::vector<std::string>& pairs = strings::split(token, "=");
    if (pairs.size() == 2) {
      result[pairs[0]] = pairs[1];
    } else if (pairs.size() == 1) {
      result[pairs[0]] = "";
    }
  }

  return result;
}

// Returns a percent-encoded string according to RFC 3986.
// The input string must not already be percent encoded.
inline std::string encode(const std::string& s)
{
  std::ostringstream out;

  foreach (unsigned char c, s) {
    switch (c) {
      // Reserved characters.
      case '$':
      case '&':
      case '+':
      case ',':
      case '/':
      case ':':
      case ';':
      case '=':
      case '?':
      case '@':
      // Unsafe characters.
      case ' ':
      case '"':
      case '<':
      case '>':
      case '#':
      case '%':
      case '{':
      case '}':
      case '|':
      case '\\':
      case '^':
      case '~':
      case '[':
      case ']':
      case '`':
        out << '%' << std::setfill('0') << std::setw(2) << std::hex << c;
        break;
      default:
        // ASCII control characters and non-ASCII characters.
        if (c < 0x20 || c > 0x7F) {
          out << '%' << std::setfill('0') << std::setw(2) << std::hex << c;
        } else {
          out << c;
        }
        break;
    }
  }

  return out.str();
}

// Decodes a percent-encoded string according to RFC 3986.
// The input string must not already be decoded.
// Returns error on the occurrence of a malformed % escape in s.
inline Try<std::string> decode(const std::string& s)
{
  std::ostringstream out;

  for (size_t i = 0; i < s.length(); ++i) {
    if (s[i] != '%') {
      out << s[i];
      continue;
    }

    // We now expect two more characters: % HEXDIG HEXDIG
    if (i + 2 >= s.length() || !isxdigit(s[i+1]) || !isxdigit(s[i+2])) {
      return Try<std::string>::error(
          "Malformed % escape in '" + s + "': '" + s.substr(i, 3) + "'");
    }

    // Convert from HEXDIG HEXDIG to char value.
    std::istringstream in(s.substr(i + 1, 2));
    unsigned long l;
    in >> std::hex >> l;
    CHECK(l >= 0 && l <= UCHAR_MAX);
    out << static_cast<unsigned char>(l);

    i += 2;
  }

  return out.str();
}

}  // namespace query {

// Sends a blocking HTTP GET request to the process with the given upid.
// Returns the HTTP response from the process, read asynchronously.
// The query is the string version of the path and arguments:
// eg. browse.json?path=sandbox
//
// TODO(bmahler): Have the request sent asynchronously as well.
// TODO(bmahler): For efficiency, this should properly use the ResponseDecoder
// on the read stream, rather than parsing the full string response at the end.
Future<Response> get(const PID<>& pid,
                     const std::string& query = "",
                     const std::string& body = "");

// Status code reason strings, from the HTTP1.1 RFC:
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html
extern hashmap<uint16_t, std::string> statuses;

inline void initialize()
{
  statuses[100] = "100 Continue";
  statuses[101] = "101 Switching Protocols";
  statuses[200] = "200 OK";
  statuses[201] = "201 Created";
  statuses[202] = "202 Accepted";
  statuses[203] = "203 Non-Authoritative Information";
  statuses[204] = "204 No Content";
  statuses[205] = "205 Reset Content";
  statuses[206] = "206 Partial Content";
  statuses[300] = "300 Multiple Choices";
  statuses[301] = "301 Moved Permanently";
  statuses[302] = "302 Found";
  statuses[303] = "303 See Other";
  statuses[304] = "304 Not Modified";
  statuses[305] = "305 Use Proxy";
  statuses[307] = "307 Temporary Redirect";
  statuses[400] = "400 Bad Request";
  statuses[401] = "401 Unauthorized";
  statuses[402] = "402 Payment Required";
  statuses[403] = "403 Forbidden";
  statuses[404] = "404 Not Found";
  statuses[405] = "405 Method Not Allowed";
  statuses[406] = "406 Not Acceptable";
  statuses[407] = "407 Proxy Authentication Required";
  statuses[408] = "408 Request Time-out";
  statuses[409] = "409 Conflict";
  statuses[410] = "410 Gone";
  statuses[411] = "411 Length Required";
  statuses[412] = "412 Precondition Failed";
  statuses[413] = "413 Request Entity Too Large";
  statuses[414] = "414 Request-URI Too Large";
  statuses[415] = "415 Unsupported Media Type";
  statuses[416] = "416 Requested range not satisfiable";
  statuses[417] = "417 Expectation Failed";
  statuses[500] = "500 Internal Server Error";
  statuses[501] = "501 Not Implemented";
  statuses[502] = "502 Bad Gateway";
  statuses[503] = "503 Service Unavailable";
  statuses[504] = "504 Gateway Time-out";
  statuses[505] = "505 HTTP Version not supported";
}

} // namespace http {
} // namespace process {

#endif // __PROCESS_HTTP_HPP__
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Facebook, Inc.
 *
 * 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.
 */

#include <vector>

#include <gtest/gtest.h>
#include <pcap.h>

#include <folly/io/IOBuf.h>
#include <folly/Conv.h>
#include <thrift/lib/cpp/async/TAsyncServerSocket.h>
#include <thrift/lib/cpp/async/TAsyncSSLSocket.h>
#include <thrift/lib/cpp/async/TAsyncSocket.h>
#include <thrift/lib/cpp/async/TEventBase.h>

using apache::thrift::async::TAsyncServerSocket;
using apache::thrift::async::TAsyncSocket;
using apache::thrift::async::TAsyncSSLSocket;
using apache::thrift::async::TEventBase;
using apache::thrift::async::WriteFlags;
using folly::SSLContext;
using folly::SocketAddress;
using apache::thrift::transport::TTransportException;
using std::shared_ptr;
using folly::IOBuf;
using std::string;
using std::vector;
using std::unique_ptr;

class AcceptCallback : public TAsyncServerSocket::AcceptCallback {
 public:
  explicit AcceptCallback(const std::function<void(int)>& fn) : fn_(fn) {}

  void connectionAccepted(int fd, const folly::SocketAddress& addr) noexcept {
    fn_(fd);
  }
  void acceptError(const std::exception& ex) noexcept {
    LOG(FATAL) << "acceptError(): " << ex.what();
  }

 private:
  std::function<void(int)> fn_;
};

class ConnectCallback : public TAsyncSocket::ConnectCallback {
 public:
  explicit ConnectCallback(const std::function<void()>& fn) : fn_(fn) {}

  void connectSuccess() noexcept {
    fn_();
  }
  void connectError(const TTransportException& ex) noexcept {
    LOG(FATAL) << "connectError(): " << ex.what();
  }

 private:
  std::function<void()> fn_;
};

class HandshakeCallback : public TAsyncSSLSocket::HandshakeCallback {
 public:
  explicit HandshakeCallback(const std::function<void()>& fn) : fn_(fn) {}

  void handshakeSuccess(TAsyncSSLSocket *sock) noexcept {
    fn_();
  }
  void handshakeError(TAsyncSSLSocket *sock,
                      const TTransportException& ex) noexcept {
    LOG(FATAL) << "handshakeError(): " << ex.what();
  }

 private:
  std::function<void()> fn_;
};

void createTCPSocketPair(TEventBase* eventBase,
                         shared_ptr<TAsyncSocket>* client,
                         shared_ptr<TAsyncSocket>* server) {
  TAsyncServerSocket::UniquePtr acceptSocket(
      new TAsyncServerSocket(eventBase));

  AcceptCallback acceptCallback([&] (int fd) {
    VLOG(4) << "socket accepted";

    // Destroy the accept socket to stop waiting on new connections,
    // so that the event loop will terminate
    acceptSocket.reset();

    *server = TAsyncSocket::newSocket(eventBase, fd);
  });

  acceptSocket->addAcceptCallback(&acceptCallback, nullptr);
  folly::SocketAddress serverAddr("127.0.0.1", 0);
  acceptSocket->bind(serverAddr);
  acceptSocket->listen(10);
  acceptSocket->getAddress(&serverAddr);
  acceptSocket->startAccepting();

  *client = TAsyncSocket::newSocket(eventBase);
  ConnectCallback clientConnectCallback([&] {
    VLOG(4) << "client connect done";
  });
  (*client)->connect(&clientConnectCallback, serverAddr);

  eventBase->loop();
}

void createSSLSocketPair(TEventBase* eventBase,
                         shared_ptr<TAsyncSSLSocket>* client,
                         shared_ptr<TAsyncSSLSocket>* server) {
  TAsyncServerSocket::UniquePtr acceptSocket(
      new TAsyncServerSocket(eventBase));

  shared_ptr<SSLContext> ctx(new SSLContext);
  ctx->loadCertificate("thrift/lib/cpp/test/ssl/tests-cert.pem");
  ctx->loadPrivateKey("thrift/lib/cpp/test/ssl/tests-key.pem");
  ctx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");

  HandshakeCallback serverHandshakeCallback([&] {
    VLOG(4) << "server handshake done";
  });

  AcceptCallback acceptCallback([&] (int fd) {
    VLOG(4) << "socket accepted";

    // Destroy the accept socket to stop waiting on new connections,
    // so that the event loop will terminate
    acceptSocket.reset();

    *server = TAsyncSSLSocket::newSocket(ctx, eventBase, fd);
    (*server)->sslAccept(&serverHandshakeCallback, 5);
  });

  acceptSocket->addAcceptCallback(&acceptCallback, nullptr);
  folly::SocketAddress serverAddr("127.0.0.1", 0);
  acceptSocket->bind(serverAddr);
  acceptSocket->listen(10);
  acceptSocket->getAddress(&serverAddr);
  acceptSocket->startAccepting();

  *client = TAsyncSSLSocket::newSocket(ctx, eventBase);
  ConnectCallback clientConnectCallback([&] {
    VLOG(4) << "client connect done";
  });
  (*client)->connect(&clientConnectCallback, serverAddr);

  eventBase->loop();
}

struct __attribute__((__packed__)) EthernetHeader {
  uint8_t dst[6];
  uint8_t src[6];
  uint16_t type;
};

struct __attribute__((__packed__)) IPv4Header {
  uint8_t versionAndHeader;
  uint8_t dscp;
  uint16_t totalLen;
  uint16_t identification;
  uint16_t flagsAndFramentOffset;
  uint8_t ttl;
  uint8_t protocol;
  uint16_t cksum;
  uint32_t src;
  uint32_t dst;
};

struct __attribute__((__packed__)) TCPHeader {
  uint16_t srcPort;
  uint16_t dstPort;
  uint32_t seq;
  uint32_t ack;
  uint8_t offset;
  uint8_t flags;
  uint16_t windowSize;
  uint16_t cksum;
  uint16_t urg;
};

class Packet {
 public:
  Packet(const struct pcap_pkthdr* hdr, const unsigned char* bytes) {
    CHECK_EQ(hdr->caplen, hdr->len);
    dataLength_ = hdr->caplen;
    data_.reset(new uint8_t[hdr->caplen]);
    memcpy(data_.get(), bytes, hdr->caplen);
    parse();
  }

  const EthernetHeader* ether() const {
    return ether_;
  }
  const IPv4Header* ip() const {
    return ip_;
  }
  const TCPHeader* tcp() const {
    return tcp_;
  }
  const folly::SocketAddress& src() const {
    return srcAddr_;
  }
  const folly::SocketAddress& dst() const {
    return dstAddr_;
  }
  const uint8_t* payload() const {
    return payload_;
  }
  uint32_t payloadLength() const {
    return payloadLength_;
  }

 private:
  void parse() {
    ether_ = reinterpret_cast<EthernetHeader*>(data_.get());
    uint16_t ethertype = ntohs(ether_->type);
    VLOG(4) << "  ethertype: " << ethertype;
    static const uint16_t kTypeIP = 2048;
    if (ethertype != kTypeIP) {
      return;
    }

    ip_ = reinterpret_cast<IPv4Header*>(data_.get() + sizeof(EthernetHeader));
    int ipVersion = (ip_->versionAndHeader & 0xf0) >> 4;
    VLOG(4) << "  IP version: " << ipVersion;
    if (ipVersion != 4) {
      ip_ = nullptr;
      return;
    }
    size_t ipHeaderLen = 4 * (ip_->versionAndHeader & 0x0f);
    VLOG(4) << "  IP header length: " << ipHeaderLen;
    VLOG(4) << "  Protocol: " << (int)ip_->protocol;
    if (ip_->protocol != IPPROTO_TCP) {
      return;
    }

    uint8_t* tcpStart = data_.get() + sizeof(EthernetHeader) + ipHeaderLen;
    tcp_ = reinterpret_cast<TCPHeader*>(tcpStart);

    struct sockaddr_in rawAddr;
    rawAddr.sin_family = AF_INET;

    rawAddr.sin_addr.s_addr = ip_->src;
    rawAddr.sin_port = tcp_->srcPort;
    srcAddr_.setFromSockaddr(&rawAddr);

    rawAddr.sin_addr.s_addr = ip_->dst;
    rawAddr.sin_port = tcp_->dstPort;
    dstAddr_.setFromSockaddr(&rawAddr);

    VLOG(4) << "  Src: " << srcAddr_;
    VLOG(4) << "  Dst: " << dstAddr_;

    size_t tcpHeaderLen = 4 * (tcp_->offset & 0x0f);
    payload_ = tcpStart + tcpHeaderLen;
    const uint8_t* bufEnd = data_.get() + dataLength_;
    payloadLength_ = bufEnd - payload_;
  }

  std::unique_ptr<uint8_t[]> data_;
  uint32_t dataLength_ = 0;
  EthernetHeader* ether_ = nullptr;
  IPv4Header* ip_ = nullptr;
  TCPHeader* tcp_ = nullptr;
  folly::SocketAddress srcAddr_;
  folly::SocketAddress dstAddr_;
  uint8_t* payload_ = nullptr;
  uint32_t payloadLength_ = 0;
};

class Capturer {
 public:
  explicit Capturer(const std::string& filter) {
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_ = pcap_open_live("lo", 65535, 0, -1, errbuf);
    // Note that this unit test needs to be run as root.
    // Otherwise you won't be able to initialize packet capturing, and the
    // following check will fail.
    CHECK(pcap_ != nullptr) << "unable to initialize packet capturing: "
                            << errbuf;

    struct bpf_program program;
    int rc = pcap_compile(pcap_, &program, filter.c_str(), 1, 0xffffffff);
    CHECK_EQ(rc, 0) << "failed to compile pcap filter: " << errbuf;
    rc = pcap_setfilter(pcap_, &program);
    CHECK_EQ(rc, 0) << "failed to set pcap filter: " << errbuf;
    rc = pcap_setnonblock(pcap_, 1, errbuf);
    CHECK_EQ(rc, 0) << "failed to set pcap nonblocking mode: " << errbuf;
  }

  ~Capturer() {
    pcap_close(pcap_);
  }

  vector<Packet> capture(int max=1024) {
    vector<Packet> packets;
    int rc = pcap_dispatch(pcap_, max, pcapCallback,
                           reinterpret_cast<unsigned char*>(&packets));
    return packets;
  }

 private:
  static void pcapCallback(unsigned char* arg, const struct pcap_pkthdr* hdr,
                           const unsigned char* bytes) {
    vector<Packet>* packets = reinterpret_cast<vector<Packet>*>(arg);
    packets->emplace_back(hdr, bytes);
  }

  pcap_t* pcap_;
};

void ensureNPackets(const shared_ptr<TAsyncSocket>& sender,
                    const shared_ptr<TAsyncSocket>& receiver,
                    size_t expectedNumPackets,
                    const std::function<void()>& fn) {
  folly::SocketAddress senderAddr;
  folly::SocketAddress receiverAddr;
  sender->getLocalAddress(&senderAddr);
  sender->getPeerAddress(&receiverAddr);

  string captureFilter = folly::to<string>("src port ", senderAddr.getPort(),
                                           " and dst port ",
                                           receiverAddr.getPort());
  Capturer cap(captureFilter);

  // Call the function
  fn();

  auto packets = cap.capture();
  EXPECT_EQ(expectedNumPackets, packets.size());
}

void testWriteFlushing(TEventBase* eventBase,
                       const shared_ptr<TAsyncSocket>& client,
                       const shared_ptr<TAsyncSocket>& server) {
  char buf[] = "foobar";
  const int bufsize = 6;

  const int iovCount = 3;
  iovec iov[iovCount];
  iov[0].iov_base = const_cast<char*>("test1234");
  iov[0].iov_len = 8;
  iov[1].iov_base = const_cast<char*>("some more data");
  iov[1].iov_len = 14;
  iov[2].iov_base = const_cast<char*>("last bit");
  iov[2].iov_len = 8;

  // Two small writes should trigger two packets, since we use TCP_NODELAY
  ensureNPackets(client, server, 2, [&] {
    client->write(nullptr, buf, bufsize);
    client->write(nullptr, buf, bufsize);
    eventBase->loop();
  });

  // A writev() of several small buffers should go out in one packet
  ensureNPackets(client, server, 1, [&] {
    client->writev(nullptr, iov, iovCount);
    eventBase->loop();
  });

  // Calling write() with cork=true shouldn't trigger its own packet
  ensureNPackets(client, server, 2, [&] {
      client->write(nullptr, buf, bufsize, WriteFlags::CORK);
      client->write(nullptr, buf, bufsize);
      client->write(nullptr, buf, bufsize, WriteFlags::CORK);
      client->writev(nullptr, iov, iovCount, WriteFlags::CORK);
      client->write(nullptr, buf, bufsize, WriteFlags::CORK);
      client->write(nullptr, buf, bufsize);
      eventBase->loop();
  });

  // Test the behavior of IOBuf chains
  // We perform several corked writes, with 3 uncorked writes
  ensureNPackets(client, server, 3, [&] {
    auto buf1 = IOBuf::copyBuffer(buf, bufsize);
    auto buf2 = IOBuf::copyBuffer(iov[0].iov_base, iov[1].iov_len);
    buf2->appendChain(buf1->clone());

    buf2 = buf1->clone();
    auto buf3 = buf1->clone();
    buf3->appendChain(buf2->clone());

    client->writeChain(nullptr, buf3->clone(), WriteFlags::CORK);
    client->writeChain(nullptr, buf1->clone());
    client->writeChain(nullptr, buf2->clone(), WriteFlags::CORK);
    client->writeChain(nullptr, buf3->clone(), WriteFlags::CORK);
    client->writeChain(nullptr, buf3->clone());
    client->writeChain(nullptr, buf1->clone());
    eventBase->loop();
  });
}

TEST(WriteFlushTest, TAsyncSocket) {
  TEventBase eventBase;

  shared_ptr<TAsyncSocket> client;
  shared_ptr<TAsyncSocket> server;
  createTCPSocketPair(&eventBase, &client, &server);

  testWriteFlushing(&eventBase, client, server);
}

TEST(WriteFlushTest, TAsyncSSLSocket) {
  TEventBase eventBase;

  shared_ptr<TAsyncSSLSocket> client;
  shared_ptr<TAsyncSSLSocket> server;
  createSSLSocketPair(&eventBase, &client, &server);

  testWriteFlushing(&eventBase, client, server);
}
<commit_msg>Fix reading tcp payload offset<commit_after>/*
 * Copyright 2014 Facebook, Inc.
 *
 * 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.
 */

#include <vector>

#include <gtest/gtest.h>
#include <pcap.h>

#include <folly/io/IOBuf.h>
#include <folly/Conv.h>
#include <thrift/lib/cpp/async/TAsyncServerSocket.h>
#include <thrift/lib/cpp/async/TAsyncSSLSocket.h>
#include <thrift/lib/cpp/async/TAsyncSocket.h>
#include <thrift/lib/cpp/async/TEventBase.h>

using apache::thrift::async::TAsyncServerSocket;
using apache::thrift::async::TAsyncSocket;
using apache::thrift::async::TAsyncSSLSocket;
using apache::thrift::async::TEventBase;
using apache::thrift::async::WriteFlags;
using folly::SSLContext;
using folly::SocketAddress;
using apache::thrift::transport::TTransportException;
using std::shared_ptr;
using folly::IOBuf;
using std::string;
using std::vector;
using std::unique_ptr;

class AcceptCallback : public TAsyncServerSocket::AcceptCallback {
 public:
  explicit AcceptCallback(const std::function<void(int)>& fn) : fn_(fn) {}

  void connectionAccepted(int fd, const folly::SocketAddress& addr) noexcept {
    fn_(fd);
  }
  void acceptError(const std::exception& ex) noexcept {
    LOG(FATAL) << "acceptError(): " << ex.what();
  }

 private:
  std::function<void(int)> fn_;
};

class ConnectCallback : public TAsyncSocket::ConnectCallback {
 public:
  explicit ConnectCallback(const std::function<void()>& fn) : fn_(fn) {}

  void connectSuccess() noexcept {
    fn_();
  }
  void connectError(const TTransportException& ex) noexcept {
    LOG(FATAL) << "connectError(): " << ex.what();
  }

 private:
  std::function<void()> fn_;
};

class HandshakeCallback : public TAsyncSSLSocket::HandshakeCallback {
 public:
  explicit HandshakeCallback(const std::function<void()>& fn) : fn_(fn) {}

  void handshakeSuccess(TAsyncSSLSocket *sock) noexcept {
    fn_();
  }
  void handshakeError(TAsyncSSLSocket *sock,
                      const TTransportException& ex) noexcept {
    LOG(FATAL) << "handshakeError(): " << ex.what();
  }

 private:
  std::function<void()> fn_;
};

void createTCPSocketPair(TEventBase* eventBase,
                         shared_ptr<TAsyncSocket>* client,
                         shared_ptr<TAsyncSocket>* server) {
  TAsyncServerSocket::UniquePtr acceptSocket(
      new TAsyncServerSocket(eventBase));

  AcceptCallback acceptCallback([&] (int fd) {
    VLOG(4) << "socket accepted";

    // Destroy the accept socket to stop waiting on new connections,
    // so that the event loop will terminate
    acceptSocket.reset();

    *server = TAsyncSocket::newSocket(eventBase, fd);
  });

  acceptSocket->addAcceptCallback(&acceptCallback, nullptr);
  folly::SocketAddress serverAddr("127.0.0.1", 0);
  acceptSocket->bind(serverAddr);
  acceptSocket->listen(10);
  acceptSocket->getAddress(&serverAddr);
  acceptSocket->startAccepting();

  *client = TAsyncSocket::newSocket(eventBase);
  ConnectCallback clientConnectCallback([&] {
    VLOG(4) << "client connect done";
  });
  (*client)->connect(&clientConnectCallback, serverAddr);

  eventBase->loop();
}

void createSSLSocketPair(TEventBase* eventBase,
                         shared_ptr<TAsyncSSLSocket>* client,
                         shared_ptr<TAsyncSSLSocket>* server) {
  TAsyncServerSocket::UniquePtr acceptSocket(
      new TAsyncServerSocket(eventBase));

  shared_ptr<SSLContext> ctx(new SSLContext);
  ctx->loadCertificate("thrift/lib/cpp/test/ssl/tests-cert.pem");
  ctx->loadPrivateKey("thrift/lib/cpp/test/ssl/tests-key.pem");
  ctx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");

  HandshakeCallback serverHandshakeCallback([&] {
    VLOG(4) << "server handshake done";
  });

  AcceptCallback acceptCallback([&] (int fd) {
    VLOG(4) << "socket accepted";

    // Destroy the accept socket to stop waiting on new connections,
    // so that the event loop will terminate
    acceptSocket.reset();

    *server = TAsyncSSLSocket::newSocket(ctx, eventBase, fd);
    (*server)->sslAccept(&serverHandshakeCallback, 5);
  });

  acceptSocket->addAcceptCallback(&acceptCallback, nullptr);
  folly::SocketAddress serverAddr("127.0.0.1", 0);
  acceptSocket->bind(serverAddr);
  acceptSocket->listen(10);
  acceptSocket->getAddress(&serverAddr);
  acceptSocket->startAccepting();

  *client = TAsyncSSLSocket::newSocket(ctx, eventBase);
  ConnectCallback clientConnectCallback([&] {
    VLOG(4) << "client connect done";
  });
  (*client)->connect(&clientConnectCallback, serverAddr);

  eventBase->loop();
}

struct __attribute__((__packed__)) EthernetHeader {
  uint8_t dst[6];
  uint8_t src[6];
  uint16_t type;
};

struct __attribute__((__packed__)) IPv4Header {
  uint8_t versionAndHeader;
  uint8_t dscp;
  uint16_t totalLen;
  uint16_t identification;
  uint16_t flagsAndFramentOffset;
  uint8_t ttl;
  uint8_t protocol;
  uint16_t cksum;
  uint32_t src;
  uint32_t dst;
};

struct __attribute__((__packed__)) TCPHeader {
  uint16_t srcPort;
  uint16_t dstPort;
  uint32_t seq;
  uint32_t ack;
  uint8_t offset;
  uint8_t flags;
  uint16_t windowSize;
  uint16_t cksum;
  uint16_t urg;
};

class Packet {
 public:
  Packet(const struct pcap_pkthdr* hdr, const unsigned char* bytes) {
    CHECK_EQ(hdr->caplen, hdr->len);
    dataLength_ = hdr->caplen;
    data_.reset(new uint8_t[hdr->caplen]);
    memcpy(data_.get(), bytes, hdr->caplen);
    parse();
  }

  const EthernetHeader* ether() const {
    return ether_;
  }
  const IPv4Header* ip() const {
    return ip_;
  }
  const TCPHeader* tcp() const {
    return tcp_;
  }
  const folly::SocketAddress& src() const {
    return srcAddr_;
  }
  const folly::SocketAddress& dst() const {
    return dstAddr_;
  }
  const uint8_t* payload() const {
    return payload_;
  }
  uint32_t payloadLength() const {
    return payloadLength_;
  }

 private:
  void parse() {
    ether_ = reinterpret_cast<EthernetHeader*>(data_.get());
    uint16_t ethertype = ntohs(ether_->type);
    VLOG(4) << "  ethertype: " << ethertype;
    static const uint16_t kTypeIP = 2048;
    if (ethertype != kTypeIP) {
      return;
    }

    ip_ = reinterpret_cast<IPv4Header*>(data_.get() + sizeof(EthernetHeader));
    int ipVersion = (ip_->versionAndHeader & 0xf0) >> 4;
    VLOG(4) << "  IP version: " << ipVersion;
    if (ipVersion != 4) {
      ip_ = nullptr;
      return;
    }
    size_t ipHeaderLen = 4 * (ip_->versionAndHeader & 0x0f);
    VLOG(4) << "  IP header length: " << ipHeaderLen;
    VLOG(4) << "  Protocol: " << (int)ip_->protocol;
    if (ip_->protocol != IPPROTO_TCP) {
      return;
    }

    uint8_t* tcpStart = data_.get() + sizeof(EthernetHeader) + ipHeaderLen;
    tcp_ = reinterpret_cast<TCPHeader*>(tcpStart);

    struct sockaddr_in rawAddr;
    rawAddr.sin_family = AF_INET;

    rawAddr.sin_addr.s_addr = ip_->src;
    rawAddr.sin_port = tcp_->srcPort;
    srcAddr_.setFromSockaddr(&rawAddr);

    rawAddr.sin_addr.s_addr = ip_->dst;
    rawAddr.sin_port = tcp_->dstPort;
    dstAddr_.setFromSockaddr(&rawAddr);

    VLOG(4) << "  Src: " << srcAddr_;
    VLOG(4) << "  Dst: " << dstAddr_;

    size_t tcpHeaderLen = 4 * (tcp_->offset >> 4);
    payload_ = tcpStart + tcpHeaderLen;
    const uint8_t* bufEnd = data_.get() + dataLength_;
    payloadLength_ = bufEnd - payload_;
  }

  std::unique_ptr<uint8_t[]> data_;
  uint32_t dataLength_ = 0;
  EthernetHeader* ether_ = nullptr;
  IPv4Header* ip_ = nullptr;
  TCPHeader* tcp_ = nullptr;
  folly::SocketAddress srcAddr_;
  folly::SocketAddress dstAddr_;
  uint8_t* payload_ = nullptr;
  uint32_t payloadLength_ = 0;
};

class Capturer {
 public:
  explicit Capturer(const std::string& filter) {
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_ = pcap_open_live("lo", 65535, 0, -1, errbuf);
    // Note that this unit test needs to be run as root.
    // Otherwise you won't be able to initialize packet capturing, and the
    // following check will fail.
    CHECK(pcap_ != nullptr) << "unable to initialize packet capturing: "
                            << errbuf;

    struct bpf_program program;
    int rc = pcap_compile(pcap_, &program, filter.c_str(), 1, 0xffffffff);
    CHECK_EQ(rc, 0) << "failed to compile pcap filter: " << errbuf;
    rc = pcap_setfilter(pcap_, &program);
    CHECK_EQ(rc, 0) << "failed to set pcap filter: " << errbuf;
    rc = pcap_setnonblock(pcap_, 1, errbuf);
    CHECK_EQ(rc, 0) << "failed to set pcap nonblocking mode: " << errbuf;
  }

  ~Capturer() {
    pcap_close(pcap_);
  }

  vector<Packet> capture(int max=1024) {
    vector<Packet> packets;
    int rc = pcap_dispatch(pcap_, max, pcapCallback,
                           reinterpret_cast<unsigned char*>(&packets));
    return packets;
  }

 private:
  static void pcapCallback(unsigned char* arg, const struct pcap_pkthdr* hdr,
                           const unsigned char* bytes) {
    vector<Packet>* packets = reinterpret_cast<vector<Packet>*>(arg);
    packets->emplace_back(hdr, bytes);
  }

  pcap_t* pcap_;
};

void ensureNPackets(const shared_ptr<TAsyncSocket>& sender,
                    const shared_ptr<TAsyncSocket>& receiver,
                    size_t expectedNumPackets,
                    const std::function<void()>& fn) {
  folly::SocketAddress senderAddr;
  folly::SocketAddress receiverAddr;
  sender->getLocalAddress(&senderAddr);
  sender->getPeerAddress(&receiverAddr);

  string captureFilter = folly::to<string>("src port ", senderAddr.getPort(),
                                           " and dst port ",
                                           receiverAddr.getPort());
  Capturer cap(captureFilter);

  // Call the function
  fn();

  auto packets = cap.capture();
  EXPECT_EQ(expectedNumPackets, packets.size());
}

void testWriteFlushing(TEventBase* eventBase,
                       const shared_ptr<TAsyncSocket>& client,
                       const shared_ptr<TAsyncSocket>& server) {
  char buf[] = "foobar";
  const int bufsize = 6;

  const int iovCount = 3;
  iovec iov[iovCount];
  iov[0].iov_base = const_cast<char*>("test1234");
  iov[0].iov_len = 8;
  iov[1].iov_base = const_cast<char*>("some more data");
  iov[1].iov_len = 14;
  iov[2].iov_base = const_cast<char*>("last bit");
  iov[2].iov_len = 8;

  // Two small writes should trigger two packets, since we use TCP_NODELAY
  ensureNPackets(client, server, 2, [&] {
    client->write(nullptr, buf, bufsize);
    client->write(nullptr, buf, bufsize);
    eventBase->loop();
  });

  // A writev() of several small buffers should go out in one packet
  ensureNPackets(client, server, 1, [&] {
    client->writev(nullptr, iov, iovCount);
    eventBase->loop();
  });

  // Calling write() with cork=true shouldn't trigger its own packet
  ensureNPackets(client, server, 2, [&] {
      client->write(nullptr, buf, bufsize, WriteFlags::CORK);
      client->write(nullptr, buf, bufsize);
      client->write(nullptr, buf, bufsize, WriteFlags::CORK);
      client->writev(nullptr, iov, iovCount, WriteFlags::CORK);
      client->write(nullptr, buf, bufsize, WriteFlags::CORK);
      client->write(nullptr, buf, bufsize);
      eventBase->loop();
  });

  // Test the behavior of IOBuf chains
  // We perform several corked writes, with 3 uncorked writes
  ensureNPackets(client, server, 3, [&] {
    auto buf1 = IOBuf::copyBuffer(buf, bufsize);
    auto buf2 = IOBuf::copyBuffer(iov[0].iov_base, iov[1].iov_len);
    buf2->appendChain(buf1->clone());

    buf2 = buf1->clone();
    auto buf3 = buf1->clone();
    buf3->appendChain(buf2->clone());

    client->writeChain(nullptr, buf3->clone(), WriteFlags::CORK);
    client->writeChain(nullptr, buf1->clone());
    client->writeChain(nullptr, buf2->clone(), WriteFlags::CORK);
    client->writeChain(nullptr, buf3->clone(), WriteFlags::CORK);
    client->writeChain(nullptr, buf3->clone());
    client->writeChain(nullptr, buf1->clone());
    eventBase->loop();
  });
}

TEST(WriteFlushTest, TAsyncSocket) {
  TEventBase eventBase;

  shared_ptr<TAsyncSocket> client;
  shared_ptr<TAsyncSocket> server;
  createTCPSocketPair(&eventBase, &client, &server);

  testWriteFlushing(&eventBase, client, server);
}

TEST(WriteFlushTest, TAsyncSSLSocket) {
  TEventBase eventBase;

  shared_ptr<TAsyncSSLSocket> client;
  shared_ptr<TAsyncSSLSocket> server;
  createSSLSocketPair(&eventBase, &client, &server);

  testWriteFlushing(&eventBase, client, server);
}
<|endoftext|>


<commit_before>//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  Extracts a Symbol Graph from a .swiftmodule file.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/LLVMInitialize.h"
#include "swift/Basic/Version.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/SymbolGraphGen/SymbolGraphGen.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/CommandLine.h"

using namespace swift;
using namespace llvm::opt;

namespace options {
static llvm::cl::OptionCategory Category("swift-symbolgraph-extract Options");

static llvm::cl::opt<std::string>
ModuleName("module-name", llvm::cl::desc("Name of the module to extract"), llvm::cl::cat(Category));

static llvm::cl::list<std::string>
FrameworkSearchPaths("F", llvm::cl::desc("add a directory to the framework search paths"), llvm::cl::ZeroOrMore,
                     llvm::cl::cat(Category));

static llvm::cl::list<std::string>
LibrarySearchPaths("L", llvm::cl::desc("Add a directory to the library search paths"), llvm::cl::ZeroOrMore,
                   llvm::cl::cat(Category));

static llvm::cl::list<std::string>
ImportSearchPaths("I", llvm::cl::desc("Add directory to the import search paths"), llvm::cl::ZeroOrMore,
                  llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
ModuleCachePath("module-cache-path", llvm::cl::desc("Specifies a path to cache modules"), llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
SDK("sdk", llvm::cl::desc("Path to the SDK"),
    llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
Target("target", llvm::cl::desc("Target triple"),
       llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
SwiftVersion("swift-version", llvm::cl::desc("Interpret input according to a specific Swift language version number"), llvm::cl::cat(Category));

static llvm::cl::opt<bool>
PrettyPrint("pretty-print", llvm::cl::desc("Pretty-print the resulting Symbol Graph JSON"), llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
MinimumAccessLevel("minimum-access-level", llvm::cl::desc("Include symbols with this access level or more"), llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
OutputPath("o", llvm::cl::desc("Symbol Graph JSON Output Path"), llvm::cl::cat(Category));
} // end namespace options

int swift_symbolgraph_extract_main(ArrayRef<const char *> Args, const char *Argv0, void *MainAddr) {
  INITIALIZE_LLVM();

  llvm::cl::HideUnrelatedOptions(options::Category);

  // LLVM Command Line expects to trim off argv[0].
  SmallVector<const char *, 8> ArgsWithArgv0 { Argv0 };
  ArgsWithArgv0.append(Args.begin(), Args.end());

  llvm::cl::ParseCommandLineOptions(ArgsWithArgv0.size(),
      llvm::makeArrayRef(ArgsWithArgv0).data(), "Swift Symbol Graph Extractor\n");

  CompilerInvocation Invocation;

  Invocation.setMainExecutablePath(
      llvm::sys::fs::getMainExecutable(Argv0, MainAddr));
  Invocation.setModuleName("swift_symbolgraph_extract");
  Invocation.setSDKPath(options::SDK);
  Invocation.setTargetTriple(options::Target);

  std::vector<SearchPathOptions::FrameworkSearchPath> FrameworkSearchPaths;
  for (const auto &Path : options::FrameworkSearchPaths) {
    FrameworkSearchPaths.push_back({ Path, /*isSystem*/ false});
  }
  Invocation.setFrameworkSearchPaths(FrameworkSearchPaths);
  Invocation.getSearchPathOptions().LibrarySearchPaths = options::LibrarySearchPaths;
  Invocation.setImportSearchPaths(options::ImportSearchPaths);

  Invocation.getLangOptions().EnableObjCInterop = llvm::Triple(options::Target).isOSDarwin();
  Invocation.getLangOptions().DebuggerSupport = true;

  Invocation.getFrontendOptions().EnableLibraryEvolution = true;

  Invocation.setClangModuleCachePath(options::ModuleCachePath);
  Invocation.getClangImporterOptions().ModuleCachePath = options::ModuleCachePath;

  if (!options::SwiftVersion.empty()) {
    using version::Version;
    bool isValid = false;
    if (auto Version = Version::parseVersionString(options::SwiftVersion,
                                                   SourceLoc(), nullptr)) {
      if (auto Effective = Version.getValue().getEffectiveLanguageVersion()) {
        Invocation.getLangOptions().EffectiveLanguageVersion = *Effective;
        isValid = true;
      }
    }
    if (!isValid) {
      llvm::errs() << "Unsupported Swift Version.\n";
      return EXIT_FAILURE;
    }
  }

  symbolgraphgen::SymbolGraphOptions Options {
    options::OutputPath,
    llvm::Triple(options::Target),
    options::PrettyPrint,
    AccessLevel::Public,
  };

  if (!options::MinimumAccessLevel.empty()) {
    Options.MinimumAccessLevel =
        llvm::StringSwitch<AccessLevel>(options::MinimumAccessLevel)
      .Case("open", AccessLevel::Open)
      .Case("public", AccessLevel::Public)
      .Case("internal", AccessLevel::Internal)
      .Case("fileprivate", AccessLevel::FilePrivate)
      .Case("private", AccessLevel::Private)
      .Default(AccessLevel::Public);
  }

  PrintingDiagnosticConsumer DiagPrinter;

  CompilerInstance CI;
  CI.getDiags().addConsumer(DiagPrinter);

  if (CI.setup(Invocation)) {
    llvm::outs() << "Failed to setup compiler instance\n";
    return EXIT_FAILURE;
  }

  auto M = CI.getASTContext().getModuleByName(options::ModuleName);
  if (!M) {
    llvm::errs()
      << "Couldn't load module '" << options::ModuleName << '\''
      << " in the current SDK and search paths.\n";
    SmallVector<Identifier, 32> VisibleModuleNames;
    CI.getASTContext().getVisibleTopLevelModuleNames(VisibleModuleNames);

    if (VisibleModuleNames.empty()) {
      llvm::errs() << "Could not find any modules.\n";
    } else {
      std::sort(VisibleModuleNames.begin(), VisibleModuleNames.end(),
                [](const Identifier &A, const Identifier &B) -> bool {
        return A.str() < B.str();
      });
      llvm::errs() << "Current visible modules:\n";
      for (const auto &ModuleName : VisibleModuleNames) {
        llvm::errs() << ModuleName.str() << "\n";
      }
    }
    return EXIT_FAILURE;
  }

  return symbolgraphgen::emitSymbolGraphForModule(M,
    Options);
}
<commit_msg>Pass -Xcc flags through swift-symbolgraph-extract<commit_after>//===--- swift_indent_main.cpp - Swift code formatting tool ---------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  Extracts a Symbol Graph from a .swiftmodule file.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/LLVMInitialize.h"
#include "swift/Basic/Version.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/SymbolGraphGen/SymbolGraphGen.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/CommandLine.h"

using namespace swift;
using namespace llvm::opt;

namespace options {
static llvm::cl::OptionCategory Category("swift-symbolgraph-extract Options");

static llvm::cl::opt<std::string>
ModuleName("module-name", llvm::cl::desc("Name of the module to extract"), llvm::cl::cat(Category));

static llvm::cl::list<std::string>
FrameworkSearchPaths("F", llvm::cl::desc("add a directory to the framework search paths"), llvm::cl::ZeroOrMore,
                     llvm::cl::cat(Category));

static llvm::cl::list<std::string>
LibrarySearchPaths("L", llvm::cl::desc("Add a directory to the library search paths"), llvm::cl::ZeroOrMore,
                   llvm::cl::cat(Category));

static llvm::cl::list<std::string>
ImportSearchPaths("I", llvm::cl::desc("Add directory to the import search paths"), llvm::cl::ZeroOrMore,
                  llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
ModuleCachePath("module-cache-path", llvm::cl::desc("Specifies a path to cache modules"), llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
SDK("sdk", llvm::cl::desc("Path to the SDK"),
    llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
Target("target", llvm::cl::desc("Target triple"),
       llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
SwiftVersion("swift-version", llvm::cl::desc("Interpret input according to a specific Swift language version number"), llvm::cl::cat(Category));

static llvm::cl::opt<bool>
PrettyPrint("pretty-print", llvm::cl::desc("Pretty-print the resulting Symbol Graph JSON"), llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
MinimumAccessLevel("minimum-access-level", llvm::cl::desc("Include symbols with this access level or more"), llvm::cl::cat(Category));

static llvm::cl::list<std::string>
Xcc("Xcc", llvm::cl::desc("Pass the following command-line flag to Clang"),
         llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
OutputPath("o", llvm::cl::desc("Symbol Graph JSON Output Path"), llvm::cl::cat(Category));
} // end namespace options

int swift_symbolgraph_extract_main(ArrayRef<const char *> Args, const char *Argv0, void *MainAddr) {
  INITIALIZE_LLVM();

  llvm::cl::HideUnrelatedOptions(options::Category);

  // LLVM Command Line expects to trim off argv[0].
  SmallVector<const char *, 8> ArgsWithArgv0 { Argv0 };
  ArgsWithArgv0.append(Args.begin(), Args.end());

  llvm::cl::ParseCommandLineOptions(ArgsWithArgv0.size(),
      llvm::makeArrayRef(ArgsWithArgv0).data(), "Swift Symbol Graph Extractor\n");

  CompilerInvocation Invocation;

  Invocation.setMainExecutablePath(
      llvm::sys::fs::getMainExecutable(Argv0, MainAddr));
  Invocation.setModuleName("swift_symbolgraph_extract");
  Invocation.setSDKPath(options::SDK);
  Invocation.setTargetTriple(options::Target);

  for (auto &Arg : options::Xcc) {
    Invocation.getClangImporterOptions().ExtraArgs.push_back(Arg);
  }

  std::vector<SearchPathOptions::FrameworkSearchPath> FrameworkSearchPaths;
  for (const auto &Path : options::FrameworkSearchPaths) {
    FrameworkSearchPaths.push_back({ Path, /*isSystem*/ false});
  }
  Invocation.setFrameworkSearchPaths(FrameworkSearchPaths);
  Invocation.getSearchPathOptions().LibrarySearchPaths = options::LibrarySearchPaths;
  Invocation.setImportSearchPaths(options::ImportSearchPaths);

  Invocation.getLangOptions().EnableObjCInterop = llvm::Triple(options::Target).isOSDarwin();
  Invocation.getLangOptions().DebuggerSupport = true;

  Invocation.getFrontendOptions().EnableLibraryEvolution = true;

  Invocation.setClangModuleCachePath(options::ModuleCachePath);
  Invocation.getClangImporterOptions().ModuleCachePath = options::ModuleCachePath;

  if (!options::SwiftVersion.empty()) {
    using version::Version;
    bool isValid = false;
    if (auto Version = Version::parseVersionString(options::SwiftVersion,
                                                   SourceLoc(), nullptr)) {
      if (auto Effective = Version.getValue().getEffectiveLanguageVersion()) {
        Invocation.getLangOptions().EffectiveLanguageVersion = *Effective;
        isValid = true;
      }
    }
    if (!isValid) {
      llvm::errs() << "Unsupported Swift Version.\n";
      return EXIT_FAILURE;
    }
  }

  symbolgraphgen::SymbolGraphOptions Options {
    options::OutputPath,
    llvm::Triple(options::Target),
    options::PrettyPrint,
    AccessLevel::Public,
  };

  if (!options::MinimumAccessLevel.empty()) {
    Options.MinimumAccessLevel =
        llvm::StringSwitch<AccessLevel>(options::MinimumAccessLevel)
      .Case("open", AccessLevel::Open)
      .Case("public", AccessLevel::Public)
      .Case("internal", AccessLevel::Internal)
      .Case("fileprivate", AccessLevel::FilePrivate)
      .Case("private", AccessLevel::Private)
      .Default(AccessLevel::Public);
  }

  PrintingDiagnosticConsumer DiagPrinter;

  CompilerInstance CI;
  CI.getDiags().addConsumer(DiagPrinter);

  if (CI.setup(Invocation)) {
    llvm::outs() << "Failed to setup compiler instance\n";
    return EXIT_FAILURE;
  }

  auto M = CI.getASTContext().getModuleByName(options::ModuleName);
  if (!M) {
    llvm::errs()
      << "Couldn't load module '" << options::ModuleName << '\''
      << " in the current SDK and search paths.\n";
    SmallVector<Identifier, 32> VisibleModuleNames;
    CI.getASTContext().getVisibleTopLevelModuleNames(VisibleModuleNames);

    if (VisibleModuleNames.empty()) {
      llvm::errs() << "Could not find any modules.\n";
    } else {
      std::sort(VisibleModuleNames.begin(), VisibleModuleNames.end(),
                [](const Identifier &A, const Identifier &B) -> bool {
        return A.str() < B.str();
      });
      llvm::errs() << "Current visible modules:\n";
      for (const auto &ModuleName : VisibleModuleNames) {
        llvm::errs() << ModuleName.str() << "\n";
      }
    }
    return EXIT_FAILURE;
  }

  return symbolgraphgen::emitSymbolGraphForModule(M,
    Options);
}
<|endoftext|>