<commit_before>// Copyright (c) 2012-2013 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "dbwrapper.h"
#include "uint256.h"
#include "random.h"
#include "test/test_pivx.h"

#include <boost/test/unit_test.hpp>

// Test if a string consists entirely of null characters
bool is_null_key(const std::vector<unsigned char>& key) {
    bool isnull = true;

    for (unsigned int i = 0; i < key.size(); i++)
        isnull &= (key[i] == '\x00');

    return isnull;
}

BOOST_FIXTURE_TEST_SUITE(dbwrapper_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(dbwrapper)
{
    {
        fs::path ph = fs::temp_directory_path() / fs::unique_path();
        CDBWrapper dbw(ph, (1 << 20), true, false);
        char key = 'k';
        uint256 in = GetRandHash();
        uint256 res;

        BOOST_CHECK(dbw.Write(key, in));
        BOOST_CHECK(dbw.Read(key, res));
        BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
    }
}

// Test batch operations
BOOST_AUTO_TEST_CASE(dbwrapper_batch)
{
    {
        fs::path ph = fs::temp_directory_path() / fs::unique_path();
        CDBWrapper dbw(ph, (1 << 20), true, false);

        char key = 'i';
        uint256 in = GetRandHash();
        char key2 = 'j';
        uint256 in2 = GetRandHash();
        char key3 = 'k';
        uint256 in3 = GetRandHash();

        uint256 res;
        CDBBatch batch;

        batch.Write(key, in);
        batch.Write(key2, in2);
        batch.Write(key3, in3);

        // Remove key3 before it's even been written
        batch.Erase(key3);

        dbw.WriteBatch(batch);

        BOOST_CHECK(dbw.Read(key, res));
        BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
        BOOST_CHECK(dbw.Read(key2, res));
        BOOST_CHECK_EQUAL(res.ToString(), in2.ToString());

        // key3 never should've been written
        BOOST_CHECK(dbw.Read(key3, res) == false);
    }
}

BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
{
    {
        fs::path ph = fs::temp_directory_path() / fs::unique_path();
        CDBWrapper dbw(ph, (1 << 20), true, false);

        // The two keys are intentionally chosen for ordering
        char key = 'j';
        uint256 in = GetRandHash();
        BOOST_CHECK(dbw.Write(key, in));
        char key2 = 'k';
        uint256 in2 = GetRandHash();
        BOOST_CHECK(dbw.Write(key2, in2));

        std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator());

        // Be sure to seek past any earlier key (if it exists)
        it->Seek(key);

        char key_res;
        uint256 val_res;

        it->GetKey(key_res);
        it->GetValue(val_res);
        BOOST_CHECK_EQUAL(key_res, key);
        BOOST_CHECK_EQUAL(val_res.ToString(), in.ToString());

        it->Next();

        it->GetKey(key_res);
        it->GetValue(val_res);
        BOOST_CHECK_EQUAL(key_res, key2);
        BOOST_CHECK_EQUAL(val_res.ToString(), in2.ToString());

        it->Next();
        BOOST_CHECK_EQUAL(it->Valid(), false);
    }
}

BOOST_AUTO_TEST_CASE(iterator_ordering)
{
    fs::path ph = fs::temp_directory_path() / fs::unique_path();
    CDBWrapper dbw(ph, (1 << 20), true, false);
    for (int x=0x00; x<256; ++x) {
        uint8_t key = x;
        uint32_t value = x*x;
        if (!(x & 1)) BOOST_CHECK(dbw.Write(key, value));
    }

    // Check that creating an iterator creates a snapshot
    std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator());

    for (int x=0x00; x<256; ++x) {
        uint8_t key = x;
        uint32_t value = x*x;
        if (x & 1) BOOST_CHECK(dbw.Write(key, value));
    }

    for (int seek_start : {0x00, 0x80}) {
        it->Seek((uint8_t)seek_start);
        for (int x=seek_start; x<255; ++x) {
            uint8_t key;
            uint32_t value;
            BOOST_CHECK(it->Valid());
            if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure
                break;
            BOOST_CHECK(it->GetKey(key));
            if (x & 1) {
                BOOST_CHECK_EQUAL(key, x + 1);
                continue;
            }
            BOOST_CHECK(it->GetValue(value));
            BOOST_CHECK_EQUAL(key, x);
            BOOST_CHECK_EQUAL(value, x*x);
            it->Next();
        }
        BOOST_CHECK(!it->Valid());
    }
}

struct StringContentsSerializer {
    // Used to make two serialized objects the same while letting them have a different lengths
    // This is a terrible idea
    std::string str;
    StringContentsSerializer() {}
    StringContentsSerializer(const std::string& inp) : str(inp) {}

    StringContentsSerializer& operator+=(const std::string& s) {
        str += s;
        return *this;
    }
    StringContentsSerializer& operator+=(const StringContentsSerializer& s) { return *this += s.str; }

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        if (ser_action.ForRead()) {
            str.clear();
            char c = 0;
            while (true) {
                try {
                    READWRITE(c);
                    str.push_back(c);
                } catch (const std::ios_base::failure& e) {
                    break;
                }
            }
        } else {
            for (size_t i = 0; i < str.size(); i++)
                READWRITE(str[i]);
        }
    }
};

BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{
    char buf[10];

    fs::path ph = fs::temp_directory_path() / fs::unique_path();
    CDBWrapper dbw(ph, (1 << 20), true, false);
    for (int x=0x00; x<10; ++x) {
        for (int y = 0; y < 10; y++) {
            snprintf(buf, sizeof(buf), "%d", x);
            StringContentsSerializer key(buf);
            for (int z = 0; z < y; z++)
                key += key;
            uint32_t value = x*x;
            BOOST_CHECK(dbw.Write(key, value));
        }
    }

    boost::scoped_ptr<CDBIterator> it(const_cast<CDBWrapper*>(&dbw)->NewIterator());
    for (int c=0; c<2; ++c) {
        int seek_start;
        if (c == 0)
            seek_start = 0;
        else
            seek_start = 5;
        snprintf(buf, sizeof(buf), "%d", seek_start);
        StringContentsSerializer seek_key(buf);
        it->Seek(seek_key);
        for (int x=seek_start; x<10; ++x) {
            for (int y = 0; y < 10; y++) {
                snprintf(buf, sizeof(buf), "%d", x);
                std::string exp_key(buf);
                for (int z = 0; z < y; z++)
                    exp_key += exp_key;
                StringContentsSerializer key;
                uint32_t value;
                BOOST_CHECK(it->Valid());
                if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure
                    break;
                BOOST_CHECK(it->GetKey(key));
                BOOST_CHECK(it->GetValue(value));
                BOOST_CHECK_EQUAL(key.str, exp_key);
                BOOST_CHECK_EQUAL(value, x*x);
                it->Next();
            }
        }
        BOOST_CHECK(!it->Valid());
    }
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Add testcase to simulate bitcoin schema in leveldb<commit_after>// Copyright (c) 2012-2013 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "dbwrapper.h"
#include "uint256.h"
#include "random.h"
#include "test/test_pivx.h"

#include <boost/test/unit_test.hpp>

// Test if a string consists entirely of null characters
bool is_null_key(const std::vector<unsigned char>& key) {
    bool isnull = true;

    for (unsigned int i = 0; i < key.size(); i++)
        isnull &= (key[i] == '\x00');

    return isnull;
}

BOOST_FIXTURE_TEST_SUITE(dbwrapper_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(dbwrapper)
{
    {
        fs::path ph = fs::temp_directory_path() / fs::unique_path();
        CDBWrapper dbw(ph, (1 << 20), true, false);
        char key = 'k';
        uint256 in = GetRandHash();
        uint256 res;

        BOOST_CHECK(dbw.Write(key, in));
        BOOST_CHECK(dbw.Read(key, res));
        BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
    }
}

BOOST_AUTO_TEST_CASE(dbwrapper_basic_data)
{
    fs::path ph = GetDataDir() / "dbwrapper_1";
    CDBWrapper dbw(ph, (1 << 20), false, true);

    uint256 res;
    uint32_t res_uint_32;
    bool res_bool;

    //Simulate block raw data - "b + block hash"
    std::string key_block = "b" + InsecureRand256().ToString();

    uint256 in_block = InsecureRand256();
    BOOST_CHECK(dbw.Write(key_block, in_block));
    BOOST_CHECK(dbw.Read(key_block, res));
    BOOST_CHECK_EQUAL(res.ToString(), in_block.ToString());

    //Simulate file raw data - "f + file_number"
    std::string key_file = strprintf("f%04x", InsecureRand32());

    uint256 in_file_info = InsecureRand256();
    BOOST_CHECK(dbw.Write(key_file, in_file_info));
    BOOST_CHECK(dbw.Read(key_file, res));
    BOOST_CHECK_EQUAL(res.ToString(), in_file_info.ToString());

    //Simulate transaction raw data - "t + transaction hash"
    std::string key_transaction = "t" + InsecureRand256().ToString();

    uint256 in_transaction = InsecureRand256();
    BOOST_CHECK(dbw.Write(key_transaction, in_transaction));
    BOOST_CHECK(dbw.Read(key_transaction, res));
    BOOST_CHECK_EQUAL(res.ToString(), in_transaction.ToString());

    //Simulate UTXO raw data - "c + transaction hash"
    std::string key_utxo = "c" + InsecureRand256().ToString();

    uint256 in_utxo = InsecureRand256();
    BOOST_CHECK(dbw.Write(key_utxo, in_utxo));
    BOOST_CHECK(dbw.Read(key_utxo, res));
    BOOST_CHECK_EQUAL(res.ToString(), in_utxo.ToString());

    //Simulate last block file number - "l"
    char key_last_blockfile_number = 'l';
    uint32_t lastblockfilenumber = InsecureRand32();
    BOOST_CHECK(dbw.Write(key_last_blockfile_number, lastblockfilenumber));
    BOOST_CHECK(dbw.Read(key_last_blockfile_number, res_uint_32));
    BOOST_CHECK_EQUAL(lastblockfilenumber, res_uint_32);

    //Simulate Is Reindexing - "R"
    char key_IsReindexing = 'R';
    bool isInReindexing = InsecureRandBool();
    BOOST_CHECK(dbw.Write(key_IsReindexing, isInReindexing));
    BOOST_CHECK(dbw.Read(key_IsReindexing, res_bool));
    BOOST_CHECK_EQUAL(isInReindexing, res_bool);

    //Simulate last block hash up to which UXTO covers - 'B'
    char key_lastblockhash_uxto = 'B';
    uint256 lastblock_hash = InsecureRand256();
    BOOST_CHECK(dbw.Write(key_lastblockhash_uxto, lastblock_hash));
    BOOST_CHECK(dbw.Read(key_lastblockhash_uxto, res));
    BOOST_CHECK_EQUAL(lastblock_hash, res);

    //Simulate file raw data - "F + filename_number + filename"
    std::string file_option_tag = "F";
    uint8_t filename_length = InsecureRandBits(8);
    std::string filename = "randomfilename";
    std::string key_file_option = strprintf("%s%01x%s", file_option_tag,filename_length,filename);

    bool in_file_bool = InsecureRandBool();
    BOOST_CHECK(dbw.Write(key_file_option, in_file_bool));
    BOOST_CHECK(dbw.Read(key_file_option, res_bool));
    BOOST_CHECK_EQUAL(res_bool, in_file_bool);
}

// Test batch operations
BOOST_AUTO_TEST_CASE(dbwrapper_batch)
{
    {
        fs::path ph = fs::temp_directory_path() / fs::unique_path();
        CDBWrapper dbw(ph, (1 << 20), true, false);

        char key = 'i';
        uint256 in = GetRandHash();
        char key2 = 'j';
        uint256 in2 = GetRandHash();
        char key3 = 'k';
        uint256 in3 = GetRandHash();

        uint256 res;
        CDBBatch batch;

        batch.Write(key, in);
        batch.Write(key2, in2);
        batch.Write(key3, in3);

        // Remove key3 before it's even been written
        batch.Erase(key3);

        dbw.WriteBatch(batch);

        BOOST_CHECK(dbw.Read(key, res));
        BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
        BOOST_CHECK(dbw.Read(key2, res));
        BOOST_CHECK_EQUAL(res.ToString(), in2.ToString());

        // key3 never should've been written
        BOOST_CHECK(dbw.Read(key3, res) == false);
    }
}

BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
{
    {
        fs::path ph = fs::temp_directory_path() / fs::unique_path();
        CDBWrapper dbw(ph, (1 << 20), true, false);

        // The two keys are intentionally chosen for ordering
        char key = 'j';
        uint256 in = GetRandHash();
        BOOST_CHECK(dbw.Write(key, in));
        char key2 = 'k';
        uint256 in2 = GetRandHash();
        BOOST_CHECK(dbw.Write(key2, in2));

        std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator());

        // Be sure to seek past any earlier key (if it exists)
        it->Seek(key);

        char key_res;
        uint256 val_res;

        it->GetKey(key_res);
        it->GetValue(val_res);
        BOOST_CHECK_EQUAL(key_res, key);
        BOOST_CHECK_EQUAL(val_res.ToString(), in.ToString());

        it->Next();

        it->GetKey(key_res);
        it->GetValue(val_res);
        BOOST_CHECK_EQUAL(key_res, key2);
        BOOST_CHECK_EQUAL(val_res.ToString(), in2.ToString());

        it->Next();
        BOOST_CHECK_EQUAL(it->Valid(), false);
    }
}

BOOST_AUTO_TEST_CASE(iterator_ordering)
{
    fs::path ph = fs::temp_directory_path() / fs::unique_path();
    CDBWrapper dbw(ph, (1 << 20), true, false);
    for (int x=0x00; x<256; ++x) {
        uint8_t key = x;
        uint32_t value = x*x;
        if (!(x & 1)) BOOST_CHECK(dbw.Write(key, value));
    }

    // Check that creating an iterator creates a snapshot
    std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator());

    for (int x=0x00; x<256; ++x) {
        uint8_t key = x;
        uint32_t value = x*x;
        if (x & 1) BOOST_CHECK(dbw.Write(key, value));
    }

    for (int seek_start : {0x00, 0x80}) {
        it->Seek((uint8_t)seek_start);
        for (int x=seek_start; x<255; ++x) {
            uint8_t key;
            uint32_t value;
            BOOST_CHECK(it->Valid());
            if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure
                break;
            BOOST_CHECK(it->GetKey(key));
            if (x & 1) {
                BOOST_CHECK_EQUAL(key, x + 1);
                continue;
            }
            BOOST_CHECK(it->GetValue(value));
            BOOST_CHECK_EQUAL(key, x);
            BOOST_CHECK_EQUAL(value, x*x);
            it->Next();
        }
        BOOST_CHECK(!it->Valid());
    }
}

