├── .gitignore ├── .vscode └── launch.json ├── CMakeLists.txt ├── LICENSE.txt ├── Makefile ├── README.md ├── data ├── fav.png └── hello.html ├── examples ├── accu-2024.cpp ├── accu-client-2024.cpp ├── client.cpp ├── http-server-template.cpp ├── overview.cpp ├── server.cpp └── simple-http-server.cpp ├── include ├── Makefile └── stdnet │ ├── basic_socket.hpp │ ├── basic_stream_socket.hpp │ ├── buffer.hpp │ ├── container.hpp │ ├── context_base.hpp │ ├── cpo.hpp │ ├── endpoint.hpp │ ├── internet.hpp │ ├── io_base.hpp │ ├── io_context.hpp │ ├── io_context_scheduler.hpp │ ├── libevent_context.hpp │ ├── netfwd.hpp │ ├── poll_context.hpp │ ├── socket.hpp │ ├── socket_base.hpp │ └── timer.hpp └── test └── stdnet └── buffer.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | mkerr 35 | olderr 36 | build 37 | stdexec 38 | libevent 39 | openssl 40 | ssl 41 | .vscode/settings.json 42 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(lldb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/build/accu-2024", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${fileDirname}", 15 | "environment": [], 16 | "externalConsole": false, 17 | "MIMode": "lldb" 18 | }, 19 | { 20 | "name": "(lldb) Attach", 21 | "type": "cppdbg", 22 | "request": "attach", 23 | "program": "${workspaceFolder}/build/accu-2024", 24 | "MIMode": "lldb" 25 | } 26 | 27 | ] 28 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 2 | # 3 | # Licensed under the Apache License Version 2.0 with LLVM Exceptions 4 | # (the "License"); you may not use this file except in compliance with 5 | # the License. You may obtain a copy of the License at 6 | # 7 | # https://llvm.org/LICENSE.txt 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. 14 | 15 | cmake_minimum_required(VERSION 3.22.1) 16 | project(stdnet) 17 | 18 | add_compile_options(-fsanitize=address -g) 19 | add_link_options(-fsanitize=address) 20 | 21 | # --- Make Catch2 available ---------------------------------------------------- 22 | include(FetchContent) 23 | 24 | FetchContent_Declare( 25 | Catch2 26 | GIT_REPOSITORY https://github.com/catchorg/Catch2.git 27 | GIT_TAG v3.4.0 # or a later release 28 | ) 29 | 30 | FetchContent_MakeAvailable(Catch2) 31 | include(Catch) 32 | 33 | # --- Make stdexec available --------------------------------------------------- 34 | set(STDEXEC_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 35 | set(STDEXEC_BUILD_TESTS OFF CACHE BOOL "" FORCE) 36 | set(BUILD_TESTING OFF CACHE BOOL "" FORCE) 37 | add_subdirectory(stdexec) 38 | include_directories(stdexec/include) 39 | 40 | # --- Make libevent available -------------------------------------------------- 41 | 42 | add_subdirectory(libevent) 43 | 44 | # --- stdnet ------------------------------------------------------------------- 45 | include_directories(include) 46 | add_compile_options(-Wno-deprecated-declarations) 47 | set(CMAKE_CXX_STANDARD 23) 48 | 49 | list(APPEND stdnet_examples 50 | accu-2024 51 | ) 52 | list(APPEND xstdnet_examples 53 | overview 54 | client 55 | simple-http-server 56 | server 57 | http-server-template 58 | accu-client-2024 59 | ) 60 | foreach(example ${stdnet_examples}) 61 | add_executable(${example} examples/${example}.cpp) 62 | target_link_libraries(${example} STDEXEC::stdexec event_core_shared) 63 | endforeach() 64 | 65 | list (APPEND stdnet_tests 66 | buffer 67 | ) 68 | 69 | if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) 70 | include(CTest) 71 | list(TRANSFORM stdnet_tests PREPEND "test/stdnet/") 72 | list(TRANSFORM stdnet_tests APPEND ".cpp") 73 | 74 | add_executable(test_stdnet ${stdnet_tests}) 75 | set_target_properties(test_stdnet PROPERTIES 76 | CXX_STANDARD 20 77 | CXX_STANDARD_REQUIRED ON 78 | CXX_EXTENSIONS OFF 79 | ) 80 | target_link_libraries(test_stdnet 81 | STDEXEC::stdexec 82 | event_core_shared 83 | Catch2::Catch2WithMain 84 | ) 85 | catch_discover_tests(test_stdnet) 86 | endif() 87 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | The stdnet Project is under the Apache License v2.0 with LLVM Exceptions: 3 | ============================================================================== 4 | 5 | Apache License 6 | Version 2.0, January 2004 7 | http://www.apache.org/licenses/ 8 | 9 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 10 | 11 | 1. Definitions. 12 | 13 | "License" shall mean the terms and conditions for use, reproduction, 14 | and distribution as defined by Sections 1 through 9 of this document. 15 | 16 | "Licensor" shall mean the copyright owner or entity authorized by 17 | the copyright owner that is granting the License. 18 | 19 | "Legal Entity" shall mean the union of the acting entity and all 20 | other entities that control, are controlled by, or are under common 21 | control with that entity. For the purposes of this definition, 22 | "control" means (i) the power, direct or indirect, to cause the 23 | direction or management of such entity, whether by contract or 24 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 25 | outstanding shares, or (iii) beneficial ownership of such entity. 26 | 27 | "You" (or "Your") shall mean an individual or Legal Entity 28 | exercising permissions granted by this License. 29 | 30 | "Source" form shall mean the preferred form for making modifications, 31 | including but not limited to software source code, documentation 32 | source, and configuration files. 33 | 34 | "Object" form shall mean any form resulting from mechanical 35 | transformation or translation of a Source form, including but 36 | not limited to compiled object code, generated documentation, 37 | and conversions to other media types. 38 | 39 | "Work" shall mean the work of authorship, whether in Source or 40 | Object form, made available under the License, as indicated by a 41 | copyright notice that is included in or attached to the work 42 | (an example is provided in the Appendix below). 43 | 44 | "Derivative Works" shall mean any work, whether in Source or Object 45 | form, that is based on (or derived from) the Work and for which the 46 | editorial revisions, annotations, elaborations, or other modifications 47 | represent, as a whole, an original work of authorship. For the purposes 48 | of this License, Derivative Works shall not include works that remain 49 | separable from, or merely link (or bind by name) to the interfaces of, 50 | the Work and Derivative Works thereof. 51 | 52 | "Contribution" shall mean any work of authorship, including 53 | the original version of the Work and any modifications or additions 54 | to that Work or Derivative Works thereof, that is intentionally 55 | submitted to Licensor for inclusion in the Work by the copyright owner 56 | or by an individual or Legal Entity authorized to submit on behalf of 57 | the copyright owner. For the purposes of this definition, "submitted" 58 | means any form of electronic, verbal, or written communication sent 59 | to the Licensor or its representatives, including but not limited to 60 | communication on electronic mailing lists, source code control systems, 61 | and issue tracking systems that are managed by, or on behalf of, the 62 | Licensor for the purpose of discussing and improving the Work, but 63 | excluding communication that is conspicuously marked or otherwise 64 | designated in writing by the copyright owner as "Not a Contribution." 65 | 66 | "Contributor" shall mean Licensor and any individual or Legal Entity 67 | on behalf of whom a Contribution has been received by Licensor and 68 | subsequently incorporated within the Work. 69 | 70 | 2. Grant of Copyright License. Subject to the terms and conditions of 71 | this License, each Contributor hereby grants to You a perpetual, 72 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 73 | copyright license to reproduce, prepare Derivative Works of, 74 | publicly display, publicly perform, sublicense, and distribute the 75 | Work and such Derivative Works in Source or Object form. 76 | 77 | 3. Grant of Patent License. Subject to the terms and conditions of 78 | this License, each Contributor hereby grants to You a perpetual, 79 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 80 | (except as stated in this section) patent license to make, have made, 81 | use, offer to sell, sell, import, and otherwise transfer the Work, 82 | where such license applies only to those patent claims licensable 83 | by such Contributor that are necessarily infringed by their 84 | Contribution(s) alone or by combination of their Contribution(s) 85 | with the Work to which such Contribution(s) was submitted. If You 86 | institute patent litigation against any entity (including a 87 | cross-claim or counterclaim in a lawsuit) alleging that the Work 88 | or a Contribution incorporated within the Work constitutes direct 89 | or contributory patent infringement, then any patent licenses 90 | granted to You under this License for that Work shall terminate 91 | as of the date such litigation is filed. 92 | 93 | 4. Redistribution. You may reproduce and distribute copies of the 94 | Work or Derivative Works thereof in any medium, with or without 95 | modifications, and in Source or Object form, provided that You 96 | meet the following conditions: 97 | 98 | (a) You must give any other recipients of the Work or 99 | Derivative Works a copy of this License; and 100 | 101 | (b) You must cause any modified files to carry prominent notices 102 | stating that You changed the files; and 103 | 104 | (c) You must retain, in the Source form of any Derivative Works 105 | that You distribute, all copyright, patent, trademark, and 106 | attribution notices from the Source form of the Work, 107 | excluding those notices that do not pertain to any part of 108 | the Derivative Works; and 109 | 110 | (d) If the Work includes a "NOTICE" text file as part of its 111 | distribution, then any Derivative Works that You distribute must 112 | include a readable copy of the attribution notices contained 113 | within such NOTICE file, excluding those notices that do not 114 | pertain to any part of the Derivative Works, in at least one 115 | of the following places: within a NOTICE text file distributed 116 | as part of the Derivative Works; within the Source form or 117 | documentation, if provided along with the Derivative Works; or, 118 | within a display generated by the Derivative Works, if and 119 | wherever such third-party notices normally appear. The contents 120 | of the NOTICE file are for informational purposes only and 121 | do not modify the License. You may add Your own attribution 122 | notices within Derivative Works that You distribute, alongside 123 | or as an addendum to the NOTICE text from the Work, provided 124 | that such additional attribution notices cannot be construed 125 | as modifying the License. 126 | 127 | You may add Your own copyright statement to Your modifications and 128 | may provide additional or different license terms and conditions 129 | for use, reproduction, or distribution of Your modifications, or 130 | for any such Derivative Works as a whole, provided Your use, 131 | reproduction, and distribution of the Work otherwise complies with 132 | the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, 135 | any Contribution intentionally submitted for inclusion in the Work 136 | by You to the Licensor shall be under the terms and conditions of 137 | this License, without any additional terms or conditions. 138 | Notwithstanding the above, nothing herein shall supersede or modify 139 | the terms of any separate license agreement you may have executed 140 | with Licensor regarding such Contributions. 141 | 142 | 6. Trademarks. This License does not grant permission to use the trade 143 | names, trademarks, service marks, or product names of the Licensor, 144 | except as required for reasonable and customary use in describing the 145 | origin of the Work and reproducing the content of the NOTICE file. 146 | 147 | 7. Disclaimer of Warranty. Unless required by applicable law or 148 | agreed to in writing, Licensor provides the Work (and each 149 | Contributor provides its Contributions) on an "AS IS" BASIS, 150 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 151 | implied, including, without limitation, any warranties or conditions 152 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 153 | PARTICULAR PURPOSE. You are solely responsible for determining the 154 | appropriateness of using or redistributing the Work and assume any 155 | risks associated with Your exercise of permissions under this License. 156 | 157 | 8. Limitation of Liability. In no event and under no legal theory, 158 | whether in tort (including negligence), contract, or otherwise, 159 | unless required by applicable law (such as deliberate and grossly 160 | negligent acts) or agreed to in writing, shall any Contributor be 161 | liable to You for damages, including any direct, indirect, special, 162 | incidental, or consequential damages of any character arising as a 163 | result of this License or out of the use or inability to use the 164 | Work (including but not limited to damages for loss of goodwill, 165 | work stoppage, computer failure or malfunction, or any and all 166 | other commercial damages or losses), even if such Contributor 167 | has been advised of the possibility of such damages. 168 | 169 | 9. Accepting Warranty or Additional Liability. While redistributing 170 | the Work or Derivative Works thereof, You may choose to offer, 171 | and charge a fee for, acceptance of support, warranty, indemnity, 172 | or other liability obligations and/or rights consistent with this 173 | License. However, in accepting such obligations, You may act only 174 | on Your own behalf and on Your sole responsibility, not on behalf 175 | of any other Contributor, and only if You agree to indemnify, 176 | defend, and hold each Contributor harmless for any liability 177 | incurred by, or claims asserted against, such Contributor by reason 178 | of your accepting any such warranty or additional liability. 179 | 180 | END OF TERMS AND CONDITIONS 181 | 182 | APPENDIX: How to apply the Apache License to your work. 183 | 184 | To apply the Apache License to your work, attach the following 185 | boilerplate notice, with the fields enclosed by brackets "[]" 186 | replaced with your own identifying information. (Don't include 187 | the brackets!) The text should be enclosed in the appropriate 188 | comment syntax for the file format. We also recommend that a 189 | file or class name and description of purpose be included on the 190 | same "printed page" as the copyright notice for easier 191 | identification within third-party archives. 192 | 193 | Copyright [yyyy] [name of copyright owner] 194 | 195 | Licensed under the Apache License, Version 2.0 (the "License"); 196 | you may not use this file except in compliance with the License. 197 | You may obtain a copy of the License at 198 | 199 | http://www.apache.org/licenses/LICENSE-2.0 200 | 201 | Unless required by applicable law or agreed to in writing, software 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 204 | See the License for the specific language governing permissions and 205 | limitations under the License. 206 | 207 | 208 | ---- LLVM Exceptions to the Apache 2.0 License ---- 209 | 210 | As an exception, if, as a result of your compiling your source code, portions 211 | of this Software are embedded into an Object form of such source code, you 212 | may redistribute such embedded portions in such Object form without complying 213 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 214 | 215 | In addition, if you combine or link compiled forms of this Software with 216 | software that is licensed under the GPLv2 ("Combined Software") and if a 217 | court of competent jurisdiction determines that the patent provision (Section 218 | 3), the indemnity provision (Section 9) or other Section of the License 219 | conflicts with the conditions of the GPLv2, you may retroactively and 220 | prospectively choose to deem waived or otherwise exclude such Section(s) of 221 | the License, but only in their entirety and only with respect to the Combined 222 | Software. 223 | 224 | ============================================================================== 225 | Software from third parties included in the LLVM Project: 226 | ============================================================================== 227 | The LLVM Project contains third party software which is under different license 228 | terms. All such code will be identified clearly using at least one of two 229 | mechanisms: 230 | 1) It will be in a separate directory tree with its own `LICENSE.txt` or 231 | `LICENSE` file at the top containing the specific license and restrictions 232 | which apply to that software, or 233 | 2) It will contain specific license and restriction terms at the top of every 234 | file. 235 | 236 | ============================================================================== 237 | Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): 238 | ============================================================================== 239 | University of Illinois/NCSA 240 | Open Source License 241 | 242 | Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. 243 | All rights reserved. 244 | 245 | Developed by: 246 | 247 | LLVM Team 248 | 249 | University of Illinois at Urbana-Champaign 250 | 251 | http://llvm.org 252 | 253 | Permission is hereby granted, free of charge, to any person obtaining a copy of 254 | this software and associated documentation files (the "Software"), to deal with 255 | the Software without restriction, including without limitation the rights to 256 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 257 | of the Software, and to permit persons to whom the Software is furnished to do 258 | so, subject to the following conditions: 259 | 260 | * Redistributions of source code must retain the above copyright notice, 261 | this list of conditions and the following disclaimers. 262 | 263 | * Redistributions in binary form must reproduce the above copyright notice, 264 | this list of conditions and the following disclaimers in the 265 | documentation and/or other materials provided with the distribution. 266 | 267 | * Neither the names of the LLVM Team, University of Illinois at 268 | Urbana-Champaign, nor the names of its contributors may be used to 269 | endorse or promote products derived from this Software without specific 270 | prior written permission. 271 | 272 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 273 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 274 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 275 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 276 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 277 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 278 | SOFTWARE. 279 | 280 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile -*-makefile-*- 2 | # ---------------------------------------------------------------------------- 3 | # 4 | # Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | # 6 | # Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # https://llvm.org/LICENSE.txt 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # ---------------------------------------------------------------------------- 19 | 20 | COMPILER = unknown 21 | BUILD = build/$(COMPILER) 22 | RM = rm -f 23 | CMAKE_CC = $(CC) 24 | CMAKE_CXX = $(CXX) 25 | 26 | .PHONY: default build test distclean clean 27 | 28 | default: test 29 | 30 | stdexec: 31 | git clone https://github.com/NVIDIA/stdexec 32 | 33 | libevent: 34 | git clone https://github.com/libevent/libevent 35 | 36 | test: build 37 | ./$(BUILD)/test_stdnet 38 | 39 | build: stdexec libevent 40 | @mkdir -p $(BUILD) 41 | cd $(BUILD); cmake ../.. # -DCMAKE_C_COMPILER=$(CMAKE_CC) -DCMAKE_CC_COMPILER=$(CMAKE_CXX) 42 | cmake --build $(BUILD) 43 | 44 | clean: 45 | $(RM) mkerr olderr *~ 46 | 47 | distclean: clean 48 | $(RM) -r $(BUILD) 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Standard C++ Networking 2 | 3 | Demo implementation of C++ networking based on the [Networking 4 | TS](http://wg21.link/n4771.pdf) and the [sender/receiver for 5 | networking proposal](http://wg21.link/p2762). 6 | 7 | ## Building 8 | 9 | The implementation requires [stdexec](https://github.com/NVIDIA/stdexec) 10 | for the implementation of the sender library. It may need to be cloned 11 | into the root directory: 12 | 13 | git clone https://github.com/NVIDIA/stdexec 14 | 15 | When using the `Makefile` the repository should be cloned automatically. 16 | -------------------------------------------------------------------------------- /data/fav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dietmarkuehl/stdnet/cf9a67ffdfa02e42062c4547fe44a3687c9826d5/data/fav.png -------------------------------------------------------------------------------- /data/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello 4 | 5 | 6 | 7 | Hello, ACCU! 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/accu-2024.cpp: -------------------------------------------------------------------------------- 1 | // http-server-template.cpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | // 4 | // Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | // 6 | // Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | // (the "License"); you may not use this file except in compliance with 8 | // the License. You may obtain a copy of the License at 9 | // 10 | // https://llvm.org/LICENSE.txt 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // ---------------------------------------------------------------------------- 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | using namespace std::chrono_literals; 51 | using namespace std::string_view_literals; 52 | 53 | // ---------------------------------------------------------------------------- 54 | // stdnet::ip::tcp::acceptor 55 | // stdnet::ip::tcp::endpoint 56 | // 200 OK 57 | // 301 Moved Permanently (Location: URL) 58 | // 400 Bad Request 59 | // 404 Not Found 60 | // 500 Internal Server Error 61 | // 501 Not Implemented 62 | 63 | using on_exit = std::unique_ptr; 64 | 65 | auto send_response(auto& stream, std::string head, std::string body) 66 | { 67 | std::ostringstream out; 68 | out << "HTTP/1.1 " << head << "\r\n" 69 | << "Content-Length: " << body.size() << "\r\n" 70 | << "\r\n" 71 | << body; 72 | 73 | return stdexec::just() | stdexec::let_value([data = out.str(), &stream]()mutable noexcept { 74 | return stdnet::async_send(stream, stdnet::buffer(data)) 75 | ; 76 | } 77 | ) 78 | ; 79 | } 80 | 81 | std::unordered_map data{ 82 | {"/", "data/hello.html" }, 83 | {"fav.png", "data/fav.png" } 84 | }; 85 | 86 | exec::task make_client(auto stream, bool& keep_running) 87 | { 88 | on_exit msg("exiting client"); 89 | std::cout << "inside client\n"; 90 | std::vector b(16); 91 | std::size_t end{}; 92 | for (std::size_t n; (n = co_await stdnet::async_receive(stream, stdnet::buffer(b.data() + end, b.size() - end))); ) 93 | { 94 | end += n; 95 | auto pos = std::string_view(b.data(), b.data() + end).find("\r\n\r\n"); 96 | if (pos != std::string_view::npos) 97 | { 98 | std::cout << "read header\n"; 99 | std::istringstream in(std::string(b.data(), b.data() + pos)); 100 | std::string method, url, version; 101 | if (in >> method >> url >> version) 102 | { 103 | if (method == "GET" && data.contains(url)) 104 | { 105 | std::ifstream in(data[url]); 106 | co_await send_response(stream, "200 OK", 107 | std::string(std::istreambuf_iterator(in), {})) 108 | ; 109 | } 110 | else if (method == "GET" && url == "/exit") 111 | { 112 | co_await send_response(stream, "200 OK", "shutting down"); 113 | keep_running = false; 114 | break; 115 | } 116 | else 117 | { 118 | co_await send_response(stream, "404 not found", ""); 119 | } 120 | } 121 | 122 | b.erase(b.begin(), b.begin() + pos); 123 | end -= pos; 124 | } 125 | if (b.size() == end) 126 | { 127 | b.resize(b.size() * 2); 128 | } 129 | } 130 | } 131 | 132 | auto timeout(auto scheduler, auto duration, auto sender) 133 | { 134 | return exec::when_any( 135 | stdnet::async_resume_after(scheduler, duration), 136 | std::move(sender) 137 | ); 138 | } 139 | 140 | exec::task make_server(auto& context, auto& scope, bool& keep_running, auto ep) 141 | { 142 | on_exit msg("exiting server"); 143 | std::cout << "inside server: " << ep << "\n"; 144 | stdnet::ip::tcp::endpoint endpoint(ep, 12345); 145 | stdnet::ip::tcp::acceptor acceptor(context, endpoint); 146 | while (true) 147 | { 148 | auto[stream, client] = co_await stdnet::async_accept(acceptor); 149 | std::cout << "connection from " << client << "\n"; 150 | scope.spawn( 151 | timeout(context.get_scheduler(), 5s, 152 | make_client(std::move(stream), keep_running)) 153 | | stdexec::upon_error([](auto&&){ std::cout << "receiver an error\n";}) 154 | ); 155 | } 156 | } 157 | 158 | int main() 159 | { 160 | on_exit msg("goodbye"); 161 | std::cout << "hello, world\n"; 162 | stdnet::io_context context; 163 | exec::async_scope scope; 164 | bool keep_running{true}; 165 | scope.spawn(make_server(context, scope, keep_running, 166 | stdnet::ip::address_v4::any())); 167 | scope.spawn(make_server(context, scope, keep_running, 168 | stdnet::ip::address_v6::any())); 169 | while (keep_running && 0 < context.run_one()) 170 | { 171 | } 172 | scope.get_stop_source().request_stop(); 173 | stdexec::sync_wait(scope.on_empty()); 174 | } -------------------------------------------------------------------------------- /examples/accu-client-2024.cpp: -------------------------------------------------------------------------------- 1 | // http-server-template.cpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | // 4 | // Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | // 6 | // Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | // (the "License"); you may not use this file except in compliance with 8 | // the License. You may obtain a copy of the License at 9 | // 10 | // https://llvm.org/LICENSE.txt 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // ---------------------------------------------------------------------------- 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | using namespace std::chrono_literals; 51 | using namespace std::string_view_literals; 52 | 53 | // ---------------------------------------------------------------------------- 54 | // stdnet::ip::tcp::acceptor 55 | // stdnet::ip::tcp::endpoint 56 | // 200 OK 57 | // 301 Moved Permanently (Location: URL) 58 | // 400 Bad Request 59 | // 404 Not Found 60 | // 500 Internal Server Error 61 | // 501 Not Implemented 62 | 63 | exec::task make_client(auto& context, std::string url) 64 | { 65 | using stream_socket = stdnet::basic_stream_socket; 66 | stream_socket client(context, stdnet::ip::tcp::endpoint(stdnet::ip::address_v4::loopback(), 12345)); 67 | co_await stdnet::async_connect(client); 68 | std::string request("GET / HTTP/1.1\r\n\r\n"); 69 | co_await stdnet::async_send(client, stdnet::buffer(request)); 70 | std::vector b(10000); 71 | auto n = co_await stdnet::async_receive(client, stdnet::buffer(b)); 72 | std::cout << "received=>>>" << std::string_view(b.data(), n) << "<<<<\n"; 73 | } 74 | 75 | int main(int ac, char* av[]) 76 | { 77 | stdnet::io_context context; 78 | exec::async_scope scope; 79 | scope.spawn(make_client(context, ac == 2? av[1]: "/")); 80 | context.run(); 81 | stdexec::sync_wait(scope.on_empty()); 82 | } -------------------------------------------------------------------------------- /examples/client.cpp: -------------------------------------------------------------------------------- 1 | // examples/client.cpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | // 4 | // Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | // 6 | // Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | // (the "License"); you may not use this file except in compliance with 8 | // the License. You may obtain a copy of the License at 9 | // 10 | // https://llvm.org/LICENSE.txt 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // ---------------------------------------------------------------------------- 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | // ---------------------------------------------------------------------------- 32 | 33 | int main() 34 | { 35 | using stream_socket = stdnet::basic_stream_socket; 36 | stdnet::io_context context; 37 | exec::async_scope scope; 38 | scope.spawn(std::invoke([](auto& context)->exec::task { 39 | try 40 | { 41 | context.get_scheduler(); 42 | stream_socket client(context, stream_socket::endpoint_type(stdnet::ip::address_v4::loopback(), 12345)); 43 | client.get_scheduler(); 44 | char buffer[] = {'h', 'e', 'l', 'l', 'o'}; 45 | co_await stdnet::async_connect(client); 46 | auto n = co_await stdnet::async_send(client, stdnet::buffer(buffer)); 47 | } 48 | catch(std::exception const& ex) 49 | { 50 | std::cerr << "ERROR: " << ex.what() << '\n'; 51 | } 52 | }, context)); 53 | 54 | std::cout << "running context\n"; 55 | context.run(); 56 | std::cout << "running done\n"; 57 | } 58 | -------------------------------------------------------------------------------- /examples/http-server-template.cpp: -------------------------------------------------------------------------------- 1 | // http-server-template.cpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | // 4 | // Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | // 6 | // Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | // (the "License"); you may not use this file except in compliance with 8 | // the License. You may obtain a copy of the License at 9 | // 10 | // https://llvm.org/LICENSE.txt 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // ---------------------------------------------------------------------------- 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | using namespace std::chrono_literals; 51 | using namespace std::string_view_literals; 52 | 53 | // ---------------------------------------------------------------------------- 54 | // stdnet::ip::tcp::acceptor 55 | // stdnet::ip::tcp::endpoint 56 | // 200 OK 57 | // 301 Moved Permanently (Location: URL) 58 | // 400 Bad Request 59 | // 404 Not Found 60 | // 500 Internal Server Error 61 | // 501 Not Implemented 62 | 63 | int main() 64 | { 65 | std::cout << "hello, world\n"; 66 | } -------------------------------------------------------------------------------- /examples/overview.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 3 | * 4 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 5 | * (the "License"); you may not use this file except in compliance with 6 | * the License. You may obtain a copy of the License at 7 | * 8 | * https://llvm.org/LICENSE.txt 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 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | // --- deal with an extern C interface (naively) ------------------------------ 30 | 31 | extern "C" void callback(int, short, void* ptr) 32 | { 33 | (*static_cast*>(ptr))(); 34 | } 35 | 36 | // --- read sender ------------------------------------------------------------ 37 | 38 | template 39 | struct read_op 40 | { 41 | std::remove_cvref_t receiver; 42 | event_base* eb; 43 | int fd; 44 | char* buffer; 45 | std::size_t size; 46 | std::function trampoline; 47 | event ev{}; 48 | 49 | friend void tag_invoke(stdexec::start_t, read_op& self) noexcept 50 | { 51 | self.trampoline = [&self]{ 52 | int n = read(self.fd, self.buffer, self.size); 53 | stdexec::set_value(std::move(self.receiver), n); 54 | }; 55 | event_assign(&self.ev, self.eb, 0, EV_READ, callback, &self.trampoline); 56 | event_add(&self.ev, nullptr); 57 | } 58 | }; 59 | 60 | struct read_sender 61 | { 62 | using is_sender = void; 63 | using completion_signatures = stdexec::completion_signatures< 64 | stdexec::set_value_t(int) 65 | >; 66 | 67 | event_base* eb; 68 | int fd; 69 | char* buffer; 70 | std::size_t size; 71 | 72 | template 73 | friend read_op tag_invoke(stdexec::connect_t, read_sender&& self, R&& r) 74 | { 75 | return { std::forward(r), self.eb, self.fd, self.buffer, self.size }; 76 | } 77 | }; 78 | 79 | // --- timer sender ----------------------------------------------------------- 80 | 81 | template 82 | struct timer_op 83 | { 84 | struct cancel_callback 85 | { 86 | timer_op* self; 87 | void operator()() const noexcept 88 | { 89 | if (0 == event_del(&self->ev)) 90 | stdexec::set_stopped(std::move(self->receiver)); 91 | } 92 | }; 93 | using env_t = decltype(stdexec::get_env(std::declval())); 94 | using stop_token_t = decltype(stdexec::get_stop_token(std::declval())); 95 | using callback_t = typename stop_token_t::template callback_type; 96 | 97 | R receiver; 98 | event_base* eb; 99 | timeval time; 100 | std::function trampoline; 101 | event ev; 102 | std::optional cancel; 103 | 104 | friend void tag_invoke(stdexec::start_t, timer_op& self) noexcept 105 | { 106 | self.trampoline = [&self]{ 107 | self.cancel.reset(); 108 | stdexec::set_value(std::move(self.receiver)); 109 | }; 110 | event_assign(&self.ev, self.eb, 0, EV_TIMEOUT, callback, &self.trampoline); 111 | self.cancel.emplace(stdexec::get_stop_token(stdexec::get_env(self.receiver)), 112 | cancel_callback{&self}); 113 | event_add(&self.ev, &self.time); 114 | } 115 | }; 116 | 117 | struct timer_sender 118 | { 119 | using is_sender = void; 120 | using completion_signatures = stdexec::completion_signatures< 121 | stdexec::set_value_t(), 122 | stdexec::set_stopped_t() 123 | >; 124 | 125 | event_base* eb; 126 | timeval time; 127 | 128 | template 129 | friend timer_op> 130 | tag_invoke(stdexec::connect_t, timer_sender&& self, R&& r) 131 | { 132 | return { std::forward(r), self.eb, self.time }; 133 | } 134 | }; 135 | 136 | // ---------------------------------------------------------------------------- 137 | 138 | struct libevent_context 139 | { 140 | struct receiver 141 | { 142 | using is_receiver = void; 143 | bool& done; 144 | friend void tag_invoke(stdexec::set_value_t, receiver const& self, auto&&...) { self.done = true; } 145 | friend void tag_invoke(stdexec::set_error_t, receiver const& self, auto&&) noexcept { self.done = true; } 146 | friend void tag_invoke(stdexec::set_value_t, receiver const& self) noexcept { self.done = true; } 147 | }; 148 | 149 | bool done{false}; 150 | event_base* eb; 151 | libevent_context(): eb(event_base_new()) {} 152 | ~libevent_context() { event_base_free(eb); } 153 | 154 | template 155 | void run_loop(S&& s) 156 | { 157 | auto state = stdexec::connect(std::forward(s), receiver{done}); 158 | stdexec::start(state); 159 | 160 | while (!done) 161 | { 162 | event_base_loop(eb, EVLOOP_ONCE); 163 | } 164 | } 165 | }; 166 | 167 | int main(int ac, char* av[]) 168 | { 169 | libevent_context context; 170 | exec::async_scope scope; 171 | 172 | char buffer[64]; 173 | scope.spawn( 174 | read_sender{context.eb, 0, buffer, sizeof(buffer)} 175 | | stdexec::then([buffer = +buffer, &scope](int n){ 176 | std::cout << "read='" << std::string_view(buffer, n) << "'\n"; 177 | scope.get_stop_source().request_stop(); 178 | }) 179 | ); 180 | scope.spawn( 181 | std::invoke([](event_base* eb, int timeout)->exec::task{ 182 | while (true) 183 | { 184 | co_await timer_sender{eb, {timeout, 0}}; 185 | std::cout << "time passed\n"; 186 | } 187 | }, context.eb, ac == 2? std::stoi(av[1]): 1) 188 | | stdexec::upon_stopped([]{ std::cout << "task cancelled\n"; }) 189 | ); 190 | 191 | std::cout << "starting loop\n"; 192 | context.run_loop(scope.when_empty(stdexec::just())); 193 | std::cout << "loop done\n"; 194 | } 195 | -------------------------------------------------------------------------------- /examples/server.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 3 | * 4 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 5 | * (the "License"); you may not use this file except in compliance with 6 | * the License. You may obtain a copy of the License at 7 | * 8 | * https://llvm.org/LICENSE.txt 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 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | template 30 | struct error_handler_base 31 | { 32 | void operator()(E) 33 | { 34 | std::cout << "error_handler\n"; 35 | } 36 | }; 37 | template 38 | struct error_handler 39 | : error_handler_base... 40 | { 41 | }; 42 | 43 | auto make_client(exec::async_scope& scope, auto client) -> exec::task 44 | { 45 | try 46 | { 47 | char buffer[8]; 48 | while (auto size = co_await stdnet::async_receive(client, ::stdnet::buffer(buffer))) 49 | { 50 | std::string_view message(+buffer, size); 51 | std::cout << "received<" << size << ">(" << message << ")\n"; 52 | if (message.starts_with("exit")) 53 | { 54 | std::cout << "exiting\n"; 55 | scope.get_stop_source().request_stop(); 56 | } 57 | auto ssize = co_await stdnet::async_send(client, ::stdnet::mutable_buffer(buffer, size)); 58 | std::cout << "sent(" << ::std::string_view(buffer, ssize) << ")\n"; 59 | } 60 | std::cout << "client done\n"; 61 | } 62 | catch(const std::exception& e) 63 | { 64 | std::cerr << e.what() << '\n'; 65 | } 66 | 67 | } 68 | 69 | int main() 70 | { 71 | std::cout << std::unitbuf; 72 | std::cout << "example server\n"; 73 | try 74 | { 75 | exec::async_scope scope; 76 | stdnet::io_context context; 77 | stdnet::ip::tcp::endpoint endpoint(stdnet::ip::address_v4::any(), 12345); 78 | stdnet::ip::tcp::acceptor acceptor(context, endpoint); 79 | 80 | //tag_invoke(::stdnet::async_accept, acceptor); 81 | auto s = stdnet::async_accept(acceptor); 82 | auto s1 = stdnet::async_accept(::stdexec::just(), acceptor); 83 | //auto s2 = stdexec::just() | stdnet::async_accept(acceptor); 84 | static_assert(::stdexec::sender); 85 | static_assert(::stdexec::sender); 86 | 87 | std::cout << "spawning accept\n"; 88 | scope.spawn(std::invoke([](auto& scope, auto& acceptor)->exec::task{ 89 | while (true) 90 | { 91 | auto[stream, endpoint] = co_await stdnet::async_accept(acceptor); 92 | scope.spawn( 93 | make_client(scope, std::move(stream)) 94 | | stdexec::upon_stopped([]{ std::cout << "client cancelled\n"; }) 95 | ); 96 | std::cout << "accepted a client\n"; 97 | } 98 | }, scope, acceptor) 99 | | stdexec::upon_stopped([]{ std::cout << "acceptor cancelled\n"; }) 100 | ); 101 | 102 | scope.spawn(std::invoke([](auto scheduler)->exec::task{ 103 | using namespace std::chrono_literals; 104 | for (int i{}; i < 100; ++i) 105 | { 106 | co_await stdnet::async_resume_after(scheduler, 1'000'000us); 107 | std::cout << "relative timer fired\n"; 108 | co_await stdnet::async_resume_at(scheduler, ::std::chrono::system_clock::now() + 1s); 109 | std::cout << "absolute timer fired\n"; 110 | } 111 | }, context.get_scheduler())); 112 | 113 | std::cout << "running context\n"; 114 | context.run(); 115 | std::cout << "running done\n"; 116 | } 117 | catch (std::exception const& ex) 118 | { 119 | std::cout << "EXCEPTION: " << ex.what() << "\n"; 120 | abort(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /examples/simple-http-server.cpp: -------------------------------------------------------------------------------- 1 | // examples/simple-http-server.cpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | // 4 | // Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | // 6 | // Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | // (the "License"); you may not use this file except in compliance with 8 | // the License. You may obtain a copy of the License at 9 | // 10 | // https://llvm.org/LICENSE.txt 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // ---------------------------------------------------------------------------- 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | using namespace std::chrono_literals; 36 | using namespace std::string_view_literals; 37 | 38 | // ---------------------------------------------------------------------------- 39 | 40 | struct parser 41 | { 42 | char const* it{nullptr}; 43 | char const* end{it}; 44 | 45 | bool empty() const { return it == end; } 46 | std::string_view find(char c) 47 | { 48 | auto f{std::find(it, end, c)}; 49 | auto begin{it}; 50 | it = f; 51 | return {begin, it == end? it: it++}; 52 | } 53 | void skip_whitespace() 54 | { 55 | while (it != end && *it == ' ' || *it == '\t') 56 | ++it; 57 | } 58 | std::string_view search(std::string_view v) 59 | { 60 | auto s{std::search(it, end, v.begin(), v.end())}; 61 | auto begin{it}; 62 | it = s != end? s + v.size(): s; 63 | return {begin, s}; 64 | } 65 | }; 66 | 67 | template 68 | struct buffered_stream 69 | { 70 | static constexpr char sep[]{ '\r', '\n', '\r', '\n', '\0' }; 71 | Stream stream; 72 | std::vector buffer = std::vector(1024u); 73 | std::size_t pos{}; 74 | std::size_t end{}; 75 | 76 | void consume() 77 | { 78 | buffer.erase(buffer.begin(), buffer.begin() + pos); 79 | end -= pos; 80 | pos = 0; 81 | } 82 | auto read_head() -> exec::task 83 | { 84 | while (true) 85 | { 86 | if (buffer.size() == end) 87 | buffer.resize(buffer.size() * 2); 88 | auto n = co_await stdnet::async_receive(stream, stdnet::buffer(buffer.data() + end, buffer.size() - end)); 89 | if (n == 0u) 90 | co_return {}; 91 | end += n; 92 | pos = std::string_view(buffer.data(), end).find(sep); 93 | if (pos != std::string_view::npos) 94 | co_return {buffer.data(), pos += std::size(sep) - 1}; 95 | } 96 | } 97 | auto write_response(std::string_view message, std::string_view response) -> exec::task 98 | { 99 | std::ostringstream out; 100 | out << "HTTP/1.1 " << message << "\r\n" 101 | << "Content-Length: " << response.size() << "\r\n" 102 | << "\r\n" 103 | ; 104 | std::string head(out.str()); 105 | std::size_t p{}, n{}; 106 | do 107 | n = co_await stdnet::async_send(stream, stdnet::buffer(head.data() + p, head.size() - p)); 108 | while (0 < n && (p += n) != head.size()); 109 | 110 | p = 0; 111 | do 112 | n = co_await stdnet::async_send(stream, stdnet::buffer(response.data() + p, response.size() - p)); 113 | while (0 < n && (p += n) != response.size()); 114 | } 115 | }; 116 | 117 | struct request 118 | { 119 | std::string method, uri, version; 120 | std::unordered_map headers; 121 | std::string body; 122 | }; 123 | 124 | auto read_http_request(auto& stream) -> exec::task 125 | { 126 | request r; 127 | auto head = co_await stream.read_head(); 128 | if (not head.empty()) 129 | { 130 | parser p{head.begin(), head.end() - 2}; 131 | r.method = p.find(' '); 132 | r.uri = p.find(' '); 133 | r.version = p.search("\r\n"); 134 | 135 | while (not p.empty()) 136 | { 137 | std::string key{p.find(':')}; 138 | p.skip_whitespace(); 139 | auto value{p.search("\r\n")}; 140 | r.headers[key] = value; 141 | } 142 | } 143 | stream.consume(); 144 | co_return r; 145 | } 146 | 147 | std::unordered_map res 148 | { 149 | {"/", "data/hello.html"}, 150 | {"/fav.png", "data/fav.png"} 151 | }; 152 | 153 | template 154 | auto make_client(Stream s) -> exec::task 155 | { 156 | std::unique_ptr dtor("stopping client\n"); 157 | std::cout << "starting client\n"; 158 | 159 | buffered_stream stream{std::move(s)}; 160 | bool keep_alive{true}; 161 | 162 | while (keep_alive) 163 | { 164 | auto r = co_await read_http_request(stream); 165 | if (r.method.empty()) 166 | { 167 | co_await stream.write_response("550 ERROR", ""); 168 | co_return; 169 | } 170 | 171 | if (r.method == "GET"sv) 172 | { 173 | auto it = res.find(r.uri); 174 | std::cout << "getting '" << r.uri << "'->" << (it == res.end()? "404": "OK") << "\n"; 175 | if (it == res.end()) 176 | { 177 | co_await stream.write_response("404 NOT FOUND", "not found"); 178 | } 179 | else 180 | { 181 | std::ifstream in(it->second); 182 | std::string res(std::istreambuf_iterator(in), {}); 183 | co_await stream.write_response("200 OK", res); 184 | } 185 | } 186 | 187 | keep_alive = r.headers["Connection"] == "keep-alive"sv; 188 | std::cout << "keep-alive=" << std::boolalpha << keep_alive << "\n"; 189 | } 190 | } 191 | 192 | auto make_server(auto& context, auto& scope, auto endpoint) -> exec::task 193 | { 194 | stdnet::ip::tcp::acceptor acceptor(context, endpoint); 195 | while (true) 196 | { 197 | auto[stream, client] = co_await stdnet::async_accept(acceptor); 198 | std::cout << "received a connection from '" << client << "'\n"; 199 | scope.spawn(exec::when_any( 200 | stdnet::async_resume_after(context.get_scheduler(), 10s), 201 | make_client(::std::move(stream)) 202 | ) 203 | | stdexec::upon_error([](auto){ std::cout << "error\n"; }) 204 | ); 205 | } 206 | } 207 | 208 | int main() 209 | { 210 | std::cout << std::unitbuf; 211 | try 212 | { 213 | stdnet::io_context context; 214 | stdnet::ip::tcp::endpoint endpoint(stdnet::ip::address_v4::any(), 12345); 215 | exec::async_scope scope; 216 | 217 | scope.spawn(make_server(context, scope, endpoint)); 218 | 219 | context.run(); 220 | } 221 | catch (std::exception const& ex) 222 | { 223 | std::cout << "ERROR: " << ex.what() << "\n"; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /include/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile -*-makefile-*- 2 | # ---------------------------------------------------------------------------- 3 | # 4 | # Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | # 6 | # Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # https://llvm.org/LICENSE.txt 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | #/ 18 | # ---------------------------------------------------------------------------- 19 | 20 | .PHONY: default clean distclean test 21 | 22 | default clean distclean test: 23 | cd ..; $(MAKE) $@ 24 | -------------------------------------------------------------------------------- /include/stdnet/basic_socket.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/basic_socket.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_BASIC_SOCKET 21 | #define INCLUDED_STDNET_BASIC_SOCKET 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include //-dk:TODO 28 | 29 | // ---------------------------------------------------------------------------- 30 | 31 | template 32 | class stdnet::basic_socket 33 | : public ::stdnet::socket_base 34 | { 35 | public: 36 | using scheduler_type = ::stdnet::_Hidden::_Io_context_scheduler; 37 | using protocol_type = _Protocol; 38 | 39 | private: 40 | static constexpr ::stdnet::_Hidden::_Socket_id _S_unused{0xffff'ffff}; 41 | ::stdnet::_Hidden::_Context_base* _D_context; 42 | protocol_type _D_protocol{::stdnet::ip::tcp::v6()}; 43 | ::stdnet::_Hidden::_Socket_id _D_id{_S_unused}; 44 | 45 | public: 46 | basic_socket() 47 | : _D_context(nullptr) 48 | { 49 | } 50 | basic_socket(::stdnet::_Hidden::_Context_base* _Context, ::stdnet::_Hidden::_Socket_id _Id) 51 | : _D_context(_Context) 52 | , _D_id(_Id) 53 | { 54 | } 55 | basic_socket(basic_socket&& _Other) 56 | : _D_context(_Other._D_context) 57 | , _D_protocol(_Other._D_protocol) 58 | , _D_id(::std::exchange(_Other._D_id, _S_unused)) 59 | { 60 | } 61 | ~basic_socket() 62 | { 63 | if (this->_D_id != _S_unused) 64 | { 65 | ::std::error_code _Error{}; 66 | this->_D_context->_Release(this->_D_id, _Error); 67 | } 68 | } 69 | auto get_scheduler() noexcept -> scheduler_type 70 | { 71 | return scheduler_type{this->_D_context}; 72 | } 73 | auto _Id() const -> ::stdnet::_Hidden::_Socket_id { return this->_D_id; } 74 | }; 75 | 76 | 77 | // ---------------------------------------------------------------------------- 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /include/stdnet/basic_stream_socket.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/basic_stream_socket.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_BASIC_STREAM_SOCKET 21 | #define INCLUDED_STDNET_BASIC_STREAM_SOCKET 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | // ---------------------------------------------------------------------------- 30 | 31 | template 32 | class stdnet::basic_stream_socket 33 | : public basic_socket<_Protocol> 34 | { 35 | public: 36 | using native_handle_type = _Stdnet_native_handle_type; 37 | using protocol_type = _Protocol; 38 | using endpoint_type = typename protocol_type::endpoint; 39 | 40 | private: 41 | endpoint_type _D_endpoint; 42 | 43 | public: 44 | basic_stream_socket(basic_stream_socket&&) = default; 45 | basic_stream_socket& operator= (basic_stream_socket&&) = default; 46 | basic_stream_socket(::stdnet::_Hidden::_Context_base* _Context, ::stdnet::_Hidden::_Socket_id _Id) 47 | : basic_socket<_Protocol>(_Context, _Id) 48 | { 49 | } 50 | basic_stream_socket(::stdnet::io_context& _Context, endpoint_type const& _Endpoint) 51 | : stdnet::basic_socket<_Protocol>(_Context.get_scheduler()._Get_context(), 52 | ::std::invoke([_P = _Endpoint.protocol(), &_Context]{ 53 | ::std::error_code _Error{}; 54 | auto _Rc(_Context._Make_socket(_P.family(), _P.type(), _P.protocol(), _Error)); 55 | if (_Error) 56 | { 57 | throw ::std::system_error(_Error); 58 | } 59 | return _Rc; 60 | })) 61 | , _D_endpoint(_Endpoint) 62 | { 63 | } 64 | 65 | auto get_endpoint() const -> endpoint_type { return this->_D_endpoint; } 66 | }; 67 | 68 | 69 | // ---------------------------------------------------------------------------- 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /include/stdnet/buffer.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/buffer.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | // Copyright (C) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 4 | // 5 | // Permission is hereby granted, free of charge, to any person 6 | // obtaining a copy of this software and associated documentation 7 | // files (the "Software"), to deal in the Software without restriction, 8 | // including without limitation the rights to use, copy, modify, 9 | // merge, publish, distribute, sublicense, and/or sell copies of 10 | // the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | // OTHER DEALINGS IN THE SOFTWARE. 24 | // ---------------------------------------------------------------------------- 25 | 26 | #ifndef INCLUDED_STDNET_BUFFER 27 | #define INCLUDED_STDNET_BUFFER 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | // ---------------------------------------------------------------------------- 36 | 37 | namespace stdnet 38 | { 39 | enum class stream_errc: int; 40 | 41 | auto stream_category() noexcept -> ::std::error_category const&; 42 | 43 | auto make_error_code(::stdnet::stream_errc) noexcept -> ::std::error_code; 44 | auto make_error_condition(::stdnet::stream_errc) noexcept -> ::std::error_condition; 45 | 46 | class mutable_buffer; 47 | class const_buffer; 48 | 49 | template struct is_mutable_buffer_sequence; 50 | template struct is_const_buffer_sequence; 51 | template struct is_dynamic_buffer; 52 | 53 | struct buffer_sequence; 54 | 55 | template <::std::size_t _S> 56 | auto buffer(char (&)[_S]) -> ::stdnet::mutable_buffer; 57 | auto buffer(char*, ::std::size_t) -> ::stdnet::mutable_buffer; 58 | auto buffer(char const*, ::std::size_t) -> ::stdnet::const_buffer; 59 | template 60 | requires requires(_CT&& _C){ _C.data(); _C.size(); } 61 | auto buffer(_CT&& _C) 62 | { 63 | return stdnet::buffer(_C.data(), _C.size()); 64 | } 65 | } 66 | 67 | // ---------------------------------------------------------------------------- 68 | 69 | enum class stdnet::stream_errc: int 70 | { 71 | eof, 72 | not_found 73 | }; 74 | 75 | // ---------------------------------------------------------------------------- 76 | 77 | inline auto stdnet::stream_category() noexcept -> ::std::error_category const& 78 | { 79 | struct _Category 80 | : ::std::error_category 81 | { 82 | auto name() const noexcept -> char const* override 83 | { 84 | return "stream_error"; 85 | } 86 | auto message(int) const noexcept -> ::std::string override 87 | { 88 | return {}; 89 | } 90 | }; 91 | static _Category _Rc{}; 92 | return _Rc; 93 | } 94 | 95 | // ---------------------------------------------------------------------------- 96 | 97 | struct stdnet::mutable_buffer 98 | { 99 | ::iovec _Vec; 100 | mutable_buffer(void* _B, ::std::size_t _L): _Vec{ .iov_base = _B, .iov_len = _L } {} 101 | 102 | auto data() -> ::iovec* { return &this->_Vec; } 103 | auto size() -> ::std::size_t { return 1u; } 104 | }; 105 | 106 | struct stdnet::const_buffer 107 | { 108 | ::iovec _Vec; 109 | const_buffer(void const* _B, ::std::size_t _L): _Vec{ .iov_base = const_cast(_B), .iov_len = _L } {} 110 | 111 | auto data() -> ::iovec* { return &this->_Vec; } 112 | auto size() -> ::std::size_t { return 1u; } 113 | }; 114 | 115 | template <::std::size_t _S> 116 | inline auto stdnet::buffer(char (&_B)[_S]) -> ::stdnet::mutable_buffer 117 | { 118 | return ::stdnet::mutable_buffer(_B, _S); 119 | } 120 | 121 | inline auto stdnet::buffer(char* _B, ::std::size_t _Size) -> ::stdnet::mutable_buffer 122 | { 123 | return ::stdnet::mutable_buffer(_B, _Size); 124 | } 125 | 126 | inline auto stdnet::buffer(char const* _B, ::std::size_t _Size) -> ::stdnet::const_buffer 127 | { 128 | return ::stdnet::const_buffer(_B, _Size); 129 | } 130 | 131 | // ---------------------------------------------------------------------------- 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /include/stdnet/container.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/container.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_CONTAINER 21 | #define INCLUDED_STDNET_CONTAINER 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | // ---------------------------------------------------------------------------- 29 | 30 | namespace stdnet::_Hidden 31 | { 32 | template 33 | class _Container; 34 | } 35 | 36 | // ---------------------------------------------------------------------------- 37 | 38 | template 39 | class stdnet::_Hidden::_Container 40 | { 41 | private: 42 | ::std::vector<::std::variant<::std::size_t, _Record>> _Records; 43 | ::std::size_t _Free{}; 44 | 45 | public: 46 | auto _Insert(_Record _R) -> ::stdnet::_Hidden::_Socket_id; 47 | auto _Erase(::stdnet::_Hidden::_Socket_id _Id) -> void; 48 | auto operator[](::stdnet::_Hidden::_Socket_id _Id) -> _Record&; 49 | }; 50 | 51 | // ---------------------------------------------------------------------------- 52 | 53 | template 54 | inline auto stdnet::_Hidden::_Container<_Record>::_Insert(_Record _R) -> ::stdnet::_Hidden::_Socket_id 55 | { 56 | if (this->_Free == this->_Records.size()) 57 | { 58 | this->_Records.emplace_back(::std::move(_R)); 59 | return ::stdnet::_Hidden::_Socket_id(this->_Free++); 60 | } 61 | else 62 | { 63 | ::std::size_t _Rc(std::exchange(this->_Free, ::std::get<0>(this->_Records[this->_Free]))); 64 | this->_Records[_Rc] = ::std::move(_R); 65 | return ::stdnet::_Hidden::_Socket_id(_Rc); 66 | } 67 | } 68 | 69 | template 70 | inline auto stdnet::_Hidden::_Container<_Record>::_Erase(::stdnet::_Hidden::_Socket_id _Id) -> void 71 | { 72 | this->_Records[::std::size_t(_Id)] = std::exchange(this->_Free, ::std::size_t(_Id)); 73 | } 74 | 75 | template 76 | inline auto stdnet::_Hidden::_Container<_Record>::operator[](::stdnet::_Hidden::_Socket_id _Id) -> _Record& 77 | { 78 | return ::std::get<1>(this->_Records[::std::size_t(_Id)]); 79 | } 80 | 81 | // ---------------------------------------------------------------------------- 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /include/stdnet/context_base.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/context_base.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_CONTEXT_BASE 21 | #define INCLUDED_STDNET_CONTEXT_BASE 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | // ---------------------------------------------------------------------------- 32 | 33 | namespace stdnet::_Hidden 34 | { 35 | struct _Context_base; 36 | } 37 | 38 | // ---------------------------------------------------------------------------- 39 | 40 | struct stdnet::_Hidden::_Context_base 41 | { 42 | using _Accept_operation = ::stdnet::_Hidden::_Io_operation< 43 | ::std::tuple<::stdnet::_Hidden::_Endpoint, 44 | ::socklen_t, 45 | ::std::optional<::stdnet::_Hidden::_Socket_id> 46 | > 47 | >; 48 | using _Connect_operation = ::stdnet::_Hidden::_Io_operation< 49 | ::std::tuple<::stdnet::_Hidden::_Endpoint> 50 | >; 51 | using _Receive_operation = ::stdnet::_Hidden::_Io_operation< 52 | ::std::tuple<::msghdr, int, ::std::size_t> 53 | >; 54 | using _Send_operation = ::stdnet::_Hidden::_Io_operation< 55 | ::std::tuple<::msghdr, int, ::std::size_t> 56 | >; 57 | using _Resume_after_operation = ::stdnet::_Hidden::_Io_operation< 58 | ::std::tuple<::std::chrono::microseconds, ::timeval> 59 | >; 60 | using _Resume_at_operation = ::stdnet::_Hidden::_Io_operation< 61 | ::std::tuple<::std::chrono::system_clock::time_point, ::timeval> 62 | >; 63 | 64 | virtual ~_Context_base() = default; 65 | virtual auto _Make_socket(int) -> ::stdnet::_Hidden::_Socket_id = 0; 66 | virtual auto _Make_socket(int, int, int, ::std::error_code&) -> ::stdnet::_Hidden::_Socket_id = 0; 67 | virtual auto _Release(::stdnet::_Hidden::_Socket_id, ::std::error_code&) -> void = 0; 68 | virtual auto _Native_handle(::stdnet::_Hidden::_Socket_id) -> _Stdnet_native_handle_type = 0; 69 | virtual auto _Set_option(::stdnet::_Hidden::_Socket_id, int, int, void const*, ::socklen_t, ::std::error_code&) -> void = 0; 70 | virtual auto _Bind(::stdnet::_Hidden::_Socket_id, ::stdnet::_Hidden::_Endpoint const&, ::std::error_code&) -> void = 0; 71 | virtual auto _Listen(::stdnet::_Hidden::_Socket_id, int, ::std::error_code&) -> void = 0; 72 | 73 | virtual auto run_one() -> ::std::size_t = 0; 74 | 75 | virtual auto _Cancel(::stdnet::_Hidden::_Io_base*, ::stdnet::_Hidden::_Io_base*) -> void = 0; 76 | virtual auto _Accept(::stdnet::_Hidden::_Context_base::_Accept_operation*) -> bool = 0; 77 | virtual auto _Connect(::stdnet::_Hidden::_Context_base::_Connect_operation*) -> bool = 0; 78 | virtual auto _Receive(::stdnet::_Hidden::_Context_base::_Receive_operation*) -> bool = 0; 79 | virtual auto _Send(::stdnet::_Hidden::_Context_base::_Send_operation*) -> bool = 0; 80 | virtual auto _Resume_after(::stdnet::_Hidden::_Context_base::_Resume_after_operation*) -> bool = 0; 81 | virtual auto _Resume_at(::stdnet::_Hidden::_Context_base::_Resume_at_operation*) -> bool = 0; 82 | }; 83 | 84 | // ---------------------------------------------------------------------------- 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /include/stdnet/cpo.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/cpo.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | // 4 | // Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | // 6 | // Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | // (the "License"); you may not use this file except in compliance with 8 | // the License. You may obtain a copy of the License at 9 | // 10 | // https://llvm.org/LICENSE.txt 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_CPO 21 | #define INCLUDED_STDNET_CPO 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | // ---------------------------------------------------------------------------- 30 | 31 | namespace stdnet::_Hidden 32 | { 33 | template struct _Cpo; 34 | } 35 | 36 | // ---------------------------------------------------------------------------- 37 | 38 | template <::stdexec::receiver _Receiver> 39 | struct _Cpo_State_base 40 | { 41 | _Receiver _D_receiver; 42 | ::std::atomic _D_outstanding{}; 43 | template <::stdexec::receiver _RT> 44 | _Cpo_State_base(_RT&& _R) 45 | : _D_receiver(::std::forward<_RT>(_R)) 46 | { 47 | } 48 | virtual auto _Start() -> void = 0; 49 | }; 50 | 51 | template <::stdexec::receiver _Receiver> 52 | struct _Upstream_receiver 53 | { 54 | using is_receiver = void; 55 | _Cpo_State_base<_Receiver>* _D_state; 56 | friend auto tag_invoke(::stdexec::set_value_t, _Upstream_receiver&& _Self) noexcept -> void 57 | { 58 | _Self._D_state->_Start(); 59 | } 60 | template 61 | friend auto tag_invoke(::stdexec::set_error_t, _Upstream_receiver&& _Self, _Error&& _E) -> void 62 | { 63 | ::stdexec::set_error(::std::move(_Self._D_state->_D_receiver), ::std::forward<_Error>(_E)); 64 | } 65 | friend auto tag_invoke(::stdexec::set_stopped_t, _Upstream_receiver&& _Self) -> void 66 | { 67 | ::stdexec::set_stopped(::std::move(_Self._D_state->_D_receiver)); 68 | } 69 | friend auto tag_invoke(::stdexec::get_env_t, _Upstream_receiver const& _Self) noexcept 70 | { 71 | return ::stdexec::get_env(_Self._D_state->_D_receiver); 72 | } 73 | }; 74 | 75 | template 76 | struct _Cpo_State 77 | : _Desc::_Operation 78 | , _Cpo_State_base<_Receiver> 79 | { 80 | struct _Cancel_callback 81 | : ::stdnet::_Hidden::_Io_base //-dk:TODO use a less heavy-weight base 82 | { 83 | _Cpo_State* _D_state; 84 | _Cancel_callback(_Cpo_State* _S) 85 | : ::stdnet::_Hidden::_Io_base(::stdnet::_Hidden::_Socket_id(), 0) 86 | , _D_state(_S) 87 | { 88 | } 89 | auto operator()() 90 | { 91 | if (1 < ++this->_D_state->_D_outstanding) 92 | { 93 | this->_D_state->_D_data._Get_scheduler()._Cancel(this, this->_D_state); 94 | } 95 | } 96 | auto _Complete() -> void override final 97 | { 98 | if (0u == --this->_D_state->_D_outstanding) 99 | { 100 | ::stdexec::set_stopped(::std::move(this->_D_state->_D_receiver)); 101 | } 102 | } 103 | auto _Error(::std::error_code) -> void override final 104 | { 105 | this->_Complete(); 106 | } 107 | auto _Cancel() -> void override final 108 | { 109 | this->_Complete(); 110 | } 111 | }; 112 | using _Upstream_state_t = decltype(::stdexec::connect(::std::declval<_Upstream&>(), ::std::declval<_Upstream_receiver<_Receiver>>())); 113 | using _Stop_token = std::remove_cvref_t())))>; 114 | using _Callback = typename _Stop_token::template callback_type<_Cancel_callback>; 115 | 116 | _Data _D_data; 117 | _Upstream_state_t _D_state; 118 | ::std::optional<_Callback> _D_callback; 119 | 120 | template 121 | _Cpo_State(_DT&& _D, _RT&& _R, _Upstream _Up) 122 | : _Desc::_Operation(_D._Id(), _D._Events()) 123 | , _Cpo_State_base<_Receiver>(::std::forward<_RT>(_R)) 124 | , _D_data(::std::forward<_DT>(_D)) 125 | , _D_state(::stdexec::connect(_Up, _Upstream_receiver<_Receiver>{this})) 126 | { 127 | } 128 | friend auto tag_invoke(::stdexec::start_t, _Cpo_State& _Self) noexcept -> void 129 | { 130 | ::stdexec::start(_Self._D_state); 131 | } 132 | auto _Start() -> void override final 133 | { 134 | auto _Token(::stdexec::get_stop_token(::stdexec::get_env(this->_D_receiver))); 135 | ++this->_D_outstanding; 136 | this->_D_callback.emplace(_Token, _Cancel_callback(this)); 137 | if (_Token.stop_requested()) 138 | { 139 | this->_D_callback.reset(); 140 | this->_Cancel(); 141 | return; 142 | } 143 | if (!this->_D_data._Submit(this)) 144 | { 145 | this->_Complete(); 146 | } 147 | } 148 | auto _Complete() -> void override final 149 | { 150 | _D_callback.reset(); 151 | if (0 == --this->_D_outstanding) 152 | { 153 | this->_D_data._Set_value(*this, ::std::move(this->_D_receiver)); 154 | } 155 | } 156 | auto _Error(::std::error_code _Err) -> void override final 157 | { 158 | _D_callback.reset(); 159 | if (0 == --this->_D_outstanding) 160 | { 161 | ::stdexec::set_error(::std::move(this->_D_receiver), std::move(_Err)); 162 | } 163 | } 164 | auto _Cancel() -> void override final 165 | { 166 | if (0 == --this->_D_outstanding) 167 | { 168 | ::stdexec::set_stopped(::std::move(this->_D_receiver)); 169 | } 170 | } 171 | }; 172 | template 173 | struct _Cpo_Sender 174 | { 175 | using is_sender = void; 176 | friend auto tag_invoke(stdexec::get_completion_signatures_t, _Cpo_Sender const&, auto) noexcept { 177 | return ::stdexec::completion_signatures< 178 | typename _Data::_Completion_signature, 179 | ::stdexec::set_error_t(::std::error_code), //-dk:TODO merge with _Upstream errors 180 | ::stdexec::set_stopped_t() 181 | >(); 182 | } 183 | _Data _D_data; 184 | _Upstream _D_upstream; 185 | 186 | template <::stdexec::receiver _Receiver> 187 | friend auto tag_invoke(::stdexec::connect_t, _Cpo_Sender const& _Self, _Receiver&& _R) 188 | { 189 | return _Cpo_State<_Desc, _Data, ::std::remove_cvref_t<_Receiver>, _Upstream>( 190 | _Self._D_data, 191 | ::std::forward<_Receiver>(_R), 192 | _Self._D_upstream 193 | ); 194 | } 195 | template <::stdexec::receiver _Receiver> 196 | friend auto tag_invoke(::stdexec::connect_t, _Cpo_Sender&& _Self, _Receiver&& _R) 197 | { 198 | return _Cpo_State<_Desc, _Data, ::std::remove_cvref_t<_Receiver>, _Upstream>( 199 | ::std::move(_Self._D_data), 200 | ::std::forward<_Receiver>(_R), 201 | ::std::move(_Self._D_upstream) 202 | ); 203 | } 204 | }; 205 | 206 | template 207 | struct stdnet::_Hidden::_Cpo 208 | { 209 | template <::stdexec::sender _Upstream, typename... _Args_t> 210 | friend auto tag_invoke(_Cpo const&, _Upstream&& _U, _Args_t&&... _Args) 211 | { 212 | using _Data = _Desc::template _Data<_Args_t...>; 213 | return _Cpo_Sender<_Desc, _Data, ::std::remove_cvref_t<_Upstream>>{{_Data{::std::forward<_Args_t>(_Args)...}}, ::std::forward<_Upstream>(_U)}; 214 | } 215 | 216 | template 217 | requires (!::stdexec::sender<::std::remove_cvref_t<_Arg0_t>>) 218 | && ::stdexec::tag_invocable<_Cpo const, decltype(::stdexec::just()), _Arg0_t, _Args_t...> 219 | auto operator()(_Arg0_t&& _Arg0, _Args_t&&... _Args) const 220 | { 221 | return tag_invoke(*this, ::stdexec::just(), ::std::forward<_Arg0_t>(_Arg0), ::std::forward<_Args_t>(_Args)...); 222 | } 223 | template <::stdexec::sender _Upstream, typename... _Args_t> 224 | requires ::stdexec::tag_invocable<_Cpo const, _Upstream, _Args_t...> 225 | auto operator()(_Upstream&& _U, _Args_t&&... _Args) const 226 | { 227 | return tag_invoke(*this, ::std::forward<_Upstream>(_U), ::std::forward<_Args_t>(_Args)...); 228 | } 229 | }; 230 | 231 | // ---------------------------------------------------------------------------- 232 | 233 | #endif 234 | -------------------------------------------------------------------------------- /include/stdnet/endpoint.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/endpoint.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | // 4 | // Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | // 6 | // Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | // (the "License"); you may not use this file except in compliance with 8 | // the License. You may obtain a copy of the License at 9 | // 10 | // https://llvm.org/LICENSE.txt 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_ENDPOINT 21 | #define INCLUDED_STDNET_ENDPOINT 22 | 23 | #include 24 | #include 25 | 26 | // ---------------------------------------------------------------------------- 27 | 28 | namespace stdnet::_Hidden 29 | { 30 | class _Endpoint; 31 | } 32 | 33 | // ---------------------------------------------------------------------------- 34 | 35 | struct stdnet::_Hidden::_Endpoint 36 | { 37 | private: 38 | ::sockaddr_storage _D_data{}; 39 | ::socklen_t _D_size{sizeof(::sockaddr_storage)}; 40 | public: 41 | _Endpoint() = default; 42 | _Endpoint(void const* _Data, ::socklen_t _Size) 43 | : _D_size(_Size) 44 | { 45 | ::std::memcpy(&this->_D_data, _Data, ::std::min(_Size, ::socklen_t(sizeof(::sockaddr_storage)))); 46 | } 47 | template 48 | _Endpoint(_ET& _E): _Endpoint(_E._Data(), _E._Size()) {} 49 | 50 | auto _Storage() -> ::sockaddr_storage& { return this->_D_data; } 51 | auto _Storage() const -> ::sockaddr_storage const& { return this->_D_data; } 52 | auto _Data() -> ::sockaddr* { return reinterpret_cast<::sockaddr*>(&this->_D_data); } 53 | auto _Data() const -> ::sockaddr const* { return reinterpret_cast<::sockaddr const*>(&this->_D_data); } 54 | auto _Size() const -> ::socklen_t { return this->_D_size; } 55 | auto _Size() -> ::socklen_t& { return this->_D_size; } 56 | }; 57 | 58 | // ---------------------------------------------------------------------------- 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /include/stdnet/internet.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/internet.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_INTERNET 21 | #define INCLUDED_STDNET_INTERNET 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | // ---------------------------------------------------------------------------- 38 | 39 | namespace stdnet::ip 40 | { 41 | using port_type = ::std::uint_least16_t; 42 | 43 | class tcp; 44 | class address_v4; 45 | class address_v6; 46 | class address; 47 | template class basic_endpoint; 48 | } 49 | 50 | // ---------------------------------------------------------------------------- 51 | 52 | class stdnet::ip::tcp 53 | { 54 | private: 55 | int _D_family; 56 | 57 | constexpr tcp(int _F): _D_family(_F) {} 58 | 59 | public: 60 | using endpoint = basic_endpoint; 61 | using socket = basic_stream_socket; 62 | using acceptor = basic_socket_acceptor; 63 | 64 | tcp() = delete; 65 | 66 | static constexpr auto v4() -> tcp { return tcp(PF_INET); } 67 | static constexpr auto v6() -> tcp { return tcp(PF_INET6); } 68 | 69 | constexpr auto family() const -> int { return this->_D_family; } 70 | constexpr auto type() const -> int { return SOCK_STREAM; } 71 | constexpr auto protocol() const -> int { return IPPROTO_TCP; } 72 | }; 73 | 74 | // ---------------------------------------------------------------------------- 75 | 76 | class stdnet::ip::address_v4 77 | { 78 | public: 79 | using uint_type = uint_least32_t; 80 | struct bytes_type; 81 | 82 | private: 83 | uint_type _D_address; 84 | 85 | public: 86 | constexpr address_v4() noexcept: _D_address() {} 87 | constexpr address_v4(address_v4 const&) noexcept = default; 88 | constexpr address_v4(bytes_type const&); 89 | explicit constexpr address_v4(uint_type _A) 90 | : _D_address(_A) 91 | { 92 | if (!(_A <= 0xFF'FF'FF'FF)) 93 | { 94 | throw ::std::out_of_range("IPv4 address is out of range"); 95 | } 96 | } 97 | 98 | auto operator=(const address_v4& a) noexcept -> address_v4& = default; 99 | 100 | constexpr auto is_unspecified() const noexcept -> bool { return this->to_uint() == 0u; } 101 | constexpr auto is_loopback() const noexcept -> bool { return (this->to_uint() & 0xFF'00'00'00) == 0x7F'00'00'00; } 102 | constexpr auto is_multicast() const noexcept -> bool{ return (this->to_uint() & 0xF0'00'00'00) == 0xE0'00'00'00; } 103 | constexpr auto to_bytes() const noexcept -> bytes_type; 104 | constexpr auto to_uint() const noexcept -> uint_type { return this->_D_address; } 105 | template> 106 | auto to_string(const _Allocator& = _Allocator()) const 107 | -> ::std::basic_string, _Allocator>; 108 | 109 | static constexpr auto any() noexcept -> address_v4 { return address_v4(); } 110 | static constexpr auto loopback() noexcept -> address_v4 { return address_v4(0x7F'00'00'01u); } 111 | static constexpr auto broadcast() noexcept -> address_v4 { return address_v4(0xFF'FF'FF'FFu); } 112 | 113 | friend ::std::ostream& operator<< (::std::ostream& _Out, address_v4 const& _A) 114 | { 115 | return _Out << ((_A._D_address >> 24) & 0xFFu) << '.' 116 | << ((_A._D_address >> 16) & 0xFFu) << '.' 117 | << ((_A._D_address >> 8) & 0xFFu) << '.' 118 | << ((_A._D_address >> 0) & 0xFFu) 119 | ; 120 | } 121 | }; 122 | 123 | #if 0 124 | constexpr bool operator==(const address_v4& a, const address_v4& b) noexcept; 125 | constexpr bool operator!=(const address_v4& a, const address_v4& b) noexcept; 126 | constexpr bool operator< (const address_v4& a, const address_v4& b) noexcept; 127 | constexpr bool operator> (const address_v4& a, const address_v4& b) noexcept; 128 | constexpr bool operator<=(const address_v4& a, const address_v4& b) noexcept; 129 | constexpr bool operator>=(const address_v4& a, const address_v4& b) noexcept; 130 | // 21.5.6, address_v4 creation: 131 | constexpr address_v4 make_address_v4(const address_v4::bytes_type& bytes); 132 | constexpr address_v4 make_address_v4(address_v4::uint_type val); 133 | constexpr address_v4 make_address_v4(v4_mapped_t, const address_v6& a); 134 | address_v4 make_address_v4(const char* str); 135 | address_v4 make_address_v4(const char* str, error_code& ec) noexcept; 136 | address_v4 make_address_v4(const string& str); 137 | address_v4 make_address_v4(const string& str, error_code& ec) noexcept; 138 | address_v4 make_address_v4(string_view str); 139 | address_v4 make_address_v4(string_view str, error_code& ec) noexcept; 140 | // 21.5.7, address_v4 I/O: 141 | template 142 | basic_ostream& operator<<( 143 | basic_ostream& os, const address_v4& addr); 144 | #endif 145 | 146 | // ---------------------------------------------------------------------------- 147 | 148 | class stdnet::ip::address_v6 149 | { 150 | public: 151 | struct bytes_type 152 | : ::std::array 153 | { 154 | template 155 | explicit constexpr bytes_type(_Tt... _T) 156 | : std::array{{ static_cast(_T)... }} 157 | { 158 | } 159 | }; 160 | 161 | private: 162 | bytes_type _D_bytes; 163 | 164 | public: 165 | static constexpr auto any() noexcept -> address_v6; 166 | static constexpr auto loopback() noexcept -> address_v6; 167 | 168 | constexpr address_v6() noexcept; 169 | constexpr address_v6(address_v6 const&) noexcept = default; 170 | constexpr address_v6(unsigned char const (&_Addr)[16]) noexcept 171 | { 172 | ::std::memcpy(_D_bytes.data(), _Addr, 16); 173 | } 174 | 175 | auto operator= (address_v6 const&) noexcept -> address_v6& = default; 176 | constexpr auto operator== (address_v6 const&) const -> bool = default; 177 | constexpr auto operator<=> (address_v6 const&) const -> ::std::strong_ordering; 178 | 179 | auto _Get_address(::sockaddr_in6& _Addr, ::stdnet::ip::port_type _Port) const 180 | -> ::socklen_t 181 | { 182 | _Addr.sin6_family = AF_INET6; 183 | _Addr.sin6_port = htons(_Port); 184 | _Addr.sin6_flowinfo = 0; 185 | ::std::memcpy(_Addr.sin6_addr.s6_addr, this->_D_bytes.data(), 16); 186 | _Addr.sin6_scope_id = 0; 187 | return sizeof(::sockaddr_in6); 188 | } 189 | 190 | constexpr auto is_unspecified() const noexcept -> bool; 191 | constexpr auto is_loopback() const noexcept -> bool; 192 | constexpr auto is_multicast() const noexcept -> bool; 193 | constexpr auto is_link_local() const noexcept -> bool; 194 | constexpr auto is_site_local() const noexcept -> bool; 195 | constexpr auto is_v4_mapped() const noexcept -> bool; 196 | constexpr auto is_multicast_node_local() const noexcept -> bool; 197 | constexpr auto is_multicast_link_local() const noexcept -> bool; 198 | constexpr auto is_multicast_site_local() const noexcept -> bool; 199 | constexpr auto is_multicast_org_local() const noexcept -> bool; 200 | constexpr auto is_multicast_global() const noexcept -> bool; 201 | constexpr auto to_bytes() const noexcept -> bytes_type; 202 | template > 203 | auto to_string(Allocator const& = {}) const 204 | -> ::std::basic_string, Allocator>; 205 | 206 | friend ::std::ostream& operator<< (::std::ostream& _Out, address_v6 const& _A) 207 | { 208 | return _Out << "::1"; 209 | } 210 | }; 211 | 212 | inline constexpr stdnet::ip::address_v6::address_v6() noexcept 213 | : _D_bytes() 214 | { 215 | } 216 | 217 | inline constexpr auto stdnet::ip::address_v6::any() noexcept 218 | -> ::stdnet::ip::address_v6 219 | { 220 | return ::stdnet::ip::address_v6(); 221 | } 222 | 223 | inline constexpr auto stdnet::ip::address_v6::loopback() noexcept 224 | -> ::stdnet::ip::address_v6 225 | { 226 | return ::stdnet::ip::address_v6(); 227 | } 228 | 229 | // ---------------------------------------------------------------------------- 230 | 231 | class stdnet::ip::address 232 | { 233 | private: 234 | union _Address_t 235 | { 236 | ::sockaddr_storage _Storage; 237 | ::sockaddr_in _Inet; 238 | ::sockaddr_in6 _Inet6; 239 | }; 240 | 241 | _Address_t _D_address; 242 | 243 | public: 244 | constexpr address() noexcept 245 | : _D_address() 246 | { 247 | this->_D_address._Storage.ss_family = PF_INET; 248 | } 249 | constexpr address(address const&) noexcept = default; 250 | /*-dk:TODO constexpr*/ address(::stdnet::ip::address_v4 const& _Address) noexcept 251 | { 252 | this->_D_address._Inet.sin_family = AF_INET; 253 | this->_D_address._Inet.sin_addr.s_addr = htonl(_Address.to_uint()); 254 | this->_D_address._Inet.sin_port = 0xFF'FF; 255 | } 256 | /*-dk:TODO constexpr*/ address(::stdnet::ip::address_v6 const& _Address) noexcept 257 | { 258 | _Address._Get_address(this->_D_address._Inet6, 0xFF'FF); 259 | } 260 | 261 | auto operator=(address const&) noexcept -> address& = default; 262 | auto operator=(::stdnet::ip::address_v4 const&) noexcept -> address&; 263 | auto operator=(::stdnet::ip::address_v6 const&) noexcept -> address&; 264 | 265 | auto _Data() const -> ::sockaddr_storage const& { return this->_D_address._Storage; } 266 | constexpr auto is_v4() const noexcept -> bool { return this->_D_address._Storage.ss_family == PF_INET; } 267 | constexpr auto is_v6() const noexcept -> bool { return this->_D_address._Storage.ss_family == PF_INET6; } 268 | /*constexpr -dk:TODO*/ auto to_v4() const -> ::stdnet::ip::address_v4 269 | { 270 | return ::stdnet::ip::address_v4(ntohl(reinterpret_cast<::sockaddr_in const&>(this->_D_address._Storage).sin_addr.s_addr)); 271 | } 272 | constexpr auto to_v6() const -> ::stdnet::ip::address_v6 273 | { 274 | return ::stdnet::ip::address_v6(this->_D_address._Inet6.sin6_addr.s6_addr); 275 | } 276 | constexpr auto is_unspecified() const noexcept -> bool; 277 | constexpr auto is_loopback() const noexcept -> bool; 278 | constexpr auto is_multicast() const noexcept -> bool; 279 | template> 280 | auto to_string(_Allocator const& = _Allocator()) const 281 | -> ::std::basic_string, _Allocator>; 282 | friend ::std::ostream& operator<< (::std::ostream& _Out, address const& _A) 283 | { 284 | if (_A.is_v4()) 285 | return _Out << _A.to_v4(); 286 | else 287 | return _Out << _A.to_v6(); 288 | } 289 | }; 290 | 291 | // ---------------------------------------------------------------------------- 292 | 293 | template 294 | class stdnet::ip::basic_endpoint 295 | : public ::stdnet::_Hidden::_Endpoint 296 | { 297 | public: 298 | using protocol_type = _Protocol; 299 | 300 | constexpr basic_endpoint() noexcept 301 | : basic_endpoint(::stdnet::ip::address(), ::stdnet::ip::port_type()) 302 | { 303 | } 304 | constexpr basic_endpoint(::stdnet::_Hidden::_Endpoint const& _Ep) noexcept 305 | : ::stdnet::_Hidden::_Endpoint(_Ep) 306 | { 307 | } 308 | constexpr basic_endpoint(const protocol_type&, ::stdnet::ip::port_type) noexcept; 309 | constexpr basic_endpoint(const ip::address& _Address, ::stdnet::ip::port_type _Port) noexcept 310 | : ::stdnet::_Hidden::_Endpoint(&_Address._Data(), _Address.is_v4()? sizeof(::sockaddr_in): sizeof(::sockaddr_in6)) 311 | { 312 | (_Address.is_v4() 313 | ? reinterpret_cast<::sockaddr_in&>(this->_Storage()).sin_port 314 | : reinterpret_cast<::sockaddr_in6&>(this->_Storage()).sin6_port) = htons(_Port); 315 | } 316 | 317 | constexpr auto protocol() const noexcept -> protocol_type 318 | { 319 | return this->_Storage().ss_family == PF_INET? ::stdnet::ip::tcp::v4(): ::stdnet::ip::tcp::v6(); 320 | } 321 | /*-dk:TODO constexpr*/ auto address() const noexcept -> ::stdnet::ip::address 322 | { 323 | switch (this->_Storage().ss_family) 324 | { 325 | default: return {}; 326 | case PF_INET: return ::stdnet::ip::address_v4(ntohl(reinterpret_cast<::sockaddr_in const&>(this->_Storage()).sin_addr.s_addr)); 327 | //-dk:TODO case PF_INET6: return ::stdnet::ip::address_v6(reinterpret_cast<::sockaddr_in6 const&>(this->_Storage()).sin6_addr.s_addr); 328 | } 329 | } 330 | auto address(::stdnet::ip::address const&) noexcept -> void; 331 | constexpr auto port() const noexcept -> ::stdnet::ip::port_type 332 | { 333 | switch (this->_Storage().ss_family) 334 | { 335 | default: return {}; 336 | case PF_INET: return ntohs(reinterpret_cast<::sockaddr_in const&>(this->_Storage()).sin_port); 337 | case PF_INET6: return ntohs(reinterpret_cast<::sockaddr_in6 const&>(this->_Storage()).sin6_port); 338 | } 339 | } 340 | auto port(::stdnet::ip::port_type) noexcept -> void; 341 | 342 | auto _Size() const -> ::socklen_t 343 | { 344 | return this->_Storage().ss_family == PF_INET? sizeof(::sockaddr_in): sizeof(::sockaddr_in6); 345 | } 346 | 347 | friend ::std::ostream& operator<< (std::ostream& _Out, basic_endpoint const& _Ep) 348 | { 349 | return _Out << _Ep.address() << ":" << _Ep.port(); 350 | } 351 | }; 352 | 353 | // ---------------------------------------------------------------------------- 354 | 355 | #endif 356 | -------------------------------------------------------------------------------- /include/stdnet/io_base.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/io_base.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_IO_BASE 21 | #define INCLUDED_STDNET_IO_BASE 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | // ---------------------------------------------------------------------------- 28 | 29 | namespace stdnet::_Hidden { 30 | struct _Io_base; 31 | template struct _Io_operation; 32 | } 33 | 34 | // ---------------------------------------------------------------------------- 35 | // The struct _Io_base is used as base class of operation states. Objects of 36 | // this type are also used to kick off the actual work once a readiness 37 | // indication was received. 38 | 39 | struct stdnet::_Hidden::_Io_base 40 | { 41 | using _Extra_t = ::std::unique_ptrvoid>; 42 | 43 | _Io_base* _Next{nullptr}; // used for an intrusive list 44 | ::stdnet::_Hidden::_Context_base* _Context{nullptr}; 45 | ::stdnet::_Hidden::_Socket_id _Id; // the entity affected 46 | int _Event; // mask for expected events 47 | auto (*_Work)(::stdnet::_Hidden::_Context_base&, _Io_base*) -> bool = nullptr; 48 | _Extra_t _Extra{nullptr, +[](void*){}}; 49 | 50 | _Io_base(::stdnet::_Hidden::_Socket_id _Id, int _Event): _Id(_Id), _Event(_Event) {} 51 | 52 | virtual auto _Complete() -> void = 0; 53 | virtual auto _Error(::std::error_code) -> void = 0; 54 | virtual auto _Cancel() -> void = 0; 55 | }; 56 | 57 | 58 | // ---------------------------------------------------------------------------- 59 | // The struct _Io_operation is an _Io_base storing operation specific data. 60 | 61 | template 62 | struct stdnet::_Hidden::_Io_operation 63 | : _Io_base 64 | , _Data 65 | { 66 | template 67 | _Io_operation(::stdnet::_Hidden::_Socket_id _Id, int _Event, _D&& _A = _Data()) 68 | : _Io_base(_Id, _Event) 69 | , _Data(::std::forward<_D>(_A)) 70 | { 71 | } 72 | }; 73 | 74 | // ---------------------------------------------------------------------------- 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /include/stdnet/io_context.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/io_context.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_IO_CONTEXT 21 | #define INCLUDED_STDNET_IO_CONTEXT 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | // ---------------------------------------------------------------------------- 39 | 40 | namespace stdnet 41 | { 42 | class io_context; 43 | } 44 | 45 | // ---------------------------------------------------------------------------- 46 | 47 | class stdnet::io_context 48 | { 49 | private: 50 | ::std::unique_ptr<::stdnet::_Hidden::_Context_base> _D_ownedx{new ::stdnet::_Hidden::_Poll_context()}; 51 | ::std::unique_ptr<::stdnet::_Hidden::_Context_base> _D_owned{new ::stdnet::_Hidden::_Libevent_context()}; 52 | ::stdnet::_Hidden::_Context_base& _D_context{*this->_D_owned}; 53 | 54 | public: 55 | using scheduler_type = ::stdnet::_Hidden::_Io_context_scheduler; 56 | class executor_type {}; 57 | 58 | io_context() { std::signal(SIGPIPE, SIG_IGN); } 59 | io_context(::stdnet::_Hidden::_Context_base& _Context): _D_owned(), _D_context(_Context) {} 60 | io_context(io_context&&) = delete; 61 | 62 | auto _Make_socket(int _D, int _T, int _P, ::std::error_code& _Error) -> ::stdnet::_Hidden::_Socket_id 63 | { 64 | return this->_D_context._Make_socket(_D, _T, _P, _Error); 65 | } 66 | auto _Release(::stdnet::_Hidden::_Socket_id _Id, ::std::error_code& _Error) -> void 67 | { 68 | return this->_D_context._Release(_Id, _Error); 69 | } 70 | auto _Native_handle(::stdnet::_Hidden::_Socket_id _Id) -> _Stdnet_native_handle_type 71 | { 72 | return this->_D_context._Native_handle(_Id); 73 | } 74 | auto _Set_option(::stdnet::_Hidden::_Socket_id _Id, 75 | int _Level, 76 | int _Name, 77 | void const* _Data, 78 | ::socklen_t _Size, 79 | ::std::error_code& _Error) -> void 80 | { 81 | this->_D_context._Set_option(_Id, _Level, _Name, _Data, _Size, _Error); 82 | } 83 | auto _Bind(::stdnet::_Hidden::_Socket_id _Id, ::stdnet::ip::basic_endpoint<::stdnet::ip::tcp> const& _Endpoint, ::std::error_code& _Error) 84 | { 85 | this->_D_context._Bind(_Id, ::stdnet::_Hidden::_Endpoint(_Endpoint), _Error); 86 | } 87 | auto _Listen(::stdnet::_Hidden::_Socket_id _Id, int _No, ::std::error_code& _Error) 88 | { 89 | this->_D_context._Listen(_Id, _No, _Error); 90 | } 91 | auto get_scheduler() -> scheduler_type { return scheduler_type(&this->_D_context); } 92 | 93 | ::std::size_t run_one() { return this->_D_context.run_one(); } 94 | ::std::size_t run() 95 | { 96 | ::std::size_t _Count{}; 97 | while (::std::size_t _C = this->run_one()) 98 | { 99 | _Count += _C; 100 | } 101 | return _Count; 102 | } 103 | }; 104 | 105 | // ---------------------------------------------------------------------------- 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /include/stdnet/io_context_scheduler.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/io_context_scheduler.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_IO_CONTEXT_SCHEDULER 21 | #define INCLUDED_STDNET_IO_CONTEXT_SCHEDULER 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | // ---------------------------------------------------------------------------- 28 | 29 | namespace stdnet::_Hidden 30 | { 31 | class _Io_context_scheduler; 32 | } 33 | 34 | // ---------------------------------------------------------------------------- 35 | 36 | class stdnet::_Hidden::_Io_context_scheduler 37 | { 38 | private: 39 | ::stdnet::_Hidden::_Context_base* _D_context; 40 | 41 | public: 42 | _Io_context_scheduler(::stdnet::_Hidden::_Context_base* _Context) 43 | : _D_context(_Context) 44 | { 45 | assert(this->_D_context); 46 | } 47 | 48 | auto _Get_context() const { return this->_D_context; } 49 | 50 | auto _Cancel(_Hidden::_Io_base* _Cancel_op, _Hidden::_Io_base* _Op) -> void 51 | { 52 | this->_D_context->_Cancel(_Cancel_op, _Op); 53 | } 54 | auto _Accept(_Hidden::_Context_base::_Accept_operation* _Op) -> bool 55 | { 56 | return this->_D_context->_Accept(_Op); 57 | } 58 | auto _Connect(_Hidden::_Context_base::_Connect_operation* _Op) -> bool 59 | { 60 | return this->_D_context->_Connect(_Op); 61 | } 62 | auto _Receive(_Hidden::_Context_base::_Receive_operation* _Op) -> bool 63 | { 64 | return this->_D_context->_Receive(_Op); 65 | } 66 | auto _Send(_Hidden::_Context_base::_Send_operation* _Op) -> bool 67 | { 68 | return this->_D_context->_Send(_Op); 69 | } 70 | auto _Resume_after(_Hidden::_Context_base::_Resume_after_operation* _Op) -> bool 71 | { 72 | return this->_D_context->_Resume_after(_Op); 73 | } 74 | auto _Resume_at(_Hidden::_Context_base::_Resume_at_operation* _Op) -> bool 75 | { 76 | return this->_D_context->_Resume_at(_Op); 77 | } 78 | }; 79 | 80 | // ---------------------------------------------------------------------------- 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /include/stdnet/libevent_context.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/libevent_context.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_LIBEVENT_CONTEXT 21 | #define INCLUDED_STDNET_LIBEVENT_CONTEXT 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | // ---------------------------------------------------------------------------- 38 | 39 | namespace stdnet::_Hidden 40 | { 41 | extern "C" 42 | { 43 | auto _Libevent_callback(int, short, void*) -> void; 44 | } 45 | class _Libevent_error_category_t; 46 | class _Libevent_record; 47 | class _Libevent_context; 48 | 49 | auto _Libevent_error_category() -> _Libevent_error_category_t const&; 50 | } 51 | 52 | // ---------------------------------------------------------------------------- 53 | 54 | class stdnet::_Hidden::_Libevent_error_category_t 55 | : public ::std::error_category 56 | { 57 | auto name() const noexcept -> char const* override { return "libevent-category"; } 58 | auto message(int _E) const -> ::std::string override { return evutil_socket_error_to_string(_E); } 59 | }; 60 | 61 | inline auto stdnet::_Hidden::_Libevent_error_category() -> stdnet::_Hidden::_Libevent_error_category_t const& 62 | { 63 | static stdnet::_Hidden::_Libevent_error_category_t _Rc{}; 64 | return _Rc; 65 | } 66 | 67 | // ---------------------------------------------------------------------------- 68 | 69 | inline auto stdnet::_Hidden::_Libevent_callback(int, short, void* _Arg) -> void 70 | { 71 | auto _Op(static_cast<::stdnet::_Hidden::_Io_base*>(_Arg)); 72 | _Op->_Work(*_Op->_Context, _Op); 73 | } 74 | 75 | // ---------------------------------------------------------------------------- 76 | 77 | struct stdnet::_Hidden::_Libevent_record final 78 | { 79 | _Libevent_record(::stdnet::_Stdnet_native_handle_type _H): _Handle(_H) {} 80 | ::stdnet::_Stdnet_native_handle_type _Handle; 81 | bool _Blocking{true}; 82 | }; 83 | 84 | // ---------------------------------------------------------------------------- 85 | 86 | class stdnet::_Hidden::_Libevent_context 87 | : public ::stdnet::_Hidden::_Context_base 88 | { 89 | private: 90 | ::stdnet::_Hidden::_Container<::stdnet::_Hidden::_Libevent_record> _D_sockets; 91 | ::std::unique_ptr<::event_base, auto(*)(event_base*)->void> _Context; 92 | 93 | auto _Make_socket(int) -> ::stdnet::_Hidden::_Socket_id override; 94 | auto _Make_socket(int, int, int, ::std::error_code&) -> ::stdnet::_Hidden::_Socket_id override; 95 | auto _Release(::stdnet::_Hidden::_Socket_id, ::std::error_code&) -> void override; 96 | auto _Native_handle(::stdnet::_Hidden::_Socket_id) -> _Stdnet_native_handle_type override; 97 | auto _Set_option(::stdnet::_Hidden::_Socket_id, int, int, void const*, ::socklen_t, ::std::error_code&) -> void override; 98 | auto _Bind(::stdnet::_Hidden::_Socket_id, ::stdnet::_Hidden::_Endpoint const&, ::std::error_code&) -> void override; 99 | auto _Listen(::stdnet::_Hidden::_Socket_id, int, ::std::error_code&) -> void override; 100 | 101 | auto run_one() -> ::std::size_t override; 102 | 103 | auto _Cancel(::stdnet::_Hidden::_Io_base*, ::stdnet::_Hidden::_Io_base*) -> void override; 104 | auto _Accept(::stdnet::_Hidden::_Context_base::_Accept_operation*) -> bool override; 105 | auto _Connect(::stdnet::_Hidden::_Context_base::_Connect_operation*) -> bool override; 106 | auto _Receive(::stdnet::_Hidden::_Context_base::_Receive_operation*) -> bool override; 107 | auto _Send(::stdnet::_Hidden::_Context_base::_Send_operation*) -> bool override; 108 | auto _Resume_after(::stdnet::_Hidden::_Context_base::_Resume_after_operation*) -> bool override; 109 | auto _Resume_at(::stdnet::_Hidden::_Context_base::_Resume_at_operation*) -> bool override; 110 | 111 | public: 112 | _Libevent_context(); 113 | _Libevent_context(::event_base*); 114 | }; 115 | 116 | // ---------------------------------------------------------------------------- 117 | 118 | inline stdnet::_Hidden::_Libevent_context::_Libevent_context() 119 | : _Context(::event_base_new(), +[](::event_base* _C){ ::event_base_free(_C); }) 120 | { 121 | } 122 | 123 | inline stdnet::_Hidden::_Libevent_context::_Libevent_context(::event_base* _C) 124 | : _Context(_C, +[](::event_base*){}) 125 | { 126 | } 127 | 128 | // ---------------------------------------------------------------------------- 129 | 130 | inline auto stdnet::_Hidden::_Libevent_context::_Make_socket(int _Fd) -> ::stdnet::_Hidden::_Socket_id 131 | { 132 | return this->_D_sockets._Insert(_Fd); 133 | } 134 | 135 | inline auto stdnet::_Hidden::_Libevent_context::_Make_socket(int _D, int _T, int _P, ::std::error_code& _Error) 136 | -> ::stdnet::_Hidden::_Socket_id 137 | { 138 | int _Fd(::socket(_D, _T, _P)); 139 | if (_Fd < 0) 140 | { 141 | _Error = ::std::error_code(errno, ::std::system_category()); 142 | return ::stdnet::_Hidden::_Socket_id::_Invalid; 143 | } 144 | return this->_Make_socket(_Fd); 145 | } 146 | 147 | inline auto stdnet::_Hidden::_Libevent_context::_Release(::stdnet::_Hidden::_Socket_id _Id, ::std::error_code& _Error) -> void 148 | { 149 | _Stdnet_native_handle_type _Handle(this->_D_sockets[_Id]._Handle); 150 | this->_D_sockets._Erase(_Id); 151 | if (::close(_Handle) < 0) 152 | { 153 | _Error = ::std::error_code(errno, ::std::system_category()); 154 | } 155 | } 156 | 157 | inline auto stdnet::_Hidden::_Libevent_context::_Native_handle(::stdnet::_Hidden::_Socket_id _Id) -> _Stdnet_native_handle_type 158 | { 159 | return this->_D_sockets[_Id]._Handle; 160 | } 161 | 162 | inline auto stdnet::_Hidden::_Libevent_context::_Set_option(::stdnet::_Hidden::_Socket_id _Id, 163 | int _Level, 164 | int _Name, 165 | void const* _Data, 166 | ::socklen_t _Size, 167 | ::std::error_code& _Error) 168 | -> void 169 | { 170 | if (::setsockopt(this->_Native_handle(_Id), _Level, _Name, _Data, _Size) < 0) 171 | { 172 | _Error = ::std::error_code(errno, ::std::system_category()); 173 | } 174 | } 175 | 176 | inline auto stdnet::_Hidden::_Libevent_context::_Bind(::stdnet::_Hidden::_Socket_id _Id, 177 | ::stdnet::_Hidden::_Endpoint const& _Endpoint, 178 | ::std::error_code& _Error) 179 | -> void 180 | { 181 | if (::bind(this->_Native_handle(_Id), _Endpoint._Data(), _Endpoint._Size()) < 0) 182 | { 183 | _Error = ::std::error_code(errno, ::std::system_category()); 184 | } 185 | } 186 | 187 | inline auto stdnet::_Hidden::_Libevent_context::_Listen(::stdnet::_Hidden::_Socket_id _Id, 188 | int _No, 189 | ::std::error_code& _Error) 190 | -> void 191 | { 192 | if (::listen(this->_Native_handle(_Id), _No) < 0) 193 | { 194 | _Error = ::std::error_code(errno, ::std::system_category()); 195 | } 196 | } 197 | 198 | inline auto stdnet::_Hidden::_Libevent_context::run_one() -> ::std::size_t 199 | { 200 | // event_base_loop(..., EVLOOP_ONCE) may process multiple events but 201 | // it doesn't say how many. As a result, the return from run_one() may 202 | // be incorrect. 203 | return 0 == event_base_loop(this->_Context.get(), EVLOOP_ONCE)? 1u: 0; 204 | } 205 | 206 | inline auto stdnet::_Hidden::_Libevent_context::_Cancel(::stdnet::_Hidden::_Io_base* _Cancel_op, 207 | ::stdnet::_Hidden::_Io_base* _Op) -> void 208 | { 209 | if (_Op->_Extra && -1 == event_del(static_cast<::event *>(_Op->_Extra.get()))) 210 | { 211 | assert("deleting a libevent event failed!" == nullptr); 212 | } 213 | _Cancel_op->_Cancel(); 214 | _Op->_Cancel(); 215 | } 216 | 217 | inline auto stdnet::_Hidden::_Libevent_context::_Accept(::stdnet::_Hidden::_Context_base::_Accept_operation* _Op) -> bool 218 | { 219 | auto _Handle(this->_Native_handle(_Op->_Id)); 220 | ::event* _Ev(::event_new(this->_Context.get(), _Handle, EV_READ, _Libevent_callback, _Op)); 221 | if (_Ev == nullptr) 222 | { 223 | _Op->_Error(::std::error_code(evutil_socket_geterror(_Handle), stdnet::_Hidden::_Libevent_error_category())); 224 | return false; 225 | } 226 | _Op->_Context = this; 227 | _Op->_Extra = std::unique_ptrvoid>( 228 | _Ev, 229 | +[](void* _Ev){ ::event_free(static_cast<::event*>(_Ev)); }); 230 | 231 | _Op->_Work = 232 | [](::stdnet::_Hidden::_Context_base& _Ctxt, ::stdnet::_Hidden::_Io_base* _Op) 233 | { 234 | auto _Id{_Op->_Id}; 235 | auto& _Completion(*static_cast<_Accept_operation*>(_Op)); 236 | 237 | while (true) 238 | { 239 | int _Rc = ::accept(_Ctxt._Native_handle(_Id), ::std::get<0>(_Completion)._Data(), &::std::get<1>(_Completion)); 240 | if (0 <= _Rc) 241 | { 242 | ::std::get<2>(_Completion) = _Ctxt._Make_socket(_Rc); 243 | _Completion._Complete(); 244 | return true; 245 | } 246 | else 247 | { 248 | switch (errno) 249 | { 250 | default: 251 | _Completion._Error(::std::error_code(errno, ::std::system_category())); 252 | return true; 253 | case EINTR: 254 | break; 255 | case EWOULDBLOCK: 256 | return false; 257 | } 258 | } 259 | } 260 | }; 261 | 262 | ::event_add(_Ev, nullptr); 263 | return true; 264 | } 265 | // ---------------------------------------------------------------------------- 266 | 267 | inline auto stdnet::_Hidden::_Libevent_context::_Connect(::stdnet::_Hidden::_Context_base::_Connect_operation* _Op) -> bool 268 | { 269 | auto _Handle(this->_Native_handle(_Op->_Id)); 270 | auto const& _Endpoint(::std::get<0>(*_Op)); 271 | if (-1 == ::fcntl(_Handle, F_SETFL, O_NONBLOCK)) 272 | { 273 | _Op->_Error(::std::error_code(errno, ::std::system_category())); 274 | return false; 275 | } 276 | if (0 == ::connect(_Handle, _Endpoint._Data(), _Endpoint._Size())) 277 | { 278 | _Op->_Complete(); 279 | return false; 280 | } 281 | switch (errno) 282 | { 283 | default: 284 | _Op->_Error(::std::error_code(errno, ::std::system_category())); 285 | return false; 286 | case EINPROGRESS: 287 | case EINTR: 288 | break; 289 | } 290 | 291 | ::event* _Ev(::event_new(this->_Context.get(), _Handle, EV_READ | EV_WRITE, _Libevent_callback, _Op)); 292 | if (_Ev == nullptr) 293 | { 294 | _Op->_Error(::std::error_code(evutil_socket_geterror(_Handle), stdnet::_Hidden::_Libevent_error_category())); 295 | return false; 296 | } 297 | _Op->_Context = this; 298 | _Op->_Extra = std::unique_ptrvoid>( 299 | _Ev, 300 | +[](void* _Ev){ ::event_free(static_cast<::event*>(_Ev)); }); 301 | 302 | _Op->_Work = 303 | [](::stdnet::_Hidden::_Context_base& _Ctxt, ::stdnet::_Hidden::_Io_base* _Op) 304 | { 305 | auto _Handle{_Ctxt._Native_handle(_Op->_Id)}; 306 | auto& _Completion(*static_cast<_Connect_operation*>(_Op)); 307 | 308 | int _Error{}; 309 | ::socklen_t _Len{sizeof(_Error)}; 310 | if (-1 == ::getsockopt(_Handle, SOL_SOCKET, SO_ERROR, &_Error, &_Len)) 311 | { 312 | _Op->_Error(::std::error_code(errno, ::std::system_category())); 313 | return true; 314 | } 315 | if (0 == _Error) 316 | { 317 | _Op->_Complete(); 318 | } 319 | else 320 | { 321 | _Op->_Error(::std::error_code(_Error, ::std::system_category())); 322 | } 323 | return true; 324 | }; 325 | 326 | ::event_add(_Ev, nullptr); 327 | return true; 328 | } 329 | 330 | // ---------------------------------------------------------------------------- 331 | 332 | inline auto stdnet::_Hidden::_Libevent_context::_Receive(::stdnet::_Hidden::_Context_base::_Receive_operation* _Op) -> bool 333 | { 334 | auto _Handle(this->_Native_handle(_Op->_Id)); 335 | ::event* _Ev(::event_new(this->_Context.get(), _Handle, EV_READ, _Libevent_callback, _Op)); 336 | if (_Ev == nullptr) 337 | { 338 | _Op->_Error(::std::error_code(evutil_socket_geterror(_Handle), stdnet::_Hidden::_Libevent_error_category())); 339 | return false; 340 | } 341 | _Op->_Context = this; 342 | _Op->_Extra = std::unique_ptrvoid>( 343 | _Ev, 344 | +[](void* _Ev){ ::event_free(static_cast<::event*>(_Ev)); }); 345 | 346 | _Op->_Work = [](::stdnet::_Hidden::_Context_base& _Ctxt, ::stdnet::_Hidden::_Io_base* _Op) 347 | { 348 | auto _Id{_Op->_Id}; 349 | auto& _Completion(*static_cast<_Receive_operation*>(_Op)); 350 | 351 | while (true) 352 | { 353 | int _Rc = ::recvmsg(_Ctxt._Native_handle(_Id), 354 | &::std::get<0>(_Completion), 355 | ::std::get<1>(_Completion)); 356 | if (0 <= _Rc) 357 | { 358 | ::std::get<2>(_Completion) = _Rc; 359 | _Completion._Complete(); 360 | return true; 361 | } 362 | else 363 | { 364 | switch (errno) 365 | { 366 | default: 367 | _Completion._Error(::std::error_code(errno, ::std::system_category())); 368 | return true; 369 | case ECONNRESET: 370 | case EPIPE: 371 | ::std::get<2>(_Completion) = 0u; 372 | _Completion._Complete(); 373 | return true; 374 | case EINTR: 375 | break; 376 | case EWOULDBLOCK: 377 | return false; 378 | } 379 | } 380 | } 381 | }; 382 | 383 | ::event_add(_Ev, nullptr); 384 | return true; 385 | } 386 | 387 | inline auto stdnet::_Hidden::_Libevent_context::_Send(::stdnet::_Hidden::_Context_base::_Send_operation* _Op) -> bool 388 | { 389 | auto _Handle(this->_Native_handle(_Op->_Id)); 390 | ::event* _Ev(::event_new(this->_Context.get(), _Handle, EV_WRITE, _Libevent_callback, _Op)); 391 | if (_Ev == nullptr) 392 | { 393 | _Op->_Error(::std::error_code(evutil_socket_geterror(_Handle), stdnet::_Hidden::_Libevent_error_category())); 394 | return false; 395 | } 396 | _Op->_Context = this; 397 | _Op->_Extra = std::unique_ptrvoid>( 398 | _Ev, 399 | +[](void* _Ev){ ::event_free(static_cast<::event*>(_Ev)); }); 400 | 401 | _Op->_Work = [](::stdnet::_Hidden::_Context_base& _Ctxt, ::stdnet::_Hidden::_Io_base* _Op) 402 | { 403 | auto _Id{_Op->_Id}; 404 | auto& _Completion(*static_cast<_Receive_operation*>(_Op)); 405 | 406 | while (true) 407 | { 408 | int _Rc = ::sendmsg(_Ctxt._Native_handle(_Id), 409 | &::std::get<0>(_Completion), 410 | ::std::get<1>(_Completion)); 411 | if (0 <= _Rc) 412 | { 413 | ::std::get<2>(_Completion) = _Rc; 414 | _Completion._Complete(); 415 | return true; 416 | } 417 | else 418 | { 419 | switch (errno) 420 | { 421 | default: 422 | _Completion._Error(::std::error_code(errno, ::std::system_category())); 423 | return true; 424 | case ECONNRESET: 425 | case EPIPE: 426 | ::std::get<2>(_Completion) = 0u; 427 | _Completion._Complete(); 428 | return true; 429 | case EINTR: 430 | break; 431 | case EWOULDBLOCK: 432 | return false; 433 | } 434 | } 435 | } 436 | }; 437 | 438 | ::event_add(_Ev, nullptr); 439 | return true; 440 | } 441 | 442 | auto ::stdnet::_Hidden::_Libevent_context::_Resume_after(::stdnet::_Hidden::_Context_base::_Resume_after_operation* _Op) -> bool 443 | { 444 | ::event* _Ev(evtimer_new(this->_Context.get(), _Libevent_callback, _Op)); 445 | if (_Ev == nullptr) 446 | { 447 | _Op->_Error(::std::error_code(evutil_socket_geterror(_Handle), stdnet::_Hidden::_Libevent_error_category())); 448 | return false; 449 | } 450 | _Op->_Context = this; 451 | _Op->_Extra = std::unique_ptrvoid>( 452 | _Ev, 453 | +[](void* _Ev){ ::event_free(static_cast<::event*>(_Ev)); }); 454 | 455 | _Op->_Work = [](::stdnet::_Hidden::_Context_base& _Ctxt, ::stdnet::_Hidden::_Io_base* _Op) 456 | { 457 | auto& _Completion(*static_cast<_Resume_after_operation*>(_Op)); 458 | _Completion._Complete(); 459 | return true; 460 | }; 461 | 462 | constexpr unsigned long long _F(1'000'000); 463 | ::std::chrono::microseconds _Duration(::std::get<0>(*_Op)); 464 | ::timeval& _Tv(::std::get<1>(*_Op)); 465 | _Tv.tv_sec = _Duration.count() / _F; 466 | _Tv.tv_usec = 1000 * _Duration.count() % _F; 467 | ::evtimer_add(_Ev, &_Tv); 468 | return true; 469 | } 470 | 471 | auto ::stdnet::_Hidden::_Libevent_context::_Resume_at(::stdnet::_Hidden::_Context_base::_Resume_at_operation* _Op) -> bool 472 | { 473 | auto _Now(::std::chrono::system_clock::now()); 474 | auto _Time(::std::get<0>(*_Op)); 475 | if (_Time <= _Now) 476 | { 477 | _Op->_Complete(); 478 | return false; 479 | } 480 | 481 | ::event* _Ev(evtimer_new(this->_Context.get(), _Libevent_callback, _Op)); 482 | if (_Ev == nullptr) 483 | { 484 | _Op->_Error(::std::error_code(evutil_socket_geterror(_Handle), stdnet::_Hidden::_Libevent_error_category())); 485 | return false; 486 | } 487 | _Op->_Context = this; 488 | _Op->_Extra = std::unique_ptrvoid>( 489 | _Ev, 490 | +[](void* _Ev){ ::event_free(static_cast<::event*>(_Ev)); }); 491 | 492 | _Op->_Work = [](::stdnet::_Hidden::_Context_base& _Ctxt, ::stdnet::_Hidden::_Io_base* _Op) 493 | { 494 | auto& _Completion(*static_cast<_Resume_after_operation*>(_Op)); 495 | _Completion._Complete(); 496 | return true; 497 | }; 498 | 499 | constexpr unsigned long long _F(1'000'000); 500 | auto _Duration(::std::chrono::duration_cast<::std::chrono::microseconds>(_Time - _Now)); 501 | ::timeval& _Tv(::std::get<1>(*_Op)); 502 | _Tv.tv_sec = _Duration.count() / _F; 503 | _Tv.tv_usec = 1000 * _Duration.count() % _F; 504 | ::evtimer_add(_Ev, &_Tv); 505 | return true; 506 | } 507 | 508 | // ---------------------------------------------------------------------------- 509 | 510 | #endif 511 | -------------------------------------------------------------------------------- /include/stdnet/netfwd.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/netfwd.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_NETFWD 21 | #define INCLUDED_STDNET_NETFWD 22 | #pragma once 23 | 24 | #include 25 | #include 26 | 27 | // ---------------------------------------------------------------------------- 28 | 29 | namespace stdnet 30 | { 31 | namespace _Hidden 32 | { 33 | enum _Socket_id: ::std::uint_least32_t 34 | { 35 | _Invalid = ::std::numeric_limits<::std::uint_least32_t>::max() 36 | }; 37 | } 38 | namespace _Hidden 39 | { 40 | class _Context_base; 41 | } 42 | using _Stdnet_native_handle_type = int; 43 | inline constexpr _Stdnet_native_handle_type _Stdnet_invalid_handle{-1}; 44 | 45 | 46 | class io_context; 47 | class socket_base; 48 | template class basic_socket; 49 | template class basic_stream_socket; 50 | template class basic_socket_acceptor; 51 | namespace ip 52 | { 53 | template class basic_endpoint; 54 | class tcp; 55 | class address; 56 | class address_v4; 57 | class address_v6; 58 | } 59 | } 60 | 61 | // ---------------------------------------------------------------------------- 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /include/stdnet/poll_context.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/poll_context.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_POLL_CONTEXT 21 | #define INCLUDED_STDNET_POLL_CONTEXT 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | // ---------------------------------------------------------------------------- 32 | 33 | namespace stdnet::_Hidden 34 | { 35 | struct _Poll_record; 36 | struct _Poll_context; 37 | } 38 | 39 | // ---------------------------------------------------------------------------- 40 | 41 | struct stdnet::_Hidden::_Poll_record final 42 | { 43 | _Poll_record(::stdnet::_Stdnet_native_handle_type _H): _Handle(_H) {} 44 | ::stdnet::_Stdnet_native_handle_type _Handle; 45 | bool _Blocking{true}; 46 | }; 47 | 48 | // ---------------------------------------------------------------------------- 49 | 50 | struct stdnet::_Hidden::_Poll_context final 51 | : ::stdnet::_Hidden::_Context_base 52 | { 53 | ::stdnet::_Hidden::_Container<::stdnet::_Hidden::_Poll_record> _D_sockets; 54 | ::std::vector<::pollfd> _D_poll; 55 | ::std::vector<::stdnet::_Hidden::_Io_base*> _D_outstanding; 56 | 57 | auto _Make_socket(int _Fd) -> ::stdnet::_Hidden::_Socket_id override final 58 | { 59 | return this->_D_sockets._Insert(_Fd); 60 | } 61 | auto _Make_socket(int _D, int _T, int _P, ::std::error_code& _Error) 62 | -> ::stdnet::_Hidden::_Socket_id override final 63 | { 64 | int _Fd(::socket(_D, _T, _P)); 65 | if (_Fd < 0) 66 | { 67 | _Error = ::std::error_code(errno, ::std::system_category()); 68 | return ::stdnet::_Hidden::_Socket_id::_Invalid; 69 | } 70 | return this->_Make_socket(_Fd); 71 | } 72 | auto _Release(::stdnet::_Hidden::_Socket_id _Id, ::std::error_code& _Error) -> void override final 73 | { 74 | _Stdnet_native_handle_type _Handle(this->_D_sockets[_Id]._Handle); 75 | this->_D_sockets._Erase(_Id); 76 | if (::close(_Handle) < 0) 77 | { 78 | _Error = ::std::error_code(errno, ::std::system_category()); 79 | } 80 | } 81 | auto _Native_handle(::stdnet::_Hidden::_Socket_id _Id) -> _Stdnet_native_handle_type override final 82 | { 83 | return this->_D_sockets[_Id]._Handle; 84 | } 85 | auto _Set_option(::stdnet::_Hidden::_Socket_id _Id, 86 | int _Level, 87 | int _Name, 88 | void const* _Data, 89 | ::socklen_t _Size, 90 | ::std::error_code& _Error) -> void override final 91 | { 92 | if (::setsockopt(this->_Native_handle(_Id), _Level, _Name, _Data, _Size) < 0) 93 | { 94 | _Error = ::std::error_code(errno, ::std::system_category()); 95 | } 96 | } 97 | auto _Bind(::stdnet::_Hidden::_Socket_id _Id, 98 | ::stdnet::_Hidden::_Endpoint const& _Endpoint, 99 | ::std::error_code& _Error) -> void override final 100 | { 101 | if (::bind(this->_Native_handle(_Id), _Endpoint._Data(), _Endpoint._Size()) < 0) 102 | { 103 | _Error = ::std::error_code(errno, ::std::system_category()); 104 | } 105 | } 106 | auto _Listen(::stdnet::_Hidden::_Socket_id _Id, int _No, ::std::error_code& _Error) -> void override final 107 | { 108 | if (::listen(this->_Native_handle(_Id), _No) < 0) 109 | { 110 | _Error = ::std::error_code(errno, ::std::system_category()); 111 | } 112 | } 113 | 114 | auto run_one() -> ::std::size_t override final 115 | { 116 | if (this->_D_poll.empty()) 117 | { 118 | return ::std::size_t{}; 119 | } 120 | while (true) 121 | { 122 | int _Rc(::poll(this->_D_poll.data(), this->_D_poll.size(), -1)); 123 | if (_Rc < 0) 124 | { 125 | switch (errno) 126 | { 127 | default: 128 | return ::std::size_t(); 129 | case EINTR: 130 | case EAGAIN: 131 | break; 132 | } 133 | } 134 | else 135 | { 136 | for (::std::size_t _I(this->_D_poll.size()); 0 < _I--; ) 137 | { 138 | if (this->_D_poll[_I].revents & (this->_D_poll[_I].events | POLLERR)) 139 | { 140 | ::stdnet::_Hidden::_Io_base* _Completion = this->_D_outstanding[_I]; 141 | auto _Id{_Completion->_Id}; 142 | if (_I + 1u != this->_D_poll.size()) 143 | { 144 | this->_D_poll[_I] = this->_D_poll.back(); 145 | this->_D_outstanding[_I] = this->_D_outstanding.back(); 146 | } 147 | this->_D_poll.pop_back(); 148 | this->_D_outstanding.pop_back(); 149 | _Completion->_Work(*this, _Completion); 150 | return ::std::size_t(1); 151 | } 152 | } 153 | } 154 | } 155 | return ::std::size_t{}; 156 | } 157 | auto _Wakeup() -> void 158 | { 159 | //-dk:TODO wake-up polling thread 160 | } 161 | 162 | auto _Add_Outstanding(::stdnet::_Hidden::_Io_base* _Completion) -> bool 163 | { 164 | auto _Id{_Completion->_Id}; 165 | if (this->_D_sockets[_Id]._Blocking || !_Completion->_Work(*this, _Completion)) 166 | { 167 | this->_D_poll.emplace_back(::pollfd{this->_Native_handle(_Id), short(_Completion->_Event), short()}); 168 | this->_D_outstanding.emplace_back(_Completion); 169 | this->_Wakeup(); 170 | return false; 171 | } 172 | return true; 173 | } 174 | 175 | auto _Cancel(::stdnet::_Hidden::_Io_base*, ::stdnet::_Hidden::_Io_base*) -> void override final 176 | { 177 | //-dk:TODO 178 | } 179 | auto _Accept(::stdnet::_Hidden::_Context_base::_Accept_operation* _Completion) 180 | -> bool override final 181 | { 182 | auto _Id(_Completion->_Id); 183 | _Completion->_Work = 184 | [](::stdnet::_Hidden::_Context_base& _Ctxt, ::stdnet::_Hidden::_Io_base* _Comp) 185 | { 186 | auto _Id{_Comp->_Id}; 187 | auto& _Completion(*static_cast<_Accept_operation*>(_Comp)); 188 | 189 | while (true) 190 | { 191 | int _Rc = ::accept(_Ctxt._Native_handle(_Id), ::std::get<0>(_Completion)._Data(), &::std::get<1>(_Completion)); 192 | if (0 <= _Rc) 193 | { 194 | ::std::get<2>(_Completion) = _Ctxt._Make_socket(_Rc); 195 | _Completion._Complete(); 196 | return true; 197 | } 198 | else 199 | { 200 | switch (errno) 201 | { 202 | default: 203 | _Completion._Error(::std::error_code(errno, ::std::system_category())); 204 | return true; 205 | case EINTR: 206 | break; 207 | case EWOULDBLOCK: 208 | return false; 209 | } 210 | } 211 | } 212 | }; 213 | return this->_Add_Outstanding(_Completion); 214 | } 215 | auto _Connect(::stdnet::_Hidden::_Context_base::_Connect_operation* _Completion) -> bool override { return {}; /*-dk:TODO*/ } 216 | auto _Receive(::stdnet::_Hidden::_Context_base::_Receive_operation*) -> bool override { return {}; /*-dk:TODO*/ } 217 | auto _Send(::stdnet::_Hidden::_Context_base::_Send_operation*) -> bool override { return {}; /*-dk:TODO*/ } 218 | auto _Resume_after(::stdnet::_Hidden::_Context_base::_Resume_after_operation*) -> bool override 219 | { 220 | //-dk:TODO 221 | return {}; 222 | } 223 | auto _Resume_at(::stdnet::_Hidden::_Context_base::_Resume_at_operation*) -> bool override 224 | { 225 | //-dk:TODO 226 | return {}; 227 | } 228 | }; 229 | 230 | // ---------------------------------------------------------------------------- 231 | 232 | #endif 233 | -------------------------------------------------------------------------------- /include/stdnet/socket.hpp: -------------------------------------------------------------------------------- 1 | // include/stdnet/socket.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_SOCKET 21 | #define INCLUDED_STDNET_SOCKET 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | // ---------------------------------------------------------------------------- 43 | 44 | namespace stdnet 45 | { 46 | enum class socket_errc: int; 47 | auto socket_category() noexcept -> ::std::error_category const&; 48 | 49 | namespace _Hidden 50 | { 51 | struct _Accept_desc; 52 | struct _Connect_desc; 53 | struct _Send_desc; 54 | struct _Send_to_desc; 55 | struct _Receive_desc; 56 | struct _Receive_from_desc; 57 | } 58 | 59 | using async_accept_t = ::stdnet::_Hidden::_Cpo<::stdnet::_Hidden::_Accept_desc>; 60 | inline constexpr async_accept_t async_accept{}; 61 | using async_connect_t = ::stdnet::_Hidden::_Cpo<::stdnet::_Hidden::_Connect_desc>; 62 | inline constexpr async_connect_t async_connect{}; 63 | using async_send_t = ::stdnet::_Hidden::_Cpo<::stdnet::_Hidden::_Send_desc>; 64 | inline constexpr async_send_t async_send{}; 65 | using async_send_to_t = ::stdnet::_Hidden::_Cpo<::stdnet::_Hidden::_Send_to_desc>; 66 | inline constexpr async_send_to_t async_send_to{}; 67 | using async_receive_t = ::stdnet::_Hidden::_Cpo<::stdnet::_Hidden::_Receive_desc>; 68 | inline constexpr async_receive_t async_receive{}; 69 | using async_receive_from_t = ::stdnet::_Hidden::_Cpo<::stdnet::_Hidden::_Receive_from_desc>; 70 | inline constexpr async_receive_from_t async_receive_from{}; 71 | } 72 | 73 | struct stdnet::_Hidden::_Accept_desc 74 | { 75 | using _Operation = ::stdnet::_Hidden::_Context_base::_Accept_operation; 76 | template 77 | struct _Data 78 | { 79 | using _Acceptor_t = ::std::remove_cvref_t<_Acceptor>; 80 | using _Socket_t = _Acceptor_t::socket_type; 81 | using _Completion_signature 82 | = ::stdexec::set_value_t(_Socket_t, typename _Socket_t::endpoint_type); 83 | 84 | _Acceptor_t& _D_acceptor; 85 | _Data(_Acceptor_t& _A): _D_acceptor(_A) {} 86 | 87 | auto _Id() const { return this->_D_acceptor._Id(); } 88 | auto _Events() const { return POLLIN; } 89 | auto _Get_scheduler() { return this->_D_acceptor.get_scheduler(); } 90 | auto _Set_value(_Operation& _O, auto&& _Receiver) 91 | { 92 | ::stdexec::set_value(::std::move(_Receiver), 93 | _Socket_t(this->_D_acceptor.get_scheduler()._Get_context(), 94 | ::std::move(*::std::get<2>(_O))), 95 | typename _Socket_t::endpoint_type(::std::get<0>(_O))); 96 | } 97 | auto _Submit(auto* _Base) -> bool 98 | { 99 | ::std::get<1>(*_Base) = sizeof(::std::get<0>(*_Base)); 100 | return this->_D_acceptor.get_scheduler()._Accept(_Base); 101 | } 102 | }; 103 | }; 104 | 105 | struct stdnet::_Hidden::_Connect_desc 106 | { 107 | using _Operation = ::stdnet::_Hidden::_Context_base::_Connect_operation; 108 | template 109 | struct _Data 110 | { 111 | using _Completion_signature = ::stdexec::set_value_t(); 112 | 113 | _Socket& _D_socket; 114 | 115 | auto _Id() const { return this->_D_socket._Id(); } 116 | auto _Events() const { return POLLIN; } 117 | auto _Get_scheduler() { return this->_D_socket.get_scheduler(); } 118 | auto _Set_value(_Operation&, auto&& _Receiver) 119 | { 120 | ::stdexec::set_value(::std::move(_Receiver)); 121 | } 122 | auto _Submit(auto* _Base) -> bool 123 | { 124 | ::std::get<0>(*_Base) = this->_D_socket.get_endpoint(); 125 | return this->_D_socket.get_scheduler()._Connect(_Base); 126 | } 127 | }; 128 | }; 129 | 130 | struct stdnet::_Hidden::_Send_desc 131 | { 132 | using _Operation = ::stdnet::_Hidden::_Context_base::_Send_operation; 133 | template 134 | struct _Data 135 | { 136 | using _Completion_signature = ::stdexec::set_value_t(::std::size_t); 137 | 138 | _Stream_t& _D_stream; 139 | _Buffers _D_buffers; 140 | 141 | auto _Id() const { return this->_D_stream._Id(); } 142 | auto _Events() const { return POLLIN; } 143 | auto _Get_scheduler() { return this->_D_stream.get_scheduler(); } 144 | auto _Set_value(_Operation& _O, auto&& _Receiver) 145 | { 146 | ::stdexec::set_value(::std::move(_Receiver), 147 | ::std::move(::std::get<2>(_O))); 148 | } 149 | auto _Submit(auto* _Base) -> bool 150 | { 151 | ::std::get<0>(*_Base).msg_iov = this->_D_buffers.data(); 152 | ::std::get<0>(*_Base).msg_iovlen = this->_D_buffers.size(); 153 | return this->_D_stream.get_scheduler()._Send(_Base); 154 | } 155 | }; 156 | }; 157 | 158 | struct stdnet::_Hidden::_Send_to_desc 159 | { 160 | using _Operation = ::stdnet::_Hidden::_Context_base::_Send_operation; 161 | template 162 | struct _Data 163 | { 164 | using _Completion_signature = ::stdexec::set_value_t(::std::size_t); 165 | 166 | _Stream_t& _D_stream; 167 | _Buffers _D_buffers; 168 | _Endpoint _D_endpoint; 169 | 170 | auto _Id() const { return this->_D_stream._Id(); } 171 | auto _Events() const { return POLLIN; } 172 | auto _Get_scheduler() { return this->_D_stream.get_scheduler(); } 173 | auto _Set_value(_Operation& _O, auto&& _Receiver) 174 | { 175 | ::stdexec::set_value(::std::move(_Receiver), ::std::get<2>(_O)); 176 | } 177 | auto _Submit(auto* _Base) -> bool 178 | { 179 | ::std::get<0>(*_Base).msg_iov = this->_D_buffers.data(); 180 | ::std::get<0>(*_Base).msg_iovlen = this->_D_buffers.size(); 181 | ::std::get<0>(*_Base).msg_name = this->_D_endpoint._Data(); 182 | ::std::get<0>(*_Base).msg_namelen = this->_D_endpoint._Size(); 183 | return this->_D_stream.get_scheduler()._Send(_Base); 184 | } 185 | }; 186 | }; 187 | 188 | struct stdnet::_Hidden::_Receive_desc 189 | { 190 | using _Operation = ::stdnet::_Hidden::_Context_base::_Receive_operation; 191 | template 192 | struct _Data 193 | { 194 | using _Completion_signature = ::stdexec::set_value_t(::std::size_t); 195 | 196 | _Stream_t& _D_stream; 197 | _Buffers _D_buffers; 198 | 199 | auto _Id() const { return this->_D_stream._Id(); } 200 | auto _Events() const { return POLLIN; } 201 | auto _Get_scheduler() { return this->_D_stream.get_scheduler(); } 202 | auto _Set_value(_Operation& _O, auto&& _Receiver) 203 | { 204 | ::stdexec::set_value(::std::move(_Receiver), ::std::get<2>(_O)); 205 | } 206 | auto _Submit(auto* _Base) -> bool 207 | { 208 | ::std::get<0>(*_Base).msg_iov = this->_D_buffers.data(); 209 | ::std::get<0>(*_Base).msg_iovlen = this->_D_buffers.size(); 210 | return this->_D_stream.get_scheduler()._Receive(_Base); 211 | } 212 | }; 213 | }; 214 | 215 | struct stdnet::_Hidden::_Receive_from_desc 216 | { 217 | using _Operation = ::stdnet::_Hidden::_Context_base::_Receive_operation; 218 | template 219 | struct _Data 220 | { 221 | using _Completion_signature = ::stdexec::set_value_t(::std::size_t); 222 | 223 | _Stream_t& _D_stream; 224 | _Buffers _D_buffers; 225 | _Endpoint _D_endpoint; 226 | 227 | auto _Id() const { return this->_D_stream._Id(); } 228 | auto _Events() const { return POLLIN; } 229 | auto _Get_scheduler() { return this->_D_stream.get_scheduler(); } 230 | auto _Set_value(_Operation& _O, auto&& _Receiver) 231 | { 232 | ::stdexec::set_value(::std::move(_Receiver), ::std::get<2>(_O)); 233 | } 234 | auto _Submit(auto* _Base) -> bool 235 | { 236 | ::std::get<0>(*_Base).msg_iov = this->_D_buffers.data(); 237 | ::std::get<0>(*_Base).msg_iovlen = this->_D_buffers.size(); 238 | ::std::get<0>(*_Base).msg_name = this->_D_buffers._Data(); 239 | ::std::get<0>(*_Base).msg_namelen = this->_D_buffers._Size(); 240 | return this->_D_stream.get_scheduler()._Receive(_Base); 241 | } 242 | }; 243 | }; 244 | 245 | enum class stdnet::socket_errc: int 246 | { 247 | already_open = 1, 248 | not_found 249 | }; 250 | 251 | auto stdnet::socket_category() noexcept -> ::std::error_category const& 252 | { 253 | struct _Category 254 | : ::std::error_category 255 | { 256 | auto name() const noexcept -> char const* override final { return "socket"; } 257 | auto message(int _Error) const -> ::std::string override final 258 | { 259 | switch (::stdnet::socket_errc(_Error)) 260 | { 261 | default: return "none"; 262 | case ::stdnet::socket_errc::already_open: return "already open"; 263 | case ::stdnet::socket_errc::not_found: return "not found"; 264 | } 265 | } 266 | }; 267 | static const _Category _Rc{}; 268 | return _Rc; 269 | } 270 | 271 | // ---------------------------------------------------------------------------- 272 | 273 | template 274 | class stdnet::basic_socket_acceptor 275 | : public stdnet::socket_base 276 | { 277 | public: 278 | using scheduler_type = ::stdnet::io_context::scheduler_type; 279 | using executor_type = ::stdnet::io_context::executor_type; 280 | using native_handle_type = ::stdnet::_Stdnet_native_handle_type; 281 | using protocol_type = _AcceptableProtocol; 282 | using endpoint_type = typename protocol_type::endpoint; 283 | using socket_type = typename protocol_type::socket; 284 | 285 | private: 286 | ::stdnet::io_context& _D_context; 287 | protocol_type _D_protocol; 288 | ::stdnet::_Hidden::_Socket_id _D_id{}; 289 | 290 | private: 291 | template 292 | static void _Dispatch(_Fun_t&& _Fun) 293 | { 294 | ::std::error_code _error{}; 295 | _Fun(_error); 296 | if (_error) 297 | { 298 | throw ::std::system_error(_error); 299 | } 300 | } 301 | 302 | public: 303 | //explicit basic_socket_acceptor(::stdnet::io_context&); 304 | basic_socket_acceptor(::stdnet::io_context&, protocol_type const& protocol); 305 | basic_socket_acceptor(::stdnet::io_context& _Context, endpoint_type const& _Endpoint, bool _Reuse = true) 306 | : ::stdnet::socket_base() 307 | , _D_context(_Context) 308 | , _D_protocol(_Endpoint.protocol()) 309 | , _D_id(::stdnet::_Hidden::_Socket_id::_Invalid) 310 | { 311 | this->open(_Endpoint.protocol()); 312 | if (_Reuse) 313 | { 314 | this->set_option(::stdnet::socket_base::reuse_address(true)); 315 | } 316 | this->bind(_Endpoint); 317 | this->listen(); 318 | } 319 | basic_socket_acceptor(::stdnet::io_context&, protocol_type const&, native_handle_type const&); 320 | basic_socket_acceptor(basic_socket_acceptor const&) = delete; 321 | basic_socket_acceptor(basic_socket_acceptor&& _Other) 322 | : ::stdnet::socket_base() 323 | , _D_protocol(_Other._D_protocol) 324 | , _D_id(::std::exchange(_Other._D_id, ::stdnet::_Hidden::_Socket_id::_Invalid)) 325 | { 326 | } 327 | template 328 | basic_socket_acceptor(::stdnet::basic_socket_acceptor<_OtherProtocol>&&); 329 | ~basic_socket_acceptor() 330 | { 331 | if (this->_D_id != ::stdnet::_Hidden::_Socket_id::_Invalid) 332 | { 333 | ::std::error_code _Error{}; 334 | this->close(_Error); 335 | } 336 | } 337 | basic_socket_acceptor& operator=(basic_socket_acceptor const&) = delete; 338 | basic_socket_acceptor& operator=(basic_socket_acceptor&&); 339 | template 340 | basic_socket_acceptor& operator=(::stdnet::basic_socket_acceptor<_OtherProtocol>&&); 341 | 342 | auto _Get_context() -> ::stdnet::io_context& { return this->_D_context; } 343 | auto get_scheduler() noexcept -> scheduler_type 344 | { 345 | return this->_D_context.get_scheduler(); 346 | } 347 | executor_type get_executor() noexcept; 348 | auto native_handle() -> native_handle_type { return this->_D_context._Native_handle(this->_D_id); } 349 | auto _Native_handle() const -> native_handle_type { return this->_D_context._Native_handle(this->_D_id); } 350 | auto _Id() const -> ::stdnet::_Hidden::_Socket_id { return this->_D_id; } 351 | auto open(protocol_type const& _P = protocol_type()) -> void 352 | { 353 | _Dispatch([this, &_P](::std::error_code& _Error){ this->open(_P, _Error); }); 354 | } 355 | auto open(protocol_type const& _P, ::std::error_code& _Error) -> void 356 | { 357 | if (this->is_open()) 358 | { 359 | _Error = ::std::error_code(int(socket_errc::already_open), ::stdnet::socket_category()); 360 | } 361 | this->_D_id = this->_D_context._Make_socket(_P.family(), _P.type(), _P.protocol(), _Error); 362 | } 363 | void assign(protocol_type const&, native_handle_type const&); 364 | void assign(protocol_type const&, native_handle_type const&, ::std::error_code&); 365 | native_handle_type release(); 366 | native_handle_type release(::std::error_code&); 367 | auto is_open() const noexcept -> bool { return this->_D_id != ::stdnet::_Hidden::_Socket_id::_Invalid; } 368 | auto close() -> void 369 | { 370 | _Dispatch([this](auto& _Error){ return this->close(_Error); }); 371 | } 372 | auto close(::std::error_code& _Error) -> void 373 | { 374 | //-dk:TODO cancel outstanding work 375 | if (this->is_open()) 376 | { 377 | this->_D_context._Release(this->_Id(), _Error); 378 | } 379 | } 380 | void cancel(); 381 | void cancel(::std::error_code&); 382 | template 383 | auto set_option(_SettableSocketOption const& _Option) -> void 384 | { 385 | _Dispatch([this, _Option](::std::error_code& _Error){ this->set_option(_Option, _Error); }); 386 | } 387 | template 388 | auto set_option(_SettableSocketOption const& _Option, ::std::error_code& _Error) -> void 389 | { 390 | this->_D_context._Set_option( 391 | this->_Id(), 392 | _Option.level(this->_D_protocol), 393 | _Option.name(this->_D_protocol), 394 | _Option.data(this->_D_protocol), 395 | _Option.size(this->_D_protocol), 396 | _Error); 397 | } 398 | 399 | template 400 | void get_option(_GettableSocketOption&) const; 401 | template 402 | void get_option(_GettableSocketOption&, ::std::error_code&) const; 403 | template 404 | void io_control(_IoControlCommand&); 405 | template 406 | void io_control(_IoControlCommand&, ::std::error_code&); 407 | void non_blocking(bool); 408 | void non_blocking(bool, ::std::error_code&); 409 | bool non_blocking() const; 410 | void native_non_blocking(bool); 411 | void native_non_blocking(bool, ::std::error_code&); 412 | bool native_non_blocking() const; 413 | auto bind(endpoint_type const& _Endpoint) -> void 414 | { 415 | _Dispatch([this, _Endpoint](::std::error_code& _Error){ this->bind(_Endpoint, _Error); }); 416 | } 417 | auto bind(endpoint_type const& _Endpoint, ::std::error_code& _Error) -> void 418 | { 419 | this->_D_context._Bind(this->_D_id, _Endpoint, _Error); 420 | } 421 | auto listen(int _No = ::stdnet::socket_base::max_listen_connections) -> void 422 | { 423 | _Dispatch([this, _No](auto& _Error){ this->listen(_No, _Error); }); 424 | } 425 | auto listen(int _No, ::std::error_code& _Error) -> void 426 | { 427 | this->_D_context._Listen(this->_D_id, _No, _Error); 428 | } 429 | endpoint_type local_endpoint() const; 430 | endpoint_type local_endpoint(::std::error_code&) const; 431 | void enable_connection_aborted(bool); 432 | bool enable_connection_aborted() const; 433 | socket_type accept(); 434 | socket_type accept(::std::error_code&); 435 | socket_type accept(io_context&); 436 | socket_type accept(io_context&, ::std::error_code&); 437 | template 438 | void /*DEDUCED*/ async_accept(_CompletionToken&&); 439 | template 440 | void /*DEDUCED*/ async_accept(::stdnet::io_context&, _CompletionToken&&); 441 | socket_type accept(endpoint_type&); 442 | socket_type accept(endpoint_type&, ::std::error_code&); 443 | socket_type accept(::stdnet::io_context&, endpoint_type&); 444 | socket_type accept(::stdnet::io_context&, endpoint_type&, ::std::error_code&); 445 | template 446 | void /*DEDUCED*/ async_accept(endpoint_type&, _CompletionToken&&); 447 | template 448 | void /*DEDUCED*/ async_accept(::stdnet::io_context&, endpoint_type&, _CompletionToken&&); 449 | void wait(::stdnet::socket_base::wait_type); 450 | void wait(::stdnet::socket_base::wait_type, ::std::error_code& ec); 451 | template 452 | void /*DEDUCED*/ async_wait(::stdnet::socket_base::wait_type, _CompletionToken&&); 453 | }; 454 | 455 | // ---------------------------------------------------------------------------- 456 | 457 | #endif 458 | -------------------------------------------------------------------------------- /include/stdnet/socket_base.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/socket_base.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | /* 4 | * Copyright (c) 2023 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | * 6 | * Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | * (the "License"); you may not use this file except in compliance with 8 | * the License. You may obtain a copy of the License at 9 | * 10 | * https://llvm.org/LICENSE.txt 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_SOCKET_BASE 21 | #define INCLUDED_STDNET_SOCKET_BASE 22 | 23 | #include 24 | #include 25 | 26 | // ---------------------------------------------------------------------------- 27 | 28 | class stdnet::socket_base 29 | { 30 | public: 31 | template 32 | class _Socket_option 33 | { 34 | private: 35 | _Value_t _D_value; 36 | 37 | public: 38 | explicit _Socket_option(_Value_t _V): _D_value(_V) {} 39 | _Value_t _Value() const { return this->_D_value; } 40 | template auto data(_Protocol&&) const -> _Value_t const* { return &this->_D_value; } 41 | template auto data(_Protocol&&) -> _Value_t const* { return &this->_D_value; } 42 | template constexpr auto level(_Protocol&&) const -> int { return _Level; } 43 | template constexpr auto name(_Protocol&&) const -> int { return _Name; } 44 | template constexpr auto size(_Protocol&&) const -> ::socklen_t { return sizeof(_Value_t); } 45 | }; 46 | class broadcast; 47 | class debug; 48 | class do_not_route; 49 | class keep_alive; 50 | class linger; 51 | class out_of_band_inline; 52 | class receive_buffer_size; 53 | class receive_low_watermark; 54 | class reuse_address 55 | : public _Socket_option 56 | { 57 | public: 58 | explicit reuse_address(bool _Value): _Socket_option(_Value) {} 59 | explicit operator bool() const { return this->_Value(); } 60 | }; 61 | class send_buffer_size; 62 | class send_low_watermark; 63 | 64 | using shutdown_type = int; //-dk:TODO 65 | static constexpr shutdown_type shutdown_receive{1}; 66 | static constexpr shutdown_type shutdown_send{2}; 67 | static constexpr shutdown_type shutdown_both{3}; 68 | 69 | using wait_type = int; //-dk:TODO 70 | static constexpr wait_type wait_read{1}; 71 | static constexpr wait_type wait_write{2}; 72 | static constexpr wait_type wait_error{3}; 73 | 74 | using message_flags = int; //-dk:TODO 75 | static constexpr message_flags message_peek{1}; 76 | static constexpr message_flags message_out_of_band{2}; 77 | static constexpr message_flags message_do_not_route{3}; 78 | 79 | static constexpr int max_listen_connections{SOMAXCONN}; 80 | 81 | protected: 82 | socket_base() = default; 83 | ~socket_base() = default; 84 | }; 85 | 86 | // ---------------------------------------------------------------------------- 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /include/stdnet/timer.hpp: -------------------------------------------------------------------------------- 1 | // stdnet/timer.hpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | // 4 | // Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | // 6 | // Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | // (the "License"); you may not use this file except in compliance with 8 | // the License. You may obtain a copy of the License at 9 | // 10 | // https://llvm.org/LICENSE.txt 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // ---------------------------------------------------------------------------- 19 | 20 | #ifndef INCLUDED_STDNET_TIMER 21 | #define INCLUDED_STDNET_TIMER 22 | 23 | #include 24 | #include 25 | 26 | // ---------------------------------------------------------------------------- 27 | 28 | namespace stdnet 29 | { 30 | namespace _Hidden 31 | { 32 | struct _Resume_after_desc; 33 | struct _Resume_at_desc; 34 | } 35 | 36 | using async_resume_after_t = ::stdnet::_Hidden::_Cpo<::stdnet::_Hidden::_Resume_after_desc>; 37 | using async_resume_at_t = ::stdnet::_Hidden::_Cpo<::stdnet::_Hidden::_Resume_at_desc>; 38 | 39 | inline constexpr async_resume_after_t async_resume_after{}; 40 | inline constexpr async_resume_at_t async_resume_at{}; 41 | } 42 | 43 | // ---------------------------------------------------------------------------- 44 | 45 | struct stdnet::_Hidden::_Resume_after_desc 46 | { 47 | using _Operation = ::stdnet::_Hidden::_Context_base::_Resume_after_operation; 48 | template 49 | struct _Data 50 | { 51 | using _Completion_signature = ::stdexec::set_value_t(); 52 | 53 | ::std::remove_cvref_t<_Scheduler> _D_scheduler; 54 | ::std::chrono::microseconds _D_duration; 55 | 56 | auto _Id() const -> ::stdnet::_Hidden::_Socket_id { return {}; } 57 | auto _Events() const { return decltype(POLLIN)(); } 58 | auto _Get_scheduler() { return this->_D_scheduler; } 59 | auto _Set_value(_Operation& _O, auto&& _Receiver) 60 | { 61 | ::stdexec::set_value(::std::move(_Receiver)); 62 | } 63 | auto _Submit(auto* _Base) -> bool 64 | { 65 | ::std::get<0>(*_Base) = this->_D_duration; 66 | return this->_D_scheduler._Resume_after(_Base); 67 | } 68 | }; 69 | }; 70 | 71 | // ---------------------------------------------------------------------------- 72 | 73 | struct stdnet::_Hidden::_Resume_at_desc 74 | { 75 | using _Operation = ::stdnet::_Hidden::_Context_base::_Resume_at_operation; 76 | template 77 | struct _Data 78 | { 79 | using _Completion_signature = ::stdexec::set_value_t(); 80 | 81 | ::std::remove_cvref_t<_Scheduler> _D_scheduler; 82 | ::std::chrono::system_clock::time_point _D_time; 83 | 84 | auto _Id() const -> ::stdnet::_Hidden::_Socket_id { return {}; } 85 | auto _Events() const { return decltype(POLLIN)(); } 86 | auto _Get_scheduler() { return this->_D_scheduler; } 87 | auto _Set_value(_Operation& _O, auto&& _Receiver) 88 | { 89 | ::stdexec::set_value(::std::move(_Receiver)); 90 | } 91 | auto _Submit(auto* _Base) -> bool 92 | { 93 | ::std::get<0>(*_Base) = this->_D_time; 94 | return this->_D_scheduler._Resume_at(_Base); 95 | } 96 | }; 97 | }; 98 | 99 | // ---------------------------------------------------------------------------- 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /test/stdnet/buffer.cpp: -------------------------------------------------------------------------------- 1 | // test/stdnet/buffer.cpp -*-C++-*- 2 | // ---------------------------------------------------------------------------- 3 | // 4 | // Copyright (c) 2024 Dietmar Kuehl http://www.dietmar-kuehl.de 5 | // 6 | // Licensed under the Apache License Version 2.0 with LLVM Exceptions 7 | // (the "License"); you may not use this file except in compliance with 8 | // the License. You may obtain a copy of the License at 9 | // 10 | // https://llvm.org/LICENSE.txt 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // ---------------------------------------------------------------------------- 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | // ---------------------------------------------------------------------------- 25 | 26 | TEST_CASE("stream_errc", "[buffer.synop]") 27 | { 28 | REQUIRE(::std::same_as<::stdnet::stream_errc, decltype(::stdnet::stream_errc::eof)>); 29 | REQUIRE(::std::same_as<::stdnet::stream_errc, decltype(::stdnet::stream_errc::not_found)>); 30 | REQUIRE(::stdnet::stream_errc::eof != ::stdnet::stream_errc::not_found); 31 | } 32 | 33 | TEST_CASE("stream_category", "[buffer.synop]") 34 | { 35 | auto&& cat = ::stdnet::stream_category(); 36 | REQUIRE(::std::same_as<::std::error_category const&, decltype((cat))>); 37 | REQUIRE(noexcept(::stdnet::stream_category())); 38 | } 39 | 40 | TEST_CASE("make_error_code", "[buffer.synop]") 41 | { 42 | REQUIRE(::std::same_as<::std::error_code, 43 | decltype(::stdnet::make_error_code(::stdnet::stream_errc::eof))>); 44 | REQUIRE(noexcept(::stdnet::make_error_code(::stdnet::stream_errc::eof))); 45 | } 46 | 47 | TEST_CASE("make_error_condition", "[buffer.synop]") 48 | { 49 | REQUIRE(::std::same_as<::std::error_condition, 50 | decltype(::stdnet::make_error_condition(::stdnet::stream_errc::eof))>); 51 | REQUIRE(noexcept(::stdnet::make_error_condition(::stdnet::stream_errc::eof))); 52 | } 53 | 54 | TEST_CASE("buffer types", "[buffer.synop]") 55 | { 56 | using mutable_buffer = ::stdnet::mutable_buffer; 57 | using const_buffer = ::stdnet::mutable_buffer; 58 | } 59 | 60 | TEST_CASE("buffer traits", "[buffer.synop]") 61 | { 62 | using is_mutable_buffer_sequence = ::stdnet::is_mutable_buffer_sequence; 63 | using is_const_buffer_sequence = ::stdnet::is_const_buffer_sequence; 64 | using is_dynamic_buffer = ::stdnet::is_dynamic_buffer; 65 | } --------------------------------------------------------------------------------