<commit_before>/*
 * PlayerInputSystem.cpp
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Ashley Davis
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <cstdio>

#include <Ashley/core/ComponentMapper.hpp>

#include <glm/vec2.hpp>
#include <libtcod.hpp>

#include "components/Position.hpp"
#include "components/PlayerInputListener.hpp"
#include "components/Inventory.hpp"
#include "components/Interactible.hpp"
#include "components/Battling.hpp"

#include "systems/PlayerInputSystem.hpp"

#include "RGL.hpp"
#include "Map.hpp"
#include "Message.hpp"

#include "easylogging++.h"

void rgl::PlayerInputSystem::processEntity(ashley::Entity * const &entity, float deltaTime) {
	const auto inputComponent = ashley::ComponentMapper<PlayerInputListener>::getMapper().get(entity);

	if (f5Pressed) {
		const auto inventory = ashley::ComponentMapper<Inventory>::getMapper().get(entity);

		if (inventory != nullptr) {
			for (const auto &item : inventory->contents) {
				RGLL->debug("Player has: %v", item.name);
			}
		}
	}

	switch (inputComponent->state) {
	case PlayerInputState::NORMAL: {
		processNormalState(entity, deltaTime, inputComponent);
		break;
	}

	case PlayerInputState::TARGETTING: {
		processTargettingState(entity, deltaTime, inputComponent);
		break;
	}

	case PlayerInputState::RESPONDING: {
		processRespondingState(entity, deltaTime, inputComponent);
		break;
	}

	default: {
		RGLL->debug("Unhandled case in PlayerInputSystem switch.");
		break;
	}
}
}

void rgl::PlayerInputSystem::processNormalState(ashley::Entity * const &entity, float deltaTime,
        PlayerInputListener * const listener) {
	const auto pos = ashley::ComponentMapper<Position>::getMapper().get(entity);
	glm::ivec2 target(pos->position);

	if (spacePressed) {
		listener->state = PlayerInputState::TARGETTING;
		return;
	} else if (upPressed) {
		--target.y;
	} else if (downPressed) {
		++target.y;
	} else if (leftPressed) {
		--target.x;
	} else if (rightPressed) {
		++target.x;
	}

	// check if target is clear
	if (target.x >= 0 && target.x < map->getWidth() && target.y >= 0 && target.y < map->getHeight()) {
		const auto targetTile = map->getTileAt(target.x, target.y);

		if (targetTile != nullptr && !targetTile->solid) {
			pos->position = target;
		}
	}
}

void rgl::PlayerInputSystem::processTargettingState(ashley::Entity * const &entity, float deltaTime,
        PlayerInputListener * const listener) {
	const auto pos = ashley::ComponentMapper<Position>::getMapper().get(entity);
	glm::ivec2 target(pos->position);
	bool interactionDone = false;

	if (spacePressed) {
		interactionDone = true;
	} else if (upPressed) {
		interactionDone = true;
		--target.y;
	} else if (downPressed) {
		interactionDone = true;
		++target.y;
	} else if (leftPressed) {
		interactionDone = true;
		--target.x;
	} else if (rightPressed) {
		interactionDone = true;
		++target.x;
	}

	const auto targetTile = map->getTileAt(target.x, target.y);

	if (targetTile != nullptr) {
		listener->target = target;

		if (!targetTile->contains.empty()) {
			for (const auto &e : targetTile->contains) {
				const auto interactible = ashley::ComponentMapper<Interactible>::getMapper().get(e);

				if (interactible == nullptr) {
					continue;
				} else {
					listener->choice = e;

					switch (interactible->type) {
					case InteractionType::LOOT: {
						MessageHandler::globalHandler->addMessage("There's a chest here; open it? (Y/N)");
						listener->state = PlayerInputState::RESPONDING;
						return;
					}

					case InteractionType::FIGHT: {
						battleSystem->addAttack(Attack(entity, e));
						break;
					}

					default: {
						break;
					}
					}
				}
			}
		} else {
			MessageHandler::globalHandler->addMessage("There's nothing there.");
		}
	}

	if (interactionDone) {
		listener->choice = nullptr;
		listener->target.x = -1;
		listener->target.y = -1;
		listener->state = PlayerInputState::NORMAL;
	}
}

void rgl::PlayerInputSystem::processRespondingState(ashley::Entity * const &entity, float deltaTime,
        PlayerInputListener * const listener) {
	if (yPressed || nPressed) {
		if (yPressed) {
			const auto targetTile = map->getTileAt(listener->target.x, listener->target.y);
			bool targetFound = false;

			// make sure the tile still contains the interaction choice.
			if (targetTile != nullptr && listener->choice != nullptr) {
				for (const auto &content : targetTile->contains) {
					if (content == listener->choice) {
						targetFound = true;
						break;
					}
				}
			}

			if (targetTile == nullptr || listener->choice == nullptr || !targetFound) {
				MessageHandler::globalHandler->addMessage("Huh? You could've sworn there was something here!");
			} else {
				// interactible is still there so proceed with interaction.
				const auto interactible = ashley::ComponentMapper<Interactible>::getMapper().get(listener->choice);

				switch (interactible->type) {
				case InteractionType::LOOT: {
					const auto inventory = ashley::ComponentMapper<Inventory>::getMapper().get(listener->choice);

					if (inventory == nullptr || inventory->contents.empty()) {
						MessageHandler::globalHandler->addMessage("Seems to be empty...");
					} else {
						MessageHandler::globalHandler->addMessage("The chest creaks open.");
						const auto playerInventory = ashley::ComponentMapper<Inventory>::getMapper().get(entity);

						if (playerInventory != nullptr) {
							for (const auto &item : inventory->contents) {
								MessageHandler::globalHandler->addObtainedMessage(item.name);
								playerInventory->contents.push_back(item);
							}

							inventory->contents.clear();
						}
					}

					break;
				}

//				case InteractionType::FIGHT: {
//					const auto battling = ashley::ComponentMapper<Battling>::getMapper().get(entity);
//
//					if (battling == nullptr) {
//						MessageHandler::globalHandler->addMessage(
//						        "It looks defenceless... you can't bring yourself to attack it.");
//					} else {
//						MessageHandler::globalHandler->addMessage("You bash it with your stick.");
//					}
//
//					break;
//				}

				default: {
					break;
				}
				}
			}
		} else if (nPressed) {
			MessageHandler::globalHandler->addMessage("Never mind.");
		}

		listener->state = PlayerInputState::NORMAL;
		listener->target.x = -1;
		listener->target.y = -1;
	}
}

void rgl::PlayerInputSystem::update(float deltaTime) {
	TCOD_key_t key;
	TCODSystem::checkForEvent(TCOD_EVENT_KEY_PRESS, &key, nullptr);

	if (key.vk != TCODK_NONE) {
		if (key.vk == TCODK_CHAR) {
			switch (key.c) {
			case 'w':
			case 'W': {
				key.vk = TCODK_UP;
				break;
			}

			case 's':
			case 'S': {
				key.vk = TCODK_DOWN;
				break;
			}

			case 'a':
			case 'A': {
				key.vk = TCODK_LEFT;
				break;
			}

			case 'd':
			case 'D': {
				key.vk = TCODK_RIGHT;
				break;
			}

			case 'y':
			case 'Y': {
				yPressed = true;
				break;
			}

			case 'n':
			case 'N': {
				nPressed = true;
				break;
			}

			default: {
				break;
			}
			}
		}

		switch (key.vk) {
		case TCODK_UP: {
			upPressed = true;
			break;
		}

		case TCODK_DOWN: {
			downPressed = true;
			break;
		}

		case TCODK_LEFT: {
			leftPressed = true;
			break;
		}

		case TCODK_RIGHT: {
			rightPressed = true;
			break;
		}

		case TCODK_SPACE: {
			spacePressed = true;
			break;
		}

		case TCODK_F5: {
			f5Pressed = true;
			break;
		}

		default:
			break;
		}

		ashley::IteratingSystem::update(deltaTime);

		resetPressedKeys();
	}
}
<commit_msg>Remove extraneous include<commit_after>/*
 * PlayerInputSystem.cpp
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Ashley Davis
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <cstdio>

#include <Ashley/core/ComponentMapper.hpp>

#include <glm/vec2.hpp>
#include <libtcod.hpp>

#include "components/Position.hpp"
#include "components/PlayerInputListener.hpp"
#include "components/Inventory.hpp"
#include "components/Interactible.hpp"
#include "components/Battling.hpp"

#include "systems/PlayerInputSystem.hpp"

#include "RGL.hpp"
#include "Map.hpp"
#include "Message.hpp"

void rgl::PlayerInputSystem::processEntity(ashley::Entity * const &entity, float deltaTime) {
	const auto inputComponent = ashley::ComponentMapper<PlayerInputListener>::getMapper().get(entity);

	if (f5Pressed) {
		const auto inventory = ashley::ComponentMapper<Inventory>::getMapper().get(entity);

		if (inventory != nullptr) {
			for (const auto &item : inventory->contents) {
				RGLL->debug("Player has: %v", item.name);
			}
		}
	}

	switch (inputComponent->state) {
	case PlayerInputState::NORMAL: {
		processNormalState(entity, deltaTime, inputComponent);
		break;
	}

	case PlayerInputState::TARGETTING: {
		processTargettingState(entity, deltaTime, inputComponent);
		break;
	}

	case PlayerInputState::RESPONDING: {
		processRespondingState(entity, deltaTime, inputComponent);
		break;
	}

	default: {
		RGLL->debug("Unhandled case in PlayerInputSystem switch.");
		break;
	}
}
}

void rgl::PlayerInputSystem::processNormalState(ashley::Entity * const &entity, float deltaTime,
        PlayerInputListener * const listener) {
	const auto pos = ashley::ComponentMapper<Position>::getMapper().get(entity);
	glm::ivec2 target(pos->position);

	if (spacePressed) {
		listener->state = PlayerInputState::TARGETTING;
		return;
	} else if (upPressed) {
		--target.y;
	} else if (downPressed) {
		++target.y;
	} else if (leftPressed) {
		--target.x;
	} else if (rightPressed) {
		++target.x;
	}

	// check if target is clear
	if (target.x >= 0 && target.x < map->getWidth() && target.y >= 0 && target.y < map->getHeight()) {
		const auto targetTile = map->getTileAt(target.x, target.y);

		if (targetTile != nullptr && !targetTile->solid) {
			pos->position = target;
		}
	}
}

void rgl::PlayerInputSystem::processTargettingState(ashley::Entity * const &entity, float deltaTime,
        PlayerInputListener * const listener) {
	const auto pos = ashley::ComponentMapper<Position>::getMapper().get(entity);
	glm::ivec2 target(pos->position);
	bool interactionDone = false;

	if (spacePressed) {
		interactionDone = true;
	} else if (upPressed) {
		interactionDone = true;
		--target.y;
	} else if (downPressed) {
		interactionDone = true;
		++target.y;
	} else if (leftPressed) {
		interactionDone = true;
		--target.x;
	} else if (rightPressed) {
		interactionDone = true;
		++target.x;
	}

	const auto targetTile = map->getTileAt(target.x, target.y);

	if (targetTile != nullptr) {
		listener->target = target;

		if (!targetTile->contains.empty()) {
			for (const auto &e : targetTile->contains) {
				const auto interactible = ashley::ComponentMapper<Interactible>::getMapper().get(e);

				if (interactible == nullptr) {
					continue;
				} else {
					listener->choice = e;

					switch (interactible->type) {
					case InteractionType::LOOT: {
						MessageHandler::globalHandler->addMessage("There's a chest here; open it? (Y/N)");
						listener->state = PlayerInputState::RESPONDING;
						return;
					}

					case InteractionType::FIGHT: {
						battleSystem->addAttack(Attack(entity, e));
						break;
					}

					default: {
						break;
					}
					}
				}
			}
		} else {
			MessageHandler::globalHandler->addMessage("There's nothing there.");
		}
	}

	if (interactionDone) {
		listener->choice = nullptr;
		listener->target.x = -1;
		listener->target.y = -1;
		listener->state = PlayerInputState::NORMAL;
	}
}

void rgl::PlayerInputSystem::processRespondingState(ashley::Entity * const &entity, float deltaTime,
        PlayerInputListener * const listener) {
	if (yPressed || nPressed) {
		if (yPressed) {
			const auto targetTile = map->getTileAt(listener->target.x, listener->target.y);
			bool targetFound = false;

			// make sure the tile still contains the interaction choice.
			if (targetTile != nullptr && listener->choice != nullptr) {
				for (const auto &content : targetTile->contains) {
					if (content == listener->choice) {
						targetFound = true;
						break;
					}
				}
			}

			if (targetTile == nullptr || listener->choice == nullptr || !targetFound) {
				MessageHandler::globalHandler->addMessage("Huh? You could've sworn there was something here!");
			} else {
				// interactible is still there so proceed with interaction.
				const auto interactible = ashley::ComponentMapper<Interactible>::getMapper().get(listener->choice);

				switch (interactible->type) {
				case InteractionType::LOOT: {
					const auto inventory = ashley::ComponentMapper<Inventory>::getMapper().get(listener->choice);

					if (inventory == nullptr || inventory->contents.empty()) {
						MessageHandler::globalHandler->addMessage("Seems to be empty...");
					} else {
						MessageHandler::globalHandler->addMessage("The chest creaks open.");
						const auto playerInventory = ashley::ComponentMapper<Inventory>::getMapper().get(entity);

						if (playerInventory != nullptr) {
							for (const auto &item : inventory->contents) {
								MessageHandler::globalHandler->addObtainedMessage(item.name);
								playerInventory->contents.push_back(item);
							}

							inventory->contents.clear();
						}
					}

					break;
				}

//				case InteractionType::FIGHT: {
//					const auto battling = ashley::ComponentMapper<Battling>::getMapper().get(entity);
//
//					if (battling == nullptr) {
//						MessageHandler::globalHandler->addMessage(
//						        "It looks defenceless... you can't bring yourself to attack it.");
//					} else {
//						MessageHandler::globalHandler->addMessage("You bash it with your stick.");
//					}
//
//					break;
//				}

				default: {
					break;
				}
				}
			}
		} else if (nPressed) {
			MessageHandler::globalHandler->addMessage("Never mind.");
		}

		listener->state = PlayerInputState::NORMAL;
		listener->target.x = -1;
		listener->target.y = -1;
	}
}

void rgl::PlayerInputSystem::update(float deltaTime) {
	TCOD_key_t key;
	TCODSystem::checkForEvent(TCOD_EVENT_KEY_PRESS, &key, nullptr);

	if (key.vk != TCODK_NONE) {
		if (key.vk == TCODK_CHAR) {
			switch (key.c) {
			case 'w':
			case 'W': {
				key.vk = TCODK_UP;
				break;
			}

			case 's':
			case 'S': {
				key.vk = TCODK_DOWN;
				break;
			}

			case 'a':
			case 'A': {
				key.vk = TCODK_LEFT;
				break;
			}

			case 'd':
			case 'D': {
				key.vk = TCODK_RIGHT;
				break;
			}

			case 'y':
			case 'Y': {
				yPressed = true;
				break;
			}

			case 'n':
			case 'N': {
				nPressed = true;
				break;
			}

			default: {
				break;
			}
			}
		}

		switch (key.vk) {
		case TCODK_UP: {
			upPressed = true;
			break;
		}

		case TCODK_DOWN: {
			downPressed = true;
			break;
		}

		case TCODK_LEFT: {
			leftPressed = true;
			break;
		}

		case TCODK_RIGHT: {
			rightPressed = true;
			break;
		}

		case TCODK_SPACE: {
			spacePressed = true;
			break;
		}

		case TCODK_F5: {
			f5Pressed = true;
			break;
		}

		default:
			break;
		}

		ashley::IteratingSystem::update(deltaTime);

		resetPressedKeys();
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2020 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 <banman.h>
#include <chainparams.h>
#include <config.h>
#include <consensus/consensus.h>
#include <net.h>
#include <net_processing.h>
#include <protocol.h>
#include <scheduler.h>
#include <script/script.h>
#include <streams.h>
#include <validationinterface.h>
#include <version.h>

#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/util/mining.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>

#include <algorithm>
#include <atomic>
#include <cassert>
#include <chrono>
#include <cstdint>
#include <iosfwd>
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>

namespace {

#ifdef MESSAGE_TYPE
#define TO_STRING_(s) #s
#define TO_STRING(s) TO_STRING_(s)
const std::string LIMIT_TO_MESSAGE_TYPE{TO_STRING(MESSAGE_TYPE)};
#else
const std::string LIMIT_TO_MESSAGE_TYPE;
#endif

const std::map<std::string, std::set<std::string>>
    EXPECTED_DESERIALIZATION_EXCEPTIONS = {
        {"CDataStream::read(): end of data: iostream error",
         {"addr", "block", "blocktxn", "cmpctblock", "feefilter", "filteradd",
          "filterload", "getblocks", "getblocktxn", "getdata", "getheaders",
          "headers", "inv", "notfound", "ping", "sendcmpct", "tx"}},
        {"CompactSize exceeds limit of type: iostream error", {"cmpctblock"}},
        {"differential value overflow: iostream error", {"getblocktxn"}},
        {"index overflowed 16 bits: iostream error", {"getblocktxn"}},
        {"index overflowed 16-bits: iostream error", {"cmpctblock"}},
        {"indexes overflowed 16 bits: iostream error", {"getblocktxn"}},
        {"non-canonical ReadCompactSize(): iostream error",
         {"addr", "block", "blocktxn", "cmpctblock", "filteradd", "filterload",
          "getblocks", "getblocktxn", "getdata", "getheaders", "headers", "inv",
          "notfound", "tx"}},
        {"ReadCompactSize(): size too large: iostream error",
         {"addr", "block", "blocktxn", "cmpctblock", "filteradd", "filterload",
          "getblocks", "getblocktxn", "getdata", "getheaders", "headers", "inv",
          "notfound", "tx"}},
        {"Superfluous witness record: iostream error",
         {"block", "blocktxn", "cmpctblock", "tx"}},
        {"Unknown transaction optional data: iostream error",
         {"block", "blocktxn", "cmpctblock", "tx"}},
};

const TestingSetup *g_setup;
} // namespace

void initialize() {
    static TestingSetup setup{
        CBaseChainParams::REGTEST,
        {
            "-nodebuglogfile",
        },
    };
    g_setup = &setup;

    for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
        MineBlock(GetConfig(), g_setup->m_node, CScript() << OP_TRUE);
    }
    SyncWithValidationInterfaceQueue();
}

void test_one_input(const std::vector<uint8_t> &buffer) {
    const Config &config = GetConfig();
    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
    ConnmanTestMsg &connman = *(ConnmanTestMsg *)g_setup->m_node.connman.get();
    const std::string random_message_type{
        fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE)
            .c_str()};
    if (!LIMIT_TO_MESSAGE_TYPE.empty() &&
        random_message_type != LIMIT_TO_MESSAGE_TYPE) {
        return;
    }
    CDataStream random_bytes_data_stream{
        fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>(), SER_NETWORK,
        PROTOCOL_VERSION};
    CNode &p2p_node =
        *std::make_unique<CNode>(
             0, ServiceFlags(NODE_NETWORK | NODE_BLOOM), 0, INVALID_SOCKET,
             CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0,
             0, CAddress{}, std::string{}, ConnectionType::OUTBOUND)
             .release();
    p2p_node.fSuccessfullyConnected = true;
    p2p_node.nVersion = PROTOCOL_VERSION;
    p2p_node.SetSendVersion(PROTOCOL_VERSION);
    connman.AddTestNode(p2p_node);
    g_setup->m_node.peer_logic->InitializeNode(config, &p2p_node);
    try {
        g_setup->m_node.peer_logic->ProcessMessage(
            config, p2p_node, random_message_type, random_bytes_data_stream,
            GetTimeMillis(), std::atomic<bool>{false});
    } catch (const std::ios_base::failure &e) {
        const std::string exception_message{e.what()};
        const auto p =
            EXPECTED_DESERIALIZATION_EXCEPTIONS.find(exception_message);
        if (p == EXPECTED_DESERIALIZATION_EXCEPTIONS.cend() ||
            p->second.count(random_message_type) == 0) {
            std::cout << "Unexpected exception when processing message type \""
                      << random_message_type << "\": " << exception_message
                      << std::endl;
            assert(false);
        }
    }
    SyncWithValidationInterfaceQueue();
    // See init.cpp for rationale for implicit locking order requirement
    LOCK2(::cs_main, g_cs_orphans);
    g_setup->m_node.connman->StopNodes();
}
<commit_msg>fuzz: Remove enumeration of expected deserialization exceptions in ProcessMessage(...) fuzzer<commit_after>// Copyright (c) 2020 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 <banman.h>
#include <chainparams.h>
#include <config.h>
#include <consensus/consensus.h>
#include <net.h>
#include <net_processing.h>
#include <protocol.h>
#include <scheduler.h>
#include <script/script.h>
#include <streams.h>
#include <validationinterface.h>
#include <version.h>

#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/util/mining.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>

#include <atomic>
#include <cassert>
#include <chrono>
#include <cstdint>
#include <iosfwd>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

namespace {

#ifdef MESSAGE_TYPE
#define TO_STRING_(s) #s
#define TO_STRING(s) TO_STRING_(s)
const std::string LIMIT_TO_MESSAGE_TYPE{TO_STRING(MESSAGE_TYPE)};
#else
const std::string LIMIT_TO_MESSAGE_TYPE;
#endif

const TestingSetup *g_setup;
} // namespace

void initialize() {
    static TestingSetup setup{
        CBaseChainParams::REGTEST,
        {
            "-nodebuglogfile",
        },
    };
    g_setup = &setup;

    for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
        MineBlock(GetConfig(), g_setup->m_node, CScript() << OP_TRUE);
    }
    SyncWithValidationInterfaceQueue();
}

void test_one_input(const std::vector<uint8_t> &buffer) {
    const Config &config = GetConfig();
    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
    ConnmanTestMsg &connman = *(ConnmanTestMsg *)g_setup->m_node.connman.get();
    const std::string random_message_type{
        fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE)
            .c_str()};
    if (!LIMIT_TO_MESSAGE_TYPE.empty() &&
        random_message_type != LIMIT_TO_MESSAGE_TYPE) {
        return;
    }
    CDataStream random_bytes_data_stream{
        fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>(), SER_NETWORK,
        PROTOCOL_VERSION};
    CNode &p2p_node =
        *std::make_unique<CNode>(
             0, ServiceFlags(NODE_NETWORK | NODE_BLOOM), 0, INVALID_SOCKET,
             CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0,
             0, CAddress{}, std::string{}, ConnectionType::OUTBOUND)
             .release();
    p2p_node.fSuccessfullyConnected = true;
    p2p_node.nVersion = PROTOCOL_VERSION;
    p2p_node.SetSendVersion(PROTOCOL_VERSION);
    connman.AddTestNode(p2p_node);
    g_setup->m_node.peer_logic->InitializeNode(config, &p2p_node);
    try {
        g_setup->m_node.peer_logic->ProcessMessage(
            config, p2p_node, random_message_type, random_bytes_data_stream,
            GetTimeMillis(), std::atomic<bool>{false});
    } catch (const std::ios_base::failure &) {
    }
    SyncWithValidationInterfaceQueue();
    // See init.cpp for rationale for implicit locking order requirement
    LOCK2(::cs_main, g_cs_orphans);
    g_setup->m_node.connman->StopNodes();
}
<|endoftext|>


<commit_before>/*
 * This File is part of Davix, The IO library for HTTP based protocols
 * Copyright (C) 2013  Adrien Devresse <adrien.devresse@cern.ch>, CERN
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
*/