struct StringContentsSerializer {
    // Used to make two serialized objects the same while letting them have a different lengths
    // This is a terrible idea
    std::string str;
    StringContentsSerializer() {}
    StringContentsSerializer(const std::string& inp) : str(inp) {}

    StringContentsSerializer& operator+=(const std::string& s) {
        str += s;
        return *this;
    }
    StringContentsSerializer& operator+=(const StringContentsSerializer& s) { return *this += s.str; }

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        if (ser_action.ForRead()) {
            str.clear();
            char c = 0;
            while (true) {
                try {
                    READWRITE(c);
                    str.push_back(c);
                } catch (const std::ios_base::failure& e) {
                    break;
                }
            }
        } else {
            for (size_t i = 0; i < str.size(); i++)
                READWRITE(str[i]);
        }
    }
};

BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{
    char buf[10];

    fs::path ph = fs::temp_directory_path() / fs::unique_path();
    CDBWrapper dbw(ph, (1 << 20), true, false);
    for (int x=0x00; x<10; ++x) {
        for (int y = 0; y < 10; y++) {
            snprintf(buf, sizeof(buf), "%d", x);
            StringContentsSerializer key(buf);
            for (int z = 0; z < y; z++)
                key += key;
            uint32_t value = x*x;
            BOOST_CHECK(dbw.Write(key, value));
        }
    }

    boost::scoped_ptr<CDBIterator> it(const_cast<CDBWrapper*>(&dbw)->NewIterator());
    for (int c=0; c<2; ++c) {
        int seek_start;
        if (c == 0)
            seek_start = 0;
        else
            seek_start = 5;
        snprintf(buf, sizeof(buf), "%d", seek_start);
        StringContentsSerializer seek_key(buf);
        it->Seek(seek_key);
        for (int x=seek_start; x<10; ++x) {
            for (int y = 0; y < 10; y++) {
                snprintf(buf, sizeof(buf), "%d", x);
                std::string exp_key(buf);
                for (int z = 0; z < y; z++)
                    exp_key += exp_key;
                StringContentsSerializer key;
                uint32_t value;
                BOOST_CHECK(it->Valid());
                if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure
                    break;
                BOOST_CHECK(it->GetKey(key));
                BOOST_CHECK(it->GetValue(value));
                BOOST_CHECK_EQUAL(key.str, exp_key);
                BOOST_CHECK_EQUAL(value, x*x);
                it->Next();
            }
        }
        BOOST_CHECK(!it->Valid());
    }
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libkeynote project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>

#include <libkeynote/KeyNoteDocument.h>

#include "libkeynote_utils.h"
#include "KN1Parser.h"
#include "KN2Parser.h"
#include "KNContentCollector.h"
#include "KNDictionary.h"
#include "KNThemeCollector.h"
#include "KNSVGGenerator.h"
#include "KNZipStream.h"
#include "KNZlibStream.h"

using boost::scoped_ptr;
using boost::shared_ptr;

namespace libkeynote
{

namespace
{

enum Version
{
  VERSION_UNKNOWN,
  VERSION_KEYNOTE_1,
  VERSION_KEYNOTE_2
};

enum Source
{
  SOURCE_UNKNOWN,
  SOURCE_APXL,
  SOURCE_APXL_GZ,
  SOURCE_PACKAGE_APXL,
  SOURCE_PACKAGE_APXL_GZ,
  SOURCE_KEY
};

Version detectVersionFromInput(const WPXInputStreamPtr_t &input)
{
  // TODO: do a real detection
  (void) input;
  return VERSION_KEYNOTE_2;
}

Version detectVersion(const WPXInputStreamPtr_t &input, Source &source)
{
  source = SOURCE_UNKNOWN;

  // check if this is a package
  if (input->isOLEStream())
  {
    scoped_ptr<WPXInputStream> apxl;

    apxl.reset(input->getDocumentOLEStream("index.apxl.gz"));
    if (bool(apxl))
    {
      source = SOURCE_PACKAGE_APXL_GZ;
      return VERSION_KEYNOTE_2;
    }

    apxl.reset(input->getDocumentOLEStream("presentation.apxl.gz"));
    if (bool(apxl))
    {
      source = SOURCE_PACKAGE_APXL_GZ;
      return VERSION_KEYNOTE_1;
    }

    apxl.reset(input->getDocumentOLEStream("index.apxl"));
    if (bool(apxl))
    {
      source = SOURCE_PACKAGE_APXL;
      return VERSION_KEYNOTE_2;
    }

    apxl.reset(input->getDocumentOLEStream("presentation.apxl"));
    if (bool(apxl))
    {
      source = SOURCE_PACKAGE_APXL;
      return VERSION_KEYNOTE_1;
    }
  }

  // check if this is a zip file (Keynote 09)
  KNZipStream zipInput(input);
  if (zipInput.isOLEStream())
  {
    scoped_ptr<WPXInputStream> apxl(zipInput.getDocumentOLEStream("index.apxl"));
    if (bool(apxl))
    {
      source = SOURCE_KEY;
      return VERSION_KEYNOTE_2;
    }
  }

  try
  {
    KNZlibStream compressedInput(input);
    source = SOURCE_APXL_GZ;
    return detectVersionFromInput(WPXInputStreamPtr_t(&compressedInput, KNDummyDeleter()));
  }
  catch (...)
  {
    // ignore
  }

  source = SOURCE_APXL;
  return detectVersionFromInput(input);
}

Version detectVersion(const WPXInputStreamPtr_t &input)
{
  Source dummy;
  return detectVersion(input, dummy);
}

shared_ptr<KNParser> makeParser(const Version version, const WPXInputStreamPtr_t &input, KNCollector *const collector)
{
  shared_ptr<KNParser> parser;

  switch (version)
  {
  case VERSION_KEYNOTE_1 :
    parser.reset(new KN1Parser(input, collector));
    break;
  case VERSION_KEYNOTE_2 :
    parser.reset(new KN2Parser(input, collector));
    break;
  default :
    KN_DEBUG_MSG(("KeyNoteDocument::parse(): unhandled version\n"));
    break;
  }

  return parser;
}

}

namespace
{

class DummyOLEStream : public WPXInputStream
{
public:
  virtual bool isOLEStream();
  virtual WPXInputStream *getDocumentOLEStream(const char *name);

  virtual const unsigned char *read(unsigned long numBytes, unsigned long &numBytesRead);
  virtual int seek(long offset, WPX_SEEK_TYPE seekType);
  virtual long tell();
  virtual bool atEOS();
};

bool DummyOLEStream::isOLEStream()
{
  return true;
}

WPXInputStream *DummyOLEStream::getDocumentOLEStream(const char *)
{
  return 0;
}

const unsigned char *DummyOLEStream::read(unsigned long, unsigned long &numBytesRead)
{
  numBytesRead = 0;
  return 0;
}

int DummyOLEStream::seek(long, WPX_SEEK_TYPE)
{
  return -1;
}

long DummyOLEStream::tell()
{
  return 0;
}

bool DummyOLEStream::atEOS()
{
  return true;
}

}

namespace
{

class CompositeStream : public WPXInputStream
{

public:
  CompositeStream(const WPXInputStreamPtr_t &input, Version version, Source source);
  virtual ~CompositeStream();

  virtual bool isOLEStream();
  virtual WPXInputStream *getDocumentOLEStream(const char *name);

  virtual const unsigned char *read(unsigned long numBytes, unsigned long &numBytesRead);
  virtual int seek(long offset, WPX_SEEK_TYPE seekType);
  virtual long tell();
  virtual bool atEOS();

private:
  WPXInputStreamPtr_t m_input;
  WPXInputStreamPtr_t m_dir;
};

CompositeStream::CompositeStream(const WPXInputStreamPtr_t &input, const Version version, const Source source)
  : m_input()
  , m_dir()
{
  if (VERSION_UNKNOWN == version)
  {
    KN_DEBUG_MSG(("cannot create a stream for unknown version\n"));
    throw GenericException();
  }

  switch (source)
  {
  case SOURCE_APXL :
    m_input = input;
    m_dir.reset(new DummyOLEStream());
    break;
  case SOURCE_APXL_GZ :
    m_input.reset(new KNZlibStream(input));
    m_dir.reset(new DummyOLEStream());
    break;
  case SOURCE_PACKAGE_APXL :
    if (VERSION_KEYNOTE_1 == version)
      m_input.reset(input->getDocumentOLEStream("presentation.apxl"));
    else if (VERSION_KEYNOTE_2 == version)
      m_input.reset(input->getDocumentOLEStream("index.apxl"));
    m_dir = input;
    break;
  case SOURCE_PACKAGE_APXL_GZ :
  {
    WPXInputStreamPtr_t compressedInput;
    if (VERSION_KEYNOTE_1 == version)
      compressedInput.reset(input->getDocumentOLEStream("presentation.apxl.gz"));
    else if (VERSION_KEYNOTE_2 == version)
      compressedInput.reset(input->getDocumentOLEStream("index.apxl.gz"));
    m_input.reset(new KNZlibStream(compressedInput));
    m_dir = input;
    break;
  }
  case SOURCE_KEY :
    m_input.reset(input->getDocumentOLEStream("index.apxl"));
    m_dir = input;
    break;
  default :
    KN_DEBUG_MSG(("cannot create a stream for unknown source type\n"));
    throw GenericException();
  }
}

CompositeStream::~CompositeStream()
{
}

bool CompositeStream::isOLEStream()
{
  return true;
}

WPXInputStream *CompositeStream::getDocumentOLEStream(const char *const name)
{
  return m_dir->getDocumentOLEStream(name);
}

const unsigned char *CompositeStream::read(const unsigned long numBytes, unsigned long &numBytesRead)
{
  return m_input->read(numBytes, numBytesRead);
}

int CompositeStream::seek(const long offset, const WPX_SEEK_TYPE seekType)
{
  return m_input->seek(offset, seekType);
}

long CompositeStream::tell()
{
  return m_input->tell();
}

bool CompositeStream::atEOS()
{
  return m_input->atEOS();
}

}

/**
Analyzes the content of an input stream to see if it can be parsed
\param input The input stream
\return A value that indicates whether the content from the input
stream is a KeyNote Document that libkeynote is able to parse
*/
bool KeyNoteDocument::isSupported(WPXInputStream *const input) try
{
  const Version version = detectVersion(WPXInputStreamPtr_t(input, KNDummyDeleter()));
  return VERSION_UNKNOWN != version;
}
catch (...)
{
  return false;
}

/**
Parses the input stream content. It will make callbacks to the functions provided by a
WPGPaintInterface class implementation when needed. This is often commonly called the
'main parsing routine'.
\param input The input stream
\param painter A WPGPaintInterface implementation
\return A value that indicates whether the parsing was successful
*/
bool KeyNoteDocument::parse(::WPXInputStream *const input, libwpg::WPGPaintInterface *const painter) try
{
  WPXInputStreamPtr_t input_(input, KNDummyDeleter());

  Source source = SOURCE_UNKNOWN;
  const Version version = detectVersion(input_, source);

  if (VERSION_UNKNOWN == version)
    return false;

  CompositeStream compositeInput(input_, version, source);
  const WPXInputStreamPtr_t compositeInput_(&compositeInput, KNDummyDeleter());

  KNDictionary dict;
  KNLayerMap_t masterPages;
  KNSize presentationSize;

  compositeInput.seek(0, WPX_SEEK_SET);

  KNThemeCollector themeCollector(dict, masterPages, presentationSize);
  shared_ptr<KNParser> parser = makeParser(version, compositeInput_, &themeCollector);
  if (!parser->parse())
    return false;

  compositeInput.seek(0, WPX_SEEK_SET);

  KNContentCollector contentCollector(painter, dict, masterPages, presentationSize);
  parser = makeParser(version, compositeInput_, &contentCollector);
  return parser->parse();
}
catch (...)
{
  return false;
}

/**
Parses the input stream content and generates a valid Scalable Vector Graphics
Provided as a convenience function for applications that support SVG internally.
\param input The input stream
\param output The output string whose content is the resulting SVG
\return A value that indicates whether the SVG generation was successful.
*/
bool KeyNoteDocument::generateSVG(::WPXInputStream *const input, KNStringVector &output) try
{
  KNSVGGenerator generator(output);
  bool result = KeyNoteDocument::parse(input, &generator);
  return result;
}
catch (...)
{
  return false;
}

}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<commit_msg>cover versions, not just the two generations of the format<commit_after>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libkeynote project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>

#include <libkeynote/KeyNoteDocument.h>

#include "libkeynote_utils.h"
#include "KN1Parser.h"
#include "KN2Parser.h"
#include "KNContentCollector.h"
#include "KNDictionary.h"
#include "KNThemeCollector.h"
#include "KNSVGGenerator.h"
#include "KNZipStream.h"
#include "KNZlibStream.h"

using boost::scoped_ptr;
using boost::shared_ptr;

namespace libkeynote
{

namespace
{

/** Version of the file format.
  *
  * Versions 2--5 use the same format, with possible changes.
  */
enum Version
{
  VERSION_UNKNOWN,
  VERSION_KEYNOTE_1,
  VERSION_KEYNOTE_2,
  VERSION_KEYNOTE_3,
  VERSION_KEYNOTE_4,
  VERSION_KEYNOTE_5,
};

enum Source
{
  SOURCE_UNKNOWN,
  SOURCE_APXL,
  SOURCE_APXL_GZ,
  SOURCE_PACKAGE_APXL,
  SOURCE_PACKAGE_APXL_GZ,
  SOURCE_KEY
};

Version detectVersionFromInput(const WPXInputStreamPtr_t &input)
{
  // TODO: do a real detection
  (void) input;
  return VERSION_KEYNOTE_5;
}

Version detectVersion(const WPXInputStreamPtr_t &input, Source &source)
{
  source = SOURCE_UNKNOWN;

  // check if this is a package
  if (input->isOLEStream())
  {
    scoped_ptr<WPXInputStream> apxl;

    apxl.reset(input->getDocumentOLEStream("index.apxl.gz"));
    if (bool(apxl))
    {
      source = SOURCE_PACKAGE_APXL_GZ;
      return VERSION_KEYNOTE_5;
    }

    apxl.reset(input->getDocumentOLEStream("presentation.apxl.gz"));
    if (bool(apxl))
    {
      source = SOURCE_PACKAGE_APXL_GZ;
      return VERSION_KEYNOTE_1;
    }

    apxl.reset(input->getDocumentOLEStream("index.apxl"));
    if (bool(apxl))
    {
      source = SOURCE_PACKAGE_APXL;
      return VERSION_KEYNOTE_5;
    }

    apxl.reset(input->getDocumentOLEStream("presentation.apxl"));
    if (bool(apxl))
    {
      source = SOURCE_PACKAGE_APXL;
      return VERSION_KEYNOTE_1;
    }
  }

  // check if this is a zip file (Keynote 09)
  KNZipStream zipInput(input);
  if (zipInput.isOLEStream())
  {
    scoped_ptr<WPXInputStream> apxl(zipInput.getDocumentOLEStream("index.apxl"));
    if (bool(apxl))
    {
      source = SOURCE_KEY;
      return VERSION_KEYNOTE_5;
    }
  }

  try
  {
    KNZlibStream compressedInput(input);
    source = SOURCE_APXL_GZ;
    return detectVersionFromInput(WPXInputStreamPtr_t(&compressedInput, KNDummyDeleter()));
  }
  catch (...)
  {
    // ignore
  }

  source = SOURCE_APXL;
  return detectVersionFromInput(input);
}

Version detectVersion(const WPXInputStreamPtr_t &input)
{
  Source dummy;
  return detectVersion(input, dummy);
}

shared_ptr<KNParser> makeParser(const Version version, const WPXInputStreamPtr_t &input, KNCollector *const collector)
{
  shared_ptr<KNParser> parser;

  switch (version)
  {
  case VERSION_KEYNOTE_1 :
    parser.reset(new KN1Parser(input, collector));
    break;
  case VERSION_KEYNOTE_2 :
  case VERSION_KEYNOTE_3 :
  case VERSION_KEYNOTE_4 :
  case VERSION_KEYNOTE_5 :
    parser.reset(new KN2Parser(input, collector));
    break;
  default :
    KN_DEBUG_MSG(("KeyNoteDocument::parse(): unhandled version\n"));
    break;
  }

  return parser;
}

}

namespace
{

class DummyOLEStream : public WPXInputStream
{
public:
  virtual bool isOLEStream();
  virtual WPXInputStream *getDocumentOLEStream(const char *name);

  virtual const unsigned char *read(unsigned long numBytes, unsigned long &numBytesRead);
  virtual int seek(long offset, WPX_SEEK_TYPE seekType);
  virtual long tell();
  virtual bool atEOS();
};

bool DummyOLEStream::isOLEStream()
{
  return true;
}

WPXInputStream *DummyOLEStream::getDocumentOLEStream(const char *)
{
  return 0;
}

const unsigned char *DummyOLEStream::read(unsigned long, unsigned long &numBytesRead)
{
  numBytesRead = 0;
  return 0;
}

int DummyOLEStream::seek(long, WPX_SEEK_TYPE)
{
  return -1;
}

long DummyOLEStream::tell()
{
  return 0;
}

bool DummyOLEStream::atEOS()
{
  return true;
}

}

namespace
{

class CompositeStream : public WPXInputStream
{

public:
  CompositeStream(const WPXInputStreamPtr_t &input, Version version, Source source);
  virtual ~CompositeStream();

  virtual bool isOLEStream();
  virtual WPXInputStream *getDocumentOLEStream(const char *name);

  virtual const unsigned char *read(unsigned long numBytes, unsigned long &numBytesRead);
  virtual int seek(long offset, WPX_SEEK_TYPE seekType);
  virtual long tell();
  virtual bool atEOS();

private:
  WPXInputStreamPtr_t m_input;
  WPXInputStreamPtr_t m_dir;
};

CompositeStream::CompositeStream(const WPXInputStreamPtr_t &input, const Version version, const Source source)
  : m_input()
  , m_dir()
{
  if (VERSION_UNKNOWN == version)
  {
    KN_DEBUG_MSG(("cannot create a stream for unknown version\n"));
    throw GenericException();
  }

  switch (source)
  {
  case SOURCE_APXL :
    m_input = input;
    m_dir.reset(new DummyOLEStream());
    break;
  case SOURCE_APXL_GZ :
    m_input.reset(new KNZlibStream(input));
    m_dir.reset(new DummyOLEStream());
    break;
  case SOURCE_PACKAGE_APXL :
    if (VERSION_KEYNOTE_1 == version)
      m_input.reset(input->getDocumentOLEStream("presentation.apxl"));
    else if (VERSION_UNKNOWN != version)
      m_input.reset(input->getDocumentOLEStream("index.apxl"));
    m_dir = input;
    break;
  case SOURCE_PACKAGE_APXL_GZ :
  {
    WPXInputStreamPtr_t compressedInput;
    if (VERSION_KEYNOTE_1 == version)
      compressedInput.reset(input->getDocumentOLEStream("presentation.apxl.gz"));
    else if (VERSION_UNKNOWN != version)
      compressedInput.reset(input->getDocumentOLEStream("index.apxl.gz"));
    m_input.reset(new KNZlibStream(compressedInput));
    m_dir = input;
    break;
  }
  case SOURCE_KEY :
    m_input.reset(input->getDocumentOLEStream("index.apxl"));
    m_dir = input;
    break;
  default :
    KN_DEBUG_MSG(("cannot create a stream for unknown source type\n"));
    throw GenericException();
  }
}

CompositeStream::~CompositeStream()
{
}

bool CompositeStream::isOLEStream()
{
  return true;
}

WPXInputStream *CompositeStream::getDocumentOLEStream(const char *const name)
{
  return m_dir->getDocumentOLEStream(name);
}

const unsigned char *CompositeStream::read(const unsigned long numBytes, unsigned long &numBytesRead)
{
  return m_input->read(numBytes, numBytesRead);
}

int CompositeStream::seek(const long offset, const WPX_SEEK_TYPE seekType)
{
  return m_input->seek(offset, seekType);
}

long CompositeStream::tell()
{
  return m_input->tell();
}

bool CompositeStream::atEOS()
{
  return m_input->atEOS();
}

}

/**
Analyzes the content of an input stream to see if it can be parsed
\param input The input stream
\return A value that indicates whether the content from the input
stream is a KeyNote Document that libkeynote is able to parse
*/
bool KeyNoteDocument::isSupported(WPXInputStream *const input) try
{
  const Version version = detectVersion(WPXInputStreamPtr_t(input, KNDummyDeleter()));
  return VERSION_UNKNOWN != version;
}
catch (...)
{
  return false;
}

/**
Parses the input stream content. It will make callbacks to the functions provided by a
WPGPaintInterface class implementation when needed. This is often commonly called the
'main parsing routine'.
\param input The input stream
\param painter A WPGPaintInterface implementation
\return A value that indicates whether the parsing was successful
*/
bool KeyNoteDocument::parse(::WPXInputStream *const input, libwpg::WPGPaintInterface *const painter) try
{
  WPXInputStreamPtr_t input_(input, KNDummyDeleter());

  Source source = SOURCE_UNKNOWN;
  const Version version = detectVersion(input_, source);

  if (VERSION_UNKNOWN == version)
    return false;

  CompositeStream compositeInput(input_, version, source);
  const WPXInputStreamPtr_t compositeInput_(&compositeInput, KNDummyDeleter());

  KNDictionary dict;
  KNLayerMap_t masterPages;
  KNSize presentationSize;

  compositeInput.seek(0, WPX_SEEK_SET);

  KNThemeCollector themeCollector(dict, masterPages, presentationSize);
  shared_ptr<KNParser> parser = makeParser(version, compositeInput_, &themeCollector);
  if (!parser->parse())
    return false;

  compositeInput.seek(0, WPX_SEEK_SET);

  KNContentCollector contentCollector(painter, dict, masterPages, presentationSize);
  parser = makeParser(version, compositeInput_, &contentCollector);
  return parser->parse();
}
catch (...)
{
  return false;
}

/**
Parses the input stream content and generates a valid Scalable Vector Graphics
Provided as a convenience function for applications that support SVG internally.
\param input The input stream
\param output The output string whose content is the resulting SVG
\return A value that indicates whether the SVG generation was successful.
*/
bool KeyNoteDocument::generateSVG(::WPXInputStream *const input, KNStringVector &output) try
{
  KNSVGGenerator generator(output);
  bool result = KeyNoteDocument::parse(input, &generator);
  return result;
}
catch (...)
{
  return false;
}

}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 <string>

#include <mesos/scheduler.hpp>

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

#include <stout/json.hpp>
#include <stout/lambda.hpp>
#include <stout/recordio.hpp>

#include "common/http.hpp"
#include "common/recordio.hpp"

#include "master/master.hpp"

#include "tests/mesos.hpp"
#include "tests/utils.hpp"

using mesos::internal::master::Master;

using mesos::internal::recordio::Reader;

using mesos::scheduler::Call;
using mesos::scheduler::Event;

using process::Future;
using process::PID;

using process::http::BadRequest;
using process::http::OK;
using process::http::Pipe;
using process::http::Response;

using recordio::Decoder;

using std::string;

using testing::WithParamInterface;

namespace mesos {
namespace internal {
namespace tests {

class HttpApiTest
  : public MesosTest,
    public WithParamInterface<string>
{
public:
  // TODO(anand): Use the serialize/deserialize from common/http.hpp
  // when they are available.
  Try<Event> deserialize(
      const std::string& contentType,
      const std::string& body)
  {
    if (contentType == APPLICATION_PROTOBUF) {
      Event event;
      if (!event.ParseFromString(body)) {
        return Error("Failed to parse body into Event protobuf");
      }
      return event;
    }

    Try<JSON::Value> value = JSON::parse(body);
    Try<Event> parse = ::protobuf::parse<Event>(value.get());
    return parse;
  }

  std::string serialize(const Call& call, const std::string& contentType)
  {
    if (contentType == APPLICATION_PROTOBUF) {
      return call.SerializeAsString();
    }

    return stringify(JSON::Protobuf(call));
  }

  master::Flags masterFlags()
  {
    master::Flags flags = CreateMasterFlags();
    flags.authenticate_frameworks = false;
    return flags;
  }
};

// The HttpApi tests are parameterized by the content type.
INSTANTIATE_TEST_CASE_P(
    ContentType,
    HttpApiTest,
    ::testing::Values(APPLICATION_PROTOBUF, APPLICATION_JSON));

// TODO(anand): Add tests for:
// - A subscribed scheduler closes it's reader and then tries to
//  subscribe again before the framework failover timeout and should
//  succeed.
//
// - A subscribed PID scheduler disconnects and then tries to
//  subscribe again as a HTTP framework before the framework failover
//  timeout and should succeed.

// TODO(anand): Add additional tests for validation.
TEST_F(HttpApiTest, NoContentType)
{
  Try<PID<Master>> master = StartMaster();
  ASSERT_SOME(master);

  // Expect a BadRequest when 'Content-Type' is omitted.
  //
  // TODO(anand): Send a valid call here to ensure that
  // the BadRequest is only due to the missing header.
  Future<Response> response = process::http::post(
      master.get(),
      "api/v1/scheduler",
      None(),
      None());

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response);
}

// This test verifies if the scheduler is able to receive a Subscribed
// event on the stream in response to a Subscribe call request.
TEST_P(HttpApiTest, Subscribe)
{
  Try<PID<Master>> master = StartMaster(masterFlags());
  ASSERT_SOME(master);

  Call call;
  call.set_type(Call::SUBSCRIBE);

  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;

  Call::Subscribe* subscribe = call.mutable_subscribe();
  subscribe->mutable_framework_info()->CopyFrom(frameworkInfo);

  // Retrieve the parameter passed as content type to this test.
  const std::string contentType = GetParam();
  hashmap<string, string> headers;
  headers["Accept"] = contentType;

  Future<Response> response = process::http::streaming::post(
      master.get(),
      "api/v1/scheduler",
      headers,
      serialize(call, contentType),
      contentType);

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
  EXPECT_SOME_EQ("chunked", response.get().headers.get("Transfer-Encoding"));
  ASSERT_EQ(Response::PIPE, response.get().type);

  Option<Pipe::Reader> reader = response.get().reader;
  ASSERT_SOME(reader);

  auto deserializer =
    lambda::bind(&HttpApiTest::deserialize, this, contentType, lambda::_1);

  Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get());

  Future<Result<Event>> event = responseDecoder.read();
  AWAIT_READY(event);
  ASSERT_SOME(event.get());

  // Check event type is subscribed and the framework id is set.
  ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type());
  EXPECT_NE("", event.get().get().subscribed().framework_id().value());

  Shutdown();
}

// This test verifies if the scheduler can subscribe on retrying,
// e.g. after a ZK blip.
TEST_P(HttpApiTest, SubscribedOnRetryWithForce)
{
  Try<PID<Master>> master = StartMaster(masterFlags());
  ASSERT_SOME(master);

  Call call;
  call.set_type(Call::SUBSCRIBE);

  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;

  Call::Subscribe* subscribe = call.mutable_subscribe();
  subscribe->mutable_framework_info()->CopyFrom(frameworkInfo);

  // Retrieve the parameter passed as content type to this test.
  const std::string contentType = GetParam();
  hashmap<string, string> headers;
  headers["Accept"] = contentType;

  auto deserializer =
    lambda::bind(&HttpApiTest::deserialize, this, contentType, lambda::_1);

  FrameworkID frameworkId;

  {
    Future<Response> response = process::http::streaming::post(
        master.get(),
        "api/v1/scheduler",
        headers,
        serialize(call, contentType),
        contentType);

    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
    ASSERT_EQ(Response::PIPE, response.get().type);

    Option<Pipe::Reader> reader = response.get().reader;
    ASSERT_SOME(reader);

    Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get());

    Future<Result<Event>> event = responseDecoder.read();
    AWAIT_READY(event);
    ASSERT_SOME(event.get());

    frameworkId = event.get().get().subscribed().framework_id();

    // Check event type is subscribed and the framework id is set.
    ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type());
    EXPECT_NE("", event.get().get().subscribed().framework_id().value());
  }

  {
    // Now subscribe again with force set.
    subscribe->set_force(true);

    call.mutable_framework_id()->CopyFrom(frameworkId);
    subscribe->mutable_framework_info()->mutable_id()->CopyFrom(frameworkId);

    Future<Response> response = process::http::streaming::post(
        master.get(),
        "api/v1/scheduler",
        headers,
        serialize(call, contentType),
        contentType);

    Option<Pipe::Reader> reader = response.get().reader;
    ASSERT_SOME(reader);

    Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get());

    // Check if we were successfully able to subscribe after the blip.
    Future<Result<Event>> event = responseDecoder.read();
    AWAIT_READY(event);
    ASSERT_SOME(event.get());

    // Check event type is subscribed and the same framework id is set.
    ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type());
    EXPECT_EQ(frameworkId, event.get().get().subscribed().framework_id());
  }

  Shutdown();
}

