├── .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