#include <davix_internal.hpp>
#include <davix.hpp>
#include <tools/davix_tool_params.hpp>
#include <tools/davix_tool_util.hpp>
#include <tools/davix_taskqueue.hpp>
#include <tools/davix_op.hpp>
#include <tools/davix_thread_pool.hpp>
#include <utils/davix_logger_internal.hpp>

// @author : Devresse Adrien
// main file for davix-get operation

using namespace Davix;
using namespace std;

#define READ_BLOCK_SIZE 4096

const std::string scope_get = "Davix::Tools::davix-get";

static std::string help_msg(const std::string &cmd_path){
    std::string help_msg = fmt::format("Usage : {} ", cmd_path);
    help_msg += Tool::get_get_description_options();
    help_msg += Tool::get_common_options();

    return help_msg;
}

int get_output_get_fstream(const Tool::OptParams & opts,  const std::string & scope, std::string uri, DavixError** err){
    int fd = -1;
    if(uri.empty() == false){
        if((fd = open(uri.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0777)) <0  ){
            davix_errno_to_davix_error(errno, scope, std::string("for destination file ").append(opts.output_file_path), err);
            return -1;
        }
    }else{
        fd = dup(STDOUT_FILENO);
    }
    return fd;
}

static int excute_get(Context& c, const Tool::OptParams & opts, int out_fd, std::string uri, DavixError** err){
        int ret;
        DavFile f(c, uri);
        ret = f.getToFd(&opts.params, out_fd, err);
        return (ret >= 0)?0:-1;
}