// This test verifies if we are able to upgrade from a PID based
// framework to HTTP when force is set.
TEST_P(HttpApiTest, UpdatePidToHttpScheduler)
{
  Try<PID<Master>> master = StartMaster(masterFlags());
  ASSERT_SOME(master);

  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
  frameworkInfo.set_failover_timeout(Weeks(2).secs());

  // Start the scheduler without credentials.
  MockScheduler sched;
  StandaloneMasterDetector detector(master.get());
  TestingMesosSchedulerDriver driver(&sched, &detector, frameworkInfo);

  Future<FrameworkID> frameworkId;
  EXPECT_CALL(sched, registered(&driver, _, _))
    .WillOnce(FutureArg<1>(&frameworkId));

  // Check that driver is notified with an error when the http
  // framework is connected.
  Future<FrameworkErrorMessage> errorMessage =
    FUTURE_PROTOBUF(FrameworkErrorMessage(), _, _);

  EXPECT_CALL(sched, error(_, _));

  driver.start();

  AWAIT_READY(frameworkId);
  EXPECT_NE("", frameworkId.get().value());

  // Now try to subscribe as an HTTP framework.
  Call call;
  call.set_type(Call::SUBSCRIBE);
  call.mutable_framework_id()->CopyFrom(frameworkId.get());

  Call::Subscribe* subscribe = call.mutable_subscribe();

  subscribe->mutable_framework_info()->CopyFrom(frameworkInfo);
  subscribe->mutable_framework_info()->mutable_id()->
    CopyFrom(frameworkId.get());

  subscribe->set_force(true);

  // Retrieve the parameter passed as content type to this test.
  const std::string contentType = GetParam();
  hashmap<string, string> headers;
  headers["Accept"] = contentType;

  Future<Response> response = process::http::streaming::post(
      master.get(),
      "api/v1/scheduler",
      headers,
      serialize(call, contentType),
      contentType);

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
  EXPECT_SOME_EQ("chunked", response.get().headers.get("Transfer-Encoding"));
  ASSERT_EQ(Response::PIPE, response.get().type);

  Option<Pipe::Reader> reader = response.get().reader;
  ASSERT_SOME(reader);

  auto deserializer =
    lambda::bind(&HttpApiTest::deserialize, this, contentType, lambda::_1);

  Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get());

  Future<Result<Event>> event = responseDecoder.read();
  AWAIT_READY(event);
  ASSERT_SOME(event.get());

  // Check event type is subscribed and the framework id is set.
  ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type());
  EXPECT_EQ(frameworkId.get(), event.get().get().subscribed().framework_id());

  driver.stop();
  driver.join();

  Shutdown();
}

