| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */ |
| /** |
| * Copyright (C) 2014 University of Arizona. |
| * |
| * GNU 3.0 License, see the LICENSE file for more information |
| * |
| * Author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu> |
| */ |
| |
| #include <sstream> |
| #include <boost/asio.hpp> |
| #include <boost/filesystem.hpp> |
| #include <boost/lexical_cast.hpp> |
| |
| #include <ndn-cpp-dev/face.hpp> |
| #include <ndn-cpp-dev/security/key-chain.hpp> |
| |
| #include "logger.hpp" |
| |
| namespace ndn { |
| |
| class NdnTrafficServer |
| { |
| public: |
| |
| explicit |
| NdnTrafficServer(char* programName) |
| : m_logger("NdnTrafficServer") |
| , m_programName(programName) |
| , m_hasError(false) |
| , m_nRegistrationsFailed(0) |
| , m_nMaximumInterests(-1) |
| , m_nInterestsReceived(0) |
| , m_contentDelay(time::milliseconds(-1)) |
| , m_ioService(new boost::asio::io_service) |
| , m_face(m_ioService) |
| { |
| m_instanceId = boost::lexical_cast<std::string>(std::rand()); |
| } |
| |
| class DataTrafficConfiguration |
| { |
| public: |
| DataTrafficConfiguration() |
| : m_contentType(-1) |
| , m_freshnessPeriod(time::milliseconds(-1)) |
| , m_contentBytes(-1) |
| , m_contentDelay(time::milliseconds(-1)) |
| , m_nInterestsReceived(0) |
| { |
| } |
| |
| void |
| printTrafficConfiguration(Logger& logger) |
| { |
| std::string detail = ""; |
| if (m_name != "") |
| detail += "Name=" + m_name + ", "; |
| if (m_contentType >= 0) |
| detail += "ContentType=" + boost::lexical_cast<std::string>(m_contentType) + ", "; |
| if (m_freshnessPeriod >= time::milliseconds(0)) |
| detail += "FreshnessPeriod=" + |
| boost::lexical_cast<std::string>(static_cast<int>(m_freshnessPeriod.count())) + ", "; |
| if (m_contentBytes >= 0) |
| detail += "ContentBytes=" + boost::lexical_cast<std::string>(m_contentBytes) + ", "; |
| if (m_contentDelay >= time::milliseconds(0)) |
| detail += "ContentDelay=" + |
| boost::lexical_cast<std::string>(m_contentDelay.count()) + ", "; |
| if (m_content != "") |
| detail += "Content=" + m_content + ", "; |
| if (detail.length() >= 2) |
| detail = detail.substr(0, detail.length() - 2); |
| logger.log(detail, false, false); |
| } |
| |
| |
| bool |
| extractParameterValue(const std::string& detail, |
| std::string& parameter, |
| std::string& value) |
| { |
| std::string allowedCharacters = ":/+._-%"; |
| parameter = ""; |
| value = ""; |
| int i = 0; |
| while (detail[i] != '=' && i < detail.length()) |
| { |
| parameter += detail[i]; |
| i++; |
| } |
| if (i == detail.length()) |
| return false; |
| i++; |
| while ((std::isalnum(detail[i]) || |
| allowedCharacters.find(detail[i]) != std::string::npos) && |
| i < detail.length()) |
| { |
| value += detail[i]; |
| i++; |
| } |
| if(parameter == "" || value == "") |
| return false; |
| return true; |
| } |
| |
| bool |
| processConfigurationDetail(const std::string& detail, |
| Logger& logger, |
| int lineNumber) |
| { |
| std::string parameter, value; |
| if (extractParameterValue(detail, parameter, value)) |
| { |
| if (parameter == "Name") |
| m_name = value; |
| else if (parameter == "ContentType") |
| m_contentType = boost::lexical_cast<int>(value); |
| else if (parameter == "FreshnessPeriod") |
| m_freshnessPeriod = time::milliseconds(boost::lexical_cast<int>(value)); |
| else if (parameter == "ContentDelay") |
| m_contentDelay = time::milliseconds(boost::lexical_cast<int>(value)); |
| else if (parameter == "ContentBytes") |
| m_contentBytes = boost::lexical_cast<int>(value); |
| else if (parameter == "Content") |
| m_content = value; |
| else |
| logger.log("Line " + boost::lexical_cast<std::string>(lineNumber) + |
| " \t- Invalid Parameter='" + parameter + "'", false, true); |
| } |
| else |
| { |
| logger.log("Line " + boost::lexical_cast<std::string>(lineNumber) + |
| " \t- Improper Traffic Configuration Line - " + detail, false, true); |
| return false; |
| } |
| return true; |
| } |
| |
| bool |
| checkTrafficDetailCorrectness() |
| { |
| return true; |
| } |
| |
| std::string m_name; |
| int m_contentType; |
| time::milliseconds m_freshnessPeriod; |
| int m_contentBytes; |
| time::milliseconds m_contentDelay; |
| std::string m_content; |
| int m_nInterestsReceived; |
| |
| }; |
| |
| void |
| usage() |
| { |
| |
| std::cout << "\nUsage: " << m_programName << " [options] <Traffic_Configuration_File>\n" |
| "Respond to Interest as per provided Traffic Configuration File\n" |
| "Multiple Prefixes can be configured for handling.\n" |
| "Set environment variable NDN_TRAFFIC_LOGFOLDER for redirecting output to a log.\n" |
| " [-d interval] - set delay before responding to interest in milliseconds\n" |
| " [-c count] - specify maximum number of interests to be satisfied\n" |
| " [-h] - print help and exit\n\n"; |
| exit(1); |
| |
| } |
| |
| void |
| setMaximumInterests(int maximumInterests) |
| { |
| if (maximumInterests < 0) |
| usage(); |
| m_nMaximumInterests = maximumInterests; |
| } |
| |
| bool |
| hasError() const |
| { |
| return m_hasError; |
| } |
| |
| void |
| setContentDelay(int contentDelay) |
| { |
| if (contentDelay < 0) |
| usage(); |
| m_contentDelay = time::milliseconds(contentDelay); |
| } |
| |
| void |
| setConfigurationFile(char* configurationFile) |
| { |
| m_configurationFile = configurationFile; |
| } |
| |
| void |
| signalHandler() |
| { |
| logStatistics(); |
| m_logger.shutdownLogger(); |
| m_face.shutdown(); |
| m_ioService->stop(); |
| if (m_hasError) |
| exit(1); |
| else |
| exit(0); |
| } |
| |
| void |
| logStatistics() |
| { |
| m_logger.log("\n\n== Interest Traffic Report ==\n", false, true); |
| m_logger.log("Total Traffic Pattern Types = " + |
| boost::lexical_cast<std::string>(m_trafficPatterns.size()), false, true); |
| m_logger.log("Total Interests Received = " + |
| boost::lexical_cast<std::string>(m_nInterestsReceived), false, true); |
| if (m_nInterestsReceived < m_nMaximumInterests) |
| m_hasError = true; |
| for (int patternId = 0; patternId < m_trafficPatterns.size(); patternId++) |
| { |
| m_logger.log("\nTraffic Pattern Type #" + |
| boost::lexical_cast<std::string>(patternId + 1), false, true); |
| m_trafficPatterns[patternId].printTrafficConfiguration(m_logger); |
| m_logger.log("Total Interests Received = " + |
| boost::lexical_cast<std::string>( |
| m_trafficPatterns[patternId].m_nInterestsReceived) + "\n", false, true); |
| } |
| } |
| |
| bool |
| checkTrafficPatternCorrectness() |
| { |
| return true; |
| } |
| |
| void |
| parseConfigurationFile() |
| { |
| std::string patternLine; |
| std::ifstream patternFile; |
| m_logger.log("Analyzing Traffic Configuration File: " + m_configurationFile, true, true); |
| patternFile.open(m_configurationFile.c_str()); |
| if (patternFile.is_open()) |
| { |
| int patternId = 0; |
| int lineNumber = 0; |
| while (getline(patternFile, patternLine)) |
| { |
| lineNumber++; |
| if (std::isalpha(patternLine[0])) |
| { |
| DataTrafficConfiguration dataData; |
| bool shouldSkipLine = false; |
| patternId++; |
| if (dataData.processConfigurationDetail(patternLine, m_logger, lineNumber)) |
| { |
| while (getline(patternFile, patternLine) && std::isalpha(patternLine[0])) |
| { |
| lineNumber++; |
| if (!dataData.processConfigurationDetail(patternLine, |
| m_logger, |
| lineNumber)) |
| { |
| shouldSkipLine = true; |
| break; |
| } |
| } |
| lineNumber++; |
| } |
| else |
| shouldSkipLine = true; |
| if (!shouldSkipLine) |
| { |
| if (dataData.checkTrafficDetailCorrectness()) |
| m_trafficPatterns.push_back(dataData); |
| } |
| } |
| } |
| patternFile.close(); |
| if (!checkTrafficPatternCorrectness()) |
| { |
| m_logger.log("ERROR - Traffic Configuration Provided Is Not Proper- " + |
| m_configurationFile, false, true); |
| m_logger.shutdownLogger(); |
| exit(1); |
| } |
| m_logger.log("Traffic Configuration File Processing Completed\n", true, false); |
| for (patternId = 0; patternId < m_trafficPatterns.size(); patternId++) |
| { |
| m_logger.log("Traffic Pattern Type #" + |
| boost::lexical_cast<std::string>(patternId + 1), false, false); |
| m_trafficPatterns[patternId].printTrafficConfiguration(m_logger); |
| m_logger.log("", false, false); |
| } |
| } |
| else |
| { |
| m_logger.log("ERROR - Unable To Open Traffic Configuration File: " + |
| m_configurationFile, false, true); |
| m_logger.shutdownLogger(); |
| exit(1); |
| } |
| } |
| |
| void |
| initializeTrafficConfiguration() |
| { |
| if (boost::filesystem::exists(boost::filesystem::path(m_configurationFile))) |
| { |
| if (boost::filesystem::is_regular_file(boost::filesystem::path(m_configurationFile))) |
| { |
| parseConfigurationFile(); |
| } |
| else |
| { |
| m_logger.log("ERROR - Traffic Configuration File Is Not A Regular File: " + |
| m_configurationFile, false, true); |
| m_logger.shutdownLogger(); |
| exit(1); |
| } |
| } |
| else |
| { |
| m_logger.log("ERROR - Traffic Configuration File Does Not Exist: " + |
| m_configurationFile, false, true); |
| m_logger.shutdownLogger(); |
| exit(1); |
| } |
| } |
| |
| static std::string |
| getRandomByteString(int randomSize) |
| { |
| std::string randomString; |
| for (int i = 0; i < randomSize; i++) |
| randomString += static_cast<char>(std::rand() % 128); |
| return randomString; |
| } |
| |
| void |
| onInterest(const Name& name, const Interest& interest, int patternId) |
| { |
| if (m_nMaximumInterests < 0 || m_nInterestsReceived < m_nMaximumInterests) |
| { |
| Data data(interest.getName()); |
| |
| if (m_trafficPatterns[patternId].m_contentType >= 0) |
| data.setContentType(m_trafficPatterns[patternId].m_contentType); |
| |
| if (m_trafficPatterns[patternId].m_freshnessPeriod >= time::milliseconds(0)) |
| data.setFreshnessPeriod(m_trafficPatterns[patternId].m_freshnessPeriod); |
| |
| std::string content = ""; |
| if (m_trafficPatterns[patternId].m_contentBytes >= 0) |
| content = getRandomByteString(m_trafficPatterns[patternId].m_contentBytes); |
| if (m_trafficPatterns[patternId].m_content != "") |
| content = m_trafficPatterns[patternId].m_content; |
| |
| data.setContent(reinterpret_cast<const uint8_t*>(content.c_str()), content.length()); |
| m_keyChain.sign(data); |
| m_nInterestsReceived++; |
| m_trafficPatterns[patternId].m_nInterestsReceived++; |
| std::string logLine = "Interest Received - PatternType=" + |
| boost::lexical_cast<std::string>(patternId + 1); |
| logLine += ", GlobalID=" + boost::lexical_cast<std::string>(m_nInterestsReceived); |
| logLine += ", LocalID=" + |
| boost::lexical_cast<std::string>(m_trafficPatterns[patternId].m_nInterestsReceived); |
| logLine += ", Name=" + m_trafficPatterns[patternId].m_name; |
| m_logger.log(logLine, true, false); |
| if (m_trafficPatterns[patternId].m_contentDelay > time::milliseconds(-1)) |
| usleep(m_trafficPatterns[patternId].m_contentDelay.count() * 1000); |
| if (m_contentDelay > time::milliseconds(-1)) |
| usleep(m_contentDelay.count() * 1000); |
| m_face.put(data); |
| } |
| if (m_nMaximumInterests >= 0 && m_nInterestsReceived == m_nMaximumInterests) |
| { |
| logStatistics(); |
| m_logger.shutdownLogger(); |
| m_face.shutdown(); |
| m_ioService->stop(); |
| } |
| } |
| |
| void |
| onRegisterFailed(const ndn::Name& prefix, const std::string& reason, int patternId) |
| { |
| std::string logLine = ""; |
| logLine += "Prefix Registration Failed - PatternType=" + |
| boost::lexical_cast<std::string>(patternId + 1); |
| logLine += ", Name=" + m_trafficPatterns[patternId].m_name; |
| m_logger.log(logLine, true, true); |
| m_nRegistrationsFailed++; |
| if (m_nRegistrationsFailed == m_trafficPatterns.size()) |
| { |
| m_hasError = true; |
| signalHandler(); |
| } |
| } |
| |
| void |
| run() |
| { |
| boost::asio::signal_set signalSet(*m_ioService, SIGINT, SIGTERM); |
| signalSet.async_wait(boost::bind(&NdnTrafficServer::signalHandler, this)); |
| m_logger.initializeLog(m_instanceId); |
| initializeTrafficConfiguration(); |
| if (m_nMaximumInterests == 0) |
| { |
| logStatistics(); |
| m_logger.shutdownLogger(); |
| return; |
| } |
| |
| for (int patternId = 0; patternId < m_trafficPatterns.size(); patternId++) |
| { |
| m_face.setInterestFilter(m_trafficPatterns[patternId].m_name, |
| bind(&NdnTrafficServer::onInterest, |
| this, _1, _2, |
| patternId), |
| bind(&NdnTrafficServer::onRegisterFailed, |
| this, _1, _2, |
| patternId)); |
| } |
| |
| try { |
| m_face.processEvents(); |
| } |
| catch (std::exception& e) { |
| m_logger.log("ERROR: " + static_cast<std::string>(e.what()), true, true); |
| m_logger.shutdownLogger(); |
| m_hasError = true; |
| m_ioService->stop(); |
| } |
| } |
| |
| private: |
| KeyChain m_keyChain; |
| std::string m_programName; |
| bool m_hasError; |
| std::string m_instanceId; |
| time::milliseconds m_contentDelay; |
| int m_nRegistrationsFailed; |
| Logger m_logger; |
| std::string m_configurationFile; |
| shared_ptr<boost::asio::io_service> m_ioService; |
| Face m_face; |
| std::vector<DataTrafficConfiguration> m_trafficPatterns; |
| int m_nMaximumInterests; |
| int m_nInterestsReceived; |
| }; |
| |
| } // namespace ndn |
| |
| int |
| main(int argc, char* argv[]) |
| { |
| std::srand(std::time(0)); |
| ndn::NdnTrafficServer ndnTrafficServer(argv[0]); |
| int option; |
| while ((option = getopt(argc, argv, "hc:d:")) != -1) { |
| switch (option) { |
| case 'h': |
| ndnTrafficServer.usage(); |
| break; |
| case 'c': |
| ndnTrafficServer.setMaximumInterests(atoi(optarg)); |
| break; |
| case 'd': |
| ndnTrafficServer.setContentDelay(atoi(optarg)); |
| break; |
| default: |
| ndnTrafficServer.usage(); |
| break; |
| } |
| } |
| |
| argc -= optind; |
| argv += optind; |
| |
| if (argv[0] == 0) |
| ndnTrafficServer.usage(); |
| |
| ndnTrafficServer.setConfigurationFile(argv[0]); |
| ndnTrafficServer.run(); |
| |
| if (ndnTrafficServer.hasError()) |
| return 1; |
| else |
| return 0; |
| } |