static int populateTaskQueue(Context& c, const Tool::OptParams & opts, std::string uri, DavixTaskQueue* tq, DavixError** err ){
    DAVIX_DIR* fd = NULL;
    DavPosix pos(&c);
    DavixError* tmp_err=NULL;
    std::string outputPath;
    struct stat st;
    struct dirent* d;
    int entry_counter=0;
    std::string last_success_entry;

    std::deque<std::pair<std::string,std::string> > dirQueue;
    std::deque<std::pair<std::string,std::string> > opQueue;

    outputPath = opts.output_file_path;

    // set up first entry
    if(!uri.empty()){
        // if output path is not specified, default to current directory
        if(outputPath.empty()){
            std::deque<std::string> dirVec;

            std::string tmp = Uri(uri).getPath();
            if(tmp == "/"){ // no output path and no token in url to use as default, fallback to ./temp
                outputPath = "./temp";
            }
            else{
                Tool::tokeniseUrl(Uri(uri).getPath(), dirVec);
                outputPath = "./"+dirVec.back();
            }
        }
        dirQueue.push_back(std::make_pair(uri, outputPath));
    }
    else
        DavixError::setupError(&tmp_err, "Davix::Tool::Get", StatusCode::InvalidArgument, " target URL is empty.");

    while(!dirQueue.empty()){
        if( (fd = pos.opendirpp(&opts.params, dirQueue.front().first, &tmp_err)) == NULL){
            Tool::errorPrint(&tmp_err);
            return -1;
        }

        // dir opened successfully, create local dir
        Tool::mkdirP(dirQueue.front().second, false);

        //if S3, remove last token of url to accommodate duplicate token in d_name
        if (opts.params.getProtocol() == RequestProtocol::AwsS3){
            dirQueue.front().first = "s3://"+(Uri(dirQueue.front().first).getHost())+"/";
        }

        while( ((d = pos.readdirpp(fd, &st, &tmp_err)) != NULL)){    // if one entry insidce a directory fails, the loop exits, the other entires are not processed

            last_success_entry = dirQueue.front().first+d->d_name;
            // for each entry, do a stat to see if it's a directory, if yes, push to dirQueue for further processing
            if(st.st_mode & S_IFDIR){
                DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_CORE, "Directory entry found, pushing {}/ to dirQueue", dirQueue.front().first+d->d_name);
                dirQueue.push_back(std::make_pair(dirQueue.front().first+d->d_name+"/",dirQueue.front().second+"/"+d->d_name));
            }
            else if(!(st.st_mode & S_IFDIR)){
                //if we spend too long in here, server will likely close the connection mid-readdirpp, need to get all the entries quickly before processing them
                opQueue.push_back(std::make_pair(dirQueue.front().first+d->d_name, dirQueue.front().second+"/"+d->d_name));
                entry_counter++;
            }
            if(!opts.debug)
                Tool::batchTransferMonitor(dirQueue.front().first, "Crawling", entry_counter, 0);
        }

        if(tmp_err){
            Tool::errorPrint(&tmp_err);
            cerr << endl << "Error occured during listing  " << dirQueue.front().first << " Number of entries processed in current directory: " << entry_counter << ". Continuing..."<< endl;
            cerr << endl << "Last succesful entry is " << last_success_entry << endl;
        }

        entry_counter = 0;

        pos.closedirpp(fd, NULL);

        int num_of_ops = opQueue.size();

        for(std::deque<std::pair<std::string,std::string> >::iterator it = opQueue.begin(); it!=opQueue.end(); ++it){
            //push op to task queue
            DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_CORE, "Adding item to work queue, target is {} and destination is {}.", opQueue.front().first, opQueue.front().second);
            GetOp* op = new GetOp(opts, (opQueue.front().first), (opQueue.front().second), c);
            tq->pushOp(op);
            opQueue.pop_front();

            entry_counter++;
            if(!opts.debug)
                Tool::batchTransferMonitor(dirQueue.front().first, "Populating task queue for", entry_counter, num_of_ops);
        }
        entry_counter = 0;
        dirQueue.pop_front();
    }
    if(tmp_err){
        DavixError::propagateError(err, tmp_err);
        return -1;
    }
    return 0;
}