// This test verifies that updating a PID based framework to HTTP
// framework fails when force is not set and the PID based
// framework is already connected.
TEST_P(HttpApiTest, UpdatePidToHttpSchedulerWithoutForce)
{
  Try<PID<Master>> master = StartMaster(masterFlags());
  ASSERT_SOME(master);

  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
  frameworkInfo.set_failover_timeout(Weeks(2).secs());

  // Start the scheduler without credentials.
  MockScheduler sched;
  StandaloneMasterDetector detector(master.get());
  TestingMesosSchedulerDriver driver(&sched, &detector, frameworkInfo);

  Future<FrameworkID> frameworkId;
  EXPECT_CALL(sched, registered(&driver, _, _))
    .WillOnce(FutureArg<1>(&frameworkId));

  driver.start();

  AWAIT_READY(frameworkId);
  EXPECT_NE("", frameworkId.get().value());

  // Now try to subscribe using a HTTP framework without setting the
  // 'force' field.
  Call call;
  call.set_type(Call::SUBSCRIBE);
  call.mutable_framework_id()->CopyFrom(frameworkId.get());

  Call::Subscribe* subscribe = call.mutable_subscribe();
  subscribe->mutable_framework_info()->CopyFrom(frameworkInfo);
  subscribe->mutable_framework_info()->mutable_id()->
    CopyFrom(frameworkId.get());

  // Retrieve the parameter passed as content type to this test.
  const std::string contentType = GetParam();
  hashmap<string, string> headers;
  headers["Accept"] = contentType;

  Future<Response> response = process::http::streaming::post(
      master.get(),
      "api/v1/scheduler",
      headers,
      serialize(call, contentType),
      contentType);

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
  EXPECT_SOME_EQ("chunked", response.get().headers.get("Transfer-Encoding"));
  ASSERT_EQ(Response::PIPE, response.get().type);

  Option<Pipe::Reader> reader = response.get().reader;
  ASSERT_SOME(reader);

  auto deserializer =
    lambda::bind(&HttpApiTest::deserialize, this, contentType, lambda::_1);

  Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get());

  Future<Result<Event>> event = responseDecoder.read();
  AWAIT_READY(event);
  ASSERT_SOME(event.get());

  // We should be receiving an error event since the PID framework
  // was already connected.
  ASSERT_EQ(Event::ERROR, event.get().get().type());

  driver.stop();
  driver.join();

  Shutdown();
}

} // namespace tests {
} // namespace internal {
} // namespace mesos {
<commit_msg>More basic call validation tests for HTTP API.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 <string>

