Toolbox snapshot
The Reactive C++ Toolbox
Loading...
Searching...
No Matches
Endpoint.hpp
Go to the documentation of this file.
1// The Reactive C++ Toolbox.
2// Copyright (C) 2013-2019 Swirly Cloud Limited
3// Copyright (C) 2021 Reactive Markets Limited
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17#ifndef TOOLBOX_NET_ENDPOINT_HPP
18#define TOOLBOX_NET_ENDPOINT_HPP
19
24
25#include <boost/asio/generic/basic_endpoint.hpp>
26#include <boost/asio/ip/basic_endpoint.hpp>
27#include <boost/asio/local/basic_endpoint.hpp>
28
29namespace toolbox {
30inline namespace net {
31
32template <typename ProtocolT>
33using BasicEndpoint = boost::asio::generic::basic_endpoint<ProtocolT>;
34
37
38template <typename ProtocolT>
39using IpEndpoint = boost::asio::ip::basic_endpoint<ProtocolT>;
40
43
44template <typename ProtocolT>
45using UnixEndpoint = boost::asio::local::basic_endpoint<ProtocolT>;
46
49
50TOOLBOX_API AddrInfoPtr parse_endpoint(std::string_view uri, int type);
51
52inline DgramEndpoint parse_dgram_endpoint(std::string_view uri)
53{
54 const auto ai = parse_endpoint(uri, SOCK_DGRAM);
55 return {ai->ai_addr, ai->ai_addrlen, ai->ai_protocol};
56}
57
59{
60 const auto ai = parse_endpoint(uri, SOCK_STREAM);
61 return {ai->ai_addr, ai->ai_addrlen, ai->ai_protocol};
62}
63
64TOOLBOX_API std::istream& operator>>(std::istream& is, DgramEndpoint& ep);
65TOOLBOX_API std::istream& operator>>(std::istream& is, StreamEndpoint& ep);
66
67namespace detail {
68template <typename StreamT, typename T>
69 requires Streamable<StreamT>
71{
72 constexpr const char* scheme = "unix://";
73 // abstract unix socket's path starts with '\0' and not null-terminated
74 const auto* path = reinterpret_cast<const sockaddr_un*>(ep.data())->sun_path;
75 if (path[0] == '\0') {
76 size_t size = ep.size() - sizeof(std::declval<sockaddr_un>().sun_family) - 1;
77 os << scheme << '|' << std::string_view{path + 1, size};
78 return os;
79 }
80 os << scheme << *ep.data();
81 return os;
82}
83} // detail namespace
84
85// Unfortunately, DgramEndpoint has an implicit converting constructor -- it accepts any type.
86// Therefore, this overload becomes a valid candidate whenever a value of any type is being streamed
87// into a stream -- resulting in ambiguous overload errors. DgramEndpoint is defined in boost::asio
88// so the impl can't be modified. Therefore, a templated type is used to constrain this overload to
89// DgramEndpoint only.
90template <typename StreamT, class T>
91 requires Streamable<StreamT> && std::same_as<T, DgramEndpoint>
93{
94 const char* scheme = "";
95 const auto p = ep.protocol();
96 if (p.family() == AF_INET) {
97 if (p.protocol() == IPPROTO_UDP) {
98 scheme = "udp4://";
99 } else {
100 scheme = "ip4://";
101 }
102 } else if (p.family() == AF_INET6) {
103 if (p.protocol() == IPPROTO_UDP) {
104 scheme = "udp6://";
105 } else {
106 scheme = "ip6://";
107 }
108 } else if (p.family() == AF_UNIX) {
110 }
111 os << scheme << *ep.data();
112 return os;
113}
114
115// Unfortunately, StreamEndpoint has an implicit converting constructor -- it accepts any type.
116// Therefore, this overload becomes a valid candidate whenever a value of any type is being streamed
117// into a stream -- resulting in ambiguous overload errors. StreamEndpoint is defined in boost::asio
118// so the impl can't be modified. Therefore, a templated type is used to constrain this overload to
119// StreamEndpoint only.
120template <typename StreamT, class T>
121 requires Streamable<StreamT> && std::same_as<T, StreamEndpoint>
123{
124 const char* scheme = "";
125 const auto p = ep.protocol();
126 if (p.family() == AF_INET) {
127 if (p.protocol() == IPPROTO_TCP) {
128 scheme = "tcp4://";
129 } else {
130 scheme = "ip4://";
131 }
132 } else if (p.family() == AF_INET6) {
133 if (p.protocol() == IPPROTO_TCP) {
134 scheme = "tcp6://";
135 } else {
136 scheme = "ip6://";
137 }
138 } else if (p.family() == AF_UNIX) {
140 }
141 os << scheme << *ep.data();
142 return os;
143}
144
145} // namespace net
146inline namespace util {
147
148template <>
150 static auto from_string(std::string_view sv) { return parse_dgram_endpoint(std::string{sv}); }
151 static auto from_string(const std::string& s) { return parse_dgram_endpoint(s); }
152};
153template <>
155 static auto from_string(std::string_view sv) { return parse_stream_endpoint(std::string{sv}); }
156 static auto from_string(const std::string& s) { return parse_stream_endpoint(s); }
157};
158
159} // namespace util
160} // namespace toolbox
161
162template <typename StreamT>
164StreamT& operator<<(StreamT& os, const sockaddr_in& sa)
165{
166 // biggest possible str: 255.255.255.255:
167 char buf[16];
168 char* p = buf;
169
170 auto write_u8 = [&p](std::uint8_t v) {
171 char rd = '0' + (v % 10u);
172 if (v >= 100u) {
173 char ld = '0' + ((v / 100u) % 10u);
174 char md = '0' + ((v / 10u) % 10u);
175 *p++ = ld;
176 *p++ = md;
177 *p++ = rd;
178 } else if (v >= 10u) {
179 char ld = '0' + ((v / 10u) % 10u);
180 *p++ = ld;
181 *p++ = rd;
182 } else {
183 *p++ = rd;
184 }
185 };
186
187 // ip address in sockaddr_in is in network order (i.e. big endian)
188 uint32_t addr = sa.sin_addr.s_addr;
189 auto* ipv4 = std::bit_cast<unsigned char*>(&addr);
190
191 write_u8(ipv4[0]);
192 *p++ = '.';
193 write_u8(ipv4[1]);
194 *p++ = '.';
195 write_u8(ipv4[2]);
196 *p++ = '.';
197 write_u8(ipv4[3]);
198 *p++ = ':';
199
200 os << std::string_view(buf, p) << ntohs(sa.sin_port);
201 return os;
202}
203
204template <typename StreamT>
206StreamT& operator<<(StreamT& os, const sockaddr_in6& sa)
207{
208 char buf[INET6_ADDRSTRLEN];
209 inet_ntop(AF_INET6, &toolbox::remove_const(sa).sin6_addr, buf, sizeof(buf));
210 os << '[' << buf << "]:" << ntohs(sa.sin6_port);
211 return os;
212}
213
214template <typename StreamT>
216StreamT& operator<<(StreamT& os, const sockaddr_un& sa)
217{
218 os << sa.sun_path;
219 return os;
220}
221
222template <typename StreamT>
224StreamT& operator<<(StreamT& os, const sockaddr& sa)
225{
226 if (sa.sa_family == AF_INET) {
227 os << reinterpret_cast<const sockaddr_in&>(sa);
228 } else if (sa.sa_family == AF_INET6) {
229 os << reinterpret_cast<const sockaddr_in6&>(sa);
230 } else if (sa.sa_family == AF_UNIX) {
231 os << reinterpret_cast<const sockaddr_un&>(sa);
232 } else {
233 os << "<sockaddr>";
234 }
235 return os;
236}
237
238template <typename StreamT>
240StreamT& operator<<(StreamT& os, const addrinfo& ai)
241{
242 const char* scheme = "";
243 if (ai.ai_family == AF_INET) {
244 if (ai.ai_protocol == IPPROTO_TCP) {
245 scheme = "tcp4://";
246 } else if (ai.ai_protocol == IPPROTO_UDP) {
247 scheme = "udp4://";
248 } else {
249 scheme = "ip4://";
250 }
251 } else if (ai.ai_family == AF_INET6) {
252 if (ai.ai_protocol == IPPROTO_TCP) {
253 scheme = "tcp6://";
254 } else if (ai.ai_protocol == IPPROTO_UDP) {
255 scheme = "udp6://";
256 } else {
257 scheme = "ip6://";
258 }
259 } else if (ai.ai_family == AF_UNIX) {
260 scheme = "unix://";
261 }
262 os << scheme << *ai.ai_addr;
263 return os;
264}
265
266#endif // TOOLBOX_NET_ENDPOINT_HPP
#define TOOLBOX_API
Definition Config.h:39
StreamT & operator<<(StreamT &os, const sockaddr_in &sa)
Definition Endpoint.hpp:164
ostream & operator<<(ostream &os, const pair< T, U > &p)
Definition Parser.ut.cpp:29
StreamT & print_unix_endpoint(StreamT &os, const T &ep)
Definition Endpoint.hpp:70
BasicEndpoint< DgramProtocol > DgramEndpoint
Definition Endpoint.hpp:35
istream & operator>>(istream &is, DgramEndpoint &ep)
Definition Endpoint.cpp:100
UnixEndpoint< UnixDgramProtocol > UnixDgramEndpoint
Definition Endpoint.hpp:47
AddrInfoPtr parse_endpoint(string_view uri, int type)
Definition Endpoint.cpp:59
std::unique_ptr< addrinfo, void(*)(addrinfo *)> AddrInfoPtr
Definition Socket.hpp:30
BasicEndpoint< StreamProtocol > StreamEndpoint
Definition Endpoint.hpp:36
boost::asio::local::basic_endpoint< ProtocolT > UnixEndpoint
Definition Endpoint.hpp:45
UnixEndpoint< UnixStreamProtocol > UnixStreamEndpoint
Definition Endpoint.hpp:48
boost::asio::ip::basic_endpoint< ProtocolT > IpEndpoint
Definition Endpoint.hpp:39
DgramEndpoint parse_dgram_endpoint(std::string_view uri)
Definition Endpoint.hpp:52
IpEndpoint< UdpProtocol > UdpEndpoint
Definition Endpoint.hpp:41
IpEndpoint< TcpProtocol > TcpEndpoint
Definition Endpoint.hpp:42
boost::asio::generic::basic_endpoint< ProtocolT > BasicEndpoint
Definition Endpoint.hpp:33
StreamEndpoint parse_stream_endpoint(std::string_view uri)
Definition Endpoint.hpp:58
auto & remove_const(const ValueT &ref)
Definition Utility.hpp:35
constexpr std::size_t size(const detail::Struct< detail::Member< TagsT, ValuesT >... > &s)
Definition Struct.hpp:98
std::string_view sv
Definition Tokeniser.hpp:26
constexpr auto bind() noexcept
Definition Slot.hpp:92
static auto from_string(const std::string &s)
Definition Endpoint.hpp:151
static auto from_string(std::string_view sv)
Definition Endpoint.hpp:150
static auto from_string(const std::string &s)
Definition Endpoint.hpp:156
static auto from_string(std::string_view sv)
Definition Endpoint.hpp:155