static int preGetCheck(Tool::OptParams & opts, DavixError** err ){
  Context c;
  configureContext(c, opts);
  File f(c, opts.vec_arg[0]);
  struct stat st;
  DavixError* tmp_err=NULL;

  // Try to understand what to do
  // If the URL is for plain HTTP then we don't want to stat
  bool justgetfile = true;
  cout << "---------------------" << opts.vec_arg[0] << "---------------" << endl;
  if ( opts.vec_arg[0].compare(0, 4, "http") != 0 ) {
    int r = f.stat(&opts.params, &st, &tmp_err);

    if (r && tmp_err) {
      DavixError::propagateError(err, tmp_err);
      return -1;
    }

    if ( !r && (st.st_mode & S_IFDIR) ) justgetfile = false;
  }

  if (justgetfile) {
    // target resource is a file or request protocol is http, just get it normally
    int out_fd= -1;
    if( ( (out_fd = get_output_get_fstream(opts, scope_get, opts.output_file_path, &tmp_err)) > 0)
      && (Tool::configureMonitorCB(opts, Transfer::Read)) == 0){
      excute_get(c, opts, out_fd, opts.vec_arg[0], &tmp_err);
    Tool::flushFinalLineShell(out_fd);
    close(out_fd);
      }
  }
  else {

    std::string url(opts.vec_arg[0]);

    if (url[url.size()-1] != '/')
      url += '/';

    DavixTaskQueue tq;

    // create threadpool instance
    DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_CORE, "Creating threadpool");
    DavixThreadPool tp(&tq);

    // if protocol is S3, set listing mode to SemiHierarchical, we want to get every object under the same prefix in one go
    if (opts.params.getProtocol() == RequestProtocol::AwsS3){
      opts.params.setS3ListingMode(S3ListingMode::SemiHierarchical);
      // unfortunately s3 defaults max-keys to 1000 and doesn't provide a way to disable the cap, set to large number
      opts.params.setS3MaxKey(999999999);
    }

    populateTaskQueue(c, opts, url, &tq, &tmp_err);

    // if task queue is empty, then all work is done, stop workers. Otherwise wait.
    while(!tq.isEmpty()){
      sleep(2);
    }
    tp.shutdown();
    Tool::flushFinalLineShell(STDOUT_FILENO);
  }

  if(tmp_err){
    DavixError::propagateError(err, tmp_err);
    return -1;
  }
  return 0;
}

int main(int argc, char** argv){
    int retcode=-1;
    Tool::OptParams opts;
    DavixError* tmp_err=NULL;
    opts.help_msg = help_msg(argv[0]);

    if( (retcode= Tool::parse_davix_get_options(argc, argv, opts, &tmp_err)) ==0
        && (retcode = Tool::configureAuth(opts)) == 0){
            retcode = preGetCheck(opts, &tmp_err);
    }
    Tool::errorPrint(&tmp_err);

    return retcode;
}

<commit_msg>Fix indentation Remove forgotten cout<commit_after>/*
 * This File is part of Davix, The IO library for HTTP based protocols
 * Copyright (C) 2013  Adrien Devresse <adrien.devresse@cern.ch>, CERN
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
*/

#include <davix_internal.hpp>
#include <davix.hpp>
#include <tools/davix_tool_params.hpp>
#include <tools/davix_tool_util.hpp>
#include <tools/davix_taskqueue.hpp>
#include <tools/davix_op.hpp>
#include <tools/davix_thread_pool.hpp>
#include <utils/davix_logger_internal.hpp>

// @author : Devresse Adrien
// main file for davix-get operation

using namespace Davix;
using namespace std;

#define READ_BLOCK_SIZE 4096

const std::string scope_get = "Davix::Tools::davix-get";

static std::string help_msg(const std::string &cmd_path){
    std::string help_msg = fmt::format("Usage : {} ", cmd_path);
    help_msg += Tool::get_get_description_options();
    help_msg += Tool::get_common_options();

    return help_msg;
}

int get_output_get_fstream(const Tool::OptParams & opts,  const std::string & scope, std::string uri, DavixError** err){
    int fd = -1;
    if(uri.empty() == false){
        if((fd = open(uri.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0777)) <0  ){
            davix_errno_to_davix_error(errno, scope, std::string("for destination file ").append(opts.output_file_path), err);
            return -1;
        }
    }else{
        fd = dup(STDOUT_FILENO);
    }
    return fd;
}