#include <mesos/scheduler.hpp>

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

#include <stout/json.hpp>
#include <stout/lambda.hpp>
#include <stout/recordio.hpp>

#include "common/http.hpp"
#include "common/recordio.hpp"

#include "master/master.hpp"

#include "tests/mesos.hpp"
#include "tests/utils.hpp"

using mesos::internal::master::Master;

using mesos::internal::recordio::Reader;

using mesos::scheduler::Call;
using mesos::scheduler::Event;

using process::Future;
using process::PID;

using process::http::BadRequest;
using process::http::OK;
using process::http::Pipe;
using process::http::Response;
using process::http::UnsupportedMediaType;

using recordio::Decoder;

using std::string;

using testing::WithParamInterface;

namespace mesos {
namespace internal {
namespace tests {

class HttpApiTest
  : public MesosTest,
    public WithParamInterface<string>
{
public:
  // TODO(anand): Use the serialize/deserialize from common/http.hpp
  // when they are available.
  Try<Event> deserialize(
      const std::string& contentType,
      const std::string& body)
  {
    if (contentType == APPLICATION_PROTOBUF) {
      Event event;
      if (!event.ParseFromString(body)) {
        return Error("Failed to parse body into Event protobuf");
      }
      return event;
    }

    Try<JSON::Value> value = JSON::parse(body);
    Try<Event> parse = ::protobuf::parse<Event>(value.get());
    return parse;
  }

  std::string serialize(const Call& call, const std::string& contentType)
  {
    if (contentType == APPLICATION_PROTOBUF) {
      return call.SerializeAsString();
    }

    return stringify(JSON::Protobuf(call));
  }

  master::Flags masterFlags()
  {
    master::Flags flags = CreateMasterFlags();
    flags.authenticate_frameworks = false;
    return flags;
  }
};

// The HttpApi tests are parameterized by the content type.
INSTANTIATE_TEST_CASE_P(
    ContentType,
    HttpApiTest,
    ::testing::Values(APPLICATION_PROTOBUF, APPLICATION_JSON));

// TODO(anand): Add tests for:
// - A subscribed scheduler closes it's reader and then tries to
//  subscribe again before the framework failover timeout and should
//  succeed.
//
// - A subscribed PID scheduler disconnects and then tries to
//  subscribe again as a HTTP framework before the framework failover
//  timeout and should succeed.

// TODO(anand): Add additional tests for validation.
TEST_F(HttpApiTest, NoContentType)
{
  Try<PID<Master>> master = StartMaster();
  ASSERT_SOME(master);

  // Expect a BadRequest when 'Content-Type' is omitted.
  //
  // TODO(anand): Send a valid call here to ensure that
  // the BadRequest is only due to the missing header.
  Future<Response> response = process::http::post(
      master.get(),
      "api/v1/scheduler",
      None(),
      None());

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response);
}

// This test sends a valid JSON blob that cannot be deserialized
// into a valid protobuf resulting in a BadRequest.
TEST_F(HttpApiTest, ValidJsonButInvalidProtobuf)
{
  Try<PID<Master>> master = StartMaster();
  ASSERT_SOME(master);

  JSON::Object object;
  object.values["string"] = "valid_json";

  hashmap<string, string> headers;
  headers["Accept"] = APPLICATION_JSON;

  Future<Response> response = process::http::post(
      master.get(),
      "api/v1/scheduler",
      headers,
      stringify(object),
      APPLICATION_JSON);

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response);
}

// This test sends a malformed body that cannot be deserialized
// into a valid protobuf resulting in a BadRequest.
TEST_P(HttpApiTest, MalformedContent)
{
  Try<PID<Master>> master = StartMaster();
  ASSERT_SOME(master);

  const std::string body = "MALFORMED_CONTENT";

  const std::string contentType = GetParam();
  hashmap<string, string> headers;
  headers["Accept"] = contentType;

  Future<Response> response = process::http::post(
      master.get(),
      "api/v1/scheduler",
      headers,
      body,
      contentType);

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response);
}

// This test sets an unsupported media type as Content-Type. This
// should result in a 415 (UnsupportedMediaType) response.
TEST_P(HttpApiTest, UnsupportedContentMediaType)
{
  Try<PID<Master>> master = StartMaster();
  ASSERT_SOME(master);

  const std::string contentType = GetParam();
  hashmap<string, string> headers;
  headers["Accept"] = contentType;

  Call call;
  call.set_type(Call::SUBSCRIBE);

  Call::Subscribe* subscribe = call.mutable_subscribe();
  subscribe->mutable_framework_info()->CopyFrom(DEFAULT_FRAMEWORK_INFO);

  const std::string unknownMediaType = "application/unknown-media-type";

  Future<Response> response = process::http::post(
      master.get(),
      "api/v1/scheduler",
      headers,
      serialize(call, contentType),
      unknownMediaType);

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(UnsupportedMediaType().status, response);
}

// This test verifies if the scheduler is able to receive a Subscribed
// event on the stream in response to a Subscribe call request.
TEST_P(HttpApiTest, Subscribe)
{
  Try<PID<Master>> master = StartMaster(masterFlags());
  ASSERT_SOME(master);

  Call call;
  call.set_type(Call::SUBSCRIBE);

  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;

  Call::Subscribe* subscribe = call.mutable_subscribe();
  subscribe->mutable_framework_info()->CopyFrom(frameworkInfo);

  // Retrieve the parameter passed as content type to this test.
  const std::string contentType = GetParam();
  hashmap<string, string> headers;
  headers["Accept"] = contentType;

  Future<Response> response = process::http::streaming::post(
      master.get(),
      "api/v1/scheduler",
      headers,
      serialize(call, contentType),
      contentType);

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
  EXPECT_SOME_EQ("chunked", response.get().headers.get("Transfer-Encoding"));
  ASSERT_EQ(Response::PIPE, response.get().type);

  Option<Pipe::Reader> reader = response.get().reader;
  ASSERT_SOME(reader);

  auto deserializer =
    lambda::bind(&HttpApiTest::deserialize, this, contentType, lambda::_1);

  Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get());

  Future<Result<Event>> event = responseDecoder.read();
  AWAIT_READY(event);
  ASSERT_SOME(event.get());

  // Check event type is subscribed and the framework id is set.
  ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type());
  EXPECT_NE("", event.get().get().subscribed().framework_id().value());

  Shutdown();
}

// This test verifies if the scheduler can subscribe on retrying,
// e.g. after a ZK blip.
TEST_P(HttpApiTest, SubscribedOnRetryWithForce)
{
  Try<PID<Master>> master = StartMaster(masterFlags());
  ASSERT_SOME(master);

  Call call;
  call.set_type(Call::SUBSCRIBE);

  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;

  Call::Subscribe* subscribe = call.mutable_subscribe();
  subscribe->mutable_framework_info()->CopyFrom(frameworkInfo);

  // Retrieve the parameter passed as content type to this test.
  const std::string contentType = GetParam();
  hashmap<string, string> headers;
  headers["Accept"] = contentType;

  auto deserializer =
    lambda::bind(&HttpApiTest::deserialize, this, contentType, lambda::_1);

  FrameworkID frameworkId;

  {
    Future<Response> response = process::http::streaming::post(
        master.get(),
        "api/v1/scheduler",
        headers,
        serialize(call, contentType),
        contentType);

    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
    ASSERT_EQ(Response::PIPE, response.get().type);

    Option<Pipe::Reader> reader = response.get().reader;
    ASSERT_SOME(reader);

    Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get());

    Future<Result<Event>> event = responseDecoder.read();
    AWAIT_READY(event);
    ASSERT_SOME(event.get());

    frameworkId = event.get().get().subscribed().framework_id();

    // Check event type is subscribed and the framework id is set.
    ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type());
    EXPECT_NE("", event.get().get().subscribed().framework_id().value());
  }

  {
    // Now subscribe again with force set.
    subscribe->set_force(true);

    call.mutable_framework_id()->CopyFrom(frameworkId);
    subscribe->mutable_framework_info()->mutable_id()->CopyFrom(frameworkId);

    Future<Response> response = process::http::streaming::post(
        master.get(),
        "api/v1/scheduler",
        headers,
        serialize(call, contentType),
        contentType);

    Option<Pipe::Reader> reader = response.get().reader;
    ASSERT_SOME(reader);

    Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get());

    // Check if we were successfully able to subscribe after the blip.
    Future<Result<Event>> event = responseDecoder.read();
    AWAIT_READY(event);
    ASSERT_SOME(event.get());

    // Check event type is subscribed and the same framework id is set.
    ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type());
    EXPECT_EQ(frameworkId, event.get().get().subscribed().framework_id());
  }

  Shutdown();
}

