├── .appveyor.yml ├── .codecov.yml ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.md ├── LICENSE_Catch2 ├── LICENSE_OpenSSL ├── NOTICE.md ├── README.md ├── cmake └── FindOHF.cmake ├── examples ├── CMakeLists.txt ├── inet_address.cpp ├── socket.cpp ├── ssl_socket.cpp └── udp_socket.cpp ├── include └── ohf │ ├── Address.hpp │ ├── Authenticator.hpp │ ├── Cache.hpp │ ├── CacheControl.hpp │ ├── Call.hpp │ ├── Callback.hpp │ ├── CertificatePinner.hpp │ ├── Client.hpp │ ├── Config.hpp │ ├── Connection.hpp │ ├── ConnectionPool.hpp │ ├── ConnectionSpec.hpp │ ├── Cookie.hpp │ ├── CookieJar.hpp │ ├── DNS.hpp │ ├── DTLSVersion.hpp │ ├── Dispatcher.hpp │ ├── Exception.hpp │ ├── FormBody.hpp │ ├── Headers.hpp │ ├── HostnameVerifier.hpp │ ├── HttpURL.hpp │ ├── IOStreamBuf.hpp │ ├── InetAddress.hpp │ ├── Interceptor.hpp │ ├── MediaType.hpp │ ├── MultipartBody.hpp │ ├── Principal.hpp │ ├── Protocol.hpp │ ├── Proxy.hpp │ ├── RangeException.hpp │ ├── Request.hpp │ ├── RequestBody.hpp │ ├── Response.hpp │ ├── ResponseBody.hpp │ ├── Socket.hpp │ ├── TLSVersion.hpp │ ├── TimeUnit.hpp │ ├── WebSocket.hpp │ ├── ssl │ ├── Challenge.hpp │ ├── CipherSuite.hpp │ ├── Context.hpp │ ├── Exception.hpp │ ├── Handshake.hpp │ ├── Initializer.hpp │ ├── SSL.hpp │ ├── Socket.hpp │ └── X509Certificate.hpp │ ├── tcp │ ├── SSLServer.hpp │ ├── SSLSocket.hpp │ ├── Server.hpp │ └── Socket.hpp │ └── udp │ └── Socket.hpp ├── src ├── Address.cpp ├── Authenticator.cpp ├── Cache.cpp ├── CacheControl.Builder.cpp ├── CacheControl.cpp ├── CertificatePinner.cpp ├── Client.cpp ├── ConnectionPool.cpp ├── ConnectionSpec.cpp ├── Cookie.Builder.cpp ├── Cookie.cpp ├── CookieJar.cpp ├── DNS.cpp ├── Dispatcher.cpp ├── Exception.cpp ├── FormBody.Builder.cpp ├── FormBody.cpp ├── Headers.Builder.cpp ├── Headers.Iterator.cpp ├── Headers.cpp ├── HostnameVerifier.cpp ├── HttpURL.Builder.cpp ├── HttpURL.cpp ├── IOStreamBuf.cpp ├── InetAddress.cpp ├── Interceptor.cpp ├── MediaType.cpp ├── MultipartBody.Builder.cpp ├── MultipartBody.Part.cpp ├── MultipartBody.cpp ├── Principal.cpp ├── Proxy.Selector.cpp ├── Proxy.cpp ├── RangeException.cpp ├── Request.Builder.cpp ├── Request.cpp ├── RequestBody.cpp ├── Response.Builder.cpp ├── Response.cpp ├── ResponseBody.StreamBuf.cpp ├── ResponseBody.cpp ├── Socket.cpp ├── SocketImpl.hpp ├── TimeUnit.cpp ├── ssl │ ├── Challenge.cpp │ ├── CipherSuite.cpp │ ├── Context.cpp │ ├── Exception.cpp │ ├── Handshake.cpp │ ├── Initializer.cpp │ ├── SSL.cpp │ ├── Socket.cpp │ ├── Util.hpp │ └── X509Certificate.cpp ├── tcp │ ├── SSLServer.cpp │ ├── SSLSocket.cpp │ ├── Server.Connection.cpp │ ├── Server.Iterator.cpp │ ├── Server.cpp │ ├── Socket.Stream.cpp │ ├── Socket.StreamBuf.cpp │ └── Socket.cpp ├── udp │ └── Socket.cpp ├── unix │ ├── SocketImpl.cpp │ └── SocketImpl.hpp ├── util │ ├── string.cpp │ ├── string.hpp │ ├── util.cpp │ └── util.hpp └── win32 │ ├── SocketImpl.cpp │ └── SocketImpl.hpp └── tests ├── CMakeLists.txt ├── CacheControl.cpp ├── Cookie.cpp ├── Exception.cpp ├── FormBody.cpp ├── Headers.cpp ├── HttpURL.cpp ├── InetAddress.cpp ├── MediaType.cpp ├── MultipartBody.cpp ├── RangeException.cpp ├── Request.cpp ├── RequestBody.cpp ├── TimeUnit.cpp ├── main.cpp ├── tcp └── Socket.cpp ├── udp └── Socket.cpp └── util ├── ExceptionCatch.hpp └── TimeUnitCatch.hpp /.appveyor.yml: -------------------------------------------------------------------------------- 1 | notifications: 2 | - provider: Email 3 | on_build_success: false 4 | 5 | clone_folder: c:\projects\okhttp-fork 6 | clone_script: 7 | - git config --global core.autocrlf input 8 | - git clone --recursive --branch=%APPVEYOR_REPO_BRANCH% https://github.com/%APPVEYOR_REPO_NAME%.git %APPVEYOR_BUILD_FOLDER% 9 | 10 | shallow_clone: true 11 | 12 | build: 13 | parallel: true 14 | 15 | platform: 16 | - x86 17 | - x64 18 | 19 | image: 20 | - Visual Studio 2017 21 | - Visual Studio 2015 22 | 23 | configuration: 24 | - Debug 25 | - Release 26 | 27 | environment: 28 | CTEST_OUTPUT_ON_FAILURE: ON 29 | matrix: 30 | - SHARED: OFF 31 | - SHARED: ON 32 | 33 | matrix: 34 | fast_finish: false 35 | allow_failures: 36 | - SHARED: ON 37 | 38 | build_script: 39 | - mkdir build 40 | - cd build 41 | - cmake .. -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_SHARED_LIBS=%SHARED% -DENABLE_DTLS=ON -DBUILD_TESTING=ON 42 | - cmake --build . 43 | 44 | test_script: 45 | - ctest -C Debug 46 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "/usr/*" 3 | - "tests/*" 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Catch2"] 2 | path = Catch2 3 | url = https://github.com/Good-Pudge/Catch2 4 | branch = fix-compilation 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | 4 | notifications: 5 | email: 6 | on_success: never 7 | on_failure: always 8 | 9 | language: cpp 10 | cache: ccache 11 | 12 | os: 13 | - linux 14 | 15 | compiler: 16 | - gcc 17 | - clang 18 | 19 | build: 20 | parallel: true 21 | 22 | addons: 23 | apt: 24 | sources: 25 | - ubuntu-toolchain-r-test 26 | packages: 27 | - g++-5 28 | 29 | matrix: 30 | include: 31 | # Tester 32 | - compiler: gcc 33 | env: BUILD_TYPE=Coverage SHARED=ON TESTS=ON TARGET=coverage 34 | before_script: 35 | - sudo apt install -y lcov gdb 36 | - sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-5 90 37 | # Installer and examples compiler 38 | - compiler: clang 39 | env: BUILD_TYPE=Release SHARED=ON EXAMPLES=ON TARGET=install 40 | 41 | env: 42 | global: 43 | - TESTS=OFF 44 | - EXAMPLES=OFF 45 | - TARGET=ohf 46 | - CTEST_OUTPUT_ON_FAILURE=ON 47 | matrix: 48 | - BUILD_TYPE=Debug SHARED=ON 49 | - BUILD_TYPE=Debug SHARED=OFF 50 | - BUILD_TYPE=Release SHARED=ON 51 | - BUILD_TYPE=Release SHARED=OFF 52 | 53 | script: 54 | - | 55 | if [ "$CC" == "gcc" ]; 56 | then 57 | export LAUNCHER="-DCMAKE_CXX_COMPILER_LAUNCHER=ccache"; 58 | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 90; 59 | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-5 90; 60 | else 61 | export LAUNCHER=""; 62 | fi 63 | - mkdir build 64 | - cd build 65 | - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_SHARED_LIBS=$SHARED -DBUILD_TESTING=$TESTS -DBUILD_EXAMPLES=$EXAMPLES $LAUNCHER 66 | - sudo env "PATH=$PATH" cmake --build . --target $TARGET -- -j4 67 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1 FATAL_ERROR) 2 | project(ohf) 3 | 4 | # Set standard 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 11) 7 | 8 | # Compiler options 9 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 10 | if(MSVC) 11 | set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "/W4") 12 | elseif(CMAKE_COMPILER_IS_GNUCXX) 13 | set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wall") 14 | endif() 15 | endif() 16 | 17 | # Options 18 | ## DTLS 19 | option(ENABLE_DTLS "Would enable DTLS" OFF) 20 | if(ENABLE_DTLS) 21 | add_definitions(-DOKHTTPFORK_DTLS) 22 | endif() 23 | 24 | ## Tests 25 | enable_testing() 26 | if(BUILD_TESTING) 27 | # Coverage 28 | if(CMAKE_BUILD_TYPE STREQUAL "Coverage") 29 | if(NOT CMAKE_COMPILER_IS_GNUCXX) 30 | message(FATAL_ERROR "Sorry, your compiler does not supported") 31 | endif() 32 | 33 | set(CMAKE_BUILD_TYPE "Debug") 34 | 35 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 --coverage -fprofile-arcs -ftest-coverage") 36 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage -fprofile-arcs -ftest-coverage") 37 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") 38 | endif() 39 | 40 | add_subdirectory(tests) 41 | endif() 42 | 43 | ## Examples 44 | option(BUILD_EXAMPLES "Would compile examples" OFF) 45 | if(BUILD_EXAMPLES) 46 | add_subdirectory(examples) 47 | endif() 48 | 49 | # Files 50 | ## Common 51 | file(GLOB_RECURSE INCLUDE_FILES RELATIVE ${PROJECT_SOURCE_DIR} "include/*.hpp") 52 | file(GLOB_RECURSE SRC_FILES RELATIVE ${PROJECT_SOURCE_DIR} "src/*.cpp" "src/*.hpp") 53 | 54 | ## For different systems 55 | if(WIN32) 56 | file(GLOB_RECURSE SYSTEM_FILES RELATIVE ${PROJECT_SOURCE_DIR} "src/unix/*") 57 | elseif(UNIX) 58 | file(GLOB_RECURSE SYSTEM_FILES RELATIVE ${PROJECT_SOURCE_DIR} "src/win32/*") 59 | endif() 60 | list(REMOVE_ITEM SRC_FILES ${SYSTEM_FILES}) 61 | 62 | add_library(ohf ${INCLUDE_FILES} ${SRC_FILES}) 63 | 64 | # Libraries 65 | ## Find 66 | find_package(OpenSSL REQUIRED) 67 | find_package(Threads REQUIRED) 68 | 69 | ## Include 70 | include_directories(include) 71 | include_directories(${OPENSSL_INCLUDE_DIR}) 72 | 73 | ## Link 74 | target_link_libraries(ohf ${OPENSSL_LIBRARIES}) 75 | target_link_libraries(ohf ${CMAKE_THREAD_LIBS_INIT}) 76 | if(WIN32) 77 | target_link_libraries(ohf ws2_32) 78 | target_link_libraries(ohf crypt32) 79 | endif() 80 | 81 | # Install 82 | install(FILES cmake/FindOHF.cmake 83 | DESTINATION "share/cmake-${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}/Modules") 84 | install(DIRECTORY include/ohf DESTINATION include) 85 | install(TARGETS ohf 86 | RUNTIME DESTINATION bin 87 | LIBRARY DESTINATION lib 88 | ARCHIVE DESTINATION lib) -------------------------------------------------------------------------------- /LICENSE_Catch2: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /NOTICE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017-2018 Lyashenko "Good_Pudge" Arsenii 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /cmake/FindOHF.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2017-2018 Lyashenko "Good_Pudge" Arsenii 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | set(FIND_PATHS 18 | ~/Library/Frameworks 19 | /Library/Frameworks 20 | /sw 21 | /opt/local 22 | /opt/csw 23 | /opt 24 | /usr 25 | /usr/local) 26 | 27 | find_library(OHF_LIBRARIES 28 | NAMES ohf 29 | HINTS "${OHF_ROOT_DIR}" 30 | PATHS "${FIND_PATHS}" 31 | PATH_SUFFIXES lib) 32 | 33 | find_path(OHF_INCLUDE_DIR 34 | NAME ohf/Config.hpp 35 | HINTS "${OHF_ROOT_DIR}" 36 | PATHS "${FIND_PATHS}" 37 | PATH_SUFFIXES include) 38 | 39 | include(FindPackageHandleStandardArgs) 40 | 41 | find_package_handle_standard_args(OHF DEFAULT_MSG 42 | OHF_INCLUDE_DIR 43 | OHF_LIBRARIES) 44 | 45 | if(OHF_FOUND) 46 | add_library(OHF::ohf UNKNOWN IMPORTED) 47 | set_target_properties(OHF::ohf PROPERTIES 48 | IMPORTED_LOCATION "${OHF_LIBRARIES}" 49 | INTERFACE_INCLUDE_DIRECTORIES "${OHF_INCLUDE_DIR}" 50 | IMPORTED_LINK_INTERFACE_LANGUAGES "C CXX") 51 | endif() 52 | 53 | mark_as_advanced(OHF_INCLUDE_DIR OHF_LIBRARIES) 54 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1 FATAL_ERROR) 2 | project(ohf_examples) 3 | 4 | include_directories("../include") 5 | 6 | file(GLOB EXAMPLES RELATIVE ${PROJECT_SOURCE_DIR} "*.cpp") 7 | foreach(item ${EXAMPLES}) 8 | string(REPLACE ".cpp" "" name ${item}) 9 | set(name "ohf_${name}") 10 | add_executable(${name} ${item}) 11 | target_link_libraries(${name} ohf) 12 | endforeach() 13 | -------------------------------------------------------------------------------- /examples/inet_address.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | try { 11 | ohf::InetAddress address("www.google.com", ohf::IPv4); 12 | std::cout << "Official host name: " << address.hostName() << std::endl; 13 | std::cout << "IP address as string: " << address.hostAddress() << std::endl; 14 | 15 | } catch (const ohf::Exception &e) { 16 | std::cerr << e.message() << std::endl; 17 | } 18 | } -------------------------------------------------------------------------------- /examples/socket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Senya on 02.09.2017. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | try { 11 | ohf::tcp::Socket socket; 12 | socket.connect("www.google.com", 80); 13 | 14 | ohf::tcp::Socket::Stream stream(socket); 15 | stream << "GET / HTTP/1.1\r\n" 16 | << "Host: www.google.com\r\n" 17 | << "Connection: close\r\n" 18 | << "\r\n"; 19 | stream.flush(); 20 | 21 | std::cout << stream.rdbuf() << std::endl; // better if you don't do it 22 | socket.disconnect(); // or close() or destructor do it 23 | } catch (ohf::Exception &e) { 24 | std::cout << e.what() << std::endl; 25 | } 26 | } -------------------------------------------------------------------------------- /examples/ssl_socket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Senya on 02.09.2017. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main() { 11 | /** 12 | * Same API as tcp::Socket, but 13 | * 1. have functions 14 | * void sni(const InetAddress &address) 15 | * void sni(bool b) 16 | * 2. need once global initialize (for OpenSSL) 17 | * 3. need ssl::Context 18 | */ 19 | 20 | ohf::ssl::Initializer initializer; // no any exception 21 | 22 | try { 23 | ohf::HttpURL url = "https://google.com"; 24 | 25 | ohf::ssl::Context context(ohf::TLSVersion::SSLv23); 26 | ohf::tcp::SSLSocket sslSocket(context); 27 | sslSocket.connect(ohf::IPv4, url); // port specified by protocol `https` 28 | sslSocket.sni(ohf::InetAddress(url.host())); 29 | 30 | ohf::tcp::Socket::Stream stream(sslSocket); 31 | stream << "GET / HTTP/1.1\r\n" 32 | << "Host: " << url.host() << "\r\n" 33 | << "Connection: close\r\n" 34 | << "\r\n"; 35 | stream.flush(); 36 | 37 | std::cout << stream.rdbuf() << std::endl; 38 | } catch (ohf::Exception &e) { 39 | std::cout << e.what() << std::endl; 40 | return 1; 41 | } 42 | 43 | return 0; 44 | } -------------------------------------------------------------------------------- /examples/udp_socket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | try { 11 | ohf::udp::Socket socket; 12 | 13 | socket.send("www.udp-server.com", 1337, "Some request"); 14 | 15 | ohf::InetAddress address; 16 | unsigned short port; 17 | std::vector response(1024); 18 | int received = socket.receive(address, port, response.data(), (int) response.size()); 19 | 20 | std::cout << std::string(response.begin(), response.end()) << std::endl; 21 | } catch(const ohf::Exception &e) { 22 | std::cerr << e.what() << std::endl; 23 | return 1; 24 | } 25 | 26 | return 0; 27 | } -------------------------------------------------------------------------------- /include/ohf/Address.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_ADDRESS_HPP 6 | #define OKHTTPFORK_ADDRESS_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace ohf { 19 | class Address { 20 | public: 21 | Address(const std::string &host, 22 | Uint16 port, 23 | DNS *dns, 24 | HostnameVerifier *hostnameVerifier, 25 | const CertificatePinner &certificatePinner, 26 | Authenticator *proxyAuthenticator, 27 | const Proxy &proxy, 28 | const std::vector &protocols, 29 | const std::vector &connectionSpecs, 30 | const Proxy::Selector &proxySelector); 31 | 32 | CertificatePinner certificatePinner() const; 33 | 34 | std::vector connectionSpecs() const; 35 | 36 | std::shared_ptr dns() const; 37 | 38 | std::shared_ptr hostnameVerifier() const; 39 | 40 | std::vector protocols() const; 41 | 42 | Proxy proxy() const; 43 | 44 | std::shared_ptr proxyAuthenticator() const; 45 | 46 | Proxy::Selector proxySelector() const; 47 | 48 | HttpURL url() const; 49 | 50 | std::string toString() const; 51 | 52 | bool operator ==(const Address &address) const; 53 | 54 | friend std::ostream& operator <<(std::ostream &stream, const Address &address); 55 | 56 | private: 57 | HttpURL mUrl; 58 | std::shared_ptr mDNS; 59 | std::shared_ptr mVerifier; 60 | CertificatePinner mPinner; 61 | std::shared_ptr mAuthenticator; 62 | Proxy mProxy; 63 | std::vector mProtocols; 64 | std::vector mSpecs; 65 | Proxy::Selector mSelector; 66 | }; 67 | } 68 | 69 | #endif //OKHTTPFORK_ADDRESS_HPP 70 | -------------------------------------------------------------------------------- /include/ohf/Authenticator.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_AUTHENTICATOR_HPP 6 | #define OKHTTPFORK_AUTHENTICATOR_HPP 7 | 8 | namespace ohf { 9 | class Authenticator { 10 | 11 | }; 12 | } 13 | 14 | #endif //OKHTTPFORK_AUTHENTICATOR_HPP 15 | -------------------------------------------------------------------------------- /include/ohf/Cache.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_CACHE_HPP 6 | #define OKHTTPFORK_CACHE_HPP 7 | 8 | namespace ohf { 9 | class Cache { 10 | 11 | }; 12 | } 13 | 14 | #endif //OKHTTPFORK_CACHE_HPP 15 | -------------------------------------------------------------------------------- /include/ohf/CacheControl.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_CACHECONTROL_HPP 6 | #define OKHTTPFORK_CACHECONTROL_HPP 7 | 8 | #include "Config.hpp" 9 | #include "Headers.hpp" 10 | 11 | namespace ohf { 12 | class CacheControl { 13 | public: 14 | class Builder { 15 | public: 16 | Builder(); 17 | 18 | CacheControl build(); 19 | 20 | Builder &immutable(); 21 | 22 | Builder &noCache(); 23 | 24 | Builder &noStore(); 25 | 26 | Builder &noTransform(); 27 | 28 | Builder &onlyIfCached(); 29 | 30 | Builder &maxAge(const TimeUnit &seconds); 31 | 32 | Builder &maxStale(const TimeUnit &seconds); 33 | 34 | Builder &minFresh(const TimeUnit &seconds); 35 | 36 | private: 37 | bool 38 | mImmutable, 39 | mNoCache, 40 | mNoStore, 41 | mNoTransform, 42 | mOnlyIfCached; 43 | TimeUnit 44 | mMaxAge, 45 | mMaxStale, 46 | mMinFresh; 47 | 48 | friend class ohf::CacheControl; 49 | }; 50 | 51 | CacheControl(); 52 | 53 | explicit CacheControl(const Headers &headers); 54 | 55 | bool isPrivate() const; 56 | 57 | bool isPublic() const; 58 | 59 | bool immutable() const; 60 | 61 | bool mustRevalidate() const; 62 | 63 | bool noCache() const; 64 | 65 | bool noStore() const; 66 | 67 | bool noTransform() const; 68 | 69 | bool onlyIfCached() const; 70 | 71 | TimeUnit maxAge() const; 72 | 73 | TimeUnit maxStale() const; 74 | 75 | TimeUnit minFresh() const; 76 | 77 | TimeUnit sMaxAge() const; 78 | 79 | std::string toString() const; 80 | 81 | bool operator==(const CacheControl &cc) const; 82 | 83 | friend std::ostream &operator <<(std::ostream &stream, const CacheControl &cacheControl); 84 | 85 | private: 86 | CacheControl(const Builder *builder); 87 | 88 | bool mPublic; 89 | bool mPrivate; 90 | bool mNoCache; 91 | bool mOnlyIfCached; 92 | bool mMustRevalidate; 93 | bool mImmutable; 94 | bool mNoStore; 95 | bool mNoTransform; 96 | 97 | TimeUnit mMaxAge; 98 | TimeUnit mSMaxAge; 99 | TimeUnit mMaxStale; 100 | TimeUnit mMinFresh; 101 | }; 102 | } 103 | 104 | #endif //OKHTTPFORK_CACHECONTROL_HPP 105 | -------------------------------------------------------------------------------- /include/ohf/Call.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_CALL_HPP 6 | #define OKHTTPFORK_CALL_HPP 7 | 8 | #include "Response.hpp" 9 | #include "Request.hpp" 10 | #include "Callback.hpp" 11 | #include 12 | 13 | namespace ohf { 14 | class Call { 15 | public: 16 | class Factory { 17 | public: 18 | virtual Call *newCall(const Request &request) = 0; 19 | }; 20 | 21 | virtual void enqueue(const Callback &callback) = 0; 22 | 23 | virtual void enqueue(void(*onResponse)(const Call &, const Response &), 24 | void(*onFailure)(const Call&, const Exception &)) = 0; 25 | 26 | virtual Response execute() = 0; 27 | 28 | virtual Request request() = 0; 29 | 30 | virtual void cancel() = 0; 31 | 32 | virtual bool isCanceled() = 0; 33 | 34 | virtual bool isExecuted() = 0; 35 | }; 36 | } 37 | 38 | #endif //OKHTTPFORK_CALL_HPP 39 | -------------------------------------------------------------------------------- /include/ohf/Callback.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_CALLBACK_HPP 6 | #define OKHTTPFORK_CALLBACK_HPP 7 | 8 | #include "Exception.hpp" 9 | #include "Response.hpp" 10 | 11 | namespace ohf { 12 | class Call; 13 | 14 | class Callback { 15 | public: 16 | virtual void onFailure(const Call &call, const Exception &e) = 0; 17 | virtual void onResponse(const Call &call, const Response &response) = 0; 18 | }; 19 | } 20 | 21 | #endif //OKHTTPFORK_CALLBACK_HPP 22 | -------------------------------------------------------------------------------- /include/ohf/CertificatePinner.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_CERTIFICATEPINNER_HPP 6 | #define OKHTTPFORK_CERTIFICATEPINNER_HPP 7 | 8 | namespace ohf { 9 | class CertificatePinner { 10 | public: 11 | 12 | bool operator ==(const CertificatePinner &pinner) const; 13 | }; 14 | } 15 | 16 | #endif //OKHTTPFORK_CERTIFICATEPINNER_HPP 17 | -------------------------------------------------------------------------------- /include/ohf/Client.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_OKHTTPCLIENT_HPP 6 | #define OKHTTPFORK_OKHTTPCLIENT_HPP 7 | 8 | #include "Call.hpp" 9 | #include "Authenticator.hpp" 10 | #include "Cache.hpp" 11 | #include "CertificatePinner.hpp" 12 | #include "ConnectionPool.hpp" 13 | #include "ConnectionSpec.hpp" 14 | #include "CookieJar.hpp" 15 | #include "Dispatcher.hpp" 16 | #include "DNS.hpp" 17 | #include "HostnameVerifier.hpp" 18 | #include "Interceptor.hpp" 19 | #include "Protocol.hpp" 20 | #include "Proxy.hpp" 21 | #include "TimeUnit.hpp" 22 | #include "WebSocket.hpp" 23 | 24 | namespace ohf { 25 | class Client { 26 | public: 27 | class Builder { 28 | public: 29 | Builder() = default; 30 | 31 | ~Builder(); 32 | 33 | Client build(); 34 | 35 | Builder& addInterceptor(const Interceptor &interceptor); 36 | 37 | Builder& addNetworkInterceptor(const Interceptor &interceptor); 38 | 39 | Builder& authenticator(const Authenticator &authenticator); 40 | 41 | Builder& cache(const Cache &cache); 42 | 43 | Builder& certificatePinner(const CertificatePinner &pinner); 44 | 45 | Builder& connectionPool(const ConnectionPool &pool); 46 | 47 | Builder& connectionSpecs(const std::vector &specs); 48 | 49 | Builder& connectTimeout(const TimeUnit &timeout); 50 | 51 | Builder& cookieJar(const CookieJar &jar); 52 | 53 | Builder& dispatcher(const Dispatcher &dispatcher); 54 | 55 | Builder& dns(const DNS &dns); 56 | 57 | Builder& followRedirects(bool follow); 58 | 59 | Builder& follorSSLRedirects(bool follow); 60 | 61 | Builder& hostnameVerifier(const HostnameVerifier &verifier); 62 | 63 | std::vector interceptors(); 64 | 65 | std::vector networkInterceptors(); 66 | 67 | Builder& pingInterval(const TimeUnit &interval); 68 | 69 | Builder& protocols(const std::vector &protocols); 70 | 71 | Builder& proxy(const Proxy &proxy); 72 | 73 | Builder& proxyAuthenticator(const Authenticator &authenticator); 74 | 75 | Builder& proxySelector(const Proxy::Selector &selector); 76 | 77 | Builder& readTimeout(const TimeUnit &timeout); 78 | 79 | Builder& retryOnConnectionFailure(bool retry); 80 | 81 | //Builder socketFactory(const SocketFactory &factory); 82 | //Builder sslSocketFactory(const SSLSocketFactory &factory); 83 | //Builder sslSocketFactory(const SSLSocketFactory &factory, const X509TrustManager &manager); 84 | 85 | Builder& writeTimeout(const TimeUnit &timeout); 86 | 87 | private: 88 | friend class ohf::Client; 89 | }; 90 | 91 | Client(); 92 | 93 | ~Client(); 94 | 95 | Authenticator authenticator(); 96 | 97 | Cache cache(); 98 | 99 | CertificatePinner certificatePinner(); 100 | 101 | ConnectionPool connectionPool(); 102 | 103 | std::vector connectionSpecs(); 104 | 105 | CookieJar& cookieJar(); 106 | 107 | Dispatcher dispatcher(); 108 | 109 | DNS& dns(); 110 | 111 | bool followRedirects(); 112 | 113 | bool followSSLRedirects(); 114 | 115 | HostnameVerifier hostnameVerifier(); 116 | 117 | std::vector interceptors(); 118 | 119 | std::vector networkInterceptors(); 120 | 121 | Builder newBuilder(); 122 | 123 | std::shared_ptr newCall(const Request &request); 124 | 125 | std::shared_ptr newWebSocket(const Request &request, const WebSocket::Listener &listener); 126 | 127 | TimeUnit pingInterval(); 128 | 129 | std::vector protocols(); 130 | 131 | Proxy proxy(); 132 | 133 | Authenticator proxyAuthenticator(); 134 | 135 | Proxy::Selector proxySelector(); 136 | 137 | TimeUnit connectTimeout(); 138 | 139 | TimeUnit readTimeout(); 140 | 141 | TimeUnit writeTimeout(); 142 | 143 | bool retryOnConnectionFailure(); 144 | 145 | // SocketFactory socketFactory(); 146 | // SSLSocketFactory sslSocketFactory(); 147 | 148 | private: 149 | TimeUnit mReadTimeout; 150 | TimeUnit mWriteTimeout; 151 | TimeUnit mConnectTimeout; 152 | bool mFollowRedirects; 153 | bool mFollowSSLRedirects; 154 | 155 | }; 156 | } 157 | 158 | #endif //OKHTTPFORK_OKHTTPCLIENT_HPP 159 | -------------------------------------------------------------------------------- /include/ohf/Config.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_CONFIG_HPP 6 | #define OKHTTPFORK_CONFIG_HPP 7 | 8 | #if defined(_WIN32) 9 | #define OKHTTPFORK_WINDOWS 10 | #elif defined(__unix__) 11 | #define OKHTTPFORK_UNIX 12 | #endif 13 | 14 | namespace ohf { 15 | typedef char Int8; 16 | typedef unsigned char Uint8; 17 | 18 | typedef short Int16; 19 | typedef unsigned short Uint16; 20 | 21 | typedef int Int32; 22 | typedef unsigned int Uint32; 23 | 24 | typedef long long Int64; 25 | typedef unsigned long long Uint64; 26 | } 27 | 28 | #endif //OKHTTPFORK_CONFIG_HPP 29 | -------------------------------------------------------------------------------- /include/ohf/Connection.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_CONNECTION_HPP 6 | #define OKHTTPFORK_CONNECTION_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace ohf { 14 | class Connection { 15 | public: 16 | ssl::Handshake handshake(); 17 | 18 | Protocol protocol(); 19 | 20 | Route route(); 21 | 22 | tcp::Socket socket(); 23 | 24 | }; 25 | } 26 | 27 | #endif //OKHTTPFORK_CONNECTION_HPP 28 | -------------------------------------------------------------------------------- /include/ohf/ConnectionPool.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_CONNECTIONPOOL_HPP 6 | #define OKHTTPFORK_CONNECTIONPOOL_HPP 7 | 8 | namespace ohf { 9 | class ConnectionPool { 10 | 11 | }; 12 | } 13 | 14 | #endif //OKHTTPFORK_CONNECTIONPOOL_HPP 15 | -------------------------------------------------------------------------------- /include/ohf/ConnectionSpec.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_CONNECTIONSPEC_HPP 6 | #define OKHTTPFORK_CONNECTIONSPEC_HPP 7 | 8 | namespace ohf { 9 | class ConnectionSpec { 10 | public: 11 | 12 | bool operator ==(const ConnectionSpec &spec) const; 13 | }; 14 | } 15 | 16 | #endif //OKHTTPFORK_CONNECTIONSPEC_HPP 17 | -------------------------------------------------------------------------------- /include/ohf/Cookie.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Senya on 31.08.2017. 3 | // 4 | 5 | #ifndef OKHTTPFORK_COOKIE_HPP 6 | #define OKHTTPFORK_COOKIE_HPP 7 | 8 | #include "HttpURL.hpp" 9 | #include "Headers.hpp" 10 | 11 | namespace ohf { 12 | class Cookie { 13 | public: 14 | class Builder { 15 | public: 16 | Builder(); 17 | 18 | Cookie build(); 19 | 20 | Builder &name(const std::string &name); 21 | 22 | Builder &value(const std::string &value); 23 | 24 | Builder &path(const std::string &path); 25 | 26 | Builder &domain(const std::string &domain); 27 | 28 | Builder &hostOnlyDomain(const std::string &domain); 29 | 30 | Builder &expiresAt(const TimeUnit &expiresAt); 31 | 32 | Builder &httpOnly(); 33 | 34 | Builder &secure(); 35 | 36 | private: 37 | TimeUnit m_expiresAt; 38 | bool 39 | m_hostOnly, 40 | m_httpOnly, 41 | m_secure, 42 | m_persistent; 43 | std::string 44 | m_name, 45 | m_value, 46 | m_path, 47 | m_domain; 48 | 49 | friend class ohf::Cookie; 50 | }; 51 | 52 | Cookie(HttpURL &httpURL, const std::string &setCookie); 53 | 54 | static std::vector parseAll(HttpURL &httpUrl, Headers &headers); 55 | 56 | TimeUnit expiresAt() const; 57 | 58 | bool hostOnly() const; 59 | 60 | bool httpOnly() const; 61 | 62 | bool persistent() const; 63 | 64 | bool secure() const; 65 | 66 | bool matches(HttpURL &httpURL) const; 67 | 68 | std::string name() const; 69 | 70 | std::string value() const; 71 | 72 | std::string path() const; 73 | 74 | std::string domain() const; 75 | 76 | std::string toString() const; 77 | 78 | bool operator==(const Cookie &cookie) const; 79 | 80 | friend std::ostream &operator<<(std::ostream &stream, const Cookie &cookie); 81 | 82 | private: 83 | Cookie(const Builder *builder); 84 | 85 | TimeUnit m_expiresAt; 86 | bool 87 | m_hostOnly, 88 | m_httpOnly, 89 | m_persistent, 90 | m_secure; 91 | std::string 92 | m_name, 93 | m_value, 94 | m_path, 95 | m_domain; 96 | }; 97 | } 98 | 99 | #endif //OKHTTPFORK_COOKIE_HPP 100 | -------------------------------------------------------------------------------- /include/ohf/CookieJar.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_COOKIEJAR_HPP 6 | #define OKHTTPFORK_COOKIEJAR_HPP 7 | 8 | #include 9 | #include "Cookie.hpp" 10 | 11 | namespace ohf { 12 | class CookieJar { 13 | public: 14 | virtual std::vector loadFromRequest(HttpURL &httpURL) = 0; 15 | 16 | virtual void saveFromResponse(HttpURL &httpURL, std::vector &cookies) = 0; 17 | 18 | class NO_COOKIES; 19 | }; 20 | 21 | class CookieJar::NO_COOKIES : public CookieJar { 22 | public: 23 | std::vector loadFromRequest(HttpURL &httpURL); 24 | 25 | void saveFromResponse(HttpURL &httpURL, std::vector &cookies); 26 | }; 27 | } 28 | 29 | #endif //OKHTTPFORK_COOKIEJAR_HPP 30 | -------------------------------------------------------------------------------- /include/ohf/DNS.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_DNS_HPP 6 | #define OKHTTPFORK_DNS_HPP 7 | 8 | #include 9 | #include 10 | #include "InetAddress.hpp" 11 | 12 | namespace ohf { 13 | class DNS { 14 | public: 15 | virtual std::vector lookup(const std::string &hostname) = 0; 16 | 17 | class SYSTEM; 18 | }; 19 | 20 | class DNS::SYSTEM : public DNS { 21 | public: 22 | std::vector lookup(const std::string &hostname) override; 23 | }; 24 | } 25 | 26 | #endif //OKHTTPFORK_DNS_HPP 27 | -------------------------------------------------------------------------------- /include/ohf/DTLSVersion.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_DTLSVERSION_HPP 6 | #define OKHTTPFORK_DTLSVERSION_HPP 7 | 8 | namespace ohf { 9 | enum class DTLSVersion { 10 | DTLS, 11 | DTLSv1, 12 | DTLSv1_2 13 | }; 14 | } 15 | 16 | #endif //OKHTTPFORK_DTLSVERSION_HPP 17 | -------------------------------------------------------------------------------- /include/ohf/Dispatcher.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_DISPATCHER_HPP 6 | #define OKHTTPFORK_DISPATCHER_HPP 7 | 8 | namespace ohf { 9 | class Dispatcher { 10 | 11 | }; 12 | } 13 | 14 | #endif //OKHTTPFORK_DISPATCHER_HPP 15 | -------------------------------------------------------------------------------- /include/ohf/Exception.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_EXCEPTION_HPP 6 | #define OKHTTPFORK_EXCEPTION_HPP 7 | 8 | #include 9 | 10 | namespace ohf { 11 | class Exception : public std::exception { 12 | public: 13 | enum class Code { 14 | OK, 15 | INVALID_MIME_TYPE, 16 | FAILED_TO_READ_STREAM, 17 | FAILED_TO_CREATE_SOCKET, 18 | UNKNOWN_HOST, 19 | INVALID_ADDRESS_TYPE, 20 | INVALID_FAMILY_TYPE, 21 | FAILED_TO_CREATE_CONNECTION, 22 | NO_DATA_TO_SEND, 23 | FAILED_TO_SEND_DATA, 24 | FAILED_TO_RECEIVE_DATA, 25 | FAILED_TO_BIND_SOCKET, 26 | FAILED_TO_ACCEPT_SOCKET, 27 | FAILED_TO_LISTEN_SOCKET, 28 | FAILED_TO_SET_SOCKET_OPTION, 29 | FAILED_TO_GET_SOCKET_NAME, 30 | DATAGRAM_PACKET_IS_TOO_BIG, 31 | SSL_CREATE_ERROR, 32 | SSL_CREATE_CONTEXT_ERROR, 33 | SSL_CREATE_CONNECTION_ERROR, 34 | SSL_ERROR, 35 | SSL_ACCEPT_ERROR, 36 | SSL_FAILED_TO_USE_CERTIFICATE_FILE, 37 | SSL_FAILED_TO_USE_PRIVATE_KEY_FILE, 38 | SSL_FAILED_TO_VERIFY_PRIVATE_KEY, 39 | SSL_PROTOCOL_DOES_NOT_SUPPORTED, 40 | INVALID_QUERY_PARAMETER, 41 | INVALID_URI, 42 | INVALID_PORT, 43 | INVALID_URI_HEX_CODE, 44 | HOST_IS_EMPTY, 45 | OUT_OF_RANGE, 46 | INVALID_COOKIE_LINE, 47 | INVALID_COOKIE_NAME_VALUE, 48 | INVALID_MAX_AGE, 49 | INVALID_S_MAX_AGE, 50 | INVALID_MAX_STALE, 51 | INVALID_MIN_FRESH, 52 | UNEXPECTED_HEADER, 53 | INVALID_CONTENT_TYPE_LINE, 54 | HEADER_IS_EMPTY, 55 | METHOD_IS_NOT_NAMED, 56 | URL_IS_NOT_NAMED, 57 | INVALID_EXCEPTION_CODE, 58 | RESPONSE_BODY_IS_NOT_SPECIFIED, 59 | REQUEST_IS_NOT_SPECIFIED, 60 | INVALID_CHALLENGE, 61 | FAILED_TO_PARSE_TIME 62 | }; 63 | 64 | Exception(const Code &code, const std::string &what) noexcept; 65 | 66 | Code code() const noexcept; 67 | 68 | std::string message() const noexcept; 69 | 70 | const char *what() const noexcept override; 71 | 72 | protected: 73 | Code m_code; 74 | std::string m_what; 75 | }; 76 | 77 | } 78 | 79 | #endif //OKHTTPFORK_EXCEPTION_HPP 80 | -------------------------------------------------------------------------------- /include/ohf/FormBody.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_FORMBODY_HPP 6 | #define OKHTTPFORK_FORMBODY_HPP 7 | 8 | #include "RequestBody.hpp" 9 | 10 | namespace ohf { 11 | class FormBody : public RequestBody { 12 | public: 13 | class Builder { 14 | public: 15 | Builder &add(const std::string &name, const std::string &value); 16 | 17 | FormBody build(); 18 | 19 | private: 20 | std::vector namesValues; 21 | 22 | friend class ohf::FormBody; 23 | }; 24 | 25 | std::string encodedName(Uint32 index) const; 26 | 27 | std::string encodedValue(Uint32 index) const; 28 | 29 | std::string name(Uint32 index) const; 30 | 31 | std::string value(Uint32 index) const; 32 | 33 | Uint32 size() const; 34 | 35 | private: 36 | FormBody(const Builder *builder); 37 | 38 | std::vector namesValues; 39 | }; 40 | } 41 | 42 | #endif //OKHTTPFORK_FORMBODY_HPP 43 | -------------------------------------------------------------------------------- /include/ohf/Headers.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_HEADERS_HPP 6 | #define OKHTTPFORK_HEADERS_HPP 7 | 8 | #include "Config.hpp" 9 | #include "TimeUnit.hpp" 10 | #include 11 | #include 12 | 13 | namespace ohf { 14 | class Headers { 15 | public: 16 | typedef std::pair Pair; 17 | 18 | class Iterator { 19 | public: 20 | Iterator(Uint32 index, const Headers *headers); 21 | 22 | bool isOutOfRange() const; 23 | 24 | const Pair& operator *() const; 25 | 26 | const Pair& operator *(); 27 | 28 | const Pair* operator ->() const; 29 | 30 | const Pair* operator ->(); 31 | 32 | const Iterator operator ++(Int32) const; 33 | 34 | const Iterator& operator ++(); 35 | 36 | const Iterator operator --(Int32) const; 37 | 38 | const Iterator& operator --(); 39 | 40 | const Iterator operator +(Uint32 index) const; 41 | 42 | const Iterator operator -(Uint32 index) const; 43 | 44 | const Iterator& operator +=(Uint32 index); 45 | 46 | const Iterator& operator -=(Uint32 index); 47 | 48 | bool operator ==(const Iterator &right) const; 49 | 50 | bool operator !=(const Iterator &right) const; 51 | 52 | private: 53 | void swapPair(); 54 | 55 | mutable bool outOfRange; 56 | Pair type; 57 | Uint32 index; 58 | const Headers *headers; 59 | }; 60 | 61 | const Iterator begin() const; 62 | Iterator begin(); 63 | 64 | const Iterator end() const; 65 | Iterator end(); 66 | 67 | class Builder { 68 | public: 69 | Builder &add(std::string line); 70 | 71 | Builder &add(const std::string &name, const std::string &value); 72 | 73 | Headers build() const; 74 | 75 | std::string get(std::string name) const; 76 | 77 | Builder &removeAll(std::string name); 78 | 79 | Builder &set(const std::string &name, const std::string &value); 80 | 81 | private: 82 | std::vector namesValues; 83 | 84 | friend class ohf::Headers; 85 | }; 86 | 87 | explicit Headers(const std::map &headers); 88 | 89 | std::string get(std::string name) const; 90 | 91 | TimeUnit getDate() const; 92 | 93 | std::string name(Uint32 index) const; 94 | 95 | std::vector names() const; 96 | 97 | Builder newBuilder() const; 98 | 99 | // static Headers of(...); 100 | Uint32 size() const; 101 | 102 | std::string value(Uint32 index) const; 103 | 104 | std::vector values(std::string name) const; 105 | 106 | Pair pair(Uint32 index) const; 107 | 108 | std::string toString() const; 109 | 110 | std::string operator [](const std::string &name) const; 111 | 112 | bool operator ==(const Headers &headers) const; 113 | 114 | friend std::ostream &operator<<(std::ostream &stream, const Headers &headers); 115 | 116 | private: 117 | Headers(const Builder *builder); 118 | 119 | std::vector namesValues; 120 | }; 121 | } 122 | 123 | #endif //OKHTTPFORK_HEADERS_HPP 124 | -------------------------------------------------------------------------------- /include/ohf/HostnameVerifier.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_HOSTNAMEVERIFIER_HPP 6 | #define OKHTTPFORK_HOSTNAMEVERIFIER_HPP 7 | 8 | namespace ohf { 9 | class HostnameVerifier { 10 | 11 | }; 12 | } 13 | 14 | #endif //OKHTTPFORK_HOSTNAMEVERIFIER_HPP 15 | -------------------------------------------------------------------------------- /include/ohf/IOStreamBuf.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef PROJECT_IOSTREAMBUF_HPP 6 | #define PROJECT_IOSTREAMBUF_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace ohf { 13 | class IOStreamBuf : public std::streambuf { 14 | protected: 15 | IOStreamBuf(Int32 write, Int32 read); 16 | 17 | Int32 sync() override; 18 | 19 | Int32 overflow(Int32 c) override; 20 | 21 | Int32 underflow() override; 22 | 23 | virtual Int32 write(const char *data, Int32 length) = 0; 24 | 25 | virtual Int32 read(char *data, Int32 length) = 0; 26 | private: 27 | std::vector wb; // writing buffer 28 | std::vector rb; // reading buffer 29 | }; 30 | } 31 | 32 | #endif //PROJECT_IOSTREAMBUF_HPP 33 | -------------------------------------------------------------------------------- /include/ohf/InetAddress.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_INETADDRESS_HPP 6 | #define OKHTTPFORK_INETADDRESS_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include "Config.hpp" 12 | #include "Socket.hpp" 13 | 14 | namespace ohf { 15 | class InetAddress { 16 | public: 17 | InetAddress(); 18 | 19 | InetAddress(const char *host); 20 | 21 | InetAddress(const std::string &host, Int32 af); 22 | 23 | InetAddress(const std::string &host, Socket::Family type); 24 | 25 | explicit InetAddress(const std::string &host); 26 | 27 | InetAddress(Uint8 b1, Uint8 b2, Uint8 b3, Uint8 b4); 28 | 29 | InetAddress(Uint8 b1, Uint8 b2, Uint8 b3, Uint8 b4, Uint8 b5, Uint8 b6, Uint8 b7, Uint8 b8, 30 | Uint8 b9, Uint8 b10, Uint8 b11, Uint8 b12, Uint8 b13, Uint8 b14, Uint8 b15, Uint8 b16); 31 | 32 | static std::vector getAllByName(const std::string &host, Int32 af); 33 | 34 | static std::vector getAllByName(const std::string &host, Socket::Family family); 35 | 36 | static std::vector getAllByName(const std::string &host); 37 | 38 | std::array address() const; 39 | 40 | std::string hostAddress() const; 41 | 42 | std::string hostName() const; 43 | 44 | std::string canonicalName() const; 45 | 46 | Socket::Family family() const; 47 | 48 | Int32 originalType() const; 49 | 50 | friend std::ostream& operator <<(std::ostream &stream, const InetAddress &address); 51 | 52 | private: 53 | std::string mHostName; 54 | std::string mCanonName; 55 | std::array mIP; 56 | 57 | Socket::Family mFamily; 58 | Int32 mOriginalType; 59 | }; 60 | 61 | namespace ipv4 { 62 | static InetAddress BROADCAST = {255, 255, 255, 255}; 63 | static InetAddress ANY = {0, 0, 0, 0}; 64 | static InetAddress LOCALHOST = {127, 0, 0, 1}; 65 | } 66 | 67 | namespace ipv6 { 68 | static InetAddress BROADCAST = {255, 255, 255, 255, 255, 255, 255, 255, 69 | 255, 255, 255, 255, 255, 255, 255, 255}; 70 | static InetAddress ANY = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 71 | static InetAddress LOCALHOST = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; 72 | } 73 | } 74 | 75 | #endif //OKHTTPFORK_INETADDRESS_HPP 76 | -------------------------------------------------------------------------------- /include/ohf/Interceptor.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_INTERCEPTOR_HPP 6 | #define OKHTTPFORK_INTERCEPTOR_HPP 7 | 8 | #include "TimeUnit.hpp" 9 | #include "Call.hpp" 10 | #include "Connection.hpp" 11 | 12 | namespace ohf { 13 | class Interceptor { 14 | class Chain { 15 | public: 16 | Call* call(); 17 | 18 | Connection connection(); 19 | 20 | TimeUnit connectTimeout(); 21 | 22 | 23 | }; 24 | }; 25 | } 26 | 27 | #endif //OKHTTPFORK_INTERCEPTOR_HPP 28 | -------------------------------------------------------------------------------- /include/ohf/MediaType.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_MEDIATYPE_HPP 6 | #define OKHTTPFORK_MEDIATYPE_HPP 7 | 8 | #include 9 | 10 | namespace ohf { 11 | class MediaType { 12 | public: 13 | MediaType() = default; 14 | 15 | explicit MediaType(const std::string &str); 16 | 17 | MediaType(const char *str); 18 | 19 | std::string boundary() const; 20 | 21 | std::string boundary(const std::string &defaultValue) const; 22 | 23 | std::string charset() const; 24 | 25 | std::string charset(const std::string &defaultValue) const; 26 | 27 | std::string subtype() const; 28 | 29 | std::string type() const; 30 | 31 | bool operator==(const MediaType &mediaType) const; 32 | 33 | std::string toString() const; 34 | 35 | friend std::ostream &operator<<(std::ostream &stream, const MediaType &mediaType); 36 | 37 | private: 38 | std::string 39 | mBoundary, 40 | mCharset, 41 | mSubType, 42 | mType; 43 | }; 44 | } 45 | 46 | #endif //OKHTTPFORK_MEDIATYPE_HPP 47 | -------------------------------------------------------------------------------- /include/ohf/MultipartBody.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_MULTIPARTBODY_HPP 6 | #define OKHTTPFORK_MULTIPARTBODY_HPP 7 | 8 | #include "MediaType.hpp" 9 | #include "RequestBody.hpp" 10 | #include "Headers.hpp" 11 | 12 | namespace ohf { 13 | class MultipartBody : public RequestBody { 14 | public: 15 | static MediaType MIXED; 16 | static MediaType ALTERNATIVE; 17 | static MediaType DIGEST; 18 | static MediaType PARALLEL; 19 | static MediaType FORM; 20 | 21 | class Part { 22 | public: 23 | Part(const std::string &name, const std::string &value); 24 | 25 | Part(const std::string &name, const std::string &filename, const RequestBody &body); 26 | 27 | Part(const Headers &headers, const RequestBody &body); 28 | 29 | explicit Part(const RequestBody &body); 30 | 31 | RequestBody body() const; 32 | 33 | Headers headers() const; 34 | 35 | private: 36 | RequestBody mBody; 37 | Headers mHeaders; 38 | 39 | friend class ohf::MultipartBody; 40 | }; 41 | 42 | class Builder { 43 | public: 44 | Builder(); 45 | 46 | explicit Builder(const std::string &boundary); 47 | 48 | Builder &addFormDataPart(const std::string &name, const std::string &value); 49 | 50 | Builder &addFormDataPart(const std::string &name, const std::string &filename, const RequestBody &body); 51 | 52 | Builder &addPart(const Headers &headers, const RequestBody &body); 53 | 54 | Builder &addPart(const Part &part); 55 | 56 | Builder &addPart(const RequestBody &body); 57 | 58 | Builder &setType(const MediaType &type); 59 | 60 | MultipartBody build(); 61 | 62 | private: 63 | std::string mBoundary; 64 | MediaType mType; 65 | std::vector mParts; 66 | 67 | friend class ohf::MultipartBody; 68 | }; 69 | 70 | std::string boundary(); 71 | 72 | Part part(Uint64 index); 73 | 74 | std::vector parts(); 75 | 76 | Uint64 size(); 77 | 78 | MediaType type(); 79 | 80 | private: 81 | MultipartBody(const Builder *builder); 82 | 83 | std::string mBoundary; 84 | MediaType mType; 85 | std::vector mParts; 86 | }; 87 | } 88 | 89 | #endif //OKHTTPFORK_MULTIPARTBODY_HPP 90 | -------------------------------------------------------------------------------- /include/ohf/Principal.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_PRINCIPAL_HPP 6 | #define OKHTTPFORK_PRINCIPAL_HPP 7 | 8 | namespace ohf { 9 | class Principal { 10 | 11 | }; 12 | } 13 | 14 | #endif //OKHTTPFORK_PRINCIPAL_HPP 15 | -------------------------------------------------------------------------------- /include/ohf/Protocol.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_PROTOCOL_HPP 6 | #define OKHTTPFORK_PROTOCOL_HPP 7 | 8 | namespace ohf { 9 | enum class Protocol { 10 | HTTP_1_0, 11 | HTTP_1_1, 12 | HTTP_2 13 | }; 14 | } 15 | 16 | #endif //OKHTTPFORK_PROTOCOL_HPP 17 | -------------------------------------------------------------------------------- /include/ohf/Proxy.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_PROXY_HPP 6 | #define OKHTTPFORK_PROXY_HPP 7 | 8 | namespace ohf { 9 | class Proxy { 10 | public: 11 | class Selector { 12 | public: 13 | 14 | bool operator ==(const Selector &selector) const; 15 | }; 16 | 17 | bool operator ==(const Proxy &proxy) const; 18 | }; 19 | } 20 | 21 | #endif //OKHTTPFORK_PROXY_HPP 22 | -------------------------------------------------------------------------------- /include/ohf/RangeException.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_RANGEEXCEPTION_HPP 6 | #define OKHTTPFORK_RANGEEXCEPTION_HPP 7 | 8 | #include 9 | #include "Exception.hpp" 10 | 11 | namespace ohf { 12 | class RangeException : public Exception { 13 | public: 14 | explicit RangeException(Int64 index); 15 | 16 | Int64 index() const noexcept; 17 | 18 | private: 19 | Int64 m_index; 20 | }; 21 | } 22 | 23 | #endif // OKHTTPFORK_RANGEEXCEPTION_HPP 24 | -------------------------------------------------------------------------------- /include/ohf/Request.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_REQUEST_HPP 6 | #define OKHTTPFORK_REQUEST_HPP 7 | 8 | #include 9 | #include "RequestBody.hpp" 10 | #include "CacheControl.hpp" 11 | #include "HttpURL.hpp" 12 | 13 | namespace ohf { 14 | class Request { 15 | public: 16 | class Builder { 17 | public: 18 | Builder() = default; 19 | 20 | ~Builder(); 21 | 22 | Request build(); 23 | 24 | Builder& cacheControl(const CacheControl &cacheControl); 25 | 26 | Builder& delete_(); 27 | 28 | Builder& delete_(const RequestBody &body); 29 | 30 | Builder& get(); 31 | 32 | Builder& head(); 33 | 34 | Builder& patch(const RequestBody &body); 35 | 36 | Builder& post(const RequestBody &body); 37 | 38 | Builder& put(const RequestBody &body); 39 | 40 | Builder& method(const std::string &method); 41 | 42 | Builder& method(const std::string &method, const RequestBody &body); 43 | 44 | Builder& addHeader(const std::string &name, const std::string &value); 45 | 46 | Builder& header(const std::string &name, const std::string &value); 47 | 48 | Builder& headers(const Headers &headers); 49 | 50 | Builder& removeHeader(const std::string &name); 51 | 52 | Builder& url(const HttpURL &url); 53 | private: 54 | Builder(Request *request); 55 | 56 | CacheControl *mCC; 57 | std::string mMethod; 58 | RequestBody *mBody; 59 | Headers::Builder mHeaders; 60 | HttpURL *mURL; 61 | 62 | friend class ohf::Request; 63 | }; 64 | 65 | RequestBody body() const; 66 | 67 | CacheControl cacheControl() const; 68 | 69 | std::string header(const std::string &name) const; 70 | 71 | std::vector headers(const std::string &name) const; 72 | 73 | Headers headers() const; 74 | 75 | std::string method() const; 76 | 77 | HttpURL url() const; 78 | 79 | bool isHttps() const; 80 | 81 | Builder newBuilder(); 82 | 83 | private: 84 | Request(const Builder *builder); 85 | 86 | CacheControl mCC; 87 | std::string mMethod; 88 | RequestBody mBody; 89 | Headers mHeaders; 90 | HttpURL mURL; 91 | }; 92 | } 93 | 94 | #endif //OKHTTPFORK_REQUEST_HPP 95 | -------------------------------------------------------------------------------- /include/ohf/RequestBody.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_REQUESTBODY_HPP 6 | #define OKHTTPFORK_REQUESTBODY_HPP 7 | 8 | #include 9 | #include 10 | #include "Config.hpp" 11 | #include "MediaType.hpp" 12 | 13 | namespace ohf { 14 | class Request; 15 | class MultipartBody; 16 | 17 | class RequestBody { 18 | public: 19 | RequestBody(); 20 | 21 | RequestBody(const MediaType &contentType, const char *content, size_t count); 22 | 23 | RequestBody(const MediaType &contentType, const std::string &content); 24 | 25 | RequestBody(const MediaType &contentType, const std::vector &content); 26 | 27 | RequestBody(const MediaType &contentType, std::istream &stream); 28 | 29 | Uint64 contentLength(); 30 | 31 | MediaType contentType(); 32 | 33 | protected: 34 | std::vector content; 35 | MediaType mediaType; 36 | 37 | friend class ohf::MultipartBody; 38 | friend class ohf::Request; 39 | }; 40 | } 41 | 42 | #endif //OKHTTPFORK_REQUESTBODY_HPP 43 | -------------------------------------------------------------------------------- /include/ohf/Response.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_RESPONSE_HPP 6 | #define OKHTTPFORK_RESPONSE_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace ohf { 15 | class Response { 16 | public: 17 | class Builder { 18 | public: 19 | Builder(); 20 | 21 | ~Builder(); 22 | 23 | Builder& protocol(Protocol protocol); 24 | 25 | Builder& code(Int32 code); 26 | 27 | Builder& message(const std::string &message); 28 | 29 | Builder& body(const ResponseBody &body); 30 | 31 | Builder& request(const Request &request); 32 | 33 | Builder& handshake(const ssl::Handshake &handshake); 34 | 35 | //Builder& cacheResponse(const Response &cacheResponse); 36 | 37 | //Builder& networkResponse(const Response &networkResponse); 38 | 39 | //Builder& priorResponse(const Response &priorResponse); 40 | 41 | Builder& addHeader(const std::string &name, const std::string &value); 42 | 43 | Builder& header(const std::string &name, const std::string &value); 44 | 45 | Builder& removeHeader(const std::string &name); 46 | 47 | Builder& headers(const Headers &headers); 48 | 49 | Builder& sentRequest(const TimeUnit &time); 50 | 51 | Builder& receivedResponse(const TimeUnit &time); 52 | 53 | Response build(); 54 | 55 | private: 56 | Builder(const Response *response); 57 | 58 | Protocol mProtocol; 59 | Int32 mCode; 60 | std::string mMessage; 61 | 62 | ResponseBody *mBody; 63 | Request *mRequest; 64 | ssl::Handshake mHandshake; 65 | 66 | //const Response &mCacheResponse; 67 | //const Response &mNetworkResponse; 68 | //const Response &mPriorResponse; 69 | 70 | Headers::Builder mHeaders; 71 | 72 | TimeUnit mSent; 73 | TimeUnit mReceived; 74 | 75 | friend class ohf::Response; 76 | }; 77 | 78 | Protocol protocol() const; 79 | 80 | Int32 code() const; 81 | 82 | std::string message() const; 83 | 84 | ResponseBody body() const; 85 | 86 | ResponseBody peekBody(Uint64 byteCount) const; 87 | 88 | Request request() const; 89 | 90 | ssl::Handshake handshake() const; 91 | 92 | //Response cacheResponse(); 93 | 94 | //Response networkResponse(); 95 | 96 | //Response priorResponse(); 97 | 98 | std::string header(const std::string &name) const; 99 | 100 | std::string header(const std::string &name, const std::string &defaultValue) const; 101 | 102 | std::vector headers(const std::string &name) const; 103 | 104 | Headers headers() const; 105 | 106 | TimeUnit sentRequest() const; 107 | 108 | TimeUnit receivedResponse() const; 109 | 110 | CacheControl cacheControl() const; 111 | 112 | std::vector challenges() const; 113 | 114 | bool isRedirect() const; 115 | 116 | bool isSuccessful() const; 117 | 118 | Builder newBuilder(); 119 | 120 | private: 121 | Response(const Builder *builder); 122 | 123 | Protocol mProtocol; 124 | Int32 mCode; 125 | std::string mMessage; 126 | 127 | ResponseBody mBody; 128 | Request mRequest; 129 | ssl::Handshake mHandshake; 130 | 131 | Headers mHeaders; 132 | 133 | TimeUnit mSent; 134 | TimeUnit mReceived; 135 | }; 136 | } 137 | 138 | 139 | #endif //OKHTTPFORK_RESPONSE_HPP 140 | -------------------------------------------------------------------------------- /include/ohf/ResponseBody.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_RESPONSEBODY_HPP 6 | #define OKHTTPFORK_RESPONSEBODY_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace ohf { 15 | class ResponseBody { 16 | public: 17 | class StreamBuf : public IOStreamBuf { 18 | public: 19 | StreamBuf(Int32 write, Int32 read); 20 | 21 | void vector(const std::vector &vector); 22 | 23 | protected: 24 | Int32 write(const char *data, Int32 length) override; 25 | 26 | Int32 read(char *data, Int32 length) override; 27 | 28 | private: 29 | std::vector mVector; 30 | }; 31 | 32 | ResponseBody(const MediaType &mediaType, const char *content, size_t count, 33 | std::streambuf *buffer = new StreamBuf(1024, 1024)); 34 | 35 | ResponseBody(const MediaType &mediaType, const std::vector &content, 36 | std::streambuf *buffer = new StreamBuf(1024, 1024)); 37 | 38 | ResponseBody(const MediaType &mediaType, const std::string &content, 39 | std::streambuf *buffer = new StreamBuf(1024, 1024)); 40 | 41 | ResponseBody(const MediaType &mediaType, std::istream &stream, 42 | std::streambuf *buffer = new StreamBuf(1024, 1024)); 43 | 44 | std::vector bytes() const; 45 | 46 | std::string string() const; 47 | 48 | std::istream &stream() const; 49 | 50 | Uint64 contentLength() const; 51 | 52 | MediaType contentType() const; 53 | 54 | private: 55 | std::shared_ptr is; 56 | std::vector content; 57 | MediaType mediaType; 58 | }; 59 | } 60 | 61 | #endif //OKHTTPFORK_RESPONSEBODY_HPP 62 | -------------------------------------------------------------------------------- /include/ohf/Socket.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_SOCKET_HPP 6 | #define OKHTTPFORK_SOCKET_HPP 7 | 8 | #include "TimeUnit.hpp" 9 | 10 | namespace ohf { 11 | class Socket { 12 | public: 13 | #if defined(OKHTTPFORK_WINDOWS) 14 | #ifdef _WIN64 15 | typedef Uint64 Handle; 16 | #else 17 | typedef Uint32 Handle; 18 | #endif 19 | #elif defined(OKHTTPFORK_UNIX) 20 | typedef Int32 Handle; 21 | #endif 22 | 23 | enum class Type { 24 | TCP, 25 | UDP 26 | }; 27 | 28 | enum class Family { 29 | UNKNOWN, 30 | IPv4, 31 | IPv6 32 | }; 33 | 34 | Socket(Type type); 35 | 36 | virtual ~Socket(); 37 | 38 | Handle fd() const; 39 | 40 | virtual void create(Family af); 41 | 42 | virtual void create(Handle fd); 43 | 44 | void blocking(bool mode); 45 | 46 | bool isBlocking() const; 47 | 48 | virtual void close(); 49 | 50 | bool isValid() const; 51 | 52 | explicit operator bool(); 53 | 54 | Type type() const; 55 | 56 | Family family() const; 57 | 58 | protected: 59 | Socket() = default; 60 | 61 | Handle mFD; 62 | bool mBlocking; 63 | Type mType; 64 | }; 65 | 66 | constexpr Socket::Family IP_UNKNOWN = Socket::Family::UNKNOWN; 67 | constexpr Socket::Family IPv4 = Socket::Family::IPv4; 68 | constexpr Socket::Family IPv6 = Socket::Family::IPv6; 69 | } 70 | 71 | #endif //OKHTTPFORK_SOCKET_HPP 72 | -------------------------------------------------------------------------------- /include/ohf/TLSVersion.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_TLSVERSION_HPP 6 | #define OKHTTPFORK_TLSVERSION_HPP 7 | 8 | namespace ohf { 9 | enum class TLSVersion { 10 | SSLv23, 11 | SSLv2, 12 | SSLv3, 13 | TLS, 14 | TLSv1, 15 | TLSv1_1, 16 | TLSv1_2 17 | }; 18 | } 19 | 20 | #endif //OKHTTPFORK_TLSVERSION_HPP 21 | -------------------------------------------------------------------------------- /include/ohf/TimeUnit.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_TIMEUNIT_HPP 6 | #define OKHTTPFORK_TIMEUNIT_HPP 7 | 8 | #include 9 | #include 10 | #include "Config.hpp" 11 | 12 | namespace ohf { 13 | class TimeUnit { 14 | public: 15 | enum class Type { 16 | SECONDS, 17 | MILLISECONDS, 18 | MICROSECONDS 19 | }; 20 | 21 | static const TimeUnit ZERO; 22 | static const TimeUnit MINUS_ONE_SECOND; 23 | 24 | static TimeUnit seconds(float time); 25 | static TimeUnit milliseconds(Int32 time); 26 | static TimeUnit microseconds(Int64 time); 27 | 28 | TimeUnit(Int64 count, Type type); 29 | TimeUnit(float seconds); 30 | 31 | std::time_t std_time() const; 32 | 33 | float seconds() const; 34 | Int32 milliseconds() const; 35 | Int64 microseconds() const; 36 | 37 | // timeval 38 | long sec() const; 39 | long usec() const; 40 | 41 | // automatic conversion 42 | Type type() const; 43 | Int64 value() const; 44 | float floatValue() const; 45 | 46 | // bool 47 | bool operator ==(const TimeUnit &right) const; 48 | bool operator !=(const TimeUnit &right) const; 49 | bool operator >=(const TimeUnit &right) const; 50 | bool operator <=(const TimeUnit &right) const; 51 | bool operator >(const TimeUnit &right) const; 52 | bool operator <(const TimeUnit &right) const; 53 | 54 | // math 55 | TimeUnit operator +(const TimeUnit &right) const; 56 | TimeUnit operator -(const TimeUnit &right) const; 57 | TimeUnit operator *(const TimeUnit &right) const; 58 | TimeUnit operator /(const TimeUnit &right) const; 59 | 60 | TimeUnit operator %(const TimeUnit &right) const; 61 | 62 | // self math 63 | TimeUnit& operator +=(const TimeUnit &right); 64 | TimeUnit& operator -=(const TimeUnit &right); 65 | TimeUnit& operator *=(const TimeUnit &right); 66 | TimeUnit& operator /=(const TimeUnit &right); 67 | 68 | TimeUnit& operator %=(const TimeUnit &right); 69 | 70 | private: 71 | Type mType; 72 | std::time_t mTime; // 2038 73 | Int64 mMicroseconds; 74 | long mSec; 75 | long mUSec; 76 | }; 77 | 78 | TimeUnit operator "" _s(long double seconds); 79 | TimeUnit operator "" _ms(Uint64 milliseconds); 80 | TimeUnit operator "" _us(Uint64 microseconds); 81 | } 82 | 83 | #endif //OKHTTPFORK_TIMEUNIT_HPP 84 | -------------------------------------------------------------------------------- /include/ohf/WebSocket.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_WEBSOCKET_HPP 6 | #define OKHTTPFORK_WEBSOCKET_HPP 7 | 8 | #include 9 | #include "Request.hpp" 10 | #include "Response.hpp" 11 | #include "Exception.hpp" 12 | #include 13 | 14 | namespace ohf { 15 | class WebSocket { 16 | public: 17 | class Listener { 18 | public: 19 | virtual void onClosed(const WebSocket &webSocket, Int32 code, const std::string &reason) = 0; 20 | virtual void onClosing(const WebSocket &webSocket, Int32 code, const std::string &reason) = 0; 21 | virtual void onFailure(const WebSocket &webSocket, const Exception &e, const Response &response) = 0; 22 | virtual void onMessage(const WebSocket &webSocket, const std::vector &bytes) = 0; 23 | virtual void onMessage(const WebSocket &webSocket, const std::string &text) = 0; 24 | virtual void onOpen(const WebSocket &webSocket, const Response &response) = 0; 25 | }; 26 | 27 | class Factory { 28 | public: 29 | virtual WebSocket *newWebSocket(const Request &request, const Listener &listener) = 0; 30 | }; 31 | 32 | virtual void cancel() = 0; 33 | virtual bool close(Int32 code, const std::string &reason) = 0; 34 | virtual long queueSize() = 0; 35 | virtual Request request() = 0; 36 | virtual bool send(const std::vector &bytes) = 0; 37 | virtual bool send(const std::string &text) = 0; 38 | }; 39 | } 40 | 41 | #endif //OKHTTPFORK_WEBSOCKET_HPP 42 | -------------------------------------------------------------------------------- /include/ohf/ssl/Challenge.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_CHALLENGE_HPP 6 | #define OKHTTPFORK_CHALLENGE_HPP 7 | 8 | #include "string" 9 | 10 | namespace ohf { 11 | namespace ssl { 12 | class Challenge { 13 | public: 14 | Challenge(const std::string &scheme, const std::string &realm, const std::string &charset = "ISO-8859-1"); 15 | 16 | std::string scheme() const; 17 | 18 | std::string realm() const; 19 | 20 | std::string charset() const; 21 | 22 | bool operator ==(const Challenge &right) const; 23 | 24 | private: 25 | std::string mScheme; 26 | std::string mRealm; 27 | std::string mCharset; 28 | }; 29 | } 30 | } 31 | 32 | #endif //OKHTTPFORK_CHALLENGE_HPP 33 | -------------------------------------------------------------------------------- /include/ohf/ssl/CipherSuite.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_SSL_CIPHERSUITE_HPP 6 | #define OKHTTPFORK_SSL_CIPHERSUITE_HPP 7 | 8 | #include 9 | #include "ohf/Config.hpp" 10 | 11 | namespace ohf { 12 | namespace ssl { 13 | class SSL; 14 | 15 | class CipherSuite { 16 | public: 17 | ~CipherSuite(); 18 | 19 | Uint32 id() const; 20 | 21 | std::string name() const; 22 | 23 | std::string version() const; 24 | 25 | private: 26 | CipherSuite(); 27 | 28 | struct impl; 29 | impl *pImpl; 30 | 31 | friend class SSL; 32 | }; 33 | } 34 | } 35 | 36 | #endif //OKHTTPFORK_SSL_CIPHERSUITE_HPP 37 | -------------------------------------------------------------------------------- /include/ohf/ssl/Context.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_SSL_CONTEXT_HPP 6 | #define OKHTTPFORK_SSL_CONTEXT_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace ohf { 13 | namespace ssl { 14 | class SSL; 15 | 16 | class Context { 17 | public: 18 | enum class FileType { 19 | ASN1, 20 | PEM 21 | }; 22 | 23 | explicit Context(TLSVersion version); 24 | 25 | explicit Context(DTLSVersion version); 26 | 27 | ~Context(); 28 | 29 | void loadCertificate(const std::string &file, const std::string &key); 30 | 31 | private: 32 | struct impl; 33 | impl *pImpl; 34 | 35 | friend class ohf::ssl::SSL; 36 | }; 37 | } 38 | } 39 | 40 | #endif //OKHTTPFORK_SSL_CONTEXT_HPP 41 | -------------------------------------------------------------------------------- /include/ohf/ssl/Exception.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_SSL_EXCEPTION_HPP 6 | #define OKHTTPFORK_SSL_EXCEPTION_HPP 7 | 8 | #include 9 | #include 10 | 11 | namespace ohf { 12 | namespace ssl { 13 | class Exception : public ohf::Exception { 14 | public: 15 | explicit Exception(const Code &code); 16 | 17 | Exception(const Code &code, const SSL &ssl, Int32 retCode); 18 | 19 | Int32 sslCode() const noexcept; 20 | 21 | std::string sslMessage() const noexcept; 22 | 23 | private: 24 | Int32 ssl_code; 25 | std::string ssl_message; 26 | }; 27 | } 28 | } 29 | 30 | #endif //OKHTTPFORK_SSL_EXCEPTION_HPP 31 | -------------------------------------------------------------------------------- /include/ohf/ssl/Handshake.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_SSL_HANDSHAKE_HPP 6 | #define OKHTTPFORK_SSL_HANDSHAKE_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace ohf { 16 | namespace ssl { 17 | class Handshake { 18 | public: 19 | Handshake() = default; 20 | 21 | explicit Handshake(SSL &ssl); 22 | 23 | Handshake(const TLSVersion &tlsVersion, 24 | const CipherSuite &cipherSuite, 25 | const std::vector &peerCertificates, 26 | const std::vector &localCertificates); 27 | 28 | CipherSuite cipherSuite(); 29 | 30 | bool operator ==(const Handshake &handshake) const; 31 | 32 | // static Handshake get(const SSLSession &session); 33 | static Handshake get(const TLSVersion &tlsVersion, const CipherSuite &cipherSuite, 34 | const std::vector &peerCertificates, 35 | const std::vector &localCertificates); 36 | 37 | std::vector localCertificates(); 38 | 39 | Principal localPrincipal(); 40 | 41 | std::vector peerCertificates(); 42 | 43 | Principal peerPrincipal(); 44 | 45 | TLSVersion tlsVersion(); 46 | 47 | private: 48 | TLSVersion mVersion; 49 | //CipherSuite mSuite; 50 | }; 51 | } 52 | } 53 | 54 | #endif //OKHTTPFORK_HANDSHAKE_HPP 55 | -------------------------------------------------------------------------------- /include/ohf/ssl/Initializer.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_INITIALIZER_HPP 6 | #define OKHTTPFORK_INITIALIZER_HPP 7 | 8 | namespace ohf { 9 | namespace ssl { 10 | struct Initializer { 11 | Initializer(); 12 | 13 | ~Initializer(); 14 | }; 15 | } 16 | } 17 | 18 | 19 | #endif //OKHTTPFORK_INITIALIZER_HPP 20 | -------------------------------------------------------------------------------- /include/ohf/ssl/SSL.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_SSL_HPP 6 | #define OKHTTPFORK_SSL_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace ohf { 14 | namespace ssl { 15 | class Exception; 16 | 17 | class SSL { 18 | public: 19 | explicit SSL(const Context &context); 20 | 21 | ~SSL(); 22 | 23 | void setHandle(Socket::Handle handle) const; 24 | 25 | Socket::Handle getHandle() const; 26 | 27 | void setTLSExtHostName(const std::string &hostname) const; 28 | 29 | void connect() const; 30 | 31 | Int32 write(const char *data, Int32 size) const; 32 | 33 | Int32 read(char *data, Int32 size) const; 34 | 35 | void accept() const; 36 | 37 | CipherSuite currentCipher() const; 38 | 39 | std::vector ciphers() const; 40 | 41 | private: 42 | void checkIO(Int32 length) const; 43 | 44 | struct impl; 45 | impl *pImpl; 46 | 47 | const Context &context; 48 | 49 | friend class ohf::ssl::Exception; 50 | }; 51 | } 52 | } 53 | 54 | #endif //OKHTTPFORK_SSL_HPP 55 | -------------------------------------------------------------------------------- /include/ohf/ssl/Socket.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_SSL_SOCKET_HPP 6 | #define OKHTTPFORK_SSL_SOCKET_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include "SSL.hpp" 12 | 13 | namespace ohf { 14 | namespace ssl { 15 | class Socket : public virtual ohf::Socket { 16 | public: 17 | Socket(Type type, const Context &context); 18 | 19 | using ohf::Socket::create; 20 | void create(Handle fd) override; 21 | 22 | void close() override; 23 | 24 | void sni(bool b); 25 | 26 | void sni(const InetAddress &address); 27 | 28 | bool isSNI() const; 29 | 30 | const SSL &ssl() const; 31 | protected: 32 | bool SNICalled; 33 | const Context &context; 34 | std::shared_ptr mSSL; 35 | }; 36 | } 37 | } 38 | 39 | #endif //OKHTTPFORK_SOCKET_HPP 40 | -------------------------------------------------------------------------------- /include/ohf/ssl/X509Certificate.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_SSL_X509CERTIFICATE_HPP 6 | #define OKHTTPFORK_SSL_X509CERTIFICATE_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace ohf { 13 | namespace ssl { 14 | class X509Certificate { 15 | public: 16 | enum class Type { 17 | PEM, 18 | ASN1 19 | }; 20 | 21 | explicit X509Certificate(Type type); 22 | 23 | ~X509Certificate(); 24 | 25 | bool operator ==(const X509Certificate &certificate); 26 | 27 | std::vector getEncoded(); 28 | // abstract PublicKey getPublicKey(); 29 | Type type(); 30 | // abstract void verify(const PublicKey &publicKey); 31 | // abstract void verify(const PublicKey &publicKey, const char *sigProvider); 32 | 33 | private: 34 | struct impl; 35 | impl *pImpl; 36 | }; 37 | } 38 | } 39 | 40 | #endif //OKHTTPFORK_CERTIFICATE_HPP 41 | -------------------------------------------------------------------------------- /include/ohf/tcp/SSLServer.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_SSLSERVER_HPP 6 | #define OKHTTPFORK_SSLSERVER_HPP 7 | 8 | #include 9 | #include "Server.hpp" 10 | 11 | namespace ohf { 12 | namespace tcp { 13 | class SSLServer; 14 | } 15 | } 16 | 17 | namespace std { 18 | void swap(ohf::tcp::SSLServer& a, ohf::tcp::SSLServer& b); 19 | } 20 | 21 | namespace ohf { 22 | namespace tcp { 23 | class SSLServer : public Server, public ssl::Socket { 24 | public: 25 | SSLServer(const ssl::Context &context); 26 | 27 | SSLServer(SSLServer&& server) noexcept; 28 | 29 | Connection accept() const override; 30 | 31 | SSLServer& operator =(SSLServer&& right) noexcept; 32 | 33 | private: 34 | friend void ::std::swap(SSLServer& a, SSLServer& b); 35 | }; 36 | } 37 | } 38 | 39 | #endif //OKHTTPFORK_SSLSERVER_HPP 40 | -------------------------------------------------------------------------------- /include/ohf/tcp/SSLSocket.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_TCP_SSLSOCKET_HPP 6 | #define OKHTTPFORK_TCP_SSLSOCKET_HPP 7 | 8 | #include 9 | #include 10 | 11 | namespace ohf { 12 | namespace tcp { 13 | class SSLSocket; 14 | } 15 | } 16 | 17 | namespace std { 18 | void swap(ohf::tcp::SSLSocket& a, ohf::tcp::SSLSocket& b); 19 | } 20 | 21 | namespace ohf { 22 | namespace tcp { 23 | class SSLServer; 24 | 25 | class SSLSocket : public tcp::Socket, public ssl::Socket { 26 | public: 27 | SSLSocket(const ssl::Context &context); 28 | 29 | SSLSocket(SSLSocket&& socket) noexcept; 30 | 31 | using ssl::Socket::create; 32 | 33 | using tcp::Socket::connect; 34 | void connect(const InetAddress &address, Uint16 port) override; 35 | 36 | using tcp::Socket::send; 37 | Int32 send(const char *data, Int32 size) const override; 38 | 39 | using tcp::Socket::receive; 40 | Int32 receive(char *data, Int32 size) const override; 41 | 42 | using ssl::Socket::close; 43 | 44 | SSLSocket& operator =(SSLSocket &&right) noexcept; 45 | 46 | private: 47 | friend void ::std::swap(SSLSocket& a, SSLSocket& b); 48 | 49 | friend class ohf::tcp::SSLServer; 50 | }; 51 | } 52 | } 53 | 54 | #endif //OKHTTPFORK_TCP_SSLSOCKET_HPP 55 | -------------------------------------------------------------------------------- /include/ohf/tcp/Server.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_TCP_SERVER_HPP 6 | #define OKHTTPFORK_TCP_SERVER_HPP 7 | 8 | #include 9 | #include 10 | 11 | namespace ohf { 12 | namespace tcp { 13 | class Server; 14 | } 15 | } 16 | 17 | namespace std { 18 | void swap(ohf::tcp::Server& a, ohf::tcp::Server& b); 19 | } 20 | 21 | namespace ohf { 22 | namespace tcp { 23 | class Server : public virtual ohf::Socket { 24 | public: 25 | class Connection { 26 | public: 27 | Connection(tcp::Socket *socket, const InetAddress &address, Uint16 port); 28 | 29 | ~Connection(); 30 | 31 | tcp::Socket& socket() const; 32 | 33 | InetAddress address() const; 34 | 35 | Uint16 port() const; 36 | 37 | void close() const; 38 | 39 | private: 40 | tcp::Socket *mSocket; 41 | InetAddress mAddress; 42 | Uint16 mPort; 43 | }; 44 | 45 | class Iterator { 46 | public: 47 | Iterator(const Server *server); 48 | 49 | Connection operator *() const; 50 | 51 | Connection operator ->() const; 52 | 53 | const Iterator& operator ++(Int32) const; 54 | 55 | const Iterator& operator ++() const; 56 | 57 | bool operator !=(const Iterator &right) const; 58 | 59 | private: 60 | const Server *server; 61 | }; 62 | 63 | Server(); 64 | 65 | Server(const InetAddress &address, Uint16 port); 66 | 67 | Server(Family family, const HttpURL &url); 68 | 69 | Server(Server&& server) noexcept; 70 | 71 | virtual void bind(const InetAddress &address, Uint16 port); 72 | 73 | void bind(const HttpURL &url); 74 | 75 | void listen(Int32 count) const; 76 | 77 | void listen() const; 78 | 79 | virtual Connection accept() const; 80 | 81 | const Iterator begin() const; 82 | 83 | Iterator begin(); 84 | 85 | const Iterator end() const; 86 | 87 | Iterator end(); 88 | 89 | Server& operator =(Server&& right) noexcept; 90 | 91 | private: 92 | friend void ::std::swap(Server& a, Server& b); 93 | 94 | protected: 95 | Family mFamily; 96 | }; 97 | } 98 | } 99 | 100 | #endif //OKHTTPFORK_TCP_SERVER_HPP 101 | -------------------------------------------------------------------------------- /include/ohf/tcp/Socket.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_TCP_SOCKET_HPP 6 | #define OKHTTPFORK_TCP_SOCKET_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace ohf { 19 | namespace tcp { 20 | class Socket; 21 | } 22 | } 23 | 24 | namespace std { 25 | void swap(ohf::tcp::Socket& a, ohf::tcp::Socket& b); 26 | } 27 | 28 | namespace ohf { 29 | namespace tcp { 30 | class Socket : public virtual ohf::Socket { 31 | public: 32 | class StreamBuf : public IOStreamBuf { 33 | public: 34 | StreamBuf(Int32 write, Int32 read); 35 | 36 | void socket(tcp::Socket *socket); 37 | 38 | private: 39 | tcp::Socket *mSocket; 40 | 41 | protected: 42 | Int32 write(const char *data, Int32 length) override; 43 | 44 | Int32 read(char *data, Int32 length) override; 45 | }; 46 | 47 | class Stream : public std::iostream { 48 | public: 49 | explicit Stream(tcp::Socket &socket, StreamBuf *buffer = new StreamBuf(1024, 1024)); 50 | 51 | void socket(tcp::Socket &socket); 52 | }; 53 | 54 | explicit Socket(); 55 | 56 | Socket(Socket &&socket) noexcept; 57 | 58 | virtual void connect(const InetAddress &address, Uint16 port); 59 | 60 | void connect(Family family, const HttpURL &url); 61 | 62 | void disconnect(); 63 | 64 | virtual Int32 send(const char *data, Int32 size) const; 65 | 66 | Int32 send(const std::vector &data) const; 67 | 68 | Int32 send(const std::string &data) const; 69 | 70 | virtual Int32 receive(char *data, Int32 size) const; 71 | 72 | Int32 receive(std::vector &data, Int32 size) const; 73 | 74 | Int32 receive(std::string &data, Int32 size) const; 75 | 76 | Socket& operator =(Socket &&right) noexcept; 77 | 78 | private: 79 | friend void ::std::swap(Socket& a, Socket& b); 80 | }; 81 | } 82 | } 83 | 84 | #endif //OKHTTPFORK_TCP_SOCKET_HPP 85 | -------------------------------------------------------------------------------- /include/ohf/udp/Socket.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_UDPSOCKET_HPP 6 | #define OKHTTPFORK_UDPSOCKET_HPP 7 | 8 | #include 9 | #include 10 | 11 | namespace ohf { 12 | namespace udp { 13 | class Socket; 14 | } 15 | } 16 | 17 | namespace std { 18 | void swap(ohf::udp::Socket& a, ohf::udp::Socket& b); 19 | } 20 | 21 | namespace ohf { 22 | namespace udp { 23 | class Socket : public ohf::Socket { 24 | public: 25 | Socket(); 26 | 27 | Socket(Socket &&socket) noexcept; 28 | 29 | virtual void bind(const InetAddress &address, Uint16 port); 30 | 31 | void unbind(); 32 | 33 | void send(const InetAddress &address, Uint16 port, const char *data, Int32 size); 34 | 35 | void send(const InetAddress &address, Uint16 port, const std::vector &data); 36 | 37 | void send(const InetAddress &address, Uint16 port, const std::string &data); 38 | 39 | Int32 receive(InetAddress &address, Uint16 &port, char *data, Int32 size); 40 | 41 | Int32 receive(InetAddress &address, Uint16 &port, std::vector &data, Int32 size); 42 | 43 | Int32 receive(InetAddress &address, Uint16 &port, std::string &data, Int32 size); 44 | 45 | Socket& operator =(Socket &&right) noexcept; 46 | 47 | private: 48 | friend void ::std::swap(Socket& a, Socket& b); 49 | 50 | protected: 51 | Family mFamily; 52 | }; 53 | } 54 | } 55 | 56 | #endif //OKHTTPFORK_UDPSOCKET_HPP 57 | -------------------------------------------------------------------------------- /src/Address.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | Address::Address(const std::string &host, 9 | Uint16 port, 10 | DNS *dns, 11 | HostnameVerifier *hostnameVerifier, 12 | const CertificatePinner &certificatePinner, 13 | Authenticator *proxyAuthenticator, 14 | const Proxy &proxy, 15 | const std::vector &protocols, 16 | const std::vector &connectionSpecs, 17 | const Proxy::Selector &proxySelector) : 18 | mUrl(host + std::to_string(port)), 19 | mDNS(dns), 20 | mVerifier(hostnameVerifier), 21 | mPinner(certificatePinner), 22 | mAuthenticator(proxyAuthenticator), 23 | mProxy(proxy), 24 | mProtocols(protocols), 25 | mSpecs(connectionSpecs), 26 | mSelector(proxySelector) 27 | {} 28 | 29 | CertificatePinner Address::certificatePinner() const { 30 | return mPinner; 31 | } 32 | 33 | std::vector Address::connectionSpecs() const { 34 | return mSpecs; 35 | } 36 | 37 | std::shared_ptr Address::dns() const { 38 | return mDNS; 39 | } 40 | 41 | std::shared_ptr Address::hostnameVerifier() const { 42 | return mVerifier; 43 | } 44 | 45 | std::vector Address::protocols() const { 46 | return mProtocols; 47 | } 48 | 49 | Proxy Address::proxy() const { 50 | return mProxy; 51 | } 52 | 53 | std::shared_ptr Address::proxyAuthenticator() const { 54 | return mAuthenticator; 55 | } 56 | 57 | Proxy::Selector Address::proxySelector() const { 58 | return mSelector; 59 | } 60 | 61 | HttpURL Address::url() const { 62 | return mUrl; 63 | } 64 | 65 | std::string Address::toString() const { // TODO: Implement it 66 | return ""; 67 | } 68 | 69 | bool Address::operator ==(const Address &address) const { 70 | return mUrl == address.mUrl 71 | && mDNS == address.mDNS 72 | && mVerifier == address.mVerifier 73 | && mPinner == address.mPinner 74 | && mAuthenticator == address.mAuthenticator 75 | && mProxy == address.mProxy 76 | && mProtocols == address.mProtocols 77 | && mSpecs == address.mSpecs 78 | && mSelector == address.mSelector; 79 | } 80 | 81 | std::ostream& operator <<(std::ostream &stream, const Address &address) { 82 | return stream << address.toString(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Authenticator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /src/Cache.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /src/CacheControl.Builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | CacheControl::Builder::Builder() : 9 | mImmutable(false), 10 | mNoCache(false), 11 | mNoStore(false), 12 | mNoTransform(false), 13 | mOnlyIfCached(false), 14 | mMaxAge(TimeUnit::MINUS_ONE_SECOND), 15 | mMaxStale(TimeUnit::MINUS_ONE_SECOND), 16 | mMinFresh(TimeUnit::MINUS_ONE_SECOND) {} 17 | 18 | CacheControl CacheControl::Builder::build() { 19 | return {this}; 20 | } 21 | 22 | CacheControl::Builder &CacheControl::Builder::immutable() { 23 | mImmutable = true; 24 | return *this; 25 | } 26 | 27 | CacheControl::Builder &CacheControl::Builder::noCache() { 28 | mNoCache = true; 29 | return *this; 30 | } 31 | 32 | CacheControl::Builder &CacheControl::Builder::noStore() { 33 | mNoStore = true; 34 | return *this; 35 | } 36 | 37 | CacheControl::Builder &CacheControl::Builder::noTransform() { 38 | mNoTransform = true; 39 | return *this; 40 | } 41 | 42 | CacheControl::Builder &CacheControl::Builder::onlyIfCached() { 43 | mOnlyIfCached = true; 44 | return *this; 45 | } 46 | 47 | CacheControl::Builder &CacheControl::Builder::maxAge(const TimeUnit &seconds) { 48 | mMaxAge = seconds; 49 | return *this; 50 | } 51 | 52 | CacheControl::Builder &CacheControl::Builder::maxStale(const TimeUnit &seconds) { 53 | mMaxStale = seconds; 54 | return *this; 55 | } 56 | 57 | CacheControl::Builder &CacheControl::Builder::minFresh(const TimeUnit &seconds) { 58 | mMinFresh = seconds; 59 | return *this; 60 | } 61 | } -------------------------------------------------------------------------------- /src/CertificatePinner.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | bool CertificatePinner::operator ==(const ohf::CertificatePinner &pinner) const { // TODO: Implement it 9 | return false; 10 | } 11 | } -------------------------------------------------------------------------------- /src/Client.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /src/ConnectionPool.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /src/ConnectionSpec.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | bool ConnectionSpec::operator ==(const ConnectionSpec &spec) const { // TODO: Implement it 9 | return false; 10 | } 11 | } -------------------------------------------------------------------------------- /src/Cookie.Builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | Cookie::Builder::Builder() : 9 | m_expiresAt(TimeUnit::MINUS_ONE_SECOND), 10 | m_hostOnly(false), 11 | m_httpOnly(false), 12 | m_secure(false), 13 | m_persistent(false) {}; 14 | 15 | Cookie Cookie::Builder::build() { 16 | return {this}; 17 | } 18 | 19 | Cookie::Builder &Cookie::Builder::domain(const std::string &domain) { 20 | m_domain = domain; 21 | return *this; 22 | } 23 | 24 | Cookie::Builder &Cookie::Builder::expiresAt(const ohf::TimeUnit &expiresAt) { 25 | m_expiresAt = expiresAt; 26 | return *this; 27 | } 28 | 29 | Cookie::Builder &Cookie::Builder::hostOnlyDomain(const std::string &domain) { 30 | m_domain = domain; 31 | m_hostOnly = true; 32 | return *this; 33 | } 34 | 35 | Cookie::Builder &Cookie::Builder::httpOnly() { 36 | m_httpOnly = true; 37 | return *this; 38 | } 39 | 40 | Cookie::Builder &Cookie::Builder::name(const std::string &name) { 41 | m_name = name; 42 | return *this; 43 | } 44 | 45 | Cookie::Builder &Cookie::Builder::path(const std::string &path) { 46 | m_path = path; 47 | return *this; 48 | } 49 | 50 | Cookie::Builder &Cookie::Builder::secure() { 51 | m_secure = true; 52 | return *this; 53 | } 54 | 55 | Cookie::Builder &Cookie::Builder::value(const std::string &value) { 56 | m_value = value; 57 | return *this; 58 | } 59 | } -------------------------------------------------------------------------------- /src/CookieJar.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | /* CookieJar */ 9 | std::vector CookieJar::NO_COOKIES::loadFromRequest(HttpURL &httpURL) { 10 | return {}; 11 | } 12 | 13 | void CookieJar::NO_COOKIES::saveFromResponse(HttpURL &httpURL, std::vector &cookies) {} 14 | } -------------------------------------------------------------------------------- /src/DNS.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | std::vector DNS::SYSTEM::lookup(const std::string &hostname) { 9 | return InetAddress::getAllByName(hostname); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Dispatcher.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /src/Exception.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | Exception::Exception(const Code &code, const std::string &what) noexcept : m_code(code), m_what(what) {} 9 | 10 | Exception::Code Exception::code() const noexcept { 11 | return m_code; 12 | } 13 | 14 | std::string Exception::message() const noexcept { 15 | return m_what; 16 | } 17 | 18 | const char *Exception::what() const noexcept { 19 | return m_what.c_str(); 20 | } 21 | } -------------------------------------------------------------------------------- /src/FormBody.Builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | namespace ohf { 9 | FormBody::Builder& FormBody::Builder::add(const std::string &name, const std::string &value) { 10 | namesValues.push_back(name); 11 | namesValues.push_back(value); 12 | return *this; 13 | } 14 | 15 | FormBody FormBody::Builder::build() { 16 | return {this}; 17 | } 18 | } -------------------------------------------------------------------------------- /src/FormBody.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace ohf { 12 | std::string FormBody::encodedName(Uint32 index) const { 13 | return HttpURL::encode(name(index)); 14 | } 15 | 16 | std::string FormBody::encodedValue(Uint32 index) const { 17 | return HttpURL::encode(value(index)); 18 | } 19 | 20 | std::string FormBody::name(Uint32 index) const { 21 | Uint32 i = index * 2; 22 | if(i >= namesValues.size()) 23 | throw RangeException(i); 24 | return namesValues[i]; 25 | } 26 | 27 | std::string FormBody::value(Uint32 index) const { 28 | Uint32 i = index * 2 + 1; 29 | if (i >= namesValues.size()) 30 | throw RangeException(i); 31 | return namesValues[i]; 32 | } 33 | 34 | Uint32 FormBody::size() const { 35 | return (Uint32) namesValues.size() / 2; 36 | } 37 | 38 | FormBody::FormBody(const Builder *builder) : 39 | RequestBody("application/x-www-form-urlencoded", std::vector()), 40 | namesValues(builder->namesValues) 41 | { 42 | std::stringstream ss; 43 | for (Uint32 i = 0; i < size(); i++) { 44 | ss << HttpURL::encode(name(i)); 45 | ss << '=' << HttpURL::encode(value(i)); 46 | ss << '&'; 47 | } 48 | 49 | std::string ready = ss.str(); 50 | ready.erase(ready.length() - 1); 51 | content.insert(content.end(), ready.begin(), ready.end()); 52 | } 53 | } -------------------------------------------------------------------------------- /src/Headers.Builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include "util/string.hpp" 8 | 9 | namespace ohf { 10 | Headers::Builder &Headers::Builder::add(std::string line) { 11 | Uint64 offset = line.find(": "); 12 | 13 | std::string name = line.substr(0, offset); 14 | line.erase(0, offset + 2); 15 | 16 | std::string value = line.substr(0, line.length()); 17 | 18 | this->add(name, value); 19 | return *this; 20 | } 21 | 22 | Headers::Builder &Headers::Builder::add(const std::string &name, const std::string &value) { 23 | if (name.empty() || value.empty()) { 24 | throw Exception(Exception::Code::HEADER_IS_EMPTY, "Header is empty: name: \"" + name 25 | + "\" value: \"" + value + "\""); 26 | } 27 | 28 | namesValues.push_back(name); 29 | namesValues.push_back(value); 30 | 31 | return *this; 32 | } 33 | 34 | Headers Headers::Builder::build() const { 35 | return {this}; 36 | } 37 | 38 | std::string Headers::Builder::get(std::string name) const { 39 | util::string::toLower(name); 40 | for (auto it = namesValues.begin(); it != namesValues.end(); it += 2) { 41 | std::string element = *it; 42 | util::string::toLower(element); 43 | if (name == element) return *(++it); 44 | } 45 | return std::string(); 46 | } 47 | 48 | Headers::Builder &Headers::Builder::removeAll(std::string name) { 49 | util::string::toLower(name); 50 | std::vector nav; 51 | for (auto it = namesValues.begin(); it != namesValues.end(); it += 2) { 52 | std::string element = *it; 53 | util::string::toLower(element); 54 | if(name != element) { 55 | nav.insert(nav.end(), it, it + 1); 56 | nav.insert(nav.end(), it + 1, it + 2); 57 | } 58 | } 59 | namesValues.swap(nav); 60 | 61 | return *this; 62 | } 63 | 64 | Headers::Builder &Headers::Builder::set(const std::string &name, const std::string &value) { 65 | removeAll(name); 66 | return add(name, value); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Headers.Iterator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | namespace ohf { 9 | Headers::Iterator::Iterator(Uint32 index, const Headers *headers) : 10 | index(index), 11 | headers(headers) 12 | { 13 | swapPair(); 14 | } 15 | 16 | void Headers::Iterator::swapPair() { 17 | if(headers && 0 <= index && index < headers->size()) { 18 | Pair valueType = headers->pair(index); 19 | type.swap(valueType); 20 | outOfRange = false; 21 | } else { 22 | outOfRange = true; 23 | } 24 | } 25 | 26 | bool Headers::Iterator::isOutOfRange() const { 27 | return outOfRange; 28 | } 29 | 30 | // operator * 31 | const Headers::Pair& Headers::Iterator::operator *() const { 32 | return type; 33 | } 34 | 35 | const Headers::Pair& Headers::Iterator::operator *() { 36 | return type; 37 | } 38 | 39 | // operator -> 40 | const Headers::Pair* Headers::Iterator::operator ->() const { 41 | return &type; 42 | } 43 | 44 | const Headers::Pair* Headers::Iterator::operator ->() { 45 | return &type; 46 | } 47 | 48 | // operator ++ 49 | const Headers::Iterator Headers::Iterator::operator ++(Int32) const { 50 | return {index + 1, headers}; 51 | } 52 | 53 | const Headers::Iterator& Headers::Iterator::operator ++() { 54 | index++; 55 | 56 | swapPair(); 57 | 58 | return *this; 59 | } 60 | 61 | // operator -- 62 | const Headers::Iterator Headers::Iterator::operator --(Int32) const { 63 | return {index - 1, headers}; 64 | } 65 | 66 | const Headers::Iterator& Headers::Iterator::operator --() { 67 | index--; 68 | 69 | swapPair(); 70 | 71 | return *this; 72 | } 73 | 74 | // operator + 75 | const Headers::Iterator Headers::Iterator::operator +(Uint32 index) const { 76 | return {this->index + index, headers}; 77 | } 78 | 79 | // operator - 80 | const Headers::Iterator Headers::Iterator::operator -(Uint32 index) const { 81 | return {this->index - index, headers}; 82 | } 83 | 84 | // operator += 85 | const Headers::Iterator& Headers::Iterator::operator +=(Uint32 index) { 86 | this->index += index; 87 | 88 | swapPair(); 89 | 90 | return *this; 91 | } 92 | 93 | // operator -= 94 | const Headers::Iterator& Headers::Iterator::operator -=(Uint32 index) { 95 | this->index -= index; 96 | 97 | swapPair(); 98 | 99 | return *this; 100 | } 101 | 102 | // operator == 103 | bool Headers::Iterator::operator ==(const Iterator &right) const { 104 | return (type == right.type) && (index == right.index); 105 | } 106 | 107 | // operator != 108 | bool Headers::Iterator::operator !=(const Iterator &right) const { 109 | return (type != right.type) && (index != right.index); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/HostnameVerifier.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /src/HttpURL.Builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include "util/string.hpp" 7 | #include 8 | 9 | namespace ohf { 10 | HttpURL::Builder::Builder() : 11 | mPathSuffix(false), 12 | mPort(80), 13 | mScheme("http") 14 | {} 15 | 16 | HttpURL::Builder& HttpURL::Builder::addPathSegments(const std::string &pathSegments) { 17 | mPathSuffix = util::string::endsWith(pathSegments, "/"); 18 | std::vector segments = util::string::split(pathSegments, "/"); 19 | if (!segments.empty()) { 20 | for (const auto &segment : segments) 21 | mPath.push_back(HttpURL::decode(segment)); 22 | } 23 | return *this; 24 | } 25 | 26 | HttpURL HttpURL::Builder::build() { 27 | return {this}; 28 | } 29 | 30 | HttpURL::Builder& HttpURL::Builder::fragment(const std::string &fragment) { 31 | mFragment = HttpURL::decode(fragment); 32 | return *this; 33 | } 34 | 35 | HttpURL::Builder& HttpURL::Builder::host(const std::string &host) { 36 | mHost = HttpURL::decode(host); 37 | return *this; 38 | } 39 | 40 | HttpURL::Builder& HttpURL::Builder::port(Uint16 port) { 41 | mPort = port; 42 | return *this; 43 | } 44 | 45 | HttpURL::Builder& HttpURL::Builder::query(const std::string &query) { 46 | std::vector queries = util::string::split(query, "&"); 47 | for (const auto ¶meter : queries) { 48 | std::vector nameValue = util::string::split(parameter, "="); 49 | if (nameValue.size() == 2) 50 | mQuery[nameValue[0]] = nameValue[1]; 51 | else if (nameValue.size() == 1) 52 | mQuery[nameValue[0]] = std::string(); 53 | else 54 | throw Exception(Exception::Code::INVALID_QUERY_PARAMETER, "Invalid query parameter: " + parameter); 55 | } 56 | return *this; 57 | } 58 | 59 | HttpURL::Builder& HttpURL::Builder::removeQueryParameter(const std::string &name) { 60 | auto it = mQuery.find(name); 61 | if (mQuery.find(name) != mQuery.end()) 62 | mQuery.erase(it); 63 | return *this; 64 | } 65 | 66 | HttpURL::Builder& HttpURL::Builder::removePathSegment(Uint32 index) { 67 | if (index < this->mPath.size()) { 68 | mPathSuffix = index != this->mPath.size() - 1; 69 | auto path_segment = std::next(this->mPath.begin(), index); 70 | this->mPath.erase(path_segment); 71 | } 72 | if(mPath.empty()) mPathSuffix = false; 73 | return *this; 74 | } 75 | 76 | HttpURL::Builder& HttpURL::Builder::scheme(const std::string &scheme) { 77 | mScheme = scheme; 78 | return *this; 79 | } 80 | 81 | HttpURL::Builder& HttpURL::Builder::setPathSegment(Uint32 index, std::string pathSegment) { 82 | bool ends = false; 83 | if (util::string::endsWith(pathSegment, "/")) { 84 | pathSegment = pathSegment.substr(0, pathSegment.length() - 1); 85 | ends = true; 86 | } 87 | pathSegment = HttpURL::decode(pathSegment); 88 | 89 | if(index < mPath.size()) { 90 | mPath[index] = pathSegment; 91 | } else { 92 | mPathSuffix = ends; 93 | mPath.push_back(pathSegment); 94 | } 95 | 96 | return *this; 97 | } 98 | 99 | HttpURL::Builder& HttpURL::Builder::setQueryParameter(const std::string &name, const std::string &value) { 100 | mQuery[name] = HttpURL::decode(value); 101 | return *this; 102 | } 103 | 104 | HttpURL::Builder& HttpURL::Builder::pathSuffix(bool b) { 105 | mPathSuffix = b; 106 | return *this; 107 | } 108 | } -------------------------------------------------------------------------------- /src/IOStreamBuf.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf{ 8 | IOStreamBuf::IOStreamBuf(Int32 write, Int32 read) : 9 | wb((Uint32) write), 10 | rb((Uint32) read) 11 | { 12 | // write 13 | Int8 *buf = wb.data(); 14 | setp(buf, buf + (wb.size() - 1)); 15 | 16 | // read 17 | Int8 *start = rb.data(); 18 | Int8 *end = start + rb.size(); 19 | setg(start, end, end); 20 | }; 21 | 22 | Int32 IOStreamBuf::sync() { 23 | if(pptr() && pptr() > pbase()) { 24 | Int32 sz = Int32(pptr() - pbase()); 25 | 26 | if (write(pbase(), sz) == sz) { 27 | pbump(-sz); 28 | return 0; 29 | } 30 | } 31 | 32 | return -1; 33 | } 34 | 35 | Int32 IOStreamBuf::overflow(Int32 c) { 36 | if(c != traits_type::eof()) { 37 | *pptr() = (Int8) c; 38 | pbump(1); 39 | 40 | if(sync() == -1) return traits_type::eof(); 41 | } 42 | 43 | return c; 44 | } 45 | 46 | Int32 IOStreamBuf::underflow() { 47 | if (gptr() < egptr()) { 48 | return *gptr(); 49 | } 50 | 51 | Int32 got = read(eback(), (Int32) rb.size()); 52 | setg(eback(), eback(), eback() + got); 53 | 54 | return got == 0 ? traits_type::eof() : *gptr(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/InetAddress.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "util/util.hpp" 9 | #include "SocketImpl.hpp" 10 | 11 | namespace ohf { 12 | static SocketImpl::Initializer socket_init; 13 | 14 | InetAddress::InetAddress() : mFamily(Socket::Family::UNKNOWN) {} 15 | 16 | InetAddress::InetAddress(const char *host) : InetAddress(std::string(host)) {} 17 | 18 | InetAddress::InetAddress(const std::string &host, ohf::Int32 af) : InetAddress(getAllByName(host, af)[0]) {} 19 | 20 | InetAddress::InetAddress(const std::string &host, Socket::Family type) : InetAddress(getAllByName(host, type)[0]) {} 21 | 22 | InetAddress::InetAddress(const std::string &host) : InetAddress(getAllByName(host)[0]) {} 23 | 24 | InetAddress::InetAddress(Uint8 b1, Uint8 b2, Uint8 b3, Uint8 b4) : 25 | mFamily(Socket::Family::IPv4), 26 | mOriginalType(AF_INET), 27 | mIP {b1, b2, b3, b4} 28 | {} 29 | 30 | InetAddress::InetAddress(Uint8 b1, Uint8 b2, Uint8 b3, Uint8 b4, Uint8 b5, Uint8 b6, Uint8 b7, Uint8 b8, 31 | Uint8 b9, Uint8 b10, Uint8 b11, Uint8 b12, Uint8 b13, Uint8 b14, Uint8 b15, Uint8 b16) : 32 | mFamily(Socket::Family::IPv6), 33 | mOriginalType(AF_INET6), 34 | mIP {b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16} 35 | {} 36 | 37 | std::vector InetAddress::getAllByName(const std::string &host, Int32 af) { 38 | std::vector ias; 39 | 40 | addrinfo *info; 41 | 42 | addrinfo hints; 43 | std::memset(&hints, 0, sizeof(hints)); 44 | hints.ai_flags = AI_CANONNAME; 45 | hints.ai_family = af; 46 | 47 | if(getaddrinfo(host.c_str(), nullptr, &hints, &info) == 0) { 48 | for(addrinfo *address = info; address != nullptr; address = address->ai_next) { 49 | InetAddress inet = SocketImpl::createInetAddress((sockaddr_storage *) address->ai_addr); 50 | 51 | inet.mHostName = host; 52 | 53 | char *canonname = address->ai_canonname; 54 | if(canonname) inet.mCanonName = canonname; 55 | 56 | ias.push_back(inet); 57 | } 58 | } else { 59 | throw Exception(Exception::Code::UNKNOWN_HOST, "Unknown host: " + host); 60 | } 61 | 62 | freeaddrinfo(info); 63 | 64 | return ias; 65 | } 66 | 67 | std::vector InetAddress::getAllByName(const std::string &host, Socket::Family family) { 68 | return getAllByName(host, SocketImpl::saf2af(family)); 69 | } 70 | 71 | std::vector InetAddress::getAllByName(const std::string &host) { 72 | return getAllByName(host, AF_UNSPEC); 73 | } 74 | 75 | std::array InetAddress::address() const { 76 | return mIP; 77 | } 78 | 79 | std::string InetAddress::hostAddress() const { 80 | std::string address(INET6_ADDRSTRLEN, 0); 81 | const char *result = inet_ntop(mOriginalType, (void *) mIP.data(), &address[0], address.size()); 82 | address.resize(std::strlen(result)); 83 | return address; 84 | } 85 | 86 | std::string InetAddress::hostName() const { 87 | return mHostName; 88 | } 89 | 90 | std::string InetAddress::canonicalName() const { 91 | return mCanonName; 92 | } 93 | 94 | Socket::Family InetAddress::family() const { 95 | return mFamily; 96 | } 97 | 98 | Int32 InetAddress::originalType() const { 99 | return mOriginalType; 100 | } 101 | 102 | std::ostream& operator <<(std::ostream &stream, const InetAddress &address) { 103 | return stream << address.hostAddress(); 104 | } 105 | } -------------------------------------------------------------------------------- /src/Interceptor.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /src/MediaType.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "util/string.hpp" 12 | 13 | namespace ohf { 14 | MediaType::MediaType(const std::string &str) { 15 | std::vector values = util::string::split(str, "; "); 16 | if (values.empty()) 17 | throw Exception(Exception::Code::INVALID_CONTENT_TYPE_LINE, "Invalid Content-Type line: " + str); 18 | // type / subtype 19 | std::vector typeSubtype = util::string::split(values[0], "/"); 20 | if (typeSubtype.size() != 2) 21 | throw Exception(Exception::Code::INVALID_MIME_TYPE, "Invalid MIME type: " + values[0]); 22 | mType = typeSubtype[0]; 23 | mSubType = typeSubtype[1]; 24 | // boundary / charset 25 | for (Uint32 i = 1; i < values.size(); i++) { 26 | std::string value = values[i]; 27 | if (util::string::startsWith(value, "charset=")) { 28 | std::string charset = value.substr(8, value.length()); 29 | util::string::toLower(charset); 30 | mCharset = charset; 31 | } else if (util::string::startsWith(value, "boundary=")) 32 | mBoundary = value.substr(9, value.length()); 33 | } 34 | } 35 | 36 | MediaType::MediaType(const char *str) : MediaType(std::string(str)) { 37 | } 38 | 39 | std::string MediaType::boundary() const { 40 | return mBoundary; 41 | } 42 | 43 | std::string MediaType::boundary(const std::string &defaultValue) const { 44 | return mBoundary.empty() ? defaultValue : mBoundary; 45 | } 46 | 47 | std::string MediaType::charset() const { 48 | return mCharset; 49 | } 50 | 51 | std::string MediaType::charset(const std::string &defaultValue) const { 52 | return mCharset.empty() ? defaultValue : mCharset; 53 | } 54 | 55 | std::string MediaType::subtype() const { 56 | return mSubType; 57 | } 58 | 59 | std::string MediaType::type() const { 60 | return mType; 61 | } 62 | 63 | bool MediaType::operator==(const MediaType &mediaType) const { 64 | return mediaType.mBoundary == this->mBoundary 65 | && mediaType.mCharset == this->mCharset 66 | && mediaType.mType == this->mType 67 | && mediaType.mSubType == this->mSubType; 68 | } 69 | 70 | std::string MediaType::toString() const { 71 | std::stringstream ss; 72 | 73 | std::string type = mType; 74 | std::string subtype = mSubType; 75 | if (!type.empty() && !subtype.empty()) 76 | ss << type << '/' << subtype << "; "; 77 | 78 | std::string charset = mCharset; 79 | if (!charset.empty()) 80 | ss << "charset=" << charset << "; "; 81 | 82 | std::string boundary = mBoundary; 83 | if (!boundary.empty()) 84 | ss << "boundary=" << boundary << "; "; 85 | 86 | std::string str = ss.str(); 87 | if (!str.empty()) 88 | str.erase(str.length() - 2, 2); 89 | return str; 90 | } 91 | 92 | std::ostream &operator<<(std::ostream &stream, const MediaType &mediaType) { 93 | return stream << mediaType.toString(); 94 | } 95 | } -------------------------------------------------------------------------------- /src/MultipartBody.Builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace ohf { 11 | MultipartBody::Builder::Builder() : mType(FORM) { 12 | long long int time = std::chrono::system_clock::now().time_since_epoch().count(); 13 | std::mt19937 mt(time); 14 | std::uniform_int_distribution random(0, 25); 15 | 16 | std::ostringstream ss; 17 | for (int i = 0; i < 10; i++) 18 | ss << char('a' + random(mt)); 19 | ss << time; 20 | for (int i = 0; i < 10; i++) 21 | ss << char('A' + random(mt)); 22 | 23 | mBoundary = ss.str(); 24 | mBoundary += std::to_string(std::hash()(mBoundary)); 25 | } 26 | 27 | MultipartBody::Builder::Builder(const std::string &boundary) : 28 | mBoundary(boundary), 29 | mType(FORM) 30 | {} 31 | 32 | MultipartBody::Builder &MultipartBody::Builder::addFormDataPart(const std::string &name, const std::string &value) { 33 | mParts.emplace_back(name, value); 34 | return *this; 35 | } 36 | 37 | MultipartBody::Builder &MultipartBody::Builder::addFormDataPart( 38 | const std::string &name, 39 | const std::string &filename, 40 | const RequestBody &body) 41 | { 42 | mParts.emplace_back(name, filename, body); 43 | return *this; 44 | } 45 | 46 | MultipartBody::Builder &MultipartBody::Builder::addPart(const Headers &headers, const RequestBody &body) { 47 | mParts.emplace_back(headers, body); 48 | return *this; 49 | } 50 | 51 | MultipartBody::Builder &MultipartBody::Builder::addPart(const Part &part) { 52 | mParts.push_back(part); 53 | return *this; 54 | } 55 | 56 | MultipartBody::Builder &MultipartBody::Builder::addPart(const RequestBody &body) { 57 | mParts.emplace_back(body); 58 | return *this; 59 | } 60 | 61 | MultipartBody::Builder &MultipartBody::Builder::setType(const MediaType &type) { 62 | mType = type; 63 | return *this; 64 | } 65 | 66 | MultipartBody MultipartBody::Builder::build() { 67 | return {this}; 68 | } 69 | } -------------------------------------------------------------------------------- /src/MultipartBody.Part.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include "../include/ohf/MultipartBody.hpp" 6 | #include "ohf/Exception.hpp" 7 | #include "util/string.hpp" 8 | 9 | namespace ohf { 10 | std::string handleQuotedString(const std::string &str) { 11 | std::string tmp; 12 | for (const char &c : str) { 13 | switch (c) { 14 | case '\n': 15 | tmp += "%A0"; 16 | break; 17 | case '\r': 18 | tmp += "%0D"; 19 | break; 20 | case '"': 21 | tmp += "%22"; 22 | break; 23 | default: 24 | tmp.push_back(c); 25 | break; 26 | } 27 | } 28 | return tmp; 29 | } 30 | 31 | MultipartBody::Part::Part(const std::string &name, const std::string &value) : 32 | mHeaders(Headers::Builder() 33 | .add("Content-Disposition", "form-data; name=\"" + handleQuotedString(name) + '"') 34 | .build()), 35 | mBody(RequestBody(MediaType(), value)) {} 36 | 37 | MultipartBody::Part::Part(const std::string &name, const std::string &filename, const RequestBody &body) : 38 | mHeaders(Headers::Builder() 39 | .add("Content-Disposition", 40 | "form-data; name=\"" + handleQuotedString(name) + 41 | "\"; filename=\"" + handleQuotedString(filename) + '"') 42 | .build()), 43 | mBody(body) {} 44 | 45 | MultipartBody::Part::Part(const Headers &headers, const RequestBody &body) : 46 | mHeaders(headers), 47 | mBody(body) 48 | { 49 | bool cdExists = false; 50 | for (auto name : mHeaders.names()) { 51 | util::string::toLower(name); 52 | if (name == "content-type") 53 | throw Exception(Exception::Code::UNEXPECTED_HEADER, "Unexpected header: Content-Type"); 54 | else if (name == "content-length") 55 | throw Exception(Exception::Code::UNEXPECTED_HEADER, "Unexpected header: Content-Length"); 56 | else if (name == "content-disposition") 57 | cdExists = true; 58 | } 59 | if (!cdExists) 60 | mHeaders = mHeaders.newBuilder() 61 | .add("Content-Disposition", "form-data") 62 | .build(); 63 | } 64 | 65 | MultipartBody::Part::Part(const RequestBody &body) : MultipartBody::Part::Part(Headers::Builder().build(), body) {} 66 | 67 | RequestBody MultipartBody::Part::body() const { 68 | return mBody; 69 | } 70 | 71 | Headers MultipartBody::Part::headers() const { 72 | return mHeaders; 73 | } 74 | } -------------------------------------------------------------------------------- /src/MultipartBody.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace ohf { 11 | MediaType MultipartBody::MIXED = "multipart/mixed"; 12 | MediaType MultipartBody::ALTERNATIVE = "multipart/alternative"; 13 | MediaType MultipartBody::DIGEST = "multipart/digest"; 14 | MediaType MultipartBody::PARALLEL = "multipart/parallel"; 15 | MediaType MultipartBody::FORM = "multipart/form-data"; 16 | 17 | std::string MultipartBody::boundary() { 18 | return mBoundary; 19 | } 20 | 21 | MultipartBody::Part MultipartBody::part(Uint64 index) { 22 | if (index >= mParts.size()) 23 | throw RangeException(index); 24 | return mParts[index]; 25 | } 26 | 27 | std::vector MultipartBody::parts() { 28 | return mParts; 29 | } 30 | 31 | Uint64 MultipartBody::size() { 32 | return mParts.size(); 33 | } 34 | 35 | MediaType MultipartBody::type() { 36 | return mType; 37 | } 38 | 39 | MultipartBody::MultipartBody(const Builder *builder) : 40 | mBoundary(builder->mBoundary), 41 | mParts(builder->mParts), 42 | mType(builder->mType), 43 | RequestBody( 44 | builder->mType.type().empty() // original MediaType 45 | ? FORM 46 | : MediaType(builder->mType.type() + '/' + builder->mType.subtype() + 47 | (builder->mType.charset().empty() 48 | ? std::string() 49 | : "; charset=" + builder->mType.charset()) + "; boundary=" + builder->mBoundary), 50 | std::vector()) 51 | { 52 | std::stringstream ss; 53 | for (auto part : mParts) { 54 | ss << "--" << mBoundary << "\r\n"; 55 | ss << part.mHeaders; 56 | 57 | std::string ct = part.mBody.contentType().toString(); 58 | if (!ct.empty()) ss << "Content-Type: " << ct << "\r\n"; 59 | 60 | ss << "\r\n"; 61 | 62 | std::vector bodyContent = part.mBody.content; 63 | ss.write(bodyContent.data(), bodyContent.size()); 64 | 65 | ss << "\r\n"; 66 | } 67 | ss << "--" << mBoundary << "--"; 68 | 69 | std::string readyContent = ss.str(); 70 | content = {readyContent.begin(), readyContent.end()}; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Principal.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /src/Proxy.Selector.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | bool Proxy::Selector::operator ==(const Proxy::Selector &selector) const { // TODO: Implement it 9 | return false; 10 | } 11 | } -------------------------------------------------------------------------------- /src/Proxy.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | bool Proxy::operator ==(const Proxy &proxy) const { // TODO: Implement it 9 | return false; 10 | } 11 | } -------------------------------------------------------------------------------- /src/RangeException.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | RangeException::RangeException(Int64 index) : 9 | Exception(Exception::Code::OUT_OF_RANGE, "Out of range: " + std::to_string(index)), 10 | m_index(index) 11 | {} 12 | 13 | Int64 RangeException::index() const noexcept { 14 | return m_index; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Request.Builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ohf { 10 | Request::Builder::~Builder() { 11 | delete mCC; 12 | delete mURL; 13 | delete mBody; 14 | } 15 | 16 | Request Request::Builder::build() { 17 | if(mMethod.empty()) 18 | throw Exception(Exception::Code::METHOD_IS_NOT_NAMED, "Method is not named: "); 19 | if(mURL == nullptr) 20 | throw Exception(Exception::Code::URL_IS_NOT_NAMED, "URL is not named: "); 21 | if(mCC == nullptr) 22 | mCC = new CacheControl(); 23 | if(mBody == nullptr) 24 | mBody = new RequestBody(); 25 | 26 | return {this}; 27 | } 28 | 29 | Request::Builder& Request::Builder::cacheControl(const CacheControl &cacheControl) { 30 | mCC = new CacheControl(cacheControl); 31 | std::string value = mCC->toString(); 32 | if(value.empty()) return removeHeader("Cache-Control"); 33 | return header("Cache-Control", value); 34 | } 35 | 36 | Request::Builder& Request::Builder::delete_() { 37 | mMethod = "DELETE"; 38 | mBody = nullptr; 39 | return *this; 40 | } 41 | 42 | Request::Builder& Request::Builder::delete_(const RequestBody &body) { 43 | mMethod = "DELETE"; 44 | mBody = new RequestBody(body); 45 | 46 | return *this; 47 | } 48 | 49 | Request::Builder& Request::Builder::get() { 50 | mMethod = "GET"; 51 | mBody = nullptr; 52 | 53 | return *this; 54 | } 55 | 56 | Request::Builder& Request::Builder::head() { 57 | mMethod = "HEAD"; 58 | mBody = nullptr; 59 | 60 | return *this; 61 | } 62 | 63 | Request::Builder& Request::Builder::patch(const RequestBody &body) { 64 | mMethod = "PATCH"; 65 | mBody = new RequestBody(body); 66 | 67 | return *this; 68 | } 69 | 70 | Request::Builder& Request::Builder::post(const RequestBody &body) { 71 | mMethod = "POST"; 72 | mBody = new RequestBody(body); 73 | 74 | return *this; 75 | } 76 | 77 | Request::Builder& Request::Builder::put(const RequestBody &body) { 78 | mMethod = "PUT"; 79 | mBody = new RequestBody(body); 80 | 81 | return *this; 82 | } 83 | 84 | Request::Builder& Request::Builder::method(const std::string &method) { 85 | mMethod = method; 86 | mBody = nullptr; 87 | 88 | return *this; 89 | } 90 | 91 | Request::Builder& Request::Builder::method(const std::string &method, const RequestBody &body) { 92 | mMethod = method; 93 | mBody = new RequestBody(body); 94 | 95 | return *this; 96 | } 97 | 98 | Request::Builder& Request::Builder::addHeader(const std::string &name, const std::string &value) { 99 | mHeaders.add(name, value); 100 | return *this; 101 | } 102 | 103 | Request::Builder& Request::Builder::header(const std::string &name, const std::string &value) { 104 | mHeaders.set(name, value); 105 | return *this; 106 | } 107 | 108 | Request::Builder& Request::Builder::headers(const Headers &headers) { 109 | mHeaders = headers.newBuilder(); 110 | return *this; 111 | } 112 | 113 | Request::Builder& Request::Builder::removeHeader(const std::string &name) { 114 | mHeaders.removeAll(name); 115 | return *this; 116 | } 117 | 118 | Request::Builder& Request::Builder::url(const HttpURL &url) { 119 | mURL = new HttpURL(url); 120 | return *this; 121 | } 122 | 123 | Request::Builder::Builder(Request *request) : 124 | mMethod(request->mMethod), 125 | mHeaders(request->mHeaders.newBuilder()), 126 | mCC(new CacheControl(request->mCC)), 127 | mURL(new HttpURL(request->mURL)), 128 | mBody(new RequestBody(request->mBody)) 129 | {} 130 | } -------------------------------------------------------------------------------- /src/Request.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | Request::Request(const Request::Builder *builder) : 9 | mCC(*builder->mCC), 10 | mMethod(builder->mMethod), 11 | mBody(*builder->mBody), 12 | mHeaders(builder->mHeaders.build()), 13 | mURL(*builder->mURL) 14 | {} 15 | 16 | RequestBody Request::body() const { 17 | return mBody; 18 | } 19 | 20 | CacheControl Request::cacheControl() const { 21 | return mCC; 22 | } 23 | 24 | std::string Request::header(const std::string &name) const { 25 | return mHeaders.get(name); 26 | } 27 | 28 | std::vector Request::headers(const std::string &name) const { 29 | return mHeaders.values(name); 30 | } 31 | 32 | Headers Request::headers() const { 33 | return mHeaders; 34 | } 35 | 36 | std::string Request::method() const { 37 | return mMethod; 38 | } 39 | 40 | HttpURL Request::url() const { 41 | return mURL; 42 | } 43 | 44 | bool Request::isHttps() const { 45 | return mURL.isHttps(); 46 | } 47 | 48 | Request::Builder Request::newBuilder() { 49 | return {this}; 50 | } 51 | } -------------------------------------------------------------------------------- /src/RequestBody.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "util/util.hpp" 11 | 12 | namespace ohf { 13 | RequestBody::RequestBody() : mediaType(MediaType()) {} 14 | 15 | RequestBody::RequestBody(const MediaType &contentType, const char *content, size_t count) : 16 | mediaType(contentType), 17 | content(std::vector(content, content + count)) 18 | {} 19 | 20 | RequestBody::RequestBody(const MediaType &contentType, const std::string &content) : 21 | mediaType(contentType), 22 | content(content.begin(), content.end()) 23 | {} 24 | 25 | RequestBody::RequestBody(const MediaType &contentType, const std::vector &content) : 26 | mediaType(contentType), 27 | content(content) {} 28 | 29 | RequestBody::RequestBody(const MediaType &contentType, std::istream &stream) : 30 | mediaType(contentType), 31 | content(util::readStream(stream)) 32 | {} 33 | 34 | Uint64 RequestBody::contentLength() { 35 | return content.size(); 36 | } 37 | 38 | MediaType RequestBody::contentType() { 39 | return mediaType; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Response.Builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | namespace ohf { 9 | Response::Builder::Builder() : 10 | mProtocol(Protocol::HTTP_1_1), 11 | mCode(200), 12 | mMessage("OK"), 13 | mSent(TimeUnit::MINUS_ONE_SECOND), 14 | mReceived(TimeUnit::MINUS_ONE_SECOND) 15 | {} 16 | 17 | Response::Builder::~Builder() { 18 | delete mBody; 19 | delete mRequest; 20 | } 21 | 22 | Response::Builder& Response::Builder::protocol(Protocol protocol) { 23 | mProtocol = protocol; 24 | return *this; 25 | } 26 | 27 | Response::Builder& Response::Builder::code(Int32 code) { 28 | mCode = code; 29 | return *this; 30 | } 31 | 32 | Response::Builder& Response::Builder::message(const std::string &message) { 33 | mMessage = message; 34 | return *this; 35 | } 36 | 37 | Response::Builder& Response::Builder::body(const ResponseBody &body) { 38 | mBody = new ResponseBody(body); 39 | return *this; 40 | } 41 | 42 | Response::Builder& Response::Builder::request(const Request &request) { 43 | mRequest = new Request(request); 44 | return *this; 45 | } 46 | 47 | Response::Builder& Response::Builder::handshake(const ssl::Handshake &handshake) { 48 | mHandshake = handshake; 49 | return *this; 50 | } 51 | 52 | Response::Builder& Response::Builder::addHeader(const std::string &name, const std::string &value) { 53 | mHeaders.add(name, value); 54 | return *this; 55 | } 56 | 57 | Response::Builder& Response::Builder::header(const std::string &name, const std::string &value) { 58 | mHeaders.set(name, value); 59 | return *this; 60 | } 61 | 62 | Response::Builder& Response::Builder::removeHeader(const std::string &name) { 63 | mHeaders.removeAll(name); 64 | return *this; 65 | } 66 | 67 | Response::Builder& Response::Builder::headers(const Headers &headers) { 68 | mHeaders = headers.newBuilder(); 69 | return *this; 70 | } 71 | 72 | Response::Builder& Response::Builder::sentRequest(const TimeUnit &time) { 73 | mSent = time; 74 | return *this; 75 | } 76 | 77 | Response::Builder& Response::Builder::receivedResponse(const TimeUnit &time) { 78 | mReceived = time; 79 | return *this; 80 | } 81 | 82 | Response Response::Builder::build() { 83 | if(mBody == nullptr) { 84 | throw Exception(Exception::Code::RESPONSE_BODY_IS_NOT_SPECIFIED, "Response body is not specified"); 85 | } 86 | 87 | if(mRequest == nullptr) { 88 | throw Exception(Exception::Code::REQUEST_IS_NOT_SPECIFIED, "Request is not specified"); 89 | } 90 | 91 | return {this}; 92 | } 93 | 94 | Response::Builder::Builder(const Response *response) : 95 | mProtocol(response->mProtocol), 96 | mCode(response->mCode), 97 | mMessage(response->mMessage), 98 | mBody(new ResponseBody(response->mBody)), 99 | mRequest(new Request(response->mRequest)), 100 | mHandshake(response->mHandshake), 101 | mHeaders(response->mHeaders.newBuilder()), 102 | mSent(response->mSent), 103 | mReceived(response->mReceived) 104 | {} 105 | } -------------------------------------------------------------------------------- /src/Response.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "util/string.hpp" 10 | 11 | namespace { 12 | inline void invalidChallenge(const std::string &name, const std::string &value) { 13 | throw ohf::Exception(ohf::Exception::Code::INVALID_CHALLENGE, "Invalid challenge: " + name + ": " + value); 14 | } 15 | 16 | std::string getByName(const std::string &name, const std::string &value) { 17 | std::string::size_type offset; 18 | if((offset = value.find(name)) != std::string::npos) { 19 | offset += name.length(); 20 | 21 | // check first quote 22 | if(value[offset++] != '"') { 23 | return {}; 24 | } 25 | 26 | auto start = offset; 27 | 28 | // check second quote 29 | if((offset = value.find('"', offset)) == std::string::npos) { 30 | return {}; 31 | } 32 | 33 | return value.substr(start, offset - start); 34 | } 35 | 36 | return {}; 37 | } 38 | } 39 | 40 | namespace ohf { 41 | Protocol Response::protocol() const { 42 | return mProtocol; 43 | } 44 | 45 | Int32 Response::code() const { 46 | return mCode; 47 | } 48 | 49 | std::string Response::message() const { 50 | return mMessage; 51 | } 52 | 53 | ResponseBody Response::body() const { 54 | return mBody; 55 | } 56 | 57 | ResponseBody Response::peekBody(Uint64 byteCount) const { 58 | auto content = mBody.bytes(); 59 | return {mBody.contentType(), 60 | std::vector(content.begin(), content.begin() + byteCount), 61 | mBody.stream().rdbuf()}; 62 | } 63 | 64 | Request Response::request() const { 65 | return mRequest; 66 | } 67 | 68 | ssl::Handshake Response::handshake() const { 69 | return mHandshake; 70 | } 71 | 72 | std::string Response::header(const std::string &name) const { 73 | return mHeaders.get(name); 74 | } 75 | 76 | std::string Response::header(const std::string &name, const std::string &defaultValue) const { 77 | std::string header = mHeaders.get(name); 78 | return header.empty() ? defaultValue : header; 79 | } 80 | 81 | std::vector Response::headers(const std::string &name) const { 82 | return mHeaders.values(name); 83 | } 84 | 85 | Headers Response::headers() const { 86 | return mHeaders; 87 | } 88 | 89 | TimeUnit Response::sentRequest() const { 90 | return mSent; 91 | } 92 | 93 | TimeUnit Response::receivedResponse() const { 94 | return mReceived; 95 | } 96 | 97 | CacheControl Response::cacheControl() const { 98 | return CacheControl(mHeaders); 99 | } 100 | 101 | std::vector Response::challenges() const { 102 | std::vector challenges; 103 | for(const auto &header : mHeaders) { 104 | std::string name = header.first; 105 | std::string value = header.second; 106 | 107 | if(name == "WWW-Authenticate" || name == "Proxy-Authenticate") { 108 | auto offset = value.find_first_of(' '); 109 | if(offset == std::string::npos) { 110 | invalidChallenge(name, value); 111 | } 112 | 113 | std::string scheme(value, 0, offset); 114 | std::string realm = getByName("realm=", value); 115 | std::string charset = getByName("charset=", value); 116 | 117 | if(realm.empty()) { 118 | invalidChallenge(name, value); 119 | } 120 | 121 | challenges.emplace_back(scheme, realm, charset); 122 | } 123 | } 124 | 125 | return challenges; 126 | } 127 | 128 | bool Response::isRedirect() const { 129 | return mCode >= 300 && mCode <= 308; 130 | } 131 | 132 | bool Response::isSuccessful() const { 133 | return mCode >= 200 && mCode < 300; 134 | } 135 | 136 | Response::Builder Response::newBuilder() { 137 | return {this}; 138 | } 139 | 140 | Response::Response(const Builder *builder) : 141 | mProtocol(builder->mProtocol), 142 | mCode(builder->mCode), 143 | mMessage(builder->mMessage), 144 | mBody(*builder->mBody), 145 | mRequest(*builder->mRequest), 146 | mHandshake(builder->mHandshake), 147 | mHeaders(builder->mHeaders.build()), 148 | mSent(builder->mSent), 149 | mReceived(builder->mReceived) 150 | {} 151 | } -------------------------------------------------------------------------------- /src/ResponseBody.StreamBuf.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | ResponseBody::StreamBuf::StreamBuf(Int32 write, Int32 read) : IOStreamBuf(write, read) {} 9 | 10 | void ResponseBody::StreamBuf::vector(const std::vector &vector) { 11 | mVector = vector; 12 | } 13 | 14 | Int32 ResponseBody::StreamBuf::write(const char *data, Int32 length) { 15 | mVector.insert(mVector.end(), data, data + length); 16 | return length; 17 | } 18 | 19 | Int32 ResponseBody::StreamBuf::read(char *data, Int32 length) { 20 | Int32 retlen; 21 | std::vector::iterator begin = mVector.begin(); 22 | std::vector::iterator end; 23 | 24 | if(length > mVector.size()) { 25 | retlen = (Int32) mVector.size(); 26 | end = mVector.end(); 27 | } else { 28 | retlen = length; 29 | end = mVector.begin() + length; 30 | } 31 | 32 | mVector.erase(begin, end); 33 | std::copy(begin, end, data); 34 | return retlen; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ResponseBody.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "util/util.hpp" 10 | 11 | namespace ohf { 12 | ResponseBody::ResponseBody(const MediaType &mediaType, const char *content, size_t count, std::streambuf *buffer) : 13 | ResponseBody(mediaType, std::vector(content, content + count), buffer) 14 | {} 15 | 16 | ResponseBody::ResponseBody(const MediaType &mediaType, const std::vector &content, std::streambuf *buffer) : 17 | mediaType(mediaType), 18 | content(content), 19 | is(std::make_shared(buffer)) 20 | {} 21 | 22 | ResponseBody::ResponseBody(const MediaType &mediaType, const std::string &content, std::streambuf *buffer) : 23 | ResponseBody(mediaType, std::vector(content.begin(), content.end()), buffer) 24 | {} 25 | 26 | ResponseBody::ResponseBody(const MediaType &mediaType, std::istream &stream, std::streambuf *buffer) : 27 | ResponseBody(mediaType, util::readStream(stream), buffer) 28 | {} 29 | 30 | std::vector ResponseBody::bytes() const { 31 | return content; 32 | } 33 | 34 | std::string ResponseBody::string() const { 35 | return {content.begin(), content.end()}; 36 | } 37 | 38 | std::istream &ResponseBody::stream() const { 39 | return *is; 40 | } 41 | 42 | Uint64 ResponseBody::contentLength() const { 43 | return content.size(); 44 | } 45 | 46 | MediaType ResponseBody::contentType() const { 47 | return mediaType; 48 | } 49 | } -------------------------------------------------------------------------------- /src/Socket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include "SocketImpl.hpp" 8 | 9 | namespace ohf { 10 | Socket::Socket(Type type) : 11 | mType(type), 12 | mFD(SocketImpl::invalidSocket()), 13 | mBlocking(true) 14 | {} 15 | 16 | Socket::~Socket() { 17 | close(); 18 | } 19 | 20 | Socket::Handle Socket::fd() const { 21 | return mFD; 22 | } 23 | 24 | void Socket::create(Family af) { 25 | if(isValid()) return; 26 | 27 | Int32 family; 28 | switch(af) { 29 | case Family::IPv4: family = AF_INET; break; 30 | case Family::IPv6: family = AF_INET6; break; 31 | default: 32 | throw Exception(Exception::Code::INVALID_FAMILY_TYPE, 33 | "Invalid family type: " + std::to_string((Int32) af)); 34 | } 35 | 36 | Socket::Handle handle = socket(family, mType == Type::TCP ? SOCK_STREAM : SOCK_DGRAM, 0); 37 | if(handle == SocketImpl::invalidSocket()) { 38 | throw Exception(Exception::Code::FAILED_TO_CREATE_SOCKET, 39 | "Failed to create socket: " + SocketImpl::getError()); 40 | } 41 | create(handle); 42 | 43 | blocking(mBlocking); 44 | } 45 | 46 | void Socket::create(Handle fd) { 47 | if(!isValid()) { 48 | mFD = fd; 49 | } 50 | } 51 | 52 | void Socket::blocking(bool mode) { 53 | if(isValid()) { 54 | SocketImpl::setBlocking(mFD, mode); 55 | } 56 | mBlocking = mode; 57 | } 58 | 59 | bool Socket::isBlocking() const { 60 | return mBlocking; 61 | } 62 | 63 | void Socket::close() { 64 | if(isValid()) { 65 | SocketImpl::close(mFD); 66 | mFD = SocketImpl::invalidSocket(); 67 | } 68 | } 69 | 70 | bool Socket::isValid() const { 71 | return mFD != SocketImpl::invalidSocket(); 72 | } 73 | 74 | Socket::operator bool() { 75 | return isValid(); 76 | } 77 | 78 | Socket::Type Socket::type() const { 79 | return mType; 80 | } 81 | 82 | Socket::Family Socket::family() const { 83 | return SocketImpl::family(mFD); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/ssl/Challenge.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | namespace ssl { 9 | Challenge::Challenge(const std::string &scheme, const std::string &realm, const std::string &charset) : 10 | mScheme(scheme), 11 | mRealm(realm), 12 | mCharset(charset) 13 | {} 14 | 15 | std::string Challenge::scheme() const { 16 | return mScheme; 17 | } 18 | 19 | std::string Challenge::realm() const { 20 | return mRealm; 21 | } 22 | 23 | std::string Challenge::charset() const { 24 | return mCharset; 25 | } 26 | 27 | bool Challenge::operator ==(const Challenge &right) const { 28 | return mScheme == right.mScheme 29 | && mRealm == right.mRealm 30 | && mCharset == right.mCharset; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/ssl/CipherSuite.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include "Util.hpp" 6 | 7 | namespace ohf { 8 | namespace ssl { 9 | CipherSuite::CipherSuite() : pImpl(new impl) {} 10 | 11 | CipherSuite::~CipherSuite() {} 12 | 13 | Uint32 CipherSuite::id() const { 14 | return SSL_CIPHER_get_id(pImpl->cipher); 15 | } 16 | 17 | std::string CipherSuite::name() const { 18 | return SSL_CIPHER_get_name(pImpl->cipher); 19 | } 20 | 21 | std::string CipherSuite::version() const { 22 | return SSL_CIPHER_get_version(pImpl->cipher); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/ssl/Exception.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include "Util.hpp" 6 | #include 7 | 8 | namespace ohf { 9 | namespace ssl { 10 | std::string get_error(Exception::Code code) { 11 | std::string m_what; 12 | switch(code) { 13 | case ohf::Exception::Code::SSL_CREATE_ERROR: 14 | m_what = "SSL create error: "; 15 | break; 16 | case ohf::Exception::Code::SSL_CREATE_CONTEXT_ERROR: 17 | m_what = "SSL create context error: "; 18 | break; 19 | case ohf::Exception::Code::SSL_CREATE_CONNECTION_ERROR: 20 | m_what = "SSL create connection error: "; 21 | break; 22 | case ohf::Exception::Code::SSL_ERROR: 23 | m_what = "SSL error: "; 24 | break; 25 | case ohf::Exception::Code::SSL_ACCEPT_ERROR: 26 | m_what = "SSL accept error: "; 27 | break; 28 | case ohf::Exception::Code::SSL_FAILED_TO_USE_CERTIFICATE_FILE: 29 | m_what = "SSL failed to use certificate file: "; 30 | break; 31 | case ohf::Exception::Code::SSL_FAILED_TO_USE_PRIVATE_KEY_FILE: 32 | m_what = "SSL failed to use private key file: "; 33 | break; 34 | case ohf::Exception::Code::SSL_FAILED_TO_VERIFY_PRIVATE_KEY: 35 | m_what = "SSL failed to verify private key: "; 36 | break; 37 | case ohf::Exception::Code::SSL_PROTOCOL_DOES_NOT_SUPPORTED: 38 | m_what = "SSL protocol doesn't supported: "; 39 | break; 40 | default: { 41 | throw ohf::Exception(Exception::Code::INVALID_EXCEPTION_CODE, 42 | "Invalid exception code: " + std::to_string((Int32) code)); 43 | } 44 | } 45 | return m_what; 46 | } 47 | 48 | Exception::Exception(const Code &code) : 49 | ohf::Exception(code, get_error(code)), 50 | ssl_code(0) 51 | { 52 | m_what += (ssl_message = getOpenSSLError()); 53 | } 54 | 55 | Exception::Exception(const Exception::Code &code, const SSL &ssl, Int32 retCode) : Exception(code) { 56 | ssl_code = SSL_get_error(ssl.pImpl->ssl, retCode); 57 | } 58 | 59 | Int32 Exception::sslCode() const noexcept { 60 | return ssl_code; 61 | } 62 | 63 | std::string Exception::sslMessage() const noexcept { 64 | return ssl_message; 65 | } 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/ssl/Handshake.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | namespace ssl { 9 | Handshake::Handshake(SSL &ssl) { 10 | 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ssl/Initializer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include "Util.hpp" 7 | 8 | namespace ohf { 9 | namespace ssl { 10 | Initializer::Initializer() { 11 | SSL_library_init(); 12 | SSLeay_add_ssl_algorithms(); 13 | SSL_load_error_strings(); 14 | } 15 | 16 | Initializer::~Initializer() { 17 | EVP_cleanup(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ssl/SSL.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include "Util.hpp" 8 | 9 | namespace ohf { 10 | namespace ssl { 11 | SSL::SSL(const Context &context) : pImpl(new impl), context(context) { 12 | ssl_st* &ssl = pImpl->ssl; 13 | ssl = SSL_new(context.pImpl->context); 14 | if(!ssl) { 15 | throw Exception(Exception::Code::SSL_CREATE_ERROR); 16 | } 17 | } 18 | 19 | SSL::~SSL() { 20 | if(pImpl->ssl) SSL_free(pImpl->ssl); 21 | delete pImpl; 22 | }; 23 | 24 | void SSL::setHandle(Socket::Handle handle) const { 25 | SSL_set_fd(pImpl->ssl, handle); 26 | } 27 | 28 | Socket::Handle SSL::getHandle() const { 29 | return (Socket::Handle) SSL_get_fd(pImpl->ssl); 30 | } 31 | 32 | void SSL::setTLSExtHostName(const std::string &hostname) const { 33 | SSL_set_tlsext_host_name(pImpl->ssl, hostname.c_str()); 34 | } 35 | 36 | void SSL::connect() const { 37 | Int32 ret = SSL_connect(pImpl->ssl); 38 | if(ret <= 0) { 39 | throw Exception(Exception::Code::SSL_CREATE_CONNECTION_ERROR, *this, ret); 40 | } 41 | } 42 | 43 | Int32 SSL::write(const char *data, Int32 size) const { 44 | if(!data || size == 0) throw ohf::Exception(Exception::Code::NO_DATA_TO_SEND, "No data to send: "); 45 | 46 | int length = SSL_write(pImpl->ssl, data, size); 47 | checkIO(length); 48 | 49 | return length; 50 | } 51 | 52 | Int32 SSL::read(char *data, Int32 size) const { 53 | int length = SSL_read(pImpl->ssl, data, size); 54 | checkIO(length); 55 | return length; 56 | } 57 | 58 | void SSL::accept() const { 59 | int error = SSL_accept(pImpl->ssl); 60 | checkIO(error); 61 | } 62 | 63 | void SSL::checkIO(Int32 length) const { 64 | if(length < 0) { 65 | int error = SSL_get_error(pImpl->ssl, length); 66 | if(error != SSL_ERROR_WANT_READ && error != SSL_ERROR_WANT_WRITE) { 67 | throw Exception(Exception::Code::SSL_ERROR, *this, length); 68 | } 69 | } 70 | } 71 | 72 | CipherSuite SSL::currentCipher() const { 73 | CipherSuite cipher; 74 | cipher.pImpl->cipher = SSL_get_current_cipher(pImpl->ssl); 75 | return cipher; 76 | } 77 | 78 | std::vector SSL::ciphers() const { 79 | std::vector ciphers; 80 | 81 | auto stack = SSL_get_ciphers(pImpl->ssl); 82 | for(int i = 0; i < sk_SSL_CIPHER_num(stack); i++) { 83 | auto ssl_cipher = sk_SSL_CIPHER_value(stack, i); 84 | 85 | CipherSuite cipher; 86 | cipher.pImpl->cipher = ssl_cipher; 87 | ciphers.push_back(cipher); 88 | } 89 | 90 | return ciphers; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/ssl/Socket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include "../SocketImpl.hpp" 7 | 8 | namespace ohf { 9 | namespace ssl { 10 | Socket::Socket(Type type, const Context &context) : 11 | ohf::Socket(type), 12 | context(context), 13 | SNICalled(true) 14 | {} 15 | 16 | void Socket::create(Handle fd) { 17 | if(fd != SocketImpl::invalidSocket()) { 18 | ohf::Socket::create(fd); 19 | 20 | mSSL = std::make_shared(context); 21 | mSSL->setHandle(mFD); 22 | } 23 | } 24 | 25 | void Socket::close() { 26 | if(mFD != SocketImpl::invalidSocket()) { 27 | mSSL.reset(); 28 | ohf::Socket::close(); 29 | } 30 | } 31 | 32 | void Socket::sni(bool b) { 33 | SNICalled = b; 34 | } 35 | 36 | void Socket::sni(const InetAddress &address) { 37 | sni(true); 38 | if(mSSL) mSSL->setTLSExtHostName(address.hostName()); 39 | } 40 | 41 | bool Socket::isSNI() const { 42 | return SNICalled; 43 | } 44 | 45 | const SSL& Socket::ssl() const { 46 | return *mSSL; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/ssl/Util.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_SSL_IMPLEMENTATIONS_HPP 6 | #define OKHTTPFORK_SSL_IMPLEMENTATIONS_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | namespace ohf { 17 | namespace ssl { 18 | struct SSL::impl { 19 | ::SSL *ssl; 20 | }; 21 | 22 | struct Context::impl { 23 | SSL_CTX *context; 24 | }; 25 | 26 | struct X509Certificate::impl { 27 | X509 *certificate; 28 | }; 29 | 30 | struct CipherSuite::impl { 31 | const SSL_CIPHER *cipher; 32 | }; 33 | 34 | inline std::string getOpenSSLError() { 35 | std::string error; 36 | unsigned long error_code; 37 | while ((error_code = ERR_get_error())) { 38 | char *str = ERR_error_string(error_code, nullptr); 39 | if (!str) return error; 40 | 41 | error += str; 42 | } 43 | return error; 44 | } 45 | } 46 | } 47 | 48 | #endif //OKHTTPFORK_IMPLEMENTATIONS_HPP 49 | -------------------------------------------------------------------------------- /src/ssl/X509Certificate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include "Util.hpp" 7 | 8 | namespace ohf { 9 | namespace ssl { 10 | X509Certificate::X509Certificate(Type type) : pImpl(new impl) { 11 | pImpl->certificate = X509_new(); 12 | } 13 | 14 | X509Certificate::~X509Certificate() { 15 | X509_free(pImpl->certificate); 16 | delete pImpl; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/tcp/SSLServer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "../SocketImpl.hpp" 9 | #include "../ssl/Util.hpp" 10 | 11 | namespace ohf { 12 | namespace tcp { 13 | SSLServer::SSLServer(const ssl::Context &context) : 14 | Server(), 15 | ssl::Socket(Type::TCP, context) 16 | {} 17 | 18 | SSLServer::SSLServer(SSLServer&& server) noexcept : 19 | Server(), 20 | ssl::Socket(Type::TCP, server.context) 21 | { 22 | mFD = server.mFD; 23 | server.mFD = SocketImpl::invalidSocket(); 24 | 25 | mBlocking = server.mBlocking; 26 | server.mBlocking = true; 27 | 28 | mSSL = std::move(server.mSSL); 29 | } 30 | 31 | SSLServer::Connection SSLServer::accept() const { 32 | sockaddr_storage addr; 33 | SocketImpl::SocketLength length = SocketImpl::addressLength(mFamily); 34 | 35 | auto fd = ::accept(ssl::Socket::mFD, (sockaddr *) &addr, &length); 36 | if (fd == SocketImpl::invalidSocket()) { 37 | throw Exception(Exception::Code::FAILED_TO_ACCEPT_SOCKET, 38 | "Failed to accept socket: " + SocketImpl::getError()); 39 | } 40 | 41 | auto *client = new tcp::SSLSocket(context); 42 | client->create(fd); 43 | client->ssl().accept(); 44 | 45 | return {client, SocketImpl::createInetAddress(&addr), SocketImpl::port(&addr)}; 46 | } 47 | 48 | SSLServer& SSLServer::operator =(SSLServer&& right) noexcept { 49 | mFD = right.mFD; 50 | right.mFD = SocketImpl::invalidSocket(); 51 | 52 | mBlocking = right.mBlocking; 53 | right.mBlocking = true; 54 | 55 | mSSL = std::move(right.mSSL); 56 | 57 | return *this; 58 | } 59 | } 60 | } 61 | 62 | namespace std { 63 | using namespace ohf; 64 | 65 | void swap(tcp::SSLServer& a, tcp::SSLServer& b) { 66 | swap(a, b); 67 | swap(a.mSSL, b.mSSL); 68 | } 69 | } -------------------------------------------------------------------------------- /src/tcp/SSLSocket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include "../SocketImpl.hpp" 7 | 8 | namespace ohf { 9 | namespace tcp { 10 | SSLSocket::SSLSocket(const ssl::Context &context) : 11 | ohf::Socket(Type::TCP), 12 | ssl::Socket(Type::TCP, context) 13 | {} 14 | 15 | SSLSocket::SSLSocket(SSLSocket &&socket) noexcept : 16 | tcp::Socket(), 17 | ssl::Socket(Type::TCP, socket.context) 18 | { 19 | mFD = socket.mFD; 20 | socket.mFD = SocketImpl::invalidSocket(); 21 | 22 | mBlocking = socket.mBlocking; 23 | socket.mBlocking = true; 24 | 25 | mSSL = std::move(socket.mSSL); 26 | } 27 | 28 | void SSLSocket::connect(const InetAddress &address, Uint16 port) { 29 | create(address.family()); 30 | 31 | if(SNICalled) mSSL->setTLSExtHostName(address.hostName()); 32 | 33 | tcp::Socket::connect(address, port); 34 | mSSL->connect(); 35 | } 36 | 37 | Int32 SSLSocket::send(const char *data, Int32 size) const { 38 | return mSSL->write(data, size); 39 | } 40 | 41 | Int32 SSLSocket::receive(char *data, Int32 size) const { 42 | return mSSL->read(data, size); 43 | } 44 | 45 | SSLSocket& SSLSocket::operator =(SSLSocket &&right) noexcept { 46 | mFD = right.mFD; 47 | right.mFD = SocketImpl::invalidSocket(); 48 | 49 | mBlocking = right.mBlocking; 50 | right.mBlocking = true; 51 | 52 | mSSL = std::move(right.mSSL); 53 | 54 | return *this; 55 | } 56 | } 57 | } 58 | 59 | namespace std { 60 | using namespace ohf; 61 | 62 | void swap(tcp::SSLSocket& a, tcp::SSLSocket& b) { 63 | swap(a, b); 64 | swap(a.mSSL, b.mSSL); 65 | } 66 | } -------------------------------------------------------------------------------- /src/tcp/Server.Connection.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | namespace tcp { 9 | Server::Connection::Connection(tcp::Socket *socket, const InetAddress &address, Uint16 port) : 10 | mSocket(socket), 11 | mAddress(address), 12 | mPort(port) 13 | {} 14 | 15 | Server::Connection::~Connection() { 16 | delete mSocket; 17 | } 18 | 19 | tcp::Socket& Server::Connection::socket() const { 20 | return *mSocket; 21 | } 22 | 23 | InetAddress Server::Connection::address() const { 24 | return mAddress; 25 | } 26 | 27 | Uint16 Server::Connection::port() const { 28 | return mPort; 29 | } 30 | 31 | void Server::Connection::close() const { 32 | mSocket->close(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/tcp/Server.Iterator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | namespace tcp { 9 | Server::Iterator::Iterator(const Server *server) : server(server) {} 10 | 11 | Server::Connection Server::Iterator::operator *() const { 12 | return server->accept(); 13 | } 14 | 15 | Server::Connection Server::Iterator::operator ->() const { 16 | return server->accept(); 17 | } 18 | 19 | const Server::Iterator& Server::Iterator::operator ++(Int32) const { 20 | return *this; 21 | } 22 | 23 | const Server::Iterator& Server::Iterator::operator ++() const { 24 | return *this; 25 | } 26 | 27 | bool Server::Iterator::operator!=(const Iterator &right) const { 28 | return server->isValid() || right.server->isValid(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/tcp/Server.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "../SocketImpl.hpp" 9 | 10 | namespace ohf { 11 | namespace tcp { 12 | Server::Server() : ohf::Socket(Type::TCP) {} 13 | 14 | Server::Server(const InetAddress &address, Uint16 port) : Server() { 15 | bind(address, port); 16 | } 17 | 18 | Server::Server(Family family, const HttpURL &url) : Server() { 19 | bind(InetAddress(url.host(), family), url.port()); 20 | } 21 | 22 | Server::Server(ohf::tcp::Server &&server) noexcept { 23 | mFD = server.mFD; 24 | server.mFD = SocketImpl::invalidSocket(); 25 | 26 | mBlocking = server.mBlocking; 27 | server.mBlocking = true; 28 | } 29 | 30 | void Server::bind(const InetAddress &address, Uint16 port) { 31 | create(address.family()); 32 | mFamily = address.family(); 33 | 34 | Int32 option = 1; 35 | if(setsockopt(mFD, SOL_SOCKET, SO_REUSEADDR, (const char *) &option, sizeof(option)) < 0) { 36 | throw Exception(Exception::Code::FAILED_TO_SET_SOCKET_OPTION, 37 | "Failed to set socket option: " + std::to_string(SO_REUSEADDR)); 38 | } 39 | 40 | SocketImpl::SocketLength length; 41 | sockaddr_storage socket_address = SocketImpl::createAddress(address, port, length); 42 | if (::bind(mFD, (sockaddr *) &socket_address, length) == -1) { 43 | throw Exception(Exception::Code::FAILED_TO_BIND_SOCKET, 44 | "Failed to bind socket: " + SocketImpl::getError()); 45 | } 46 | } 47 | 48 | void Server::bind(const HttpURL &url) { 49 | bind(InetAddress(url.host()), url.port()); 50 | } 51 | 52 | void Server::listen(Int32 count) const { 53 | if (::listen(mFD, count) < 0) { 54 | throw Exception(Exception::Code::FAILED_TO_LISTEN_SOCKET, 55 | "Failed to listen socket: " + SocketImpl::getError()); 56 | } 57 | } 58 | 59 | void Server::listen() const { 60 | listen(SOMAXCONN); 61 | } 62 | 63 | Server::Connection Server::accept() const { 64 | sockaddr_storage addr; 65 | SocketImpl::SocketLength length = SocketImpl::addressLength(mFamily); 66 | 67 | auto fd = ::accept(mFD, (sockaddr *) &addr, &length); 68 | if (fd == SocketImpl::invalidSocket()) { 69 | throw Exception(Exception::Code::FAILED_TO_ACCEPT_SOCKET, 70 | "Failed to accept socket: " + SocketImpl::getError()); 71 | } 72 | 73 | auto client = new tcp::Socket; 74 | client->create(fd); 75 | 76 | return {client, SocketImpl::createInetAddress(&addr), SocketImpl::port(&addr)}; 77 | } 78 | 79 | const Server::Iterator Server::begin() const { 80 | return {this}; 81 | } 82 | 83 | Server::Iterator Server::begin() { 84 | return {this}; 85 | } 86 | 87 | const Server::Iterator Server::end() const { 88 | return {this}; 89 | } 90 | 91 | Server::Iterator Server::end() { 92 | return {this}; 93 | } 94 | 95 | Server& Server::operator =(ohf::tcp::Server&& right) noexcept { 96 | mFD = right.mFD; 97 | right.mFD = SocketImpl::invalidSocket(); 98 | 99 | mBlocking = right.mBlocking; 100 | right.mBlocking = true; 101 | 102 | return *this; 103 | } 104 | } 105 | } 106 | 107 | namespace std { 108 | using namespace ohf; 109 | 110 | void swap(tcp::Server& a, tcp::Server& b) { 111 | swap(a.mFD, b.mFD); 112 | swap(a.mBlocking, b.mBlocking); 113 | } 114 | } -------------------------------------------------------------------------------- /src/tcp/Socket.Stream.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | namespace tcp { 9 | Socket::Stream::Stream(Socket &socket, Socket::StreamBuf *buffer) : std::iostream(buffer) { 10 | buffer->socket(&socket); 11 | exceptions(std::ios::badbit); // rethrow exceptions 12 | } 13 | 14 | void Socket::Stream::socket(Socket &socket) { 15 | ((StreamBuf *) rdbuf())->socket(&socket); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/tcp/Socket.StreamBuf.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | 7 | namespace ohf { 8 | namespace tcp { 9 | Socket::StreamBuf::StreamBuf(Int32 write, Int32 read) : IOStreamBuf(write, read) {} 10 | 11 | void Socket::StreamBuf::socket(tcp::Socket *socket) { 12 | mSocket = socket; 13 | } 14 | 15 | Int32 Socket::StreamBuf::write(const char *data, Int32 length) { 16 | return mSocket->send(data, length); 17 | } 18 | 19 | Int32 Socket::StreamBuf::read(char *data, Int32 length) { 20 | return mSocket->receive(data, length); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/tcp/Socket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include "../SocketImpl.hpp" 6 | #include 7 | 8 | #ifdef OKHTTPFORK_UNIX 9 | #define OHF_FLAGS MSG_NOSIGNAL 10 | #else 11 | #define OHF_FLAGS 0 12 | #endif 13 | 14 | namespace ohf { 15 | namespace tcp { 16 | Socket::Socket() : ohf::Socket(Type::TCP) {} 17 | 18 | Socket::Socket(tcp::Socket&& socket) noexcept : tcp::Socket() { 19 | mFD = socket.mFD; 20 | socket.mFD = SocketImpl::invalidSocket(); 21 | 22 | mBlocking = socket.mBlocking; 23 | socket.mBlocking = true; 24 | } 25 | 26 | void Socket::connect(const InetAddress &address, Uint16 port) { 27 | create(address.family()); 28 | 29 | bool is_blocking = isBlocking(); 30 | if (!is_blocking) blocking(true); 31 | 32 | SocketImpl::SocketLength length; 33 | sockaddr_storage socket_address = SocketImpl::createAddress(address, port, length); 34 | if (::connect(mFD, (sockaddr *) &socket_address, length) == -1) { 35 | throw Exception(Exception::Code::FAILED_TO_CREATE_CONNECTION, 36 | "Failed to create connection: " + SocketImpl::getError()); 37 | } 38 | 39 | blocking(is_blocking); 40 | } 41 | 42 | void Socket::connect(Family family, const HttpURL &url) { 43 | connect(InetAddress(url.host(), family), url.port()); 44 | } 45 | 46 | void Socket::disconnect() { 47 | close(); 48 | } 49 | 50 | Int32 Socket::send(const char *data, Int32 size) const { 51 | if (!data || size == 0) throw Exception(Exception::Code::NO_DATA_TO_SEND, "No data to send: "); 52 | 53 | Int32 sent = ::send(mFD, data, size, OHF_FLAGS); 54 | if (sent < 0) { 55 | throw Exception(Exception::Code::FAILED_TO_SEND_DATA, 56 | "Failed to send data: " + SocketImpl::getError()); 57 | } 58 | 59 | return sent; 60 | } 61 | 62 | Int32 Socket::send(const std::vector &data) const { 63 | return send(data.data(), data.size()); 64 | } 65 | 66 | Int32 Socket::send(const std::string &data) const { 67 | return send(data.data(), data.size()); 68 | } 69 | 70 | Int32 Socket::receive(char *data, Int32 size) const { 71 | Int32 received = recv(mFD, data, size, OHF_FLAGS); 72 | if(received < 0) { 73 | throw Exception(Exception::Code::FAILED_TO_RECEIVE_DATA, 74 | "Failed to receive data: " + SocketImpl::getError()); 75 | } 76 | return received; 77 | } 78 | 79 | Int32 Socket::receive(std::vector &data, Int32 size) const { 80 | data.clear(); 81 | data.resize(size); 82 | Int32 received = receive(data.data(), size); 83 | data.resize(received); 84 | data.shrink_to_fit(); 85 | return received; 86 | } 87 | 88 | Int32 Socket::receive(std::string &data, Int32 size) const { 89 | data.clear(); 90 | data.resize(size); 91 | Int32 received = receive(&data[0], size); 92 | data.resize(received); 93 | data.shrink_to_fit(); 94 | return received; 95 | } 96 | 97 | Socket& Socket::operator =(tcp::Socket&& right) noexcept { 98 | mFD = right.mFD; 99 | right.mFD = SocketImpl::invalidSocket(); 100 | 101 | mBlocking = right.mBlocking; 102 | right.mBlocking = true; 103 | 104 | return *this; 105 | } 106 | } 107 | } 108 | 109 | namespace std { 110 | using namespace ohf; 111 | 112 | void swap(tcp::Socket& a, tcp::Socket& b) { 113 | std::swap(a.mFD, b.mFD); 114 | std::swap(a.mBlocking, b.mBlocking); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/unix/SocketImpl.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by good-pudge on 01.11.17. 3 | // 4 | 5 | #include 6 | #include 7 | #include "SocketImpl.hpp" 8 | 9 | namespace ohf { 10 | void SocketImpl::close(Socket::Handle sock) { 11 | ::close(sock); 12 | } 13 | 14 | std::string SocketImpl::getError() { 15 | return strerror(errno); 16 | } 17 | 18 | void SocketImpl::setBlocking(Socket::Handle sock, bool blocking) { 19 | unsigned long mode = blocking ? 0 : 1; 20 | ioctl(sock, FIONBIO, &mode); 21 | } 22 | 23 | Socket::Handle SocketImpl::invalidSocket() { 24 | return -1; 25 | } 26 | } -------------------------------------------------------------------------------- /src/unix/SocketImpl.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by good-pudge on 01.11.17. 3 | // 4 | 5 | #ifndef OKHTTPFORK_UNIX_SOCKETIMPL_HPP 6 | #define OKHTTPFORK_UNIX_SOCKETIMPL_HPP 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace ohf { 18 | namespace SocketImpl { 19 | typedef socklen_t SocketLength; 20 | 21 | struct Initializer {}; 22 | 23 | void close(Socket::Handle sock); 24 | 25 | std::string getError(); 26 | 27 | void setBlocking(Socket::Handle sock, bool blocking); 28 | 29 | Socket::Handle invalidSocket(); 30 | }; 31 | } 32 | 33 | #endif //OKHTTPFORK_SOCKETIMPL_HPP 34 | -------------------------------------------------------------------------------- /src/util/string.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include 6 | #include 7 | #include "string.hpp" 8 | 9 | namespace ohf { 10 | namespace util { 11 | namespace string { 12 | std::vector split(std::string str, const std::string &delimiter) { 13 | std::vector tokens; 14 | size_t pos = 0; 15 | while ((pos = str.find(delimiter)) != std::string::npos) { 16 | std::string token = str.substr(0, pos); 17 | if (!token.empty()) tokens.push_back(token); 18 | str.erase(0, pos + delimiter.length()); 19 | } 20 | if (!str.empty()) tokens.push_back(str); 21 | return tokens; 22 | } 23 | 24 | std::vector split(std::string s, const std::vector &delimiters) { 25 | std::vector tokens; 26 | 27 | while (true) { 28 | auto offset = std::string::npos; 29 | std::string::size_type delim_size = 0; 30 | for (const auto &delimiter : delimiters) { 31 | auto current_offset = s.find(delimiter); 32 | if (current_offset < offset) { 33 | offset = current_offset; 34 | delim_size = delimiter.length(); 35 | } 36 | } 37 | 38 | if (offset == std::string::npos) { 39 | break; 40 | } 41 | 42 | std::string token(s, 0, offset); 43 | if (!token.empty()) tokens.push_back(token); 44 | 45 | s.erase(0, offset + delim_size); 46 | } 47 | 48 | if (!s.empty()) tokens.push_back(s); 49 | 50 | return tokens; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/util/string.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_STRING_HPP 6 | #define OKHTTPFORK_STRING_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace ohf { 14 | namespace util { 15 | namespace string { 16 | std::vector split(std::string s, const std::string &delimiter); 17 | 18 | std::vector split(std::string s, const std::vector &delimiters); 19 | 20 | inline bool startsWith(const std::string &s, const std::string &prefix) { 21 | if (s.length() >= prefix.length()) 22 | return s.substr(0, prefix.length()) == prefix; 23 | return false; 24 | } 25 | 26 | inline bool endsWith(const std::string &s, const std::string &postfix) { 27 | if (s.length() >= postfix.length()) 28 | return s.substr(s.length() - postfix.length(), postfix.length()) == postfix; 29 | return false; 30 | } 31 | 32 | inline bool contains(const std::string &s, const std::string &data) { 33 | return s.find(data) != std::string::npos; 34 | } 35 | 36 | inline void toLower(std::string &s) { 37 | for (char &i : s) i = std::tolower(i); 38 | } 39 | } 40 | } 41 | } 42 | 43 | #endif //OKHTTPFORK_STRING_HPP 44 | -------------------------------------------------------------------------------- /src/util/util.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include "util.hpp" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace ohf { 11 | namespace util { 12 | std::vector readStream(std::istream &stream) { 13 | std::vector buffer; 14 | int c; 15 | while ((c = stream.get()) != EOF) 16 | buffer.push_back((char) c); 17 | if (stream.bad()) 18 | throw ohf::Exception(ohf::Exception::Code::FAILED_TO_READ_STREAM, "Failed to read stream: "); 19 | return buffer; 20 | } 21 | 22 | std::time_t parseDate(const std::string &what, const std::string &format) { 23 | std::tm t{}; 24 | 25 | std::istringstream iss(what); 26 | iss >> std::get_time(&t, format.c_str()); 27 | 28 | if (iss.fail()) { 29 | throw ohf::Exception(ohf::Exception::Code::FAILED_TO_PARSE_TIME, 30 | "Failed to parse time: data: " + what + "; format: " + format); 31 | } 32 | 33 | std::time_t current = std::time(nullptr); 34 | std::time_t gmt = std::mktime(std::gmtime(¤t)); 35 | std::time_t local = std::mktime(std::localtime(¤t)); 36 | std::time_t difference = gmt - local; 37 | 38 | return std::mktime(&t) - difference; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/util/util.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_UTIL_HPP 6 | #define OKHTTPFORK_UTIL_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace ohf { 14 | namespace util { 15 | std::vector readStream(std::istream &stream); 16 | 17 | std::time_t parseDate(const std::string &what, const std::string &format); 18 | } 19 | } 20 | 21 | #endif //OKHTTPFORK_UTIL_HPP 22 | -------------------------------------------------------------------------------- /src/win32/SocketImpl.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #include "SocketImpl.hpp" 6 | #include 7 | 8 | namespace ohf { 9 | namespace SocketImpl { 10 | Initializer::Initializer() { 11 | WSAData data; 12 | WSAStartup(MAKEWORD(1, 1), &data); 13 | } 14 | 15 | Initializer::~Initializer() { 16 | WSACleanup(); 17 | } 18 | 19 | void close(Socket::Handle sock) { 20 | closesocket(sock); 21 | } 22 | 23 | std::string getError() { 24 | wchar_t *error; 25 | FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 26 | nullptr, 27 | WSAGetLastError(), 28 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 29 | (LPWSTR) &error, 30 | 0, 31 | nullptr); 32 | 33 | std::wstring werror(error); 34 | int size = WideCharToMultiByte(CP_UTF8, 0, &werror[0], werror.size(), nullptr, 0, nullptr, nullptr); 35 | std::string aerror(size, 0); 36 | WideCharToMultiByte(CP_UTF8, 0, &werror[0], werror.size(), &aerror[0], size, NULL, NULL); 37 | 38 | return aerror; 39 | } 40 | 41 | void setBlocking(Socket::Handle sock, bool blocking) { 42 | unsigned long mode = blocking ? 0 : 1; 43 | ioctlsocket(sock, FIONBIO, &mode); 44 | } 45 | 46 | Socket::Handle invalidSocket() { 47 | return INVALID_SOCKET; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/win32/SocketImpl.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef OKHTTPFORK_WIN_SOCKETIMPL_HPP 6 | #define OKHTTPFORK_WIN_SOCKETIMPL_HPP 7 | 8 | #include 9 | #include 10 | 11 | #define _WIN32_WINNT 0x600 // activate inet_pton 12 | #include 13 | #include 14 | 15 | namespace ohf { 16 | namespace SocketImpl { 17 | typedef int SocketLength; 18 | 19 | struct Initializer { 20 | Initializer(); 21 | 22 | ~Initializer(); 23 | }; 24 | 25 | void close(Socket::Handle sock); 26 | 27 | std::string getError(); 28 | 29 | void setBlocking(Socket::Handle sock, bool blocking); 30 | 31 | Socket::Handle invalidSocket(); 32 | }; 33 | } 34 | 35 | #endif // OKHTTPFORK_WIN_SOCKETIMPL_HPP 36 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1 FATAL_ERROR) 2 | project(ohf_tests) 3 | 4 | # Catch2 5 | set(CATCH_ROOT_DIR "${PROJECT_SOURCE_DIR}/../Catch2") 6 | 7 | include(${CATCH_ROOT_DIR}/contrib/ParseAndAddCatchTests.cmake) 8 | 9 | set(CATCH_INCLUDE_DIR "${CATCH_ROOT_DIR}/include") 10 | if(NOT EXISTS ${CATCH_INCLUDE_DIR}) 11 | message(FATAL_ERROR "Catch2 submodule not found") 12 | endif() 13 | 14 | file(GLOB_RECURSE CATCH_FILES ${PROJECT_SOURCE_DIR} "${CATCH_INCLUDE_DIR}/*") 15 | 16 | # Tests 17 | file(GLOB_RECURSE TESTS_FILES ${PROJECT_SOURCE_DIR} "*.hpp" "*.cpp") 18 | add_executable(ohf_tests ${TESTS_FILES} ${CATCH_FILES}) 19 | ParseAndAddCatchTests(ohf_tests) 20 | 21 | # Libraries 22 | ## Find 23 | find_package(Threads REQUIRED) 24 | 25 | ## Include 26 | include_directories(${CATCH_INCLUDE_DIR}) 27 | include_directories(.) 28 | include_directories(../include) 29 | 30 | ## Link 31 | target_link_libraries(ohf_tests ohf) 32 | target_link_libraries(ohf_tests ${CMAKE_THREAD_LIBS_INIT}) 33 | 34 | # Reporter 35 | if(WIN32) 36 | set(LCOV perl lcov/lcov.perl) 37 | set(CODECOV codecov -f coverage.info) 38 | elseif(UNIX) 39 | set(LCOV lcov) 40 | set(CODECOV curl -s https://codecov.io/bash | bash -s) 41 | endif() 42 | 43 | add_custom_target(coverage 44 | COMMAND ${LCOV} -d . -z 45 | COMMAND ${LCOV} -d . -i -c -o coverage.info 46 | COMMAND gdb -ex 'set confirm off' -ex run -ex quit ctest 47 | COMMAND ${LCOV} -d . -c -o coverage.info 48 | COMMAND ${LCOV} -r coverage.info '/usr/*' 'tests/*' 49 | COMMAND ${LCOV} --list coverage.info 50 | COMMAND ${CODECOV} 51 | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 52 | DEPENDS ohf ohf_tests 53 | COMMENT "Make Codecov report" 54 | ) 55 | -------------------------------------------------------------------------------- /tests/CacheControl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace ohf; 7 | 8 | TEST_CASE("CacheControl") { 9 | CacheControl::Builder builder; 10 | SECTION("Builder") { 11 | builder 12 | .immutable() 13 | .noCache() 14 | .noStore() 15 | .noTransform() 16 | .onlyIfCached() 17 | .maxAge(10.0_s) 18 | .maxStale(5.0_s) 19 | .minFresh(3.0_s); 20 | } 21 | 22 | CacheControl cacheControl = builder.build(); 23 | REQUIRE(cacheControl.immutable()); 24 | REQUIRE(cacheControl.noCache()); 25 | REQUIRE(cacheControl.noStore()); 26 | REQUIRE(cacheControl.noTransform()); 27 | REQUIRE(cacheControl.onlyIfCached()); 28 | REQUIRE_FALSE(cacheControl.mustRevalidate()); 29 | REQUIRE_FALSE(cacheControl.isPublic()); 30 | REQUIRE_FALSE(cacheControl.isPrivate()); 31 | REQUIRE(cacheControl.sMaxAge() == TimeUnit::MINUS_ONE_SECOND); 32 | REQUIRE(cacheControl.maxAge() == 10.0_s); 33 | REQUIRE(cacheControl.maxStale() == 5.0_s); 34 | REQUIRE(cacheControl.minFresh() == 3.0_s); 35 | 36 | Headers headers = Headers::Builder() 37 | .add("Cache-Control", "public, private, no-cache, only-if-cached, must-revalidate, proxy-revalidate, " 38 | "immutable, no-store, no-transform, max-age=10, s-maxage=7, max-stale=5, " 39 | "min-fresh=3") 40 | .build(); 41 | CacheControl cc(headers); 42 | REQUIRE_THAT(cc.sMaxAge(), TimeUnitMatcher(7.0_s)); 43 | REQUIRE(cc.mustRevalidate()); 44 | REQUIRE(cc.isPublic()); 45 | REQUIRE(cc.isPrivate()); 46 | 47 | CacheControl otherCC(headers); 48 | REQUIRE(cc == otherCC); 49 | 50 | headers = Headers::Builder() 51 | .set("Cache-Control", "max-age=INVALID") 52 | .build(); 53 | REQUIRE_THROWS_CODE(CacheControl(headers), Exception::Code::INVALID_MAX_AGE); 54 | 55 | headers = Headers::Builder() 56 | .set("Cache-Control", "public, s-maxage=INVALID") 57 | .build(); 58 | REQUIRE_THROWS_CODE(CacheControl(headers), Exception::Code::INVALID_S_MAX_AGE); 59 | 60 | headers = Headers::Builder() 61 | .set("Cache-Control", "max-stale=INVALID") 62 | .build(); 63 | REQUIRE_THROWS_CODE(CacheControl(headers), Exception::Code::INVALID_MAX_STALE); 64 | 65 | headers = Headers::Builder() 66 | .set("Cache-Control", "min-fresh=INVALID") 67 | .build(); 68 | REQUIRE_THROWS_CODE(CacheControl(headers), Exception::Code::INVALID_MIN_FRESH); 69 | 70 | CacheControl emptyCC; 71 | std::ostringstream oss; 72 | oss << emptyCC; 73 | REQUIRE(oss.str().empty()); 74 | } -------------------------------------------------------------------------------- /tests/Cookie.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/ExceptionCatch.hpp" 3 | #include 4 | 5 | using namespace ohf; 6 | 7 | TEST_CASE("Cookie") { 8 | Cookie::Builder builder; 9 | SECTION("Builder") { 10 | builder 11 | .name("Hello") 12 | .value("World") 13 | .path("/") 14 | .httpOnly() 15 | .secure() 16 | .expiresAt(10.0_s) 17 | .hostOnlyDomain("www.example.com"); 18 | } 19 | 20 | Cookie cookie = builder.build(); 21 | REQUIRE(cookie.name() == "Hello"); 22 | REQUIRE(cookie.value() == "World"); 23 | REQUIRE(cookie.path() == "/"); 24 | REQUIRE(cookie.httpOnly()); 25 | REQUIRE(cookie.secure()); 26 | REQUIRE(cookie.expiresAt() == 10.0_s); 27 | REQUIRE(cookie.hostOnly()); 28 | REQUIRE(cookie.domain() == "www.example.com"); 29 | REQUIRE_FALSE(cookie.persistent()); 30 | 31 | HttpURL url = "https://www.example.com"; 32 | REQUIRE(cookie.matches(url)); 33 | 34 | Headers headers = Headers::Builder() 35 | .add("Set-Cookie", "foo=bar; secure; httpOnly; path=/example; domain=exampledomain; expires=Sun, 16 Jul 2018 06:23:41 GMT") 36 | .add("Set-Cookie: foo=bar; secure; httpOnly; path=/example; domain=exampledomain; max-age=1531722240") 37 | .build(); 38 | std::vector cookies = Cookie::parseAll(url, headers); 39 | REQUIRE(cookies[0] == cookies[1]); 40 | std::ostringstream oss; 41 | oss << cookies[0]; 42 | REQUIRE(oss.str() == "foo=bar; Path=/example; Domain=exampledomain; Max-Age=1531722240; HttpOnly; Secure"); 43 | 44 | REQUIRE_THROWS_CODE(Cookie(url, ""), Exception::Code::INVALID_COOKIE_LINE); 45 | REQUIRE_THROWS_CODE(Cookie(url, "name value"), Exception::Code::INVALID_COOKIE_NAME_VALUE); 46 | REQUIRE_THROWS_CODE(Cookie(url, "name=; max-age=minus_one"), Exception::Code::INVALID_MAX_AGE); 47 | 48 | cookie = Cookie::Builder() 49 | .domain("exampledomain") 50 | .build(); 51 | REQUIRE(cookie.domain() == "exampledomain"); 52 | } 53 | -------------------------------------------------------------------------------- /tests/Exception.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace ohf; 6 | 7 | SCENARIO("Exception") { 8 | WHEN("Create simple exception") { 9 | GIVEN("Code: Exception::Code::OK and message: All right") { 10 | Exception exception(Exception::Code::OK, "All right"); 11 | THEN("Getters should return these data too") { 12 | REQUIRE(exception.code() == Exception::Code::OK); 13 | REQUIRE(exception.message() == "All right"); 14 | REQUIRE(strcmp(exception.what(), "All right") == 0); 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /tests/FormBody.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/ExceptionCatch.hpp" 3 | #include 4 | 5 | using namespace ohf; 6 | 7 | TEST_CASE("FormBody") { 8 | FormBody::Builder builder; 9 | SECTION("Builder") { 10 | builder 11 | .add("{q}", "Cat! and dogs?") 12 | .add("hello", "world"); 13 | } 14 | 15 | FormBody formBody = builder.build(); 16 | 17 | REQUIRE(formBody.name(0) == "{q}"); 18 | REQUIRE_THROWS_CODE(formBody.name(2), ohf::Exception::Code::OUT_OF_RANGE); 19 | REQUIRE(formBody.encodedName(0) == "%7Bq%7D"); 20 | 21 | REQUIRE(formBody.value(0) == "Cat! and dogs?"); 22 | REQUIRE_THROWS_CODE(formBody.value(2), ohf::Exception::Code::OUT_OF_RANGE); 23 | REQUIRE(formBody.encodedValue(0) == "Cat%21%20and%20dogs%3F"); 24 | 25 | REQUIRE(formBody.size() == 2); 26 | 27 | REQUIRE(formBody.contentType() == "application/x-www-form-urlencoded"); 28 | REQUIRE(formBody.contentLength() == std::string("%7Bq%7D=Cat%21%20and%20dogs%3F&hello=world").size()); 29 | } -------------------------------------------------------------------------------- /tests/Headers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/ExceptionCatch.hpp" 3 | #include 4 | 5 | using namespace ohf; 6 | 7 | TEST_CASE("Headers") { 8 | Headers::Builder builder; 9 | SECTION("Builder") { 10 | builder 11 | .add("Set-Cookie: Hello=World") 12 | .add("Set-Cookie: foo=bar") 13 | .add("Some", "one") 14 | .add("Host", "unknown") 15 | .add("Host", "example.com") 16 | .add("Date: Wed, 21 Oct 2015 07:28:00 GMT"); 17 | 18 | REQUIRE_THROWS_CODE(builder.add(""), Exception::Code::HEADER_IS_EMPTY); 19 | 20 | REQUIRE(builder.get("Set-Cookie") == "Hello=World"); 21 | REQUIRE(builder.get("Host") == "unknown"); 22 | 23 | builder.removeAll("Set-Cookie"); 24 | REQUIRE(builder.get("Set-Cookie").empty()); 25 | } 26 | 27 | Headers headers = builder.build(); 28 | REQUIRE(headers.getDate().seconds() == 1445412480.f); 29 | REQUIRE(headers.get("Some") == "one"); 30 | REQUIRE(headers["Date"] == "Wed, 21 Oct 2015 07:28:00 GMT"); 31 | REQUIRE(headers.get("Header that don't exist").empty()); 32 | REQUIRE(headers.name(0) == "Some"); 33 | REQUIRE_THROWS_CODE(headers.name(4), Exception::Code::OUT_OF_RANGE); 34 | REQUIRE(headers.value(0) == "one"); 35 | REQUIRE_THROWS_CODE(headers.value(4), Exception::Code::OUT_OF_RANGE); 36 | REQUIRE(headers.pair(0) == Headers::Pair("Some", "one")); 37 | REQUIRE(headers.size() == 4); 38 | 39 | Headers other = Headers::Builder() 40 | .add("Headers: not equal") 41 | .build(); 42 | REQUIRE_FALSE(headers == other); 43 | 44 | 45 | std::map map; 46 | map["Set-Cookie"] = "COOKIE="; 47 | map["Host"] = "example.com"; 48 | map["Connection"] = "close"; 49 | headers = Headers(map); 50 | 51 | REQUIRE(headers.name(0) == "Connection"); 52 | REQUIRE(headers.value(0) == "close"); 53 | REQUIRE(headers.pair(1) == Headers::Pair("Host", "example.com")); 54 | 55 | map.clear(); 56 | map[""] = ""; 57 | REQUIRE_THROWS_CODE(Headers(map), Exception::Code::HEADER_IS_EMPTY); 58 | 59 | const Headers const_headers = headers; 60 | 61 | const auto const_iterator = const_headers.begin(); 62 | auto iterator = headers.begin(); 63 | REQUIRE(const_iterator == iterator); 64 | REQUIRE_FALSE(const_iterator != iterator); 65 | 66 | const auto const_iterator_end = const_headers.end(); 67 | auto iterator_end = headers.end(); 68 | REQUIRE(iterator_end == const_iterator_end); 69 | REQUIRE_FALSE(iterator_end != const_iterator_end); 70 | 71 | REQUIRE(const_iterator != const_iterator_end); 72 | REQUIRE(iterator != iterator_end); 73 | 74 | const auto& const_pair_r = *const_iterator; 75 | auto& pair_r = *iterator; 76 | REQUIRE(const_pair_r == pair_r); 77 | 78 | const auto*const_pair_p = const_iterator.operator->(); 79 | auto* pair_p = iterator.operator->(); 80 | REQUIRE(*const_pair_p == *pair_p); 81 | 82 | struct Access { // access to private members 83 | mutable bool outOfRange; 84 | Headers::Pair type; 85 | Uint32 index; 86 | const Headers *headers; 87 | }; 88 | 89 | auto access = (Access *) &iterator; 90 | 91 | REQUIRE(++iterator == Headers::Iterator(1, &headers)); 92 | REQUIRE(access->index == 1); 93 | REQUIRE(--iterator == Headers::Iterator(0, &headers)); 94 | REQUIRE(access->index == 0); 95 | 96 | REQUIRE(iterator++ == Headers::Iterator(1, &headers)); 97 | REQUIRE(access->index == 0); 98 | REQUIRE((iterator + 1)-- == Headers::Iterator(0, &headers)); 99 | REQUIRE(access->index == 0); 100 | 101 | REQUIRE((iterator + 1) == Headers::Iterator(1, &headers)); 102 | REQUIRE(access->index == 0); 103 | 104 | iterator += 1; 105 | REQUIRE((iterator - 1) == Headers::Iterator(0, &headers)); 106 | iterator -= 1; 107 | REQUIRE(access->index == 0); 108 | 109 | REQUIRE((iterator += 2) == Headers::Iterator(2, &headers)); 110 | REQUIRE(access->index == 2); 111 | REQUIRE((iterator -= 2) == Headers::Iterator(0, &headers)); 112 | REQUIRE(access->index == 0); 113 | 114 | iterator += 3; // >= 115 | REQUIRE(iterator.isOutOfRange()); 116 | 117 | iterator -= 3; // == 118 | REQUIRE_FALSE(iterator.isOutOfRange()); 119 | 120 | iterator -= 3; // <= 121 | REQUIRE(iterator.isOutOfRange()); 122 | } 123 | -------------------------------------------------------------------------------- /tests/HttpURL.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/ExceptionCatch.hpp" 3 | #include 4 | 5 | using namespace ohf; 6 | 7 | TEST_CASE("HttpURL") { 8 | REQUIRE(HttpURL::defaultPort("http") == 80); 9 | REQUIRE(HttpURL::defaultPort("https") == 443); 10 | 11 | REQUIRE(HttpURL::decode("Hello%2C%20world%21") == "Hello, world!"); 12 | REQUIRE_THROWS_CODE(HttpURL::decode("%ZZ"), Exception::Code::INVALID_URI_HEX_CODE); 13 | 14 | REQUIRE(HttpURL("example.com:214").url() == "http://example.com:214/"); 15 | REQUIRE(HttpURL("example.com/#FrAgMeNt").url() == "http://example.com/#FrAgMeNt"); 16 | REQUIRE(HttpURL("example.com/?q=123&land=en-US&empty").url() == "http://example.com/?empty&land=en-US&q=123"); 17 | REQUIRE_THROWS_CODE(HttpURL("example.com?q=123"), Exception::Code::INVALID_URI); 18 | REQUIRE_THROWS_CODE(HttpURL("example.com#123"), Exception::Code::INVALID_URI); 19 | REQUIRE_THROWS_CODE(HttpURL("/?q=123"), Exception::Code::HOST_IS_EMPTY); 20 | REQUIRE_THROWS_CODE(HttpURL("example.com:INVALID"), Exception::Code::INVALID_PORT); 21 | REQUIRE_THROWS_CODE(HttpURL("example.com/?q=123=321"), Exception::Code::INVALID_QUERY_PARAMETER); 22 | 23 | HttpURL::Builder builder; 24 | SECTION("Builder") { 25 | builder 26 | .scheme("ftp") 27 | .host("example.com") 28 | .port(1234) 29 | .addPathSegments("/foo/bar") 30 | .setPathSegment(0, "wow") 31 | .setPathSegment(2, "oh/") 32 | .removePathSegment(1) 33 | .query("q=123&hello=world&empty_parameter") 34 | .setQueryParameter("unused", "parameter") 35 | .setQueryParameter("q", "321") 36 | .removeQueryParameter("unused") 37 | .fragment("some_fragment") 38 | .pathSuffix(false); 39 | 40 | REQUIRE_THROWS_CODE(builder.query("q=123=321"), Exception::Code::INVALID_QUERY_PARAMETER); 41 | } 42 | 43 | HttpURL url = builder.build(); 44 | REQUIRE(url.scheme() == "ftp"); 45 | REQUIRE(url.host() == "example.com"); 46 | REQUIRE(url.port() == 1234); 47 | 48 | REQUIRE_FALSE(url.pathSuffix()); 49 | REQUIRE(url.encodedPath() == "/wow/oh"); 50 | REQUIRE(url.pathSegments()[0] == "wow"); 51 | REQUIRE(url.pathSegments()[1] == "oh"); 52 | REQUIRE(url.pathSize() == 7); 53 | 54 | auto segments = url.encodedPathSegments(); 55 | REQUIRE(segments[0] == "wow"); 56 | REQUIRE(segments[1] == "oh"); 57 | 58 | REQUIRE(url.query() == "empty_parameter&hello=world&q=321"); 59 | REQUIRE(url.queryParameter("q") == "321"); 60 | REQUIRE(url.queryParameterName(0) == "empty_parameter"); 61 | 62 | auto query = url.queryMap(); 63 | REQUIRE(query["empty_parameter"].empty()); 64 | REQUIRE(query["hello"] == "world"); 65 | REQUIRE(query["q"] == "321"); 66 | 67 | REQUIRE_THROWS_CODE(url.queryParameterName(3), Exception::Code::OUT_OF_RANGE); 68 | REQUIRE(url.queryParameterValue(0).empty()); 69 | REQUIRE_THROWS_CODE(url.queryParameterValue(3), Exception::Code::OUT_OF_RANGE); 70 | REQUIRE(url.queryParameterNames()[0] == "empty_parameter"); 71 | REQUIRE(url.querySize() == 33); 72 | REQUIRE(url.fragment() == "some_fragment"); 73 | REQUIRE_FALSE(url.isHttps()); 74 | 75 | std::ostringstream oss; 76 | oss << url; 77 | REQUIRE(oss.str() == "ftp://example.com:1234/wow/oh?empty_parameter&hello=world&q=321#some_fragment"); 78 | 79 | url = HttpURL::Builder() 80 | .host("example.com") 81 | .addPathSegments("path/") 82 | .build(); 83 | REQUIRE(url.url() == "http://example.com/path/"); 84 | REQUIRE(url.encodedQuery().empty()); 85 | } -------------------------------------------------------------------------------- /tests/InetAddress.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/ExceptionCatch.hpp" 3 | #include 4 | 5 | #if WIN32 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | using namespace ohf; 12 | 13 | SCENARIO("InetAddress") { 14 | WHEN("Resolve several addresses") { 15 | GIVEN("Host: unknownhost and family: IP_UNKNOWN") { 16 | THEN("Should be exception") { 17 | REQUIRE_THROWS_CODE(InetAddress::getAllByName("unknownhost", IP_UNKNOWN), Exception::Code::UNKNOWN_HOST); 18 | } 19 | } 20 | 21 | GIVEN("Host: unknownhost and family: IPv4") { 22 | THEN("Should be exception") { 23 | REQUIRE_THROWS_CODE(InetAddress::getAllByName("unknownhost", IPv4), Exception::Code::UNKNOWN_HOST); 24 | } 25 | } 26 | 27 | GIVEN("Host: unknownhost and family: IPv6") { 28 | THEN("Should be exception") { 29 | REQUIRE_THROWS_CODE(InetAddress::getAllByName("unknownhost", IPv6), Exception::Code::UNKNOWN_HOST); 30 | } 31 | } 32 | } 33 | 34 | WHEN("Create InetAddress") { 35 | GIVEN("Host: localhost and family: IPv4") { 36 | InetAddress address("localhost", IPv4); 37 | THEN("Getters should return valid data") { 38 | REQUIRE(address.address() == std::array {127, 0, 0, 1}); 39 | REQUIRE(address.family() == IPv4); 40 | REQUIRE(address.hostAddress() == "127.0.0.1"); 41 | REQUIRE(address.hostName() == "localhost"); 42 | REQUIRE(address.originalType() == AF_INET); 43 | } 44 | } 45 | 46 | GIVEN("Host: localhost and family: IPv6") { 47 | InetAddress address("localhost", IPv6); 48 | THEN("Getters should return valid data") { 49 | REQUIRE(address.address() == std::array {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}); 50 | REQUIRE(address.family() == IPv6); 51 | REQUIRE(address.hostAddress() == "::1"); 52 | REQUIRE(address.hostName() == "localhost"); 53 | REQUIRE(address.originalType() == AF_INET6); 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /tests/MediaType.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/ExceptionCatch.hpp" 3 | #include 4 | 5 | using namespace ohf; 6 | 7 | TEST_CASE("MediaType") { 8 | MediaType type = "text/html; charset=utf-8; boundary=some_boundary"; 9 | REQUIRE(type.type() == "text"); 10 | REQUIRE(type.subtype() == "html"); 11 | REQUIRE(type.charset() == "utf-8"); 12 | REQUIRE(type.boundary() == "some_boundary"); 13 | REQUIRE(type.toString() == "text/html; charset=utf-8; boundary=some_boundary"); 14 | 15 | MediaType otherType = "text/xml"; 16 | REQUIRE(otherType.charset("windows-1251") == "windows-1251"); 17 | REQUIRE(otherType.boundary("--123--") == "--123--"); 18 | 19 | REQUIRE_FALSE(type == otherType); 20 | 21 | REQUIRE_THROWS_CODE(MediaType(""), Exception::Code::INVALID_CONTENT_TYPE_LINE); 22 | REQUIRE_THROWS_CODE(MediaType("INVALID"), Exception::Code::INVALID_MIME_TYPE); 23 | } 24 | -------------------------------------------------------------------------------- /tests/MultipartBody.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/ExceptionCatch.hpp" 3 | #include 4 | 5 | using namespace ohf; 6 | 7 | TEST_CASE("MultipartBody") { 8 | MultipartBody::Builder builder("BNDRY"); 9 | //SECTION("Builder") { 10 | builder 11 | .addFormDataPart("hello", "world") 12 | .addFormDataPart("photo", "image.png", ohf::RequestBody("image/png", "*content*")) 13 | .setType(MultipartBody::FORM); 14 | //} 15 | 16 | MultipartBody body = builder.build(); 17 | REQUIRE(body.size() == 2); 18 | REQUIRE(body.type() == "multipart/form-data"); 19 | REQUIRE(body.contentType() == "multipart/form-data; boundary=BNDRY"); 20 | REQUIRE(body.boundary() == "BNDRY"); 21 | 22 | SECTION("Part") { 23 | REQUIRE_THROWS_CODE(body.part(2), Exception::Code::OUT_OF_RANGE); 24 | 25 | auto part = body.part(0); 26 | REQUIRE(body.parts().size() == 2); 27 | 28 | REQUIRE(part.headers().get("Content-Disposition") == "form-data; name=\"hello\""); 29 | REQUIRE(part.body().contentLength() == 5); 30 | 31 | auto part2 = body.part(1); 32 | REQUIRE(part2.body().contentLength() == 9); 33 | REQUIRE(part2.headers().get("Content-Disposition") 34 | == "form-data; name=\"photo\"; filename=\"image.png\""); 35 | 36 | RequestBody requestBody("text/html", "*content*"); 37 | 38 | Headers headers = Headers::Builder() 39 | .set("Content-Type", "text/html") 40 | .build(); 41 | REQUIRE_THROWS_CODE(MultipartBody::Part(headers, body), Exception::Code::UNEXPECTED_HEADER); 42 | 43 | headers = Headers::Builder() 44 | .set("Content-Length", "123") 45 | .build(); 46 | REQUIRE_THROWS_CODE(MultipartBody::Part(headers, body), Exception::Code::UNEXPECTED_HEADER); 47 | } 48 | } -------------------------------------------------------------------------------- /tests/RangeException.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace ohf; 6 | 7 | SCENARIO("RangeException") { 8 | WHEN("Create simple exception") { 9 | GIVEN("Index: 534") { 10 | RangeException exception(534); 11 | THEN("Getters should return these data too") { 12 | REQUIRE(exception.code() == Exception::Code::OUT_OF_RANGE); 13 | REQUIRE(exception.message() == "Out of range: 534"); 14 | REQUIRE(strcmp(exception.what(), "Out of range: 534") == 0); 15 | REQUIRE(exception.index() == 534); 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /tests/Request.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/ExceptionCatch.hpp" 3 | #include 4 | 5 | using namespace ohf; 6 | 7 | TEST_CASE("Request") { 8 | Request::Builder builder; 9 | SECTION("Builder") { 10 | builder 11 | .url("https://example.com") 12 | .method("CREATE", RequestBody("application/json", "{}")) 13 | .headers(Headers::Builder() 14 | .add("Set-Cookie", "key=value") 15 | .add("Set-Cookie: hello=world") 16 | .add("Host", "example.org") 17 | .build()) 18 | .cacheControl(CacheControl::Builder() 19 | .immutable() 20 | .maxAge(12.0_s) 21 | .build()) 22 | .removeHeader("Set-Cookie") 23 | .addHeader("Host", "example.com") 24 | .build(); 25 | 26 | REQUIRE_THROWS_CODE(Request::Builder().build(), Exception::Code::METHOD_IS_NOT_NAMED); 27 | REQUIRE_THROWS_CODE(Request::Builder().delete_().build(), Exception::Code::URL_IS_NOT_NAMED); 28 | } 29 | 30 | Request request = builder.build(); 31 | REQUIRE(request.header("host") == "example.org"); 32 | REQUIRE(request.headers("host").size() == 2); 33 | REQUIRE(request.headers("host")[1] == "example.com"); 34 | REQUIRE(request.url() == "https://example.com"); 35 | REQUIRE(request.isHttps()); 36 | REQUIRE(request.method() == "CREATE"); 37 | REQUIRE(request.body().contentType() == "application/json"); 38 | REQUIRE(request.body().contentLength() == 2); 39 | REQUIRE(request.cacheControl() == CacheControl::Builder() 40 | .immutable() 41 | .maxAge(12.0_s) 42 | .build()); 43 | } -------------------------------------------------------------------------------- /tests/RequestBody.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/ExceptionCatch.hpp" 3 | #include 4 | 5 | TEST_CASE("RequestBody") { 6 | ohf::RequestBody body("text/html", "Hello, world!"); 7 | REQUIRE(body.contentType() == "text/html"); 8 | REQUIRE(body.contentLength() == 26); 9 | 10 | std::stringstream ss; 11 | ss << "Test reading from stream"; 12 | ohf::RequestBody body2("text/plain", ss); 13 | REQUIRE(body2.contentLength() == 24); 14 | } -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #if WIN32 6 | # include 7 | #endif 8 | 9 | #include 10 | #include 11 | 12 | int main(int argc, char **argv) { 13 | #if WIN32 14 | unsigned int cp = GetConsoleCP(); 15 | unsigned int cpo = GetConsoleOutputCP(); 16 | 17 | SetConsoleCP(CP_UTF8); 18 | SetConsoleOutputCP(CP_UTF8); 19 | #endif 20 | 21 | int result = Catch::Session().run(argc, argv); 22 | 23 | #if WIN32 24 | SetConsoleCP(cp); 25 | SetConsoleOutputCP(cpo); 26 | #endif 27 | 28 | return result; 29 | } 30 | -------------------------------------------------------------------------------- /tests/tcp/Socket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "util/ExceptionCatch.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | #define SERVER_PORT 50000 8 | 9 | using namespace ohf; 10 | 11 | void server_func(Socket::Family family) { 12 | tcp::Server server; 13 | server.bind({"localhost", family}, SERVER_PORT); 14 | server.listen(); 15 | 16 | for(const auto& connection : server) { 17 | if(family == ohf::IPv4) 18 | REQUIRE(connection.address().hostAddress() == "127.0.0.1"); 19 | else 20 | REQUIRE(connection.address().hostAddress() == "::1"); 21 | 22 | tcp::Socket::Stream stream(connection.socket()); 23 | 24 | stream << "SERVER_DATA" << std::flush; 25 | 26 | std::string response(11, 0); 27 | stream.read(&response[0], 11); 28 | REQUIRE(response == "SOCKET_DATA"); 29 | 30 | connection.close(); 31 | break; 32 | } 33 | } 34 | 35 | void socket_func(Socket::Family family) { 36 | tcp::Socket socket; 37 | socket.connect({"localhost", family}, SERVER_PORT); 38 | tcp::Socket::Stream stream(socket); 39 | 40 | std::string response(11, 0); 41 | stream.read(&response[0], 11); 42 | REQUIRE(response == "SERVER_DATA"); 43 | 44 | stream << "SOCKET_DATA" << std::flush; 45 | 46 | socket.close(); 47 | } 48 | 49 | TEST_CASE("tcp::Socket", "[socket]") { 50 | tcp::Socket socket; 51 | tcp::Server server; 52 | 53 | // exceptions 54 | REQUIRE_THROWS_CODE(socket.connect("localhost", 0), Exception::Code::FAILED_TO_CREATE_CONNECTION); 55 | REQUIRE_THROWS_CODE(socket.send(nullptr, 128), Exception::Code::NO_DATA_TO_SEND); 56 | REQUIRE_THROWS_CODE(socket.send("some_data", 0), Exception::Code::NO_DATA_TO_SEND); 57 | REQUIRE_THROWS_CODE(socket.send("some_data", 9), Exception::Code::FAILED_TO_SEND_DATA); 58 | REQUIRE_THROWS_CODE(socket.receive(nullptr, 128), Exception::Code::FAILED_TO_RECEIVE_DATA); 59 | 60 | server.bind({"localhost", ohf::IPv4}, SERVER_PORT); 61 | REQUIRE_THROWS_CODE(server.bind({"localhost", ohf::IPv4}, SERVER_PORT), Exception::Code::FAILED_TO_BIND_SOCKET); 62 | server.close(); 63 | REQUIRE_THROWS_CODE(server.listen(), Exception::Code::FAILED_TO_LISTEN_SOCKET); 64 | REQUIRE_THROWS_CODE(server.accept(), Exception::Code::FAILED_TO_ACCEPT_SOCKET); 65 | socket.close(); 66 | server.close(); 67 | 68 | // send / receive 69 | std::vector families = {ohf::IPv4, ohf::IPv4}; 70 | for(const auto& family : families) { 71 | std::thread server_thread(server_func, family); 72 | std::thread socket_thread(socket_func, family); 73 | socket_thread.join(); 74 | server_thread.join(); 75 | socket.close(); 76 | server.close(); 77 | } 78 | 79 | // swap 80 | socket.create(ohf::IPv4); 81 | server.create(ohf::IPv4); 82 | 83 | tcp::Socket b; 84 | b.create(ohf::IPv4); 85 | b.blocking(false); 86 | 87 | auto socket_fd = socket.fd(); 88 | auto b_fd = b.fd(); 89 | bool socket_blocking = socket.isBlocking(); 90 | bool b_blocking = b.isBlocking(); 91 | 92 | std::swap(socket, b); 93 | REQUIRE(socket.fd() == b_fd); 94 | REQUIRE(socket.isBlocking() == b_blocking); 95 | REQUIRE(b.fd() == socket_fd); 96 | REQUIRE(b.isBlocking() == socket_blocking); 97 | 98 | // rvalue 99 | socket_fd = socket.fd(); 100 | socket_blocking = socket.isBlocking(); 101 | tcp::Socket rvalue(std::move(socket)); 102 | REQUIRE(rvalue.fd() == socket_fd); 103 | REQUIRE(rvalue.isBlocking() == socket_blocking); 104 | REQUIRE_FALSE(socket.isValid()); 105 | REQUIRE(socket.isBlocking()); 106 | 107 | auto rvalue_fd = rvalue.fd(); 108 | bool rvalue_blocking = rvalue.isBlocking(); 109 | socket = std::move(rvalue); 110 | REQUIRE(socket.fd() == rvalue_fd); 111 | REQUIRE(socket.isBlocking() == rvalue_blocking); 112 | REQUIRE_FALSE(rvalue.isValid()); 113 | REQUIRE(rvalue.isBlocking()); 114 | } -------------------------------------------------------------------------------- /tests/udp/Socket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace ohf; 7 | 8 | #define A_PORT 50001 9 | #define B_PORT 50002 10 | #define INVALID_FD Socket::Handle(-1) 11 | 12 | void a_func(Socket::Family family) { 13 | udp::Socket a; 14 | a.bind({"localhost", family}, A_PORT); 15 | 16 | InetAddress address; 17 | Uint16 port; 18 | std::string data; 19 | a.receive(address, port, data, 128); 20 | REQUIRE(data == "B_HELLO"); 21 | REQUIRE(port == B_PORT); 22 | if(family == ohf::IPv4) 23 | REQUIRE(address.hostAddress() == "127.0.0.1"); 24 | else 25 | REQUIRE(address.hostAddress() == "::1"); 26 | 27 | a.send(address, port, "A_WORLD"); 28 | 29 | std::vector dataVector; 30 | a.receive(address, port, dataVector, 3); 31 | REQUIRE(dataVector[0] == 1); 32 | REQUIRE(dataVector[1] == 127); 33 | REQUIRE(dataVector[2] == -128); 34 | } 35 | 36 | void b_func(Socket::Family family) { 37 | udp::Socket b; 38 | b.bind({"localhost", family}, B_PORT); 39 | 40 | b.send({"localhost", family}, A_PORT, "B_HELLO"); 41 | 42 | InetAddress address; 43 | Uint16 port; 44 | std::string data; 45 | b.receive(address, port, data, 128); 46 | REQUIRE(data == "A_WORLD"); 47 | REQUIRE(port == A_PORT); 48 | if(family == ohf::IPv4) 49 | REQUIRE(address.hostAddress() == "127.0.0.1"); 50 | else 51 | REQUIRE(address.hostAddress() == "::1"); 52 | 53 | std::vector toSend = {1, 127, -128}; 54 | b.send({"localhost", family}, A_PORT, toSend); 55 | } 56 | 57 | TEST_CASE("udp::Socket", "[socket]") { 58 | udp::Socket a; 59 | udp::Socket b; 60 | 61 | // create and close 62 | a.create(ohf::IPv4); 63 | REQUIRE(a.fd() != INVALID_FD); 64 | a.close(); 65 | REQUIRE(a.fd() == INVALID_FD); 66 | 67 | a.create(ohf::IPv4); 68 | REQUIRE(a.fd() != INVALID_FD); 69 | a.unbind(); 70 | REQUIRE(a.fd() == INVALID_FD); 71 | 72 | // exceptions 73 | b.bind({"localhost", ohf::IPv4}, B_PORT); 74 | REQUIRE_THROWS_CODE(a.bind({"localhost", ohf::IPv4}, B_PORT), Exception::Code::FAILED_TO_BIND_SOCKET); 75 | b.close(); 76 | REQUIRE_THROWS_CODE(a.send({"localhost", ohf::IPv4}, 0, nullptr, 128), Exception::Code::NO_DATA_TO_SEND); 77 | REQUIRE_THROWS_CODE(a.send({"localhost", ohf::IPv4}, 0, "some data", 0), Exception::Code::NO_DATA_TO_SEND); 78 | REQUIRE_THROWS_CODE(a.send({"localhost", ohf::IPv4}, 0, "some_data", std::numeric_limits::max()), Exception::Code::DATAGRAM_PACKET_IS_TOO_BIG); 79 | a.close(); 80 | REQUIRE_THROWS_CODE(a.send({"localhost", ohf::IPv4}, 0, "abcde", 5), Exception::Code::FAILED_TO_SEND_DATA); 81 | std::string data; 82 | InetAddress address; 83 | Uint16 port; 84 | REQUIRE_THROWS_CODE(a.receive(address, port, data, 128), Exception::Code::FAILED_TO_RECEIVE_DATA); 85 | a.close(); 86 | b.close(); 87 | 88 | // send / receive 89 | std::vector families = {ohf::IPv4, ohf::IPv6}; 90 | for(const auto& family : families) { 91 | std::thread a_thread(a_func, family); 92 | std::thread b_thread(b_func, family); 93 | a_thread.join(); 94 | b_thread.join(); 95 | a.close(); 96 | b.close(); 97 | } 98 | 99 | // swap 100 | a.create(ohf::IPv4); 101 | b.create(ohf::IPv4); 102 | 103 | a.blocking(false); 104 | 105 | auto a_fd = a.fd(); 106 | auto b_fd = b.fd(); 107 | bool a_blocking = a.isBlocking(); 108 | bool b_blocking = b.isBlocking(); 109 | 110 | std::swap(a, b); 111 | REQUIRE(a.fd() == b_fd); 112 | REQUIRE(a.isBlocking() == b_blocking); 113 | REQUIRE(b.fd() == a_fd); 114 | REQUIRE(b.isBlocking() == a_blocking); 115 | 116 | // rvalue 117 | a_fd = a.fd(); 118 | a_blocking = a.isBlocking(); 119 | udp::Socket rvalue(std::move(a)); 120 | REQUIRE(rvalue.fd() == a_fd); 121 | REQUIRE(rvalue.isBlocking() == a_blocking); 122 | REQUIRE_FALSE(a.isValid()); 123 | REQUIRE(a.isBlocking()); 124 | 125 | auto rvalue_fd = rvalue.fd(); 126 | bool rvalue_blocking = rvalue.isBlocking(); 127 | a = std::move(rvalue); 128 | REQUIRE(a.fd() == rvalue_fd); 129 | REQUIRE(a.isBlocking() == rvalue_blocking); 130 | REQUIRE_FALSE(rvalue.isValid()); 131 | REQUIRE(rvalue.isBlocking()); 132 | 133 | } -------------------------------------------------------------------------------- /tests/util/ExceptionCatch.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef TESTS_EXCEPTION_CATCH_HPP 6 | #define TESTS_EXCEPTION_CATCH_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace ohf; 13 | 14 | namespace Catch { 15 | template<> 16 | struct StringMaker { 17 | static std::string convert(Exception const& e) { 18 | return std::to_string((int) e.code()); 19 | } 20 | }; 21 | } 22 | 23 | class ExceptionMatcher : public Catch::MatcherBase { 24 | public: 25 | explicit ExceptionMatcher(Exception::Code code) : mCode(code) {} 26 | 27 | bool match(Exception const &e) const override { 28 | mMessage = e.message(); 29 | return e.code() == mCode; 30 | } 31 | 32 | std::string describe() const override { 33 | std::ostringstream oss; 34 | oss << "equal " << (int) mCode << " with message: " << mMessage; 35 | return oss.str(); 36 | } 37 | 38 | private: 39 | Exception::Code mCode; 40 | mutable std::string mMessage; 41 | }; 42 | 43 | #define REQUIRE_THROWS_CODE(expr, code) \ 44 | REQUIRE_THROWS_MATCHES(expr, Exception, ExceptionMatcher(code)) 45 | 46 | #endif // TESTS_EXCEPTION_CATCH_HPP 47 | -------------------------------------------------------------------------------- /tests/util/TimeUnitCatch.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Good_Pudge. 3 | // 4 | 5 | #ifndef TESTS_TIMEUNIT_CATCH_HPP 6 | #define TESTS_TIMEUNIT_CATCH_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include "../../Catch2/include/internal/catch_matchers.h" 12 | #include "../../Catch2/include/catch.hpp" 13 | 14 | using namespace ohf; 15 | 16 | namespace Catch { 17 | template<> 18 | struct StringMaker { 19 | static std::string convert(TimeUnit const& tu) { 20 | std::string str; 21 | switch(tu.type()) { 22 | case TimeUnit::Type::SECONDS: { 23 | float integer; 24 | float fraction = std::modf(tu.floatValue(), &integer); 25 | if(fraction == 0) { 26 | str += std::to_string((ohf::Int64) integer); 27 | } else { 28 | str += std::to_string(integer + fraction); 29 | } 30 | str += " seconds"; 31 | break; 32 | } 33 | case TimeUnit::Type::MILLISECONDS: { 34 | str += std::to_string(tu.value()) 35 | + " milliseconds"; 36 | break; 37 | } 38 | case TimeUnit::Type::MICROSECONDS: { 39 | str += std::to_string(tu.value()) 40 | + " microseconds"; 41 | break; 42 | } 43 | } 44 | return str; 45 | } 46 | }; 47 | } 48 | 49 | class TimeUnitMatcher : public Catch::MatcherBase { 50 | public: 51 | explicit TimeUnitMatcher(const TimeUnit &unit) : mUnit(unit) {}; 52 | 53 | bool match(TimeUnit const &e) const override { 54 | return e.std_time() == mUnit.std_time() 55 | && e.seconds() == mUnit.seconds() 56 | && e.milliseconds() == mUnit.milliseconds() 57 | && e.microseconds() == mUnit.microseconds() 58 | && e.sec() == mUnit.sec() 59 | && e.usec() == mUnit.usec() 60 | && e.type() == mUnit.type() 61 | && e.value() == mUnit.value() 62 | && e.floatValue() == mUnit.floatValue() 63 | && e == mUnit; 64 | } 65 | 66 | std::string describe() const override { 67 | std::ostringstream oss; 68 | oss << "equal " << Catch::StringMaker::convert(mUnit); 69 | return oss.str(); 70 | } 71 | 72 | private: 73 | TimeUnit mUnit; 74 | }; 75 | 76 | #endif // TESTS_TIMEUNIT_CATCH_HPP 77 | --------------------------------------------------------------------------------