static int excute_get(Context& c, const Tool::OptParams & opts, int out_fd, std::string uri, DavixError** err){
        int ret;
        DavFile f(c, uri);
        ret = f.getToFd(&opts.params, out_fd, err);
        return (ret >= 0)?0:-1;
}

static int populateTaskQueue(Context& c, const Tool::OptParams & opts, std::string uri, DavixTaskQueue* tq, DavixError** err ){
    DAVIX_DIR* fd = NULL;
    DavPosix pos(&c);
    DavixError* tmp_err=NULL;
    std::string outputPath;
    struct stat st;
    struct dirent* d;
    int entry_counter=0;
    std::string last_success_entry;

    std::deque<std::pair<std::string,std::string> > dirQueue;
    std::deque<std::pair<std::string,std::string> > opQueue;

    outputPath = opts.output_file_path;

    // set up first entry
    if(!uri.empty()){
        // if output path is not specified, default to current directory
        if(outputPath.empty()){
            std::deque<std::string> dirVec;

            std::string tmp = Uri(uri).getPath();
            if(tmp == "/"){ // no output path and no token in url to use as default, fallback to ./temp
                outputPath = "./temp";
            }
            else{
                Tool::tokeniseUrl(Uri(uri).getPath(), dirVec);
                outputPath = "./"+dirVec.back();
            }
        }
        dirQueue.push_back(std::make_pair(uri, outputPath));
    }
    else
        DavixError::setupError(&tmp_err, "Davix::Tool::Get", StatusCode::InvalidArgument, " target URL is empty.");

    while(!dirQueue.empty()){
        if( (fd = pos.opendirpp(&opts.params, dirQueue.front().first, &tmp_err)) == NULL){
            Tool::errorPrint(&tmp_err);
            return -1;
        }

        // dir opened successfully, create local dir
        Tool::mkdirP(dirQueue.front().second, false);

        //if S3, remove last token of url to accommodate duplicate token in d_name
        if (opts.params.getProtocol() == RequestProtocol::AwsS3){
            dirQueue.front().first = "s3://"+(Uri(dirQueue.front().first).getHost())+"/";
        }

        while( ((d = pos.readdirpp(fd, &st, &tmp_err)) != NULL)){    // if one entry insidce a directory fails, the loop exits, the other entires are not processed

            last_success_entry = dirQueue.front().first+d->d_name;
            // for each entry, do a stat to see if it's a directory, if yes, push to dirQueue for further processing
            if(st.st_mode & S_IFDIR){
                DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_CORE, "Directory entry found, pushing {}/ to dirQueue", dirQueue.front().first+d->d_name);
                dirQueue.push_back(std::make_pair(dirQueue.front().first+d->d_name+"/",dirQueue.front().second+"/"+d->d_name));
            }
            else if(!(st.st_mode & S_IFDIR)){
                //if we spend too long in here, server will likely close the connection mid-readdirpp, need to get all the entries quickly before processing them
                opQueue.push_back(std::make_pair(dirQueue.front().first+d->d_name, dirQueue.front().second+"/"+d->d_name));
                entry_counter++;
            }
            if(!opts.debug)
                Tool::batchTransferMonitor(dirQueue.front().first, "Crawling", entry_counter, 0);
        }

        if(tmp_err){
            Tool::errorPrint(&tmp_err);
            cerr << endl << "Error occured during listing  " << dirQueue.front().first << " Number of entries processed in current directory: " << entry_counter << ". Continuing..."<< endl;
            cerr << endl << "Last succesful entry is " << last_success_entry << endl;
        }

        entry_counter = 0;

        pos.closedirpp(fd, NULL);

        int num_of_ops = opQueue.size();

        for(std::deque<std::pair<std::string,std::string> >::iterator it = opQueue.begin(); it!=opQueue.end(); ++it){
            //push op to task queue
            DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_CORE, "Adding item to work queue, target is {} and destination is {}.", opQueue.front().first, opQueue.front().second);
            GetOp* op = new GetOp(opts, (opQueue.front().first), (opQueue.front().second), c);
            tq->pushOp(op);
            opQueue.pop_front();

            entry_counter++;
            if(!opts.debug)
                Tool::batchTransferMonitor(dirQueue.front().first, "Populating task queue for", entry_counter, num_of_ops);
        }
        entry_counter = 0;
        dirQueue.pop_front();
    }
    if(tmp_err){
        DavixError::propagateError(err, tmp_err);
        return -1;
    }
    return 0;
}

static int preGetCheck(Tool::OptParams & opts, DavixError** err ) {
    Context c;
    configureContext(c, opts);
    File f(c, opts.vec_arg[0]);
    struct stat st;
    DavixError* tmp_err=NULL;

    // Try to understand what to do
    // If the URL is for plain HTTP then we don't want to stat
    bool justgetfile = true;

    if ( opts.vec_arg[0].compare(0, 4, "http") != 0 ) {
        int r = f.stat(&opts.params, &st, &tmp_err);
        if (r && tmp_err) {
            DavixError::propagateError(err, tmp_err);
            return -1;
        }

        if ( !r && (st.st_mode & S_IFDIR) ) justgetfile = false;
    }

    if (justgetfile) {
        // target resource is a file or request protocol is http, just get it normally
        int out_fd= -1;
        if( ( (out_fd = get_output_get_fstream(opts, scope_get, opts.output_file_path, &tmp_err)) > 0)
                  && (Tool::configureMonitorCB(opts, Transfer::Read)) == 0) {

            excute_get(c, opts, out_fd, opts.vec_arg[0], &tmp_err);
            Tool::flushFinalLineShell(out_fd);
            close(out_fd);
        }
    }
    else {

        std::string url(opts.vec_arg[0]);

        if (url[url.size()-1] != '/')
            url += '/';

        DavixTaskQueue tq;

        // create threadpool instance
        DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_CORE, "Creating threadpool");
        DavixThreadPool tp(&tq);

        // if protocol is S3, set listing mode to SemiHierarchical, we want to get every object under the same prefix in one go
        if (opts.params.getProtocol() == RequestProtocol::AwsS3){
            opts.params.setS3ListingMode(S3ListingMode::SemiHierarchical);
            // unfortunately s3 defaults max-keys to 1000 and doesn't provide a way to disable the cap, set to large number
            opts.params.setS3MaxKey(999999999);
        }

        populateTaskQueue(c, opts, url, &tq, &tmp_err);

        // if task queue is empty, then all work is done, stop workers. Otherwise wait.
        while(!tq.isEmpty()){
            sleep(2);
        }
        tp.shutdown();
        Tool::flushFinalLineShell(STDOUT_FILENO);
    }

    if(tmp_err){
        DavixError::propagateError(err, tmp_err);
        return -1;
    }
    return 0;
}

int main(int argc, char** argv){
    int retcode=-1;
    Tool::OptParams opts;
    DavixError* tmp_err=NULL;
    opts.help_msg = help_msg(argv[0]);

    if( (retcode= Tool::parse_davix_get_options(argc, argv, opts, &tmp_err)) ==0
        && (retcode = Tool::configureAuth(opts)) == 0){
            retcode = preGetCheck(opts, &tmp_err);
    }
    Tool::errorPrint(&tmp_err);

    return retcode;
}

<|endoftext|>


<commit_before>#include "serverConnection.h"
#include <vector>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>

#include <libwatcher/message.h>
#include <libwatcher/messageStatus.h>
#include <libwatcher/seekWatcherMessage.h>
#include <libwatcher/speedWatcherMessage.h>
#include <libwatcher/nodeStatusMessage.h>

#include "watcherd.h"
#include "writeDBMessageHandler.h"
#include "watcherdConfig.h"
#include "replayState.h"

using namespace std;
using namespace boost::asio;

namespace {
    using namespace watcher::event;

    /* Grr, because there is no std::copy_if have to use the negation with
     * remove_copy_if() */
    bool not_feeder_message(const MessagePtr& m)
    {
        return !isFeederEvent(m->type);
    }
}

namespace watcher {
    using namespace event;

    INIT_LOGGER(ServerConnection, "Connection.ServerConnection");

    ServerConnection::ServerConnection(Watcherd& w, boost::asio::io_service& io_service) :
        Connection(io_service),
        watcher(w),
        io_service_(io_service),
        strand_(io_service),
        write_strand_(io_service),
        conn_type(unknown),
        isPlaying_(false), isLive_(true)
    {
        TRACE_ENTER();
        TRACE_EXIT();
    }