// This test verifies if we are able to upgrade from a PID based
// framework to HTTP when force is set.
TEST_P(HttpApiTest, UpdatePidToHttpScheduler)
{
  Try<PID<Master>> master = StartMaster(masterFlags());
  ASSERT_SOME(master);

  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
  frameworkInfo.set_failover_timeout(Weeks(2).secs());

  // Start the scheduler without credentials.
  MockScheduler sched;
  StandaloneMasterDetector detector(master.get());
  TestingMesosSchedulerDriver driver(&sched, &detector, frameworkInfo);

  Future<FrameworkID> frameworkId;
  EXPECT_CALL(sched, registered(&driver, _, _))
    .WillOnce(FutureArg<1>(&frameworkId));

  // Check that driver is notified with an error when the http
  // framework is connected.
  Future<FrameworkErrorMessage> errorMessage =
    FUTURE_PROTOBUF(FrameworkErrorMessage(), _, _);

  EXPECT_CALL(sched, error(_, _));

  driver.start();

  AWAIT_READY(frameworkId);
  EXPECT_NE("", frameworkId.get().value());

  // Now try to subscribe as an HTTP framework.
  Call call;
  call.set_type(Call::SUBSCRIBE);
  call.mutable_framework_id()->CopyFrom(frameworkId.get());

  Call::Subscribe* subscribe = call.mutable_subscribe();

  subscribe->mutable_framework_info()->CopyFrom(frameworkInfo);
  subscribe->mutable_framework_info()->mutable_id()->
    CopyFrom(frameworkId.get());

  subscribe->set_force(true);

  // Retrieve the parameter passed as content type to this test.
  const std::string contentType = GetParam();
  hashmap<string, string> headers;
  headers["Accept"] = contentType;

  Future<Response> response = process::http::streaming::post(
      master.get(),
      "api/v1/scheduler",
      headers,
      serialize(call, contentType),
      contentType);

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
  EXPECT_SOME_EQ("chunked", response.get().headers.get("Transfer-Encoding"));
  ASSERT_EQ(Response::PIPE, response.get().type);

  Option<Pipe::Reader> reader = response.get().reader;
  ASSERT_SOME(reader);

  auto deserializer =
    lambda::bind(&HttpApiTest::deserialize, this, contentType, lambda::_1);

  Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get());

  Future<Result<Event>> event = responseDecoder.read();
  AWAIT_READY(event);
  ASSERT_SOME(event.get());

  // Check event type is subscribed and the framework id is set.
  ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type());
  EXPECT_EQ(frameworkId.get(), event.get().get().subscribed().framework_id());

  driver.stop();
  driver.join();

  Shutdown();
}

// This test verifies that updating a PID based framework to HTTP
// framework fails when force is not set and the PID based
// framework is already connected.
TEST_P(HttpApiTest, UpdatePidToHttpSchedulerWithoutForce)
{
  Try<PID<Master>> master = StartMaster(masterFlags());
  ASSERT_SOME(master);

  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
  frameworkInfo.set_failover_timeout(Weeks(2).secs());

  // Start the scheduler without credentials.
  MockScheduler sched;
  StandaloneMasterDetector detector(master.get());
  TestingMesosSchedulerDriver driver(&sched, &detector, frameworkInfo);

  Future<FrameworkID> frameworkId;
  EXPECT_CALL(sched, registered(&driver, _, _))
    .WillOnce(FutureArg<1>(&frameworkId));

  driver.start();

  AWAIT_READY(frameworkId);
  EXPECT_NE("", frameworkId.get().value());

  // Now try to subscribe using a HTTP framework without setting the
  // 'force' field.
  Call call;
  call.set_type(Call::SUBSCRIBE);
  call.mutable_framework_id()->CopyFrom(frameworkId.get());

  Call::Subscribe* subscribe = call.mutable_subscribe();
  subscribe->mutable_framework_info()->CopyFrom(frameworkInfo);
  subscribe->mutable_framework_info()->mutable_id()->
    CopyFrom(frameworkId.get());

  // Retrieve the parameter passed as content type to this test.
  const std::string contentType = GetParam();
  hashmap<string, string> headers;
  headers["Accept"] = contentType;

  Future<Response> response = process::http::streaming::post(
      master.get(),
      "api/v1/scheduler",
      headers,
      serialize(call, contentType),
      contentType);

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
  EXPECT_SOME_EQ("chunked", response.get().headers.get("Transfer-Encoding"));
  ASSERT_EQ(Response::PIPE, response.get().type);

  Option<Pipe::Reader> reader = response.get().reader;
  ASSERT_SOME(reader);

  auto deserializer =
    lambda::bind(&HttpApiTest::deserialize, this, contentType, lambda::_1);

  Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get());

  Future<Result<Event>> event = responseDecoder.read();
  AWAIT_READY(event);
  ASSERT_SOME(event.get());

  // We should be receiving an error event since the PID framework
  // was already connected.
  ASSERT_EQ(Event::ERROR, event.get().get().type());

  driver.stop();
  driver.join();

  Shutdown();
}

} // namespace tests {
} // namespace internal {
} // namespace mesos {
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtSystems module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QUdpSocket>
#include <QHostAddress>
#include <QCoreApplication>
#include <QBuffer>
#include <QDataStream>
#include <QTime>
#include <QTimer>
#include <QNetworkInterface>
#include <cstdio>

/* keep this in sync with qtsystems/src/serviceframework/qservicedebuglog_p.h */
enum DataType {
    Int32Type = 1,
    FloatType = 2,
    StringType = 3
};

class SFWReceiver : public QObject
{
    Q_OBJECT
public:
    SFWReceiver(const QString &intfFilter);

public slots:
    void makeSockets();
    void socketReadyRead();

private:
    QString intfFilter;
};

SFWReceiver::SFWReceiver(const QString &intfFilter) : intfFilter(intfFilter)
{
    makeSockets();
}

void SFWReceiver::makeSockets()
{
    QList<QNetworkInterface> intfs = QNetworkInterface::allInterfaces();

    bool gotone = false;
    foreach (const QNetworkInterface &intf, intfs) {
        if (!intfFilter.isEmpty() && !intf.name().startsWith(intfFilter))
            continue;

        if (intf.name() == "lo" || intf.name().startsWith("wifi")
            || intf.name().startsWith("wl") || intf.name().startsWith("ppp")
            || intf.name().startsWith("tun") || intf.name().startsWith("tap"))
            continue;

        QUdpSocket *socket = new QUdpSocket(this);
        printf("Trying interface %s ...", qPrintable(intf.name()));
        if (!socket->bind(QHostAddress::AnyIPv4, 10520, QUdpSocket::ShareAddress)) {
            printf("Couldn't bind: %s\n", qPrintable(socket->errorString()));
            delete socket;
            continue;
        }
        socket->setMulticastInterface(intf);
        if (!socket->joinMulticastGroup(QHostAddress("224.0.105.201"), intf)) {
            printf("Couldn't join group: %s\n", qPrintable(socket->errorString()));
            delete socket;
            continue;
        }

        printf("ok\n");
        if (!gotone) {
            connect(socket, SIGNAL(readyRead()),
                    this, SLOT(socketReadyRead()));
            gotone = true;
        }
    }

    if (!gotone) {
        QTimer::singleShot(200, this, SLOT(makeSockets()));
    }
}

void SFWReceiver::socketReadyRead()
{
    QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender());
    QString intf = socket->multicastInterface().name();

    while (socket->hasPendingDatagrams()) {
        QByteArray dgram;
        dgram.resize(socket->pendingDatagramSize());
        socket->readDatagram(dgram.data(), dgram.size());

        QBuffer buff(&dgram);
        buff.open(QIODevice::ReadOnly);
        QDataStream ds(&buff);

        qint32 pid;
        ds >> pid;

        char *str;
        uint len;

        ds.readBytes(str, len);
        QByteArray appName(str, len);
        delete[] str;

        QTime t = QTime::currentTime();
        printf("{%4s} %2d:%02d:%02d.%03d ", qPrintable(intf), t.hour(), t.minute(), t.second(), t.msec());
        printf("[%5d/%10s] ", pid, appName.constData());
        while (!ds.atEnd()) {
            ds.readBytes(str, len);
            QByteArray termName(str, len);
            delete[] str;

            printf("{%s, ", termName.constData());

            qint8 type;
            ds >> type;
            DataType dt = static_cast<DataType>(type);
            switch (dt) {
            case Int32Type:
                {
                    qint32 data;
                    ds >> data;
                    printf("%d} ", data);
                }
                break;
            case FloatType:
                {
                    float data;
                    ds >> data;
                    printf("%.4f} ", data);
                }
                break;
            case StringType:
                {
                    ds.readBytes(str, len);
                    QByteArray ba(str, len);
                    ba.truncate(35);
                    printf("'%s'} ", ba.constData());
                }
                break;
            }
        }

        printf("\n");
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    QString filter;
    if (argc > 1)
        filter = QString::fromLatin1(argv[1]);
    SFWReceiver recv(filter);
    return app.exec();
}

#include "main.moc"
<commit_msg>Reconnect if the interface goes away<commit_after>/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtSystems module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QUdpSocket>
#include <QHostAddress>
#include <QCoreApplication>
#include <QBuffer>
#include <QDataStream>
#include <QTime>
#include <QTimer>
#include <QNetworkInterface>
#include <cstdio>

/* keep this in sync with qtsystems/src/serviceframework/qservicedebuglog_p.h */
enum DataType {
    Int32Type = 1,
    FloatType = 2,
    StringType = 3
};

class SFWReceiver : public QObject
{
    Q_OBJECT
public:
    SFWReceiver(const QString &intfFilter);

public slots:
    void socketReadyRead();
    void socketVerify();
    void makeSockets();

private:
    QString intfFilter;
    QTimer socketCheck;
    QUdpSocket *socket;
};

SFWReceiver::SFWReceiver(const QString &intfFilter) : intfFilter(intfFilter)
{
    connect(&socketCheck, SIGNAL(timeout()), this, SLOT(socketVerify()));
    makeSockets();
}

