diff --git a/src/components/zcanreceiver/socket_can/socket_can.cpp b/src/components/zcanreceiver/socket_can/socket_can.cpp index b2e6815..8c57b41 100644 --- a/src/components/zcanreceiver/socket_can/socket_can.cpp +++ b/src/components/zcanreceiver/socket_can/socket_can.cpp @@ -31,7 +31,12 @@ SocketCan::SocketCanError_t SocketCan::sendFrame(shared_ptr fram logger->debug("TX:{}", frame->toString()); return writeFrame(canframe); } + +void SocketCan::reStartCanInterface() { startCanInterface(); } + SocketCan::SocketCanError_t SocketCan::writeFrame(const canfd_frame_t &_frame) { + lock_guard lock(thislock); + if (!m_canBusIsReady) { return kDeviceBusy; } @@ -51,11 +56,26 @@ void SocketCan::initialize(shared_ptr socketCanConfig) { // ZCHECK(socketCanConfig, "socketCanConfig is null"); m_socketCanConfig = socketCanConfig; - socketcanInitialize(); + startCanInterface(); + // start auto restart thread + m_autoRestartThread.reset(new Thread("SocketCanAutoRestartThread", [this]() { + ThisThread thisThread; + while (!thisThread.getExitFlag()) { + if (m_canErrorFlag) { + startCanInterface(); + } + thisThread.sleepForMs(30); + } + })); } SocketCan::~SocketCan() { - if (m_thread) { - endListenThread(); + if (m_readThread) { + m_readThread->join(); + m_readThread = nullptr; + } + if (m_autoRestartThread) { + m_autoRestartThread->join(); + m_autoRestartThread = nullptr; } if (m_socketCanFd > 0) { close(m_socketCanFd); @@ -63,37 +83,47 @@ SocketCan::~SocketCan() { } } -void SocketCan::startListen() { startListenThread(); } +void SocketCan::setCanBitrate(string canName, int bitrate) { + logger->info("set can:{} bitrate:{}", canName, bitrate); + dosystem(fmt::format("ip link set {} down", canName)); + dosystem(fmt::format("ip link set {} type can bitrate {}", canName, bitrate)); + dosystem(fmt::format("ip link set {} up", canName)); +} +bool SocketCan::findCan(string canName) { return access(fmt::format("/sys/class/net/{}", canName).c_str(), F_OK) == 0; } + +void SocketCan::dosystem(string cmd) { + logger->info("do cmd:{}", cmd); + system(cmd.c_str()); +} -void SocketCan::socketcanReInitialize() {} +void SocketCan::startCanInterface() { + lock_guard lock(thislock); -void SocketCan::startListenThread() { - if (m_thread) { - endListenThread(); - return; + // 尝试恢复CAN总线 + if (m_readThread) { + logger->warn("try to recover can bus............................................"); + m_readThread->join(); + m_readThread = nullptr; + + if (m_socketCanFd > 0) close(m_socketCanFd); + m_socketCanFd = -1; } - logger->info("startListenThread"); - m_thread.reset(new Thread("SocketCanThread", [this]() { - /** - * @brief call socketCanReadThreadLoop - */ + socketcanInitialize(); + + m_readThread.reset(new Thread("SocketCanThread", [this]() { + usleep(10 * 1000); socketCanReadThreadLoop(); })); - m_canBusIsReady = true; - m_autoRestartThread.reset(new Thread("SocketCanAutoRestartThread", [this]() { monitorLoop(); })); -} -void SocketCan::endListenThread() { - logger->info("endListenThread"); - if (m_thread) { - m_thread->join(); - m_thread = nullptr; - } + m_canErrorFlag = false; + m_canBusIsReady = true; + logger->warn("start can bus ok............................................"); } void SocketCan::socketcanInitialize() { // setCanBitrate(canName, bitrate); + lock_guard lock(thislock); struct ifreq ifr = {0}; struct sockaddr_can addr = {0}; @@ -108,8 +138,7 @@ void SocketCan::socketcanInitialize() { return; } - if (m_thread) endListenThread(); - if (m_socketCanFd > 0) socketcanDeInitialize(); + if (m_socketCanFd > 0) close(m_socketCanFd); logger->info("socketcanInitialize,canName:{},canBaudrate:{}", m_socketCanConfig->m_canName, m_socketCanConfig->m_canBaudrate); @@ -171,138 +200,6 @@ error: } return; } -void SocketCan::socketcanDeInitialize() {} - -void SocketCan::canReadThread() {} - -void SocketCan::setCanBitrate(string canName, int bitrate) { - logger->info("set can:{} bitrate:{}", canName, bitrate); - dosystem(fmt::format("ip link set {} down", canName)); - dosystem(fmt::format("ip link set {} type can bitrate {}", canName, bitrate)); - dosystem(fmt::format("ip link set {} up", canName)); -} -bool SocketCan::findCan(string canName) { return access(fmt::format("/sys/class/net/{}", canName).c_str(), F_OK) == 0; } - -void SocketCan::dosystem(string cmd) { - logger->info("do cmd:{}", cmd); - system(cmd.c_str()); -} - -#define TEST_CANDRIVER_DO(exptr) \ - do { \ - if ((exptr) != 0) { \ - logger->error("do {} failed,{}", #exptr, strerror(errno)); \ - if (socketId > 0) { \ - close(socketId); \ - socketId = -1; \ - return -1; \ - } \ - } \ - } while (0) - -int SocketCan::dumpCanDriverInfo(string canDevice, int baudrate) { - int socketId = -1; - struct ifreq ifr = {0}; - struct sockaddr_can addr = {0}; - can_filter_t zerofilter = {0}; - int recv_own_msgs = 0; /* 0 = disabled (default), 1 = enabled */ - int localloop = 0; - int ret = 0; - fd_set readfds, readFileDescriptors; - timeval waitTime; - int loopTime = 0; - logger_t logger = GET_LOGGER(SocketCan); - - if (!findCan(canDevice)) { - logger->error("can:{} not found", canDevice); - return -1; - } - - // set bitrate - system(fmt::format("ip link set {} down", canDevice).c_str()); - system(fmt::format("ip link set {} type can bitrate {}", canDevice, baudrate).c_str()); - system(fmt::format("ip link set {} up", canDevice).c_str()); - - socketId = socket(PF_CAN, SOCK_RAW, CAN_RAW); - if (socketId < 0) { - logger->error("socket failed,{}", strerror(errno)); - return -1; - } - - // 设置依靠名字绑定CAN设备 - strcpy(ifr.ifr_name, canDevice.c_str()); - TEST_CANDRIVER_DO(ioctl(socketId, SIOCGIFINDEX, &ifr)); - - // BIND - addr.can_family = AF_CAN; - addr.can_ifindex = ifr.ifr_ifindex; - TEST_CANDRIVER_DO(bind(socketId, (struct sockaddr *)&addr, sizeof(addr))); - - // 设置过滤器 - TEST_CANDRIVER_DO(setsockopt(socketId, SOL_CAN_RAW, CAN_RAW_FILTER, &zerofilter, sizeof(zerofilter))); - - // 打开回环 - localloop = 1; /* 0 = disabled (default), 1 = enabled */ - TEST_CANDRIVER_DO(setsockopt(socketId, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &localloop, sizeof(localloop))); - - recv_own_msgs = 1; /* 0 = disabled (default), 1 = enabled */ - TEST_CANDRIVER_DO(setsockopt(socketId, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &recv_own_msgs, sizeof(recv_own_msgs))); - - // 发送一条消息 - canfd_frame_t txframe = {0}; - canfd_frame_t rxcanframe = {0}; - - int rxloopcount = 0; - txframe.can_id = 0x12; - txframe.len = 8; - txframe.data[0] = 0x01; - - logger->info("write one frame"); - ret = write(socketId, &txframe, txframe.len + 8); - if (ret < 0) { - logger->error("write failed,{}", strerror(errno)); - close(socketId); - return -1; - } - - FD_ZERO(&readFileDescriptors); - FD_SET(socketId, &readFileDescriptors); - while (loopTime < 10) { - loopTime++; - waitTime.tv_sec = 0; - waitTime.tv_usec = 100000; // 100ms - readfds = readFileDescriptors; - - int n = select(socketId + 1, &readfds, 0, 0, &waitTime); - if (n <= 0) continue; - - int readsize = read(socketId, &rxcanframe, sizeof(rxcanframe)); - if (readsize < 0) { - logger->error("read failed,{}", strerror(errno)); - close(socketId); - return -1; - } - - if (rxcanframe.can_id == txframe.can_id && // - rxcanframe.data[0] == txframe.data[0] && // - rxcanframe.len == txframe.len) { - rxloopcount++; - logger->info("rx loop frame {}", rxloopcount); - } - } - - logger->info("Test Can Driver OK!"); - logger->info("=================Can Driver Info ================="); - logger->info("canDevice :{}", canDevice); - logger->info("baudrate :{}", baudrate); - logger->info("loop count :{}", rxloopcount); - logger->info("shouldEnableLoop:{}", rxloopcount == 1); - logger->info("=================================================="); - - close(socketId); - - return 0; -} void SocketCan::socketCanReadThreadLoop() { ThisThread thisThread; @@ -334,6 +231,7 @@ void SocketCan::socketCanReadThreadLoop() { // /usr/include/linux/can/error.h // x can-bus error event 20000004,0004000000000086 logger->error("rx can-bus error event {:x},{}", canframe.can_id, StringUtils().bytesToString(canframe.data, canframe.len)); + m_canErrorFlag = true; break; } else { shared_ptr socketCanFrame = SocketCanFrame::createFrame(canframe); @@ -342,39 +240,5 @@ void SocketCan::socketCanReadThreadLoop() { } } - m_canErrorFlag = true; m_canBusIsReady = false; -} - -void SocketCan::monitorLoop() { - ThisThread thisThread; - while (!thisThread.getExitFlag()) { - if (m_canErrorFlag) { - // 尝试恢复CAN总线 - logger->warn("try to recover can bus............................................"); - if (m_thread) { - m_thread->join(); - m_thread = nullptr; - } - logger->warn("close can bus fd:{}", m_socketCanFd); - - close(m_socketCanFd); - m_socketCanFd = -1; - - logger->warn("re init can bus"); - socketcanInitialize(); - - m_thread.reset(new Thread("SocketCanThread", [this]() { - usleep(10 * 1000); - socketCanReadThreadLoop(); - })); - - m_canErrorFlag = false; - m_canBusIsReady = true; - - logger->warn("recover can bus ok............................................"); - } - - thisThread.sleepForMs(30); - } -} +} \ No newline at end of file diff --git a/src/components/zcanreceiver/socket_can/socket_can.hpp b/src/components/zcanreceiver/socket_can/socket_can.hpp index 2ce7979..c8ba229 100644 --- a/src/components/zcanreceiver/socket_can/socket_can.hpp +++ b/src/components/zcanreceiver/socket_can/socket_can.hpp @@ -60,22 +60,8 @@ class SocketCan : public enable_shared_from_this { ENABLE_LOGGER(SocketCan); public: - typedef enum { - kTxIdle, - kTxing, - } TxState_t; - - class TxState { - public: - canfd_frame_t txbuf; - TxState_t txState = kTxIdle; - int txAckCount = 0; - zsteady_tp txLoopMessageRxTicket; - recursive_mutex lock; - }; - private: - unique_ptr m_thread; + unique_ptr m_readThread; unique_ptr m_autoRestartThread; int m_socketCanFd = -1; bool m_startListen = false; @@ -84,6 +70,7 @@ class SocketCan : public enable_shared_from_this { bool m_canErrorFlag = false; shared_ptr m_socketCanConfig; + recursive_mutex thislock; public: typedef enum { @@ -128,30 +115,15 @@ class SocketCan : public enable_shared_from_this { void initialize(shared_ptr socketCanConfig); bool isError() { return false; } - - void startListen(); - SocketCanError_t sendFrame(shared_ptr frame, int overtime); SocketCanError_t sendFrame(shared_ptr frame); - /** - * @brief 使用这个方法测试目标设备CAN驱动的行为,需要CAN总线上,除了目标设备外,至少有一个正常工作的节点。 - * - * @param canDevice - * @param baudrate - * @return int - */ - static int dumpCanDriverInfo(string canDevice, int baudrate); + void reStartCanInterface(); + private: void socketcanInitialize(); - void socketcanDeInitialize(); - void socketcanReInitialize(); - - void startListenThread(); - void endListenThread(); void socketCanReadThreadLoop(); - - void monitorLoop(); + void startCanInterface(); private: void dosystem(string cmd); @@ -159,8 +131,5 @@ class SocketCan : public enable_shared_from_this { static bool findCan(string canName); SocketCanError_t writeFrame(const canfd_frame_t &frame); - - private: - void canReadThread(); }; } // namespace iflytop \ No newline at end of file diff --git a/src/components/zcanreceiver/zcanreceiverhost.cpp b/src/components/zcanreceiver/zcanreceiverhost.cpp index 5ec76cc..6723fc3 100644 --- a/src/components/zcanreceiver/zcanreceiverhost.cpp +++ b/src/components/zcanreceiver/zcanreceiverhost.cpp @@ -20,12 +20,30 @@ void ZCanReceiverHost::initialize(string can_if_name, int baudrate) { m_can_if_name = can_if_name; m_baudrate = baudrate; m_enablLoopback = false; - resetSocketCan(); + + auto socketCanConfig = make_shared(); + socketCanConfig->enablLoopback = m_enablLoopback; // 根据 SocketCan::dumpCanDriverInfo() 的输出,确定该标志位是false还是true + socketCanConfig->m_canName = m_can_if_name; + socketCanConfig->m_canBaudrate = m_baudrate; + socketCanConfig->m_canfilters = {}; + + logger->debug("initialize() m_canName:{} {}", socketCanConfig->m_canName, socketCanConfig->m_canBaudrate); + + m_socketCan.reset(new SocketCan()); + m_socketCan->initialize(socketCanConfig); + m_socketCan->onSocketCanFrame.connect([this](shared_ptr canframe) { // + if (canframe->getId() == m_hostId) { + logger->debug("TX RAW: {} SUC", canframe->toString()); + return; + } + logger->debug("RX RAW: {}", canframe->toString()); + processRx(canframe); + }); } void ZCanReceiverHost::registerListener(onpacket_t onpacket) { m_onpacket = onpacket; } void ZCanReceiverHost::sendPacket(uint8_t *packet, size_t len) { - logger->info("TX: {}", StringUtils().bytesToString(packet, len)); + logger->debug("TX: {}", StringUtils().bytesToString(packet, len)); int npacket = len / 7 + (len % 7 == 0 ? 0 : 1); if (npacket > 255) { return; @@ -58,7 +76,7 @@ bool ZCanReceiverHost::sendPacketSub(int npacket, int packetIndex, uint8_t *pack // printf("\n"); shared_ptr frame = SocketCanFrame::createStdDataFrame(id, txdata, len + 1); - logger->info("TX RAW :{}", frame->toString()); + logger->debug("TX RAW :{}", frame->toString()); m_socketCan->sendFrame(frame, overtimems); return true; } @@ -101,29 +119,9 @@ void ZCanReceiverHost::processRx(shared_ptr frame) { /** * @brief 接收到最后一包数据 */ - logger->info("RX: {}", StringUtils().bytesToString(rxbuf->m_rxdata, rxbuf->m_rxdataLen)); + logger->debug("RX: {}", StringUtils().bytesToString(rxbuf->m_rxdata, rxbuf->m_rxdataLen)); processOnePacket(rxbuf, rxbuf->m_rxdata, rxbuf->m_rxdataLen); } } -void ZCanReceiverHost::resetSocketCan() { - auto socketCanConfig = make_shared(); - socketCanConfig->enablLoopback = m_enablLoopback; // 根据 SocketCan::dumpCanDriverInfo() 的输出,确定该标志位是false还是true - socketCanConfig->m_canName = m_can_if_name; - socketCanConfig->m_canBaudrate = m_baudrate; - socketCanConfig->m_canfilters = {}; - - logger->info("initialize() m_canName:{} {}", socketCanConfig->m_canName, socketCanConfig->m_canBaudrate); - - m_socketCan.reset(new SocketCan()); - m_socketCan->initialize(socketCanConfig); - m_socketCan->startListen(); - m_socketCan->onSocketCanFrame.connect([this](shared_ptr canframe) { // - if (canframe->getId() == m_hostId) { - logger->info("TX RAW: {} SUC", canframe->toString()); - return; - } - logger->info("RX RAW: {}", canframe->toString()); - processRx(canframe); - }); -} \ No newline at end of file +void ZCanReceiverHost::restartCanInterface() { m_socketCan->reStartCanInterface(); } diff --git a/src/components/zcanreceiver/zcanreceiverhost.hpp b/src/components/zcanreceiver/zcanreceiverhost.hpp index 2b5353c..24cb35c 100644 --- a/src/components/zcanreceiver/zcanreceiverhost.hpp +++ b/src/components/zcanreceiver/zcanreceiverhost.hpp @@ -47,13 +47,13 @@ class ZCanReceiverHost { void initialize(string can_if_name, int baudrate); void sendPacket(uint8_t *packet, size_t len); void registerListener(onpacket_t onpacket); + void restartCanInterface(); private: bool sendPacketSub(int npacket, int packetIndex, uint8_t *packet, size_t len, int overtimems); void processRx(shared_ptr frame); void processOnePacket(CanPacketRxBuffer *rxbuf, uint8_t *packet, size_t len); - void resetSocketCan(); void sendcmd(uint16_t packetindex, uint16_t cmdid, uint8_t subcmdid, uint8_t *data, size_t len); }; diff --git a/src/extapi_service.cpp b/src/extapi_service.cpp index ce1ba74..d80d9cf 100644 --- a/src/extapi_service.cpp +++ b/src/extapi_service.cpp @@ -4,6 +4,7 @@ #include "configs/version.hpp" #include "ixwebsocket/IXWebSocketServer.h" #include "utils/stringutils.hpp" +#include "utils/urlparser.hpp" // #include "iflytop/components/zcanreceiver/zcanreceiverhost.hpp" // #include "iflytop/core/components/stringutils.hpp" @@ -16,7 +17,6 @@ using namespace nlohmann; using namespace ix; #define BIND - namespace iflytop {}; void ExtAPIService::initialize(string can_if_name, int baudrate) { GET_TO_SERVICE(m_zconfig); @@ -26,8 +26,29 @@ void ExtAPIService::initialize(string can_if_name, int baudrate) { initCanPassthroughServer(); }; +void ExtAPIService::processRXData(weak_ptr webSocket, const ix::WebSocketMessagePtr &msg) {} void ExtAPIService::initCanPassthroughServer() { + m_httpServer.reset(new HttpServer(19004, "0.0.0.0")); + m_httpServer->listen(); + m_httpServer->start(); + m_httpServer->setOnConnectionCallback( // + [this](HttpRequestPtr request, std::shared_ptr connectionState) -> HttpResponsePtr { + HTTP_URL httpurl = URLParser::Parse(request->uri); + + if (httpurl.getPath() == "/restartCanif") { + logger->info("call {}", request->uri); + m_zcanreceiverhost->restartCanInterface(); + } else if (httpurl.getPath() == "/killself") { + logger->info("call {}", request->uri); + exit(1); + } else { + logger->warn("call unknown {}", httpurl.getPath()); + } + + return std::make_shared(200, "", HttpErrorCode::Ok, WebSocketHttpHeaders(), ""); + }); + /** * * protocol: websocket @@ -41,20 +62,28 @@ void ExtAPIService::initCanPassthroughServer() { auto ws = webSocket.lock(); if (!ws) return; - ws->setOnMessageCallback([this, webSocket](const ix::WebSocketMessagePtr &msg) { - if (msg->type == ix::WebSocketMessageType::Message) { - logger->info("down can bin -> {}({})", msg->str, msg->wireSize); - vector rxbyte; - StringUtils().hexStringToBytes(msg->str, "", rxbyte); - m_zcanreceiverhost->sendPacket(rxbyte.data(), rxbyte.size()); + ws->setOnMessageCallback([this, webSocket](const ix::WebSocketMessagePtr &msg) { // + try { + if (msg->type == ix::WebSocketMessageType::Message) { + logger->info("websocket-ch -> {}({})", msg->str, msg->wireSize); + string hexStr = msg->str; + vector rxbyte; + StringUtils().hexStringToBytes(hexStr, "", rxbyte); + m_zcanreceiverhost->sendPacket(rxbyte.data(), rxbyte.size()); + } else if (msg->type == ix::WebSocketMessageType::Open) { + logger->info("websocket-ch on open"); + } + } catch (const std::exception &e) { + logger->error("catch error: {}", e.what()); } }); }); m_zcanreceiverhost->registerListener([this](uint8_t fromboardid, uint8_t *packet, size_t len) { - string hexStr = StringUtils().bytesToString((uint8_t *)packet, len); - logger->info("up can bin -> {}({})", hexStr, len); - auto clients = m_canPassthroughServer->getClients(); + string hexStr = StringUtils().bytesToString((uint8_t *)packet, len); + auto clients = m_canPassthroughServer->getClients(); + logger->info("websocket-ch <- {}({})", hexStr, len); + for (auto &each : clients) { if (each) each->sendText(hexStr); } diff --git a/src/extapi_service.hpp b/src/extapi_service.hpp index ab43350..4cc3f3c 100644 --- a/src/extapi_service.hpp +++ b/src/extapi_service.hpp @@ -22,11 +22,13 @@ // #include "iflytoplinuxsdk/src/iflytop/core/components/zservice_container/zservice_container.hpp" // +#include "components/zcanreceiver/zcanreceiverhost.hpp" #include "components/zservice_container/zservice_container.hpp" #include "configs/gconfig.hpp" +#include "ixwebsocket/IXHttp.h" +#include "ixwebsocket/IXHttpServer.h" #include "ixwebsocket/IXWebSocketServer.h" #include "spdlogfactory/logger.hpp" -#include "components/zcanreceiver/zcanreceiverhost.hpp" // // @@ -42,12 +44,14 @@ namespace iflytop { using namespace std; using namespace core; +using namespace ix; class ExtAPIService { ENABLE_LOGGER(ExtAPIService); shared_ptr m_zconfig; - shared_ptr m_canPassthroughServer; // 19003 + shared_ptr m_canPassthroughServer; // 19005 + shared_ptr m_httpServer;//19004 shared_ptr m_zcanreceiverhost; public: @@ -56,5 +60,6 @@ class ExtAPIService { private: void initCanPassthroughServer(); + void processRXData(weak_ptr webSocket, const ix::WebSocketMessagePtr &msg); }; } // namespace iflytop \ No newline at end of file diff --git a/src/main_control_service.hpp b/src/main_control_service.hpp index d007ad4..8fde3e1 100644 --- a/src/main_control_service.hpp +++ b/src/main_control_service.hpp @@ -23,8 +23,8 @@ // #include "iflytoplinuxsdk/src/iflytop/core/components/zservice_container/zservice_container.hpp" // -#include "spdlogfactory/logger.hpp" #include "extapi_service.hpp" +#include "spdlogfactory/logger.hpp" // // @@ -48,7 +48,7 @@ using namespace core; class MainControlService : public enable_shared_from_this { ENABLE_LOGGER(MainControlService); - ExtAPIService m_extAPIService; + ExtAPIService m_extAPIService; public: MainControlService() {}; diff --git a/src/utils/urlparser.hpp b/src/utils/urlparser.hpp new file mode 100644 index 0000000..69ed8fc --- /dev/null +++ b/src/utils/urlparser.hpp @@ -0,0 +1,184 @@ +#pragma once + +#include +#include +#include +#include + +/** + * @brief + * + * string input_url = "http://127.0.0.1:40000/path_1/path_2/path_3/foo.git?test=1&test2=3"; + * //string input_url = "http://127.0.0.1/path_1/path_2/path_3/foo.git?test=1&test2=3"; + * //string input_url = "http://127.0.0.1/path_1/path_2/path_3"; + * //string input_url = "/path_1/path_2/path_3/?test=1&test2=3"; + * //string input_url = "https://127.0.0.1:40000/path_1/path_2/path_3/foo.git?test=&test2=3"; + * + * Usage + * Just include header file. + * + * #include "../src/url_parser.h" + * + * URLParser::HTTP_URL http_url = URLParser::Parse(input_url); + * + * std::cout << "scheme:[" << http_url.scheme << "]" << std::endl; + * std::cout << "host:[" << http_url.host << "]" << std::endl; + * std::cout << "port:[" << http_url.port << "]" << std::endl; + * + * Return + * class URLParser + * { + * public: + * struct HTTP_URL + * { + * string scheme; // Defined porotocol on URL. (http, ssh, ftp... etc.) + * string host; // Host name or ip address. + * string port; // Port + * std::vector path; // It contains path information that partitioned. (path_1, path_2...) + * string query_string; // Full of query string. + * std::unordered_map query; // It conatains query information. That made up of key, value pair. + * }; + * } + * + */ + +namespace iflytop { +using namespace std; +class URLParserFunction { + public: + static bool FindKeyword(const string& input_url, size_t& st, size_t& before, const string& delim, string& result) { + char temp[1024] = { + 0, + }; + size_t temp_st = st; + memcpy(&temp_st, &st, sizeof(temp_st)); + + st = input_url.find(delim, before); + if (st == string::npos) { + st = temp_st; + return false; + } + + memcpy(&temp[0], &input_url[before], st - before); + before = st + delim.length(); + + result = string(temp); + // if (result.empty()) return false; + return true; + }; + + static bool SplitQueryString(const string& str, const string& delim, string& key, string& value) { + char first[1024] = { + 0, + }; + char second[1024] = { + 0, + }; + + size_t st = str.find(delim, 0); + + memcpy(first, &str[0], st); + memcpy(second, &str[st + 1], str.length() - st); + + key = string(first); + value = string(second); + + return true; + }; +}; + +class HTTP_URL { + public: + string raw; + string scheme; + string host; + string port; + std::vector path; + string query_string; + std::unordered_map query; + + string getPath() { + string path; + for (auto& p : this->path) { + path += "/"; + path += p; + } + return path; + } + + void dump() { + printf("raw:%s\n", raw.c_str()); + printf("scheme:%s\n", scheme.c_str()); + printf("host:%s\n", host.c_str()); + printf("port:%s\n", port.c_str()); + printf("path:%s\n", getPath().c_str()); + printf("query_string:%s\n", query_string.c_str()); + for (auto& q : query) { + printf("query:%s=%s\n", q.first.c_str(), q.second.c_str()); + } + printf("=\n"); + } +}; + +class URLParser { + public: + static HTTP_URL Parse(const string& input_url) { + HTTP_URL http_url; + http_url.raw = input_url; + + size_t st = 0; + size_t before = 0; + + URLParserFunction::FindKeyword(input_url, st, before, "://", http_url.scheme); + URLParserFunction::FindKeyword(input_url, st, before, "/", http_url.host); + + size_t temp_st = 0; + size_t temp_before = 0; + string temp_ip; + string temp_port; + + if (true == URLParserFunction::FindKeyword(http_url.host, temp_st, temp_before, ":", temp_ip)) { + http_url.port = string(&http_url.host[temp_before]); + http_url.host = temp_ip; + } + + while (true) { + string path; + if (false == URLParserFunction::FindKeyword(input_url, st, before, "/", path)) break; + http_url.path.push_back(path); + } + + string path; + if (false == URLParserFunction::FindKeyword(input_url, st, before, "?", path)) { + path = string(&input_url[st + 1]); + http_url.path.push_back(path); + return http_url; + } + http_url.path.push_back(path); + + if (st < input_url.length()) { + http_url.query_string = string(&input_url[st + 1]); + if (false == http_url.query_string.empty()) { + string query; + st = 0; + before = 0; + + while (true) { + string key, value; + + if (false == URLParserFunction::FindKeyword(http_url.query_string, st, before, "&", query)) { + URLParserFunction::SplitQueryString(string(&http_url.query_string[before]), "=", key, value); + http_url.query.insert(std::unordered_map::value_type(key, value)); + break; + } + + URLParserFunction::SplitQueryString(query, "=", key, value); + http_url.query.insert(std::unordered_map::value_type(key, value)); + } + } + } + + return http_url; + }; +}; +} // namespace iflytop