    ServerConnection::~ServerConnection()
    {
        TRACE_ENTER();
        //shared_from_this() not allowed in destructor
        //watcher.unsubscribe(shared_from_this());
        TRACE_EXIT();
    }

    /** Initialization point for start of new ServerConnection thread. */
    void ServerConnection::run()
    {
        TRACE_ENTER();
        boost::asio::async_read(
                theSocket,
                boost::asio::buffer(incomingBuffer, DataMarshaller::header_length),
                strand_.wrap(
                    boost::bind(
                        &ServerConnection::handle_read_header,
                        shared_from_this(),
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred)));
        TRACE_EXIT();
    }

    void ServerConnection::read_error(const boost::system::error_code &e)
    {
        TRACE_ENTER();

        if (e == boost::asio::error::eof)
        {
            LOG_DEBUG("Received empty message from client or client closed connection.");
            LOG_INFO("Connection to client closed.");

        }
        else
        {
            LOG_ERROR("Error reading socket: " << e.message());
        }

        // unsubscribe to event stream, otherwise it will hold a
        // shared_ptr open
        if (conn_type == gui)
            watcher.unsubscribe(shared_from_this());

        TRACE_EXIT();
    }

    void ServerConnection::handle_read_header(const boost::system::error_code& e, size_t bytes_transferred)
    {
        TRACE_ENTER();
        if (!e)
        {
            LOG_DEBUG("Read " << bytes_transferred << " bytes.");

            size_t payloadSize;
            unsigned short numOfMessages;
            if (!DataMarshaller::unmarshalHeader(incomingBuffer.begin(), bytes_transferred, payloadSize, numOfMessages))
            {
                LOG_ERROR("Error parsing incoming message header.");

                if (conn_type == gui)
                    watcher.unsubscribe(shared_from_this());
            }
            else
            {
                LOG_DEBUG("Reading packet payload of " << payloadSize << " bytes.");

                boost::asio::async_read(
                        theSocket,
                        boost::asio::buffer(
                            incomingBuffer,
                            payloadSize),  // Should incoming buffer be new'd()?
                        strand_.wrap(
                            boost::bind(
                                &ServerConnection::handle_read_payload,
                                shared_from_this(),
                                boost::asio::placeholders::error,
                                boost::asio::placeholders::bytes_transferred,
                                numOfMessages)));
            }
        }
        else
            read_error(e);
        TRACE_EXIT();
    }

    void ServerConnection::seek(event::MessagePtr& m)
    {
        TRACE_ENTER();
        SeekMessagePtr p = boost::dynamic_pointer_cast<SeekMessage>(m);
        if (p) {
            if (p->offset == SeekMessage::eof) {
                if (isLive_) {
                    // nothing to do
                } else if (replay->speed() >= 0) {
                    // switch to live stream
                    isLive_ = true;
                    replay->pause();
                    if (isPlaying_)
                        watcher.subscribe(shared_from_this());
                } else {
                    replay->seek( SeekMessage::eof );
                }
            } else {
                replay->seek(p->offset);

                // when switching from live to replay while playing, kick off the replay strand
                if (isLive_ && isPlaying_)
                    replay->run();
                isLive_ = false;
            }
        } else {
            LOG_WARN("unable to dynamic_pointer_cast to SeekMessage!");
        }
        TRACE_EXIT();
    }