void SFWReceiver::makeSockets()
{
    QList<QNetworkInterface> intfs = QNetworkInterface::allInterfaces();

    bool gotone = false;
    foreach (const QNetworkInterface &intf, intfs) {
        if (!intfFilter.isEmpty() && !intf.name().startsWith(intfFilter))
            continue;

        if (intf.name() == "lo" || intf.name().startsWith("wifi")
            || intf.name().startsWith("wl") || intf.name().startsWith("ppp")
            || intf.name().startsWith("tun") || intf.name().startsWith("tap"))
            continue;

        socket = new QUdpSocket(this);
        printf("Trying interface %s ...", qPrintable(intf.name()));
        if (!socket->bind(QHostAddress::AnyIPv4, 10520, QUdpSocket::ShareAddress)) {
            printf("Couldn't bind: %s\n", qPrintable(socket->errorString()));
            delete socket;
            continue;
        }
        socket->setMulticastInterface(intf);
        if (!socket->joinMulticastGroup(QHostAddress("224.0.105.201"), intf)) {
            printf("Couldn't join group: %s\n", qPrintable(socket->errorString()));
            delete socket;
            continue;
        }

        printf("ok\n");
        if (!gotone) {
            connect(socket, SIGNAL(readyRead()),
                    this, SLOT(socketReadyRead()));
            gotone = true;
        }
    }

    if (!gotone) {
        QTimer::singleShot(200, this, SLOT(makeSockets()));
        socketCheck.stop();
    } else {
        socketCheck.setInterval(1000);
        socketCheck.start();
    }
}

void SFWReceiver::socketVerify()
{
    QList<QNetworkInterface> intfs = QNetworkInterface::allInterfaces();
    bool gotone = false;
    QString name = socket->multicastInterface().name();

    foreach (const QNetworkInterface &intf, intfs) {
        if (intf.name() == name) {
            gotone = true;
            break;
        }
    }

    if (!gotone) {
        printf("Interface down...\n");
        delete socket;
        socket = 0;
        makeSockets();
    }
}

void SFWReceiver::socketReadyRead()
{
    QString intf = socket->multicastInterface().name();

    while (socket->hasPendingDatagrams()) {
        QByteArray dgram;
        dgram.resize(socket->pendingDatagramSize());
        socket->readDatagram(dgram.data(), dgram.size());

        QBuffer buff(&dgram);
        buff.open(QIODevice::ReadOnly);
        QDataStream ds(&buff);

        qint32 pid;
        ds >> pid;

        char *str;
        uint len;

        ds.readBytes(str, len);
        QByteArray appName(str, len);
        delete[] str;

        QTime t = QTime::currentTime();
        printf("{%4s} %2d:%02d:%02d.%03d ", qPrintable(intf), t.hour(), t.minute(), t.second(), t.msec());
        printf("[%5d/%10s] ", pid, appName.constData());
        while (!ds.atEnd()) {
            ds.readBytes(str, len);
            QByteArray termName(str, len);
            delete[] str;

            printf("{%s, ", termName.constData());

            qint8 type;
            ds >> type;
            DataType dt = static_cast<DataType>(type);
            switch (dt) {
            case Int32Type:
                {
                    qint32 data;
                    ds >> data;
                    printf("%d} ", data);
                }
                break;
            case FloatType:
                {
                    float data;
                    ds >> data;
                    printf("%.4f} ", data);
                }
                break;
            case StringType:
                {
                    ds.readBytes(str, len);
                    QByteArray ba(str, len);
                    ba.truncate(35);
                    printf("'%s'} ", ba.constData());
                }
                break;
            }
        }

        printf("\n");
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    QString filter;
    if (argc > 1)
        filter = QString::fromLatin1(argv[1]);
    SFWReceiver recv(filter);
    return app.exec();
}

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


<commit_before>/*
 * The translation response struct.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_PROXY_TRANSLATE_RESPONSE_HXX
#define BENG_PROXY_TRANSLATE_RESPONSE_HXX

#include "Features.hxx"
#include "util/ConstBuffer.hxx"
#include "util/TrivialArray.hxx"
#if TRANSLATION_ENABLE_HTTP
#include "util/kvlist.hxx"
#include "header_forward.hxx"
#endif
#if TRANSLATION_ENABLE_WIDGET
#include "util/StringSet.hxx"
#endif
#if TRANSLATION_ENABLE_RADDRESS
#include "ResourceAddress.hxx"
#include "header_forward.hxx"
#endif
#if TRANSLATION_ENABLE_CHILD_OPTIONS
#include "spawn/ChildOptions.hxx"
#endif

#include <http/status.h>

#include <chrono>

#include <assert.h>
#include <stdint.h>

struct WidgetView;
class AllocatorPtr;
class UniqueRegex;
class MatchInfo;

struct TranslateResponse {
    /**
     * The protocol version from the BEGIN packet.
     */
    unsigned protocol_version;

    std::chrono::seconds max_age;

    /**
     * From #TRANSLATE_EXPIRES_RELATIVE
     */
    std::chrono::seconds expires_relative;

#if TRANSLATION_ENABLE_HTTP
    http_status_t status;
#else
    int status;
#endif

#if TRANSLATION_ENABLE_CHILD_OPTIONS
    ChildOptions child_options;
#endif

#if TRANSLATION_ENABLE_RADDRESS
    ResourceAddress address;
#endif

#if TRANSLATION_ENABLE_HTTP
    /**
     * Which request headers are forwarded?
     */
    struct header_forward_settings request_header_forward;

    /**
     * Which response headers are forwarded?
     */
    struct header_forward_settings response_header_forward;
#endif

    const char *base;

#if TRANSLATION_ENABLE_CACHE
    const char *regex;
    const char *inverse_regex;
#endif

    const char *site;
    const char *expand_site;

#if TRANSLATION_ENABLE_HTTP
    const char *document_root;

    /**
     * The value of #TRANSLATE_EXPAND_DOCUMENT_ROOT.  Only used by the
     * translation cache.
     */
    const char *expand_document_root;

    const char *redirect;
    const char *expand_redirect;
    const char *bounce;

    const char *scheme;
    const char *host;
    const char *uri;
    const char *expand_uri;

    const char *local_uri;

    const char *untrusted;
    const char *untrusted_prefix;
    const char *untrusted_site_suffix;
    const char *untrusted_raw_site_suffix;
#endif

    /**
     * @see #TRANSLATE_TEST_PATH
     */
    const char *test_path;

    /**
     * @see #TRANSLATE_EXPAND_TEST_PATH
     */
    const char *expand_test_path;

#if TRANSLATION_ENABLE_RADDRESS
    bool unsafe_base;

    bool easy_base;
#endif

#if TRANSLATION_ENABLE_CACHE
    bool regex_tail, regex_unescape, inverse_regex_unescape;
#endif

#if TRANSLATION_ENABLE_WIDGET
    bool direct_addressing;
#endif

#if TRANSLATION_ENABLE_SESSION
    bool stateful;

    bool discard_session;

    bool secure_cookie;
#endif

#if TRANSLATION_ENABLE_TRANSFORMATION
    bool filter_4xx;
#endif

    bool previous;

    bool transparent;

#if TRANSLATION_ENABLE_HTTP
    bool redirect_query_string;
#endif

#if TRANSLATION_ENABLE_RADDRESS
    bool auto_base;
#endif

#if TRANSLATION_ENABLE_WIDGET
    bool widget_info;

    bool anchor_absolute;
#endif

#if TRANSLATION_ENABLE_HTTP
    bool dump_headers;
#endif

#if TRANSLATION_ENABLE_CACHE
    /**
     * @see #TRANSLATE_REGEX_ON_HOST_URI
     */
    bool regex_on_host_uri;

    /**
     * @see #TRANSLATE_REGEX_ON_USER_URI
     */
    bool regex_on_user_uri;
#endif

    /**
     * @see #TRANSLATE_AUTO_DEFLATE
     */
    bool auto_deflate;

    /**
     * @see #TRANSLATE_AUTO_GZIP
     */
    bool auto_gzip;

#if TRANSLATION_ENABLE_SESSION
    /**
     * @see #TRANSLATE_REALM_FROM_AUTH_BASE
     */
    bool realm_from_auth_base;

    ConstBuffer<void> session;
#endif

#if TRANSLATION_ENABLE_HTTP
    /**
     * The payload of the #TRANSLATE_INTERNAL_REDIRECT packet.  If
     * ConstBuffer::IsNull(), then no #TRANSLATE_INTERNAL_REDIRECT
     * packet was received.
     */
    ConstBuffer<void> internal_redirect;
#endif

#if TRANSLATION_ENABLE_SESSION
    /**
     * The payload of the CHECK packet.  If ConstBuffer::IsNull(),
     * then no CHECK packet was received.
     */
    ConstBuffer<void> check;

    /**
     * The payload of the AUTH packet.  If ConstBuffer::IsNull(), then
     * no AUTH packet was received.
     */
    ConstBuffer<void> auth;

    /**
     * @see #TRANSLATE_AUTH_FILE, #TRANSLATE_EXPAND_AUTH_FILE
     */
    const char *auth_file, *expand_auth_file;

    /**
     * @see #TRANSLATE_APPEND_AUTH
     */
    ConstBuffer<void> append_auth;

    /**
     * @see #TRANSLATE_EXPAND_APPEND_AUTH
     */
    const char *expand_append_auth;
#endif

#if TRANSLATION_ENABLE_HTTP
    /**
     * The payload of the #TRANSLATE_WANT_FULL_URI packet.  If
     * ConstBuffer::IsNull(), then no #TRANSLATE_WANT_FULL_URI packet
     * was received.
     */
    ConstBuffer<void> want_full_uri;
#endif

#if TRANSLATION_ENABLE_SESSION
    const char *user;
    std::chrono::seconds user_max_age;

    const char *session_site;

    const char *language;

    const char *realm;

    HttpAddress *external_session_manager;
    std::chrono::duration<uint16_t> external_session_keepalive;

    /**
     * The value of the "WWW-Authenticate" HTTP response header.
     */
    const char *www_authenticate;

    /**
     * The value of the "Authentication-Info" HTTP response header.
     */
    const char *authentication_info;

    const char *cookie_domain;
    const char *cookie_host, *expand_cookie_host;
    const char *cookie_path;
#endif

#if TRANSLATION_ENABLE_HTTP
    KeyValueList request_headers;
    KeyValueList expand_request_headers;

    KeyValueList response_headers;
    KeyValueList expand_response_headers;
#endif

#if TRANSLATION_ENABLE_WIDGET
    WidgetView *views;

    /**
     * From #TRANSLATE_WIDGET_GROUP.
     */
    const char *widget_group;

    /**
     * From #TRANSLATE_GROUP_CONTAINER.
     */
    StringSet container_groups;
#endif

#if TRANSLATION_ENABLE_CACHE
    ConstBuffer<uint16_t> vary;
    ConstBuffer<uint16_t> invalidate;
#endif
    ConstBuffer<uint16_t> want;

#if TRANSLATION_ENABLE_RADDRESS
    ConstBuffer<void> file_not_found;

    /**
     * From #TRANSLATE_CONTENT_TYPE, but only in reply to
     * #TRANSLATE_CONTENT_TYPE_LOOKUP / #TRANSLATE_SUFFIX.
     */
    const char *content_type;

    ConstBuffer<void> enotdir;

    ConstBuffer<void> directory_index;
#endif

    ConstBuffer<void> error_document;

    /**
     * From #TRANSLATE_PROBE_PATH_SUFFIXES.
     */
    ConstBuffer<void> probe_path_suffixes;

    TrivialArray<const char *, 16> probe_suffixes;

    const char *read_file, *expand_read_file;

    struct {
        uint64_t mtime;
        const char *path;
    } validate_mtime;

    void Clear();

    bool Wants(uint16_t cmd) const {
        assert(protocol_version >= 1);

        return want.Contains(cmd);
    }

#if TRANSLATION_ENABLE_CACHE
    gcc_pure
    bool VaryContains(uint16_t cmd) const {
        return vary.Contains(cmd);
    }
#endif

#if TRANSLATION_ENABLE_SESSION
    gcc_pure
    bool HasAuth() const {
        return !auth.IsNull() ||
            auth_file != nullptr || expand_auth_file != nullptr;
    }

    bool HasUntrusted() const {
        return untrusted != nullptr || untrusted_prefix != nullptr ||
            untrusted_site_suffix != nullptr ||
            untrusted_raw_site_suffix != nullptr;
    }
#endif

    void CopyFrom(AllocatorPtr alloc, const TranslateResponse &src);

#if TRANSLATION_ENABLE_CACHE
    /**
     * Copy data from #src for storing in the translation cache.
     *
     * @return true if a #base was given and it was applied
     * successfully
     */
    bool CacheStore(AllocatorPtr alloc, const TranslateResponse &src,
                    const char *uri);

    /**
     * Throws std::runtime_error on error.
     */
    void CacheLoad(AllocatorPtr alloc, const TranslateResponse &src,
                   const char *uri);
#endif

    /**
     * Throws std::runtime_error on error.
     */
    UniqueRegex CompileRegex() const;
    UniqueRegex CompileInverseRegex() const;

#if TRANSLATION_ENABLE_EXPAND
    /**
     * Does any response need to be expanded with
     * translate_response_expand()?
     */
    gcc_pure
    bool IsExpandable() const;

    /**
     * Expand the strings in this response with the specified regex
     * result.
     *
     * Throws std::runtime_error on error.
     */
    void Expand(struct pool *pool, const MatchInfo &match_info);
#endif
};

