diff --git a/CMakeLists.txt b/CMakeLists.txt index e506d1c..8e860da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,9 +17,10 @@ endif() set( INCLUDE_PATH "${PROJECT_SOURCE_DIR}/include" ) set( SOURCE_PATH "${PROJECT_SOURCE_DIR}/src" ) -set(SOURCE_FILES main.cpp src/TcpSocket.cpp include/frnetlib/TcpSocket.h src/TcpListener.cpp include/frnetlib/TcpListener.h src/Socket.cpp include/frnetlib/Socket.h src/Packet.cpp include/frnetlib/Packet.h include/frnetlib/NetworkEncoding.h src/SocketSelector.cpp include/frnetlib/SocketSelector.h src/HttpSocket.cpp include/frnetlib/HttpSocket.h src/HttpRequest.cpp include/frnetlib/HttpRequest.h src/HttpResponse.cpp include/frnetlib/HttpResponse.h src/Http.cpp include/frnetlib/Http.h src/SSLSocket.cpp include/frnetlib/SSLSocket.h src/SSLListener.cpp include/frnetlib/SSLListener.h include/frnetlib/SSLContext.h src/SocketReactor.cpp include/frnetlib/SocketReactor.h include/frnetlib/Packetable.h include/frnetlib/Listener.h) +set(SOURCE_FILES main.cpp src/TcpSocket.cpp include/frnetlib/TcpSocket.h src/TcpListener.cpp include/frnetlib/TcpListener.h src/Socket.cpp include/frnetlib/Socket.h src/Packet.cpp include/frnetlib/Packet.h include/frnetlib/NetworkEncoding.h src/SocketSelector.cpp include/frnetlib/SocketSelector.h src/HttpSocket.cpp include/frnetlib/HttpSocket.h src/HttpRequest.cpp include/frnetlib/HttpRequest.h src/HttpResponse.cpp include/frnetlib/HttpResponse.h src/Http.cpp include/frnetlib/Http.h src/SSLSocket.cpp include/frnetlib/SSLSocket.h src/SSLListener.cpp include/frnetlib/SSLListener.h include/frnetlib/SSLContext.h src/SocketReactor.cpp include/frnetlib/SocketReactor.h include/frnetlib/Packetable.h include/frnetlib/Listener.h src/URL.cpp include/frnetlib/URL.h) include_directories(include) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -m64 -fPIC -std=c++14 -pthread -lmbedtls -lmbedx509 -lmbedcrypto") # Set the library output directory set( LIBRARY_OUTPUT_PATH "${PROJECT_BINARY_DIR}/lib" ) diff --git a/include/frnetlib/URL.h b/include/frnetlib/URL.h new file mode 100644 index 0000000..0f7d69e --- /dev/null +++ b/include/frnetlib/URL.h @@ -0,0 +1,123 @@ +// +// Created by fred.nicolson on 23/05/17. +// + +#ifndef FRNETLIB_URLPARSER_H +#define FRNETLIB_URLPARSER_H +#include +#include + +//Note, a URL looks like this: scheme:[//host[:port]][/path][?query][#fragment] + +class URL +{ +public: + enum Scheme + { + HTTP = 0, + HTTPS = 1, + FTP = 2, + MAILTO = 3, + IRC = 4, + SFTP = 5, + Unknown = 6, + }; + + /*! + * Constructors + */ + URL() = default; + URL(const std::string &url); + + /*! + * Parses a given URL, extracting its various components + * + * @param url The URL to parse + */ + void parse(std::string url); + + /*! + * Get the URL scheme + */ + inline Scheme get_scheme() + { + return scheme; + } + + /* + * Get the URL host + */ + inline const std::string &get_host() + { + return host; + } + + /* + * Get the URL port + */ + inline const std::string &get_port() + { + return port; + } + + /* + * Get the URL path + */ + inline const std::string &get_path() + { + return path; + } + + /* + * Get the URL query + */ + inline const std::string &get_query() + { + return query; + } + + /* + * Get the URL fragment + */ + inline const std::string &get_fragment() + { + return fragment; + } + + /*! + * Converts a string to a scheme enum. + * + * @param scheme The string scheme to convert + * @return The associated scheme enum value. Scheme::Unknown on failure. + */ + static Scheme string_to_scheme(const std::string &scheme); + + /*! + * Converts a scheme enum value to a string + * + * @param scheme The scheme value to convert + * @return The string version + */ + static const std::string &scheme_to_string(Scheme scheme); + +private: + /*! + * Converts a string to lower case + * + * @param str The string to convert + * @return The converted string + */ + static std::string to_lower(const std::string &str); + + //State + Scheme scheme; + std::string host; + std::string port; + std::string path; + std::string query; + std::string fragment; + static std::unordered_map scheme_string_map; +}; + + +#endif //FRNETLIB_URLPARSER_H diff --git a/src/URL.cpp b/src/URL.cpp new file mode 100644 index 0000000..b9a7409 --- /dev/null +++ b/src/URL.cpp @@ -0,0 +1,127 @@ +// +// Created by fred.nicolson on 23/05/17. +// + +#include +#include +#include +#include "frnetlib/URL.h" + +std::unordered_map URL::scheme_string_map = { + {"http", URL::HTTP}, + {"https", URL::HTTPS}, + {"sftp", URL::FTP}, + {"mailto", URL::MAILTO}, + {"irc", URL::IRC}, + {"sftp", URL::SFTP}, + {"unknown", URL::Unknown} +}; + +URL::URL(const std::string &url) +{ + parse(url); +} + + +void URL::parse(std::string url) +{ + size_t parse_offset = 0; + size_t pos = 0; + + //Check to see if a scheme exists + pos = url.find("://"); + if(pos != std::string::npos) + { + auto scheme_pos = scheme_string_map.find(to_lower(url.substr(0, pos))); + scheme = (scheme_pos == scheme_string_map.end()) ? URL::Unknown : scheme_pos->second; + parse_offset = pos + 3; + } + + //Check to see if there's a port + pos = url.find(":", parse_offset); + if(pos != std::string::npos) + { + //Store host + host = url.substr(parse_offset, pos - parse_offset); + parse_offset += host.size(); + + //Find end of port + size_t port_end = url.find("/", parse_offset); + port_end = (port_end == std::string::npos) ? url.size() : port_end; + port = url.substr(pos + 1, port_end - pos - 1); + parse_offset = port_end + 1; + } + else + { + //Store host + pos = url.find("/", parse_offset); + pos = (pos != std::string::npos) ? pos : url.find("?", parse_offset); + pos = (pos != std::string::npos) ? pos : url.find("#", parse_offset); + pos = (pos != std::string::npos) ? pos : url.size(); + host = url.substr(parse_offset, pos - parse_offset); + parse_offset = pos + 1; + } + + //Exit if done + if(parse_offset >= url.size()) + return; + + //Extract the path + pos = url.find("?", parse_offset); + if(pos != std::string::npos) + { + path = url.substr(parse_offset, pos - parse_offset); + parse_offset = pos + 1; + } + else + { + pos = url.find("#", parse_offset); + pos = (pos != std::string::npos) ? pos : url.find("?", parse_offset); + pos = (pos != std::string::npos) ? pos : url.size(); + path = url.substr(parse_offset, pos - parse_offset); + parse_offset = pos + 1; + } + + //Extract the query + pos = url.find("#", parse_offset - 1); + if(pos != std::string::npos) + { + if(pos + 1 != parse_offset) + query = url.substr(parse_offset, pos - parse_offset); + fragment = url.substr(pos + 1, url.size() - pos - 1); + } + else + { + if(parse_offset >= url.size()) + return; + query = url.substr(parse_offset, url.size() - parse_offset); + } + + return; +} + +URL::Scheme URL::string_to_scheme(const std::string &scheme) +{ + auto iter = scheme_string_map.find(to_lower(scheme)); + if(iter == scheme_string_map.end()) + return URL::Unknown; + return iter->second; +} + +std::string URL::to_lower(const std::string &str) +{ + std::string out = str; + std::transform(out.begin(), out.end(), out.begin(), ::tolower); + return out; +} + +const std::string &URL::scheme_to_string(URL::Scheme scheme) +{ + auto iter = std::find_if(scheme_string_map.begin(), scheme_string_map.end(), [](const auto &i){ + return i->second == scheme; + }); + + if(iter == scheme_string_map.end()) + throw std::logic_error("Unknown URL::Scheme value " + std::to_string(scheme)); + return iter->first; +}