    void ServerConnection::start(event::MessagePtr&)
    {
        TRACE_ENTER();
        LOG_DEBUG("in: isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        if (!isPlaying_) {
            isPlaying_ = true;
            if (isLive_)
                watcher.subscribe(shared_from_this());
            else
                replay->run();
        }
        LOG_DEBUG("out: isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        TRACE_EXIT();
    }

    void ServerConnection::stop(event::MessagePtr&)
    {
        TRACE_ENTER();
        LOG_DEBUG("isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        if (isPlaying_) {
            LOG_DEBUG("stopping playback");
            if (isLive_)
                watcher.unsubscribe(shared_from_this());
            else
                replay->pause();
            isPlaying_ = false;
        } else
            LOG_DEBUG("stop message received, but playback is stopped");
        LOG_DEBUG("out: isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        TRACE_EXIT();
    }

    void ServerConnection::speed(event::MessagePtr& m)
    {
        TRACE_ENTER();
        LOG_DEBUG("isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        SpeedMessagePtr p = boost::dynamic_pointer_cast<SpeedMessage>(m);
        if (p) {
            if (isLive_ && p->speed >= 1.0f) {
                // ignore, can't predict the future
            } else {
                replay->speed(p->speed);
                if (isLive_) {
                    isLive_ = false;
                    /* when transitioning from live playback, automatically
                     * seek to the end of the database.
                     */
                    replay->seek( SeekMessage::eof );
                    if (isPlaying_)
                        replay->run();
                }
            }
        } else {
            LOG_WARN("unable to dynamic_pointer_cast to SpeedMessage!");
        }
        LOG_DEBUG("out: isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        TRACE_EXIT();
    }

    bool ServerConnection::dispatch_gui_event(MessagePtr& m)
    {
        static const struct {
            MessageType type;
            void (ServerConnection::*fn)(event::MessagePtr&);
        } dispatch[] = {
            { START_MESSAGE_TYPE, &ServerConnection::start },
            { STOP_MESSAGE_TYPE, &ServerConnection::stop },
            { SEEK_MESSAGE_TYPE, &ServerConnection::seek },
            { SPEED_MESSAGE_TYPE, &ServerConnection::speed },
            { UNKNOWN_MESSAGE_TYPE, 0 }
        };

        TRACE_ENTER();

        for (size_t i = 0; dispatch[i].type != UNKNOWN_MESSAGE_TYPE; ++i) {
            if (m->type == dispatch[i].type) {
                if (conn_type == unknown) {
                    conn_type = gui;
                    replay.reset(new ReplayState(shared_from_this()));
                }
                (this->*(dispatch[i].fn)) (m);
                TRACE_EXIT_RET_BOOL(true);
                return true;
            }
        }
        TRACE_EXIT_RET_BOOL(false);
        return false;
    }

    void ServerConnection::handle_read_payload(const boost::system::error_code& e, size_t bytes_transferred, unsigned short numOfMessages)
    {
        TRACE_ENTER();

        if (!e)
        {
            vector<MessagePtr> arrivedMessages;
            if (DataMarshaller::unmarshalPayload(arrivedMessages, numOfMessages, incomingBuffer.begin(), bytes_transferred))
            {
                LOG_INFO("Recvd " << arrivedMessages.size() << " message" <<
                         (arrivedMessages.size()>1?"s":"") << " from " <<
                         remoteEndpoint().address());

                // Add the incoming address to the Message so everyone
                // knows who the message came from.
                boost::asio::ip::tcp::endpoint ep = getSocket().remote_endpoint();
                BOOST_FOREACH(MessagePtr m, arrivedMessages) {
                    if(m->fromNodeID==NodeIdentifier())  // is empty
                        m->fromNodeID=ep.address();
                }

                /*
                 * If this connection type is unknown or gui, then traverse the
                 * list of arrived messages.  For GUI clients, look for the
                 * STOP_MESSAGE to unsubscribe from the event stream.
                 *
                 * For unknown clients, infer the type from the message
                 * received:
                 * START_MESSAGE => gui
                 * isFeederMessage => feeder
                 */
                if (conn_type == unknown || conn_type == gui) {
                    BOOST_FOREACH(MessagePtr& i, arrivedMessages) {
                        if (dispatch_gui_event(i)) {
                            //empty
                       } else if (isFeederEvent(i->type)) {
                            conn_type = feeder;

                            /*
                             * This connection is a watcher test daemon.
                             * Add a message handler to write its event
                             * stream to the database.
                             */
                            addMessageHandler(MessageHandlerPtr(new WriteDBMessageHandler()));
                        }
                    }
                }

                /* Flag indicating whether to continue reading from this
                 * connection. */
                bool fail = false;

                BOOST_FOREACH(MessageHandlerPtr& mh, messageHandlers) {
                    if (mh->handleMessagesArrive(shared_from_this(), arrivedMessages)) {
                        fail = true;
                        LOG_DEBUG("Message handler told us to close this connection.");
                    }
                }

                if (!fail) {
                    // initiate request to read next message
                    LOG_DEBUG("Waiting for next message.");
                    run();
                }

                if (conn_type == feeder) {
                    /* relay feeder message to any client requesting the live stream.
                     * Warning: currently there is no check to make sure that a client doesn't
                     * receive a message it just sent.  This should be OK since we are just
                     * relaying feeder messages only, and the GUIs should not be sending
                     * them. */
                    vector<MessagePtr> feeder;
                    remove_copy_if(arrivedMessages.begin(), arrivedMessages.end(), back_inserter(feeder), not_feeder_message);
                    if (! feeder.empty()) {
                        LOG_DEBUG("Sending " << feeder.size() << " feeder messages to clients.");
                        watcher.sendMessage(feeder);
                    }
                }
            }
        }
        else
            read_error(e);

        // If an error occurs then no new asynchronous operations are started. This
        // means that all shared_ptr references to the ServerConnection object will
        // disappear and the object will be destroyed automatically after this
        // handler returns. The ServerConnection class's destructor closes the socket.

        TRACE_EXIT();
    }

    void ServerConnection::handle_write(const boost::system::error_code& e, MessagePtr message)
    {
        TRACE_ENTER();

        if (!e)
        {
            LOG_DEBUG("Successfully sent message to client: " << message);

            BOOST_FOREACH(MessageHandlerPtr mh, messageHandlers)
            {
#if 0
                /* melkins
                 * The reads and writes to the socket are asynchronous, so
                 * we should never be waiting for something to be read as
                 * a result of a write.
                 */
                if(waitForResponse) // someone already said they wanted a response, so ignore ret val for others
                    mh->handleMessageSent(message);
                else
                    waitForResponse=mh->handleMessageSent(message);
#endif
                    mh->handleMessageSent(message);
            }

            // melkins
            // start() calls async_read(), which is not what we want to do here
            /*
            if(waitForResponse)
                start();
                */
        }
        else
        {
            LOG_WARN("Error while sending response to client: " << e);
            if (conn_type == gui)
                watcher.unsubscribe(shared_from_this());
        }

        // No new asynchronous operations are started. This means that all shared_ptr
        // references to the connection object will disappear and the object will be
        // destroyed automatically after this handler returns. The connection class's
        // destructor closes the socket.

        /* NOTE: The refcount will not go to zero so long as there is an
         * async_read operation also oustanding. */

        TRACE_EXIT();
    }

    /** Send a single message to this connected client. */
    void ServerConnection::sendMessage(MessagePtr msg)
    {
        TRACE_ENTER();
        DataMarshaller::NetworkMarshalBuffers outBuffers;
        DataMarshaller::marshalPayload(msg, outBuffers);

        /// FIXME melkins 2004-04-19
        // is it safe to call async_write and async_read from different
        // threads at the same time?  asio::tcp::socket() is listed at not
        // shared thread safe
        async_write(theSocket,
                    outBuffers,
                    write_strand_.wrap( boost::bind( &ServerConnection::handle_write,
                                               shared_from_this(),
                                               placeholders::error,
                                               msg)));
        TRACE_EXIT();
    }

    /** Send a set of messages to this connected client. */
    void ServerConnection::sendMessage(const std::vector<MessagePtr>& msgs)
    {
        TRACE_ENTER();
        DataMarshaller::NetworkMarshalBuffers outBuffers;
        DataMarshaller::marshalPayload(msgs, outBuffers);

        /// FIXME melkins 2004-04-19
        // is it safe to call async_write and async_read from different
        // threads at the same time?
        async_write(theSocket,
                    outBuffers,
                    write_strand_.wrap( boost::bind( &ServerConnection::handle_write,
                                               shared_from_this(),
                                               placeholders::error,
                                               msgs.front())));
        TRACE_EXIT();
    }

}
<commit_msg>save timestamp when receiving StopMessage during live playback so the next StartMessage resumes from the db at the proper location<commit_after>#include "serverConnection.h"
#include <vector>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>

#include <libwatcher/message.h>
#include <libwatcher/messageStatus.h>
#include <libwatcher/seekWatcherMessage.h>
#include <libwatcher/speedWatcherMessage.h>
#include <libwatcher/nodeStatusMessage.h>

#include "watcherd.h"
#include "writeDBMessageHandler.h"
#include "watcherdConfig.h"
#include "replayState.h"

using namespace std;
using namespace boost::asio;

namespace {
    using namespace watcher::event;

    /* Grr, because there is no std::copy_if have to use the negation with
     * remove_copy_if() */
    bool not_feeder_message(const MessagePtr& m)
    {
        return !isFeederEvent(m->type);
    }
}

namespace watcher {
    using namespace event;

    INIT_LOGGER(ServerConnection, "Connection.ServerConnection");

    ServerConnection::ServerConnection(Watcherd& w, boost::asio::io_service& io_service) :
        Connection(io_service),
        watcher(w),
        io_service_(io_service),
        strand_(io_service),
        write_strand_(io_service),
        conn_type(unknown),
        isPlaying_(false), isLive_(true)
    {
        TRACE_ENTER();
        TRACE_EXIT();
    }

    ServerConnection::~ServerConnection()
    {
        TRACE_ENTER();
        //shared_from_this() not allowed in destructor
        //watcher.unsubscribe(shared_from_this());
        TRACE_EXIT();
    }

    /** Initialization point for start of new ServerConnection thread. */
    void ServerConnection::run()
    {
        TRACE_ENTER();
        boost::asio::async_read(
                theSocket,
                boost::asio::buffer(incomingBuffer, DataMarshaller::header_length),
                strand_.wrap(
                    boost::bind(
                        &ServerConnection::handle_read_header,
                        shared_from_this(),
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred)));
        TRACE_EXIT();
    }

    void ServerConnection::read_error(const boost::system::error_code &e)
    {
        TRACE_ENTER();

        if (e == boost::asio::error::eof)
        {
            LOG_DEBUG("Received empty message from client or client closed connection.");
            LOG_INFO("Connection to client closed.");

        }
        else
        {
            LOG_ERROR("Error reading socket: " << e.message());
        }

        // unsubscribe to event stream, otherwise it will hold a
        // shared_ptr open
        if (conn_type == gui)
            watcher.unsubscribe(shared_from_this());

        TRACE_EXIT();
    }

    void ServerConnection::handle_read_header(const boost::system::error_code& e, size_t bytes_transferred)
    {
        TRACE_ENTER();
        if (!e)
        {
            LOG_DEBUG("Read " << bytes_transferred << " bytes.");

            size_t payloadSize;
            unsigned short numOfMessages;
            if (!DataMarshaller::unmarshalHeader(incomingBuffer.begin(), bytes_transferred, payloadSize, numOfMessages))
            {
                LOG_ERROR("Error parsing incoming message header.");

                if (conn_type == gui)
                    watcher.unsubscribe(shared_from_this());
            }
            else
            {
                LOG_DEBUG("Reading packet payload of " << payloadSize << " bytes.");

                boost::asio::async_read(
                        theSocket,
                        boost::asio::buffer(
                            incomingBuffer,
                            payloadSize),  // Should incoming buffer be new'd()?
                        strand_.wrap(
                            boost::bind(
                                &ServerConnection::handle_read_payload,
                                shared_from_this(),
                                boost::asio::placeholders::error,
                                boost::asio::placeholders::bytes_transferred,
                                numOfMessages)));
            }
        }
        else
            read_error(e);
        TRACE_EXIT();
    }

    void ServerConnection::seek(event::MessagePtr& m)
    {
        TRACE_ENTER();
        SeekMessagePtr p = boost::dynamic_pointer_cast<SeekMessage>(m);
        if (p) {
            if (p->offset == SeekMessage::eof) {
                if (isLive_) {
                    // nothing to do
                } else if (replay->speed() >= 0) {
                    // switch to live stream
                    isLive_ = true;
                    replay->pause();
                    if (isPlaying_)
                        watcher.subscribe(shared_from_this());
                } else {
                    replay->seek( SeekMessage::eof );
                }
            } else {
                replay->seek(p->offset);

                // when switching from live to replay while playing, kick off the replay strand
                if (isLive_ && isPlaying_)
                    replay->run();
                isLive_ = false;
            }
        } else {
            LOG_WARN("unable to dynamic_pointer_cast to SeekMessage!");
        }
        TRACE_EXIT();
    }

    void ServerConnection::start(event::MessagePtr&)
    {
        TRACE_ENTER();
        LOG_DEBUG("in: isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        if (!isPlaying_) {
            isPlaying_ = true;
            if (isLive_)
                watcher.subscribe(shared_from_this());
            else
                replay->run();
        }
        LOG_DEBUG("out: isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        TRACE_EXIT();
    }

    void ServerConnection::stop(event::MessagePtr&)
    {
        TRACE_ENTER();
        LOG_DEBUG("isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        if (isPlaying_) {
            LOG_DEBUG("stopping playback");
            if (isLive_) {
                watcher.unsubscribe(shared_from_this());
                /* save current Timestamp so we can resume from the database */
                isLive_ = false;
                replay->seek( getCurrentTime() );
            } else
                replay->pause();
            isPlaying_ = false;
        } else
            LOG_DEBUG("stop message received, but playback is stopped");
        LOG_DEBUG("out: isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        TRACE_EXIT();
    }

    void ServerConnection::speed(event::MessagePtr& m)
    {
        TRACE_ENTER();
        LOG_DEBUG("isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        SpeedMessagePtr p = boost::dynamic_pointer_cast<SpeedMessage>(m);
        if (p) {
            if (isLive_ && p->speed >= 1.0f) {
                // ignore, can't predict the future
            } else {
                replay->speed(p->speed);
                if (isLive_) {
                    isLive_ = false;
                    /* when transitioning from live playback, automatically
                     * seek to the end of the database.
                     */
                    replay->seek( SeekMessage::eof );
                    if (isPlaying_)
                        replay->run();
                }
            }
        } else {
            LOG_WARN("unable to dynamic_pointer_cast to SpeedMessage!");
        }
        LOG_DEBUG("out: isPlaying_=" << isPlaying_ << ", isLive_=" << isLive_);
        TRACE_EXIT();
    }

    bool ServerConnection::dispatch_gui_event(MessagePtr& m)
    {
        static const struct {
            MessageType type;
            void (ServerConnection::*fn)(event::MessagePtr&);
        } dispatch[] = {
            { START_MESSAGE_TYPE, &ServerConnection::start },
            { STOP_MESSAGE_TYPE, &ServerConnection::stop },
            { SEEK_MESSAGE_TYPE, &ServerConnection::seek },
            { SPEED_MESSAGE_TYPE, &ServerConnection::speed },
            { UNKNOWN_MESSAGE_TYPE, 0 }
        };

        TRACE_ENTER();

        for (size_t i = 0; dispatch[i].type != UNKNOWN_MESSAGE_TYPE; ++i) {
            if (m->type == dispatch[i].type) {
                if (conn_type == unknown) {
                    conn_type = gui;
                    replay.reset(new ReplayState(shared_from_this()));
                }
                (this->*(dispatch[i].fn)) (m);
                TRACE_EXIT_RET_BOOL(true);
                return true;
            }
        }
        TRACE_EXIT_RET_BOOL(false);
        return false;
    }

    void ServerConnection::handle_read_payload(const boost::system::error_code& e, size_t bytes_transferred, unsigned short numOfMessages)
    {
        TRACE_ENTER();

        if (!e)
        {
            vector<MessagePtr> arrivedMessages;
            if (DataMarshaller::unmarshalPayload(arrivedMessages, numOfMessages, incomingBuffer.begin(), bytes_transferred))
            {
                LOG_INFO("Recvd " << arrivedMessages.size() << " message" <<
                         (arrivedMessages.size()>1?"s":"") << " from " <<
                         remoteEndpoint().address());

                // Add the incoming address to the Message so everyone
                // knows who the message came from.
                boost::asio::ip::tcp::endpoint ep = getSocket().remote_endpoint();
                BOOST_FOREACH(MessagePtr m, arrivedMessages) {
                    if(m->fromNodeID==NodeIdentifier())  // is empty
                        m->fromNodeID=ep.address();
                }

                /*
                 * If this connection type is unknown or gui, then traverse the
                 * list of arrived messages.  For GUI clients, look for the
                 * STOP_MESSAGE to unsubscribe from the event stream.
                 *
                 * For unknown clients, infer the type from the message
                 * received:
                 * START_MESSAGE => gui
                 * isFeederMessage => feeder
                 */
                if (conn_type == unknown || conn_type == gui) {
                    BOOST_FOREACH(MessagePtr& i, arrivedMessages) {
                        if (dispatch_gui_event(i)) {
                            //empty
                       } else if (isFeederEvent(i->type)) {
                            conn_type = feeder;

                            /*
                             * This connection is a watcher test daemon.
                             * Add a message handler to write its event
                             * stream to the database.
                             */
                            addMessageHandler(MessageHandlerPtr(new WriteDBMessageHandler()));
                        }
                    }
                }

                /* Flag indicating whether to continue reading from this
                 * connection. */
                bool fail = false;

                BOOST_FOREACH(MessageHandlerPtr& mh, messageHandlers) {
                    if (mh->handleMessagesArrive(shared_from_this(), arrivedMessages)) {
                        fail = true;
                        LOG_DEBUG("Message handler told us to close this connection.");
                    }
                }

                if (!fail) {
                    // initiate request to read next message
                    LOG_DEBUG("Waiting for next message.");
                    run();
                }

                if (conn_type == feeder) {
                    /* relay feeder message to any client requesting the live stream.
                     * Warning: currently there is no check to make sure that a client doesn't
                     * receive a message it just sent.  This should be OK since we are just
                     * relaying feeder messages only, and the GUIs should not be sending
                     * them. */
                    vector<MessagePtr> feeder;
                    remove_copy_if(arrivedMessages.begin(), arrivedMessages.end(), back_inserter(feeder), not_feeder_message);
                    if (! feeder.empty()) {
                        LOG_DEBUG("Sending " << feeder.size() << " feeder messages to clients.");
                        watcher.sendMessage(feeder);
                    }
                }
            }
        }
        else
            read_error(e);

        // If an error occurs then no new asynchronous operations are started. This
        // means that all shared_ptr references to the ServerConnection object will
        // disappear and the object will be destroyed automatically after this
        // handler returns. The ServerConnection class's destructor closes the socket.

        TRACE_EXIT();
    }

    void ServerConnection::handle_write(const boost::system::error_code& e, MessagePtr message)
    {
        TRACE_ENTER();

        if (!e)
        {
            LOG_DEBUG("Successfully sent message to client: " << message);

            BOOST_FOREACH(MessageHandlerPtr mh, messageHandlers)
            {
#if 0
                /* melkins
                 * The reads and writes to the socket are asynchronous, so
                 * we should never be waiting for something to be read as
                 * a result of a write.
                 */
                if(waitForResponse) // someone already said they wanted a response, so ignore ret val for others
                    mh->handleMessageSent(message);
                else
                    waitForResponse=mh->handleMessageSent(message);
#endif
                    mh->handleMessageSent(message);
            }

            // melkins
            // start() calls async_read(), which is not what we want to do here
            /*
            if(waitForResponse)
                start();
                */
        }
        else
        {
            LOG_WARN("Error while sending response to client: " << e);
            if (conn_type == gui)
                watcher.unsubscribe(shared_from_this());
        }

        // No new asynchronous operations are started. This means that all shared_ptr
        // references to the connection object will disappear and the object will be
        // destroyed automatically after this handler returns. The connection class's
        // destructor closes the socket.

        /* NOTE: The refcount will not go to zero so long as there is an
         * async_read operation also oustanding. */

        TRACE_EXIT();
    }

    /** Send a single message to this connected client. */
    void ServerConnection::sendMessage(MessagePtr msg)
    {
        TRACE_ENTER();
        DataMarshaller::NetworkMarshalBuffers outBuffers;
        DataMarshaller::marshalPayload(msg, outBuffers);

        /// FIXME melkins 2004-04-19
        // is it safe to call async_write and async_read from different
        // threads at the same time?  asio::tcp::socket() is listed at not
        // shared thread safe
        async_write(theSocket,
                    outBuffers,
                    write_strand_.wrap( boost::bind( &ServerConnection::handle_write,
                                               shared_from_this(),
                                               placeholders::error,
                                               msg)));
        TRACE_EXIT();
    }

    /** Send a set of messages to this connected client. */
    void ServerConnection::sendMessage(const std::vector<MessagePtr>& msgs)
    {
        TRACE_ENTER();
        DataMarshaller::NetworkMarshalBuffers outBuffers;
        DataMarshaller::marshalPayload(msgs, outBuffers);

        /// FIXME melkins 2004-04-19
        // is it safe to call async_write and async_read from different
        // threads at the same time?
        async_write(theSocket,
                    outBuffers,
                    write_strand_.wrap( boost::bind( &ServerConnection::handle_write,
                                               shared_from_this(),
                                               placeholders::error,
                                               msgs.front())));
        TRACE_EXIT();
    }

}
<|endoftext|>