#endif
<commit_msg>translation/Response: include http/status.h only if TRANSLATION_ENABLE_HTTP<commit_after>/*
 * The translation response struct.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_PROXY_TRANSLATE_RESPONSE_HXX
#define BENG_PROXY_TRANSLATE_RESPONSE_HXX

#include "Features.hxx"
#include "util/ConstBuffer.hxx"
#include "util/TrivialArray.hxx"
#if TRANSLATION_ENABLE_HTTP
#include "util/kvlist.hxx"
#include "header_forward.hxx"
#endif
#if TRANSLATION_ENABLE_WIDGET
#include "util/StringSet.hxx"
#endif
#if TRANSLATION_ENABLE_RADDRESS
#include "ResourceAddress.hxx"
#include "header_forward.hxx"
#endif
#if TRANSLATION_ENABLE_CHILD_OPTIONS
#include "spawn/ChildOptions.hxx"
#endif

#if TRANSLATION_ENABLE_HTTP
#include <http/status.h>
#endif

#include <chrono>

#include <assert.h>
#include <stdint.h>

struct WidgetView;
class AllocatorPtr;
class UniqueRegex;
class MatchInfo;

struct TranslateResponse {
    /**
     * The protocol version from the BEGIN packet.
     */
    unsigned protocol_version;

    std::chrono::seconds max_age;

    /**
     * From #TRANSLATE_EXPIRES_RELATIVE
     */
    std::chrono::seconds expires_relative;

#if TRANSLATION_ENABLE_HTTP
    http_status_t status;
#else
    int status;
#endif

#if TRANSLATION_ENABLE_CHILD_OPTIONS
    ChildOptions child_options;
#endif

#if TRANSLATION_ENABLE_RADDRESS
    ResourceAddress address;
#endif

#if TRANSLATION_ENABLE_HTTP
    /**
     * Which request headers are forwarded?
     */
    struct header_forward_settings request_header_forward;

    /**
     * Which response headers are forwarded?
     */
    struct header_forward_settings response_header_forward;
#endif

    const char *base;

#if TRANSLATION_ENABLE_CACHE
    const char *regex;
    const char *inverse_regex;
#endif

    const char *site;
    const char *expand_site;

#if TRANSLATION_ENABLE_HTTP
    const char *document_root;

    /**
     * The value of #TRANSLATE_EXPAND_DOCUMENT_ROOT.  Only used by the
     * translation cache.
     */
    const char *expand_document_root;

    const char *redirect;
    const char *expand_redirect;
    const char *bounce;

    const char *scheme;
    const char *host;
    const char *uri;
    const char *expand_uri;

    const char *local_uri;

    const char *untrusted;
    const char *untrusted_prefix;
    const char *untrusted_site_suffix;
    const char *untrusted_raw_site_suffix;
#endif

    /**
     * @see #TRANSLATE_TEST_PATH
     */
    const char *test_path;

    /**
     * @see #TRANSLATE_EXPAND_TEST_PATH
     */
    const char *expand_test_path;

#if TRANSLATION_ENABLE_RADDRESS
    bool unsafe_base;

    bool easy_base;
#endif

#if TRANSLATION_ENABLE_CACHE
    bool regex_tail, regex_unescape, inverse_regex_unescape;
#endif

#if TRANSLATION_ENABLE_WIDGET
    bool direct_addressing;
#endif

#if TRANSLATION_ENABLE_SESSION
    bool stateful;

    bool discard_session;

    bool secure_cookie;
#endif

#if TRANSLATION_ENABLE_TRANSFORMATION
    bool filter_4xx;
#endif

    bool previous;

    bool transparent;

#if TRANSLATION_ENABLE_HTTP
    bool redirect_query_string;
#endif

#if TRANSLATION_ENABLE_RADDRESS
    bool auto_base;
#endif

#if TRANSLATION_ENABLE_WIDGET
    bool widget_info;

    bool anchor_absolute;
#endif

#if TRANSLATION_ENABLE_HTTP
    bool dump_headers;
#endif

#if TRANSLATION_ENABLE_CACHE
    /**
     * @see #TRANSLATE_REGEX_ON_HOST_URI
     */
    bool regex_on_host_uri;

    /**
     * @see #TRANSLATE_REGEX_ON_USER_URI
     */
    bool regex_on_user_uri;
#endif

    /**
     * @see #TRANSLATE_AUTO_DEFLATE
     */
    bool auto_deflate;

    /**
     * @see #TRANSLATE_AUTO_GZIP
     */
    bool auto_gzip;

#if TRANSLATION_ENABLE_SESSION
    /**
     * @see #TRANSLATE_REALM_FROM_AUTH_BASE
     */
    bool realm_from_auth_base;

    ConstBuffer<void> session;
#endif

#if TRANSLATION_ENABLE_HTTP
    /**
     * The payload of the #TRANSLATE_INTERNAL_REDIRECT packet.  If
     * ConstBuffer::IsNull(), then no #TRANSLATE_INTERNAL_REDIRECT
     * packet was received.
     */
    ConstBuffer<void> internal_redirect;
#endif

#if TRANSLATION_ENABLE_SESSION
    /**
     * The payload of the CHECK packet.  If ConstBuffer::IsNull(),
     * then no CHECK packet was received.
     */
    ConstBuffer<void> check;

    /**
     * The payload of the AUTH packet.  If ConstBuffer::IsNull(), then
     * no AUTH packet was received.
     */
    ConstBuffer<void> auth;

    /**
     * @see #TRANSLATE_AUTH_FILE, #TRANSLATE_EXPAND_AUTH_FILE
     */
    const char *auth_file, *expand_auth_file;

    /**
     * @see #TRANSLATE_APPEND_AUTH
     */
    ConstBuffer<void> append_auth;

    /**
     * @see #TRANSLATE_EXPAND_APPEND_AUTH
     */
    const char *expand_append_auth;
#endif

#if TRANSLATION_ENABLE_HTTP
    /**
     * The payload of the #TRANSLATE_WANT_FULL_URI packet.  If
     * ConstBuffer::IsNull(), then no #TRANSLATE_WANT_FULL_URI packet
     * was received.
     */
    ConstBuffer<void> want_full_uri;
#endif

#if TRANSLATION_ENABLE_SESSION
    const char *user;
    std::chrono::seconds user_max_age;

    const char *session_site;

    const char *language;

    const char *realm;

    HttpAddress *external_session_manager;
    std::chrono::duration<uint16_t> external_session_keepalive;

    /**
     * The value of the "WWW-Authenticate" HTTP response header.
     */
    const char *www_authenticate;

    /**
     * The value of the "Authentication-Info" HTTP response header.
     */
    const char *authentication_info;

    const char *cookie_domain;
    const char *cookie_host, *expand_cookie_host;
    const char *cookie_path;
#endif

#if TRANSLATION_ENABLE_HTTP
    KeyValueList request_headers;
    KeyValueList expand_request_headers;

    KeyValueList response_headers;
    KeyValueList expand_response_headers;
#endif

#if TRANSLATION_ENABLE_WIDGET
    WidgetView *views;

    /**
     * From #TRANSLATE_WIDGET_GROUP.
     */
    const char *widget_group;

    /**
     * From #TRANSLATE_GROUP_CONTAINER.
     */
    StringSet container_groups;
#endif

#if TRANSLATION_ENABLE_CACHE
    ConstBuffer<uint16_t> vary;
    ConstBuffer<uint16_t> invalidate;
#endif
    ConstBuffer<uint16_t> want;

#if TRANSLATION_ENABLE_RADDRESS
    ConstBuffer<void> file_not_found;

    /**
     * From #TRANSLATE_CONTENT_TYPE, but only in reply to
     * #TRANSLATE_CONTENT_TYPE_LOOKUP / #TRANSLATE_SUFFIX.
     */
    const char *content_type;

    ConstBuffer<void> enotdir;

    ConstBuffer<void> directory_index;
#endif

    ConstBuffer<void> error_document;

    /**
     * From #TRANSLATE_PROBE_PATH_SUFFIXES.
     */
    ConstBuffer<void> probe_path_suffixes;

    TrivialArray<const char *, 16> probe_suffixes;

    const char *read_file, *expand_read_file;

    struct {
        uint64_t mtime;
        const char *path;
    } validate_mtime;

    void Clear();

    bool Wants(uint16_t cmd) const {
        assert(protocol_version >= 1);

        return want.Contains(cmd);
    }

#if TRANSLATION_ENABLE_CACHE
    gcc_pure
    bool VaryContains(uint16_t cmd) const {
        return vary.Contains(cmd);
    }
#endif

#if TRANSLATION_ENABLE_SESSION
    gcc_pure
    bool HasAuth() const {
        return !auth.IsNull() ||
            auth_file != nullptr || expand_auth_file != nullptr;
    }

    bool HasUntrusted() const {
        return untrusted != nullptr || untrusted_prefix != nullptr ||
            untrusted_site_suffix != nullptr ||
            untrusted_raw_site_suffix != nullptr;
    }
#endif

    void CopyFrom(AllocatorPtr alloc, const TranslateResponse &src);

#if TRANSLATION_ENABLE_CACHE
    /**
     * Copy data from #src for storing in the translation cache.
     *
     * @return true if a #base was given and it was applied
     * successfully
     */
    bool CacheStore(AllocatorPtr alloc, const TranslateResponse &src,
                    const char *uri);

    /**
     * Throws std::runtime_error on error.
     */
    void CacheLoad(AllocatorPtr alloc, const TranslateResponse &src,
                   const char *uri);
#endif

    /**
     * Throws std::runtime_error on error.
     */
    UniqueRegex CompileRegex() const;
    UniqueRegex CompileInverseRegex() const;

#if TRANSLATION_ENABLE_EXPAND
    /**
     * Does any response need to be expanded with
     * translate_response_expand()?
     */
    gcc_pure
    bool IsExpandable() const;

    /**
     * Expand the strings in this response with the specified regex
     * result.
     *
     * Throws std::runtime_error on error.
     */
    void Expand(struct pool *pool, const MatchInfo &match_info);
#endif
};

#endif
<|endoftext|>