LIBWIRE
Next-generation C++17 networking library.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
socket.hpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2018 Max Mazurov (fox.cpp) <fox.cpp [at] disroot [dot] org>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 
23 #pragma once
24 
25 #include <cstdint>
26 #include <tuple>
27 #include <system_error>
28 #include <vector>
29 #include <libwire/error.hpp>
31 
32 /*
33  * If you had to open this file to find answer for your question - we are so
34  * sorry. Please open issue with your question so we can update documentation
35  * to answer it.
36  */
37 
44 namespace libwire::tcp {
59  class socket {
60  public:
69  socket() noexcept = default;
70 
77  socket(internal_::socket&& i) noexcept;
78 
79  socket(const socket&) = delete;
80  socket(socket&&) noexcept = default;
81 
82  socket& operator=(const socket&) = delete;
83  socket& operator=(socket&&) noexcept = default;
84 
88  ~socket();
89 
95  internal_::socket::native_handle_t native_handle() const noexcept;
96 
106  bool is_open() const;
107 
146 
159  template<typename Option>
160  auto option(const Option& /* tag */) const noexcept {
161  return Option::get(*this);
162  }
163 
181  template<typename Option, typename... Args>
182  void set_option(const Option& /* tag */, Args&&... args) noexcept {
183  Option::set(*this, std::forward<Args>(args)...);
184  }
186 
205 
215  endpoint local_endpoint() const noexcept;
216 
222  endpoint remote_endpoint() const noexcept;
223 
225 
232  void connect(endpoint target, std::error_code& ec) noexcept;
233 
247  void shutdown(bool read = true, bool write = true) noexcept;
248 
257  void close() noexcept;
258 
265 
281  template<typename Buffer = std::vector<uint8_t>>
282  Buffer& read(size_t bytes_count, Buffer&, std::error_code&) noexcept;
283 
288  template<typename Buffer = std::vector<uint8_t>>
289  Buffer read(size_t bytes_count, std::error_code&) noexcept;
290 
305  template<typename Buffer = std::vector<uint8_t>>
306  Buffer& read_until(uint8_t delimiter, Buffer& buf, std::error_code&, size_t max_size = 0) noexcept;
307 
312  template<typename Buffer = std::vector<uint8_t>>
313  Buffer read_until(uint8_t delimiter, std::error_code&, size_t max_size = 0) noexcept;
314 
329  template<typename Buffer = std::vector<uint8_t>>
330  size_t write(const Buffer&, std::error_code&) noexcept;
331 
332 #ifdef __cpp_exceptions
333 
337  void connect(endpoint target);
338 
343  template<typename Buffer = std::vector<uint8_t>>
344  Buffer& read(size_t bytes_count, Buffer&);
345 
346  template<typename Buffer = std::vector<uint8_t>>
347  Buffer read(size_t bytes_count);
348 
353  template<typename Buffer = std::vector<uint8_t>>
354  size_t write(const Buffer&);
355 
360  template<typename Buffer = std::vector<uint8_t>>
361  Buffer& read_until(uint8_t delimiter, Buffer& buf, size_t max_size = 0);
362 
367  template<typename Buffer = std::vector<uint8_t>>
368  Buffer read_until(uint8_t delimiter, size_t max_size = 0);
369 #endif // ifdef __cpp_exceptions
370 
372  private:
373  internal_::socket implementation;
374 
375  // Used as internal socket state tracker.
376  bool open = false;
377  };
378 
379  template<typename Buffer>
380  Buffer& socket::read(size_t bytes_count, Buffer& output, std::error_code& ec) noexcept {
381  static_assert(sizeof(std::remove_pointer_t<decltype(output.data())>) == sizeof(uint8_t),
382  "socket::read can't be used with container with non-byte elements");
383 
384  output.resize(bytes_count);
385  size_t total_received = 0;
386  // FIXME: Needs to be improved for non-blocking I/O.
387  // Read exactly bytes_count bytes, retrying when needed.
388  while (total_received < bytes_count) {
389  size_t bytes_received =
390  implementation.read(output.data() + total_received, bytes_count - total_received, ec);
391  if (ec) {
392  if (ec != error::interrupted) {
393  return output;
394  }
395  };
396  total_received += bytes_received;
397  }
398  open = (ec != error::generic::disconnected);
399 
400  return output;
401  }
402 
403  template<typename Buffer>
404  Buffer socket::read(size_t bytes_count, std::error_code& ec) noexcept {
405  Buffer buffer{};
406  return read(bytes_count, buffer, ec);
407  }
408 
409  extern template std::vector<uint8_t> socket::read(size_t, std::error_code&);
410  extern template std::string socket::read(size_t, std::error_code&);
411 
412  extern template std::vector<uint8_t>& socket::read(size_t, std::vector<uint8_t>&, std::error_code&);
413  extern template std::string& socket::read(size_t, std::string&, std::error_code&);
414 
415  template<typename Buffer>
416  size_t socket::write(const Buffer& input, std::error_code& ec) noexcept {
417  static_assert(sizeof(std::remove_pointer_t<decltype(input.data())>) == sizeof(uint8_t),
418  "socket::write can't be used with container with non-byte elements");
419 
420  auto res = implementation.write(input.data(), input.size(), ec);
421  open = (ec != error::generic::disconnected);
422  return res;
423  }
424 
425  extern template size_t socket::write(const std::vector<uint8_t>&, std::error_code&);
426  extern template size_t socket::write(const std::string&, std::error_code&);
427 
428  template<typename Buffer>
429  Buffer& socket::read_until(uint8_t delimiter, Buffer& buf, std::error_code& ec, size_t max_size) noexcept {
430  uint8_t byte;
431  memory_view view{&byte, 1};
432  buf.clear();
433  while (read(1, view, ec), !ec) {
434  if (byte == delimiter) break;
435  if (max_size != 0 && buf.size() == max_size) break;
436  buf.push_back(byte);
437  }
438  return buf;
439  }
440 
441  template<typename Buffer>
442  Buffer socket::read_until(uint8_t delimiter, std::error_code& ec, size_t max_size) noexcept {
443  Buffer buffer{};
444  read_until(delimiter, buffer, ec, max_size);
445  return buffer;
446  }
447 
448  extern template std::vector<uint8_t> socket::read_until(uint8_t, std::error_code&, size_t);
449  extern template std::string socket::read_until(uint8_t, std::error_code&, size_t);
450 
451  extern template std::vector<uint8_t>& socket::read_until(uint8_t, std::vector<uint8_t>&, std::error_code&, size_t);
452  extern template std::string& socket::read_until(uint8_t, std::string&, std::error_code&, size_t);
453 
454 #ifdef __cpp_exceptions
455  template<typename Buffer>
456  Buffer& socket::read(size_t bytes_count, Buffer& output) {
457  std::error_code ec;
458  auto res = read<Buffer>(bytes_count, output, ec);
459  if (ec) throw std::system_error(ec);
460  return output;
461  }
462 
463  template<typename Buffer>
464  Buffer socket::read(size_t bytes_count) {
465  Buffer buffer{};
466  return read(bytes_count, buffer);
467  }
468 
469  extern template std::vector<uint8_t>& socket::read(size_t, std::vector<uint8_t>&);
470  extern template std::string& socket::read(size_t, std::string&);
471 
472  extern template std::vector<uint8_t> socket::read(size_t);
473  extern template std::string socket::read(size_t);
474 
475  template<typename Buffer>
476  size_t socket::write(const Buffer& input) {
477  std::error_code ec;
478  size_t res = write<Buffer>(input, ec);
479  if (ec) throw std::system_error(ec);
480  return res;
481  }
482 
483  extern template size_t socket::write(const std::vector<uint8_t>&);
484  extern template size_t socket::write(const std::string&);
485 
486  template<typename Buffer>
487  Buffer& socket::read_until(uint8_t delimiter, Buffer& buf, size_t max_size) {
488  std::error_code ec;
489  read_until<Buffer>(delimiter, buf, ec, max_size);
490  if (ec) throw std::system_error(ec);
491  return buf;
492  }
493 
494  template<typename Buffer>
495  Buffer socket::read_until(uint8_t delimiter, size_t max_size) {
496  std::error_code ec;
497  auto res = read_until<Buffer>(delimiter, ec, max_size);
498  if (ec) throw std::system_error(ec);
499  return res;
500  }
501 
502  extern template std::vector<uint8_t>& socket::read_until(uint8_t, std::vector<uint8_t>&, size_t);
503  extern template std::string& socket::read_until(uint8_t, std::string&, size_t);
504 
505  extern template std::vector<uint8_t> socket::read_until(uint8_t, size_t);
506  extern template std::string socket::read_until(uint8_t, size_t);
507 #endif // ifdef __cpp_exceptions
508 } // namespace libwire::tcp
internal_::socket::native_handle_t native_handle() const noexcept
Get native handle/descriptor for socket.
size_t write(const Buffer &, std::error_code &) noexcept
Write contents of buffer to socket.
Definition: socket.hpp:416
socket() noexcept=default
Create new socket object.
auto option(const Option &) const noexcept
Query socket option value specified by type tag Option.
Definition: socket.hpp:160
~socket()
Shutdown and then close socket.
Non-owning STL-like wrapper for raw memory.
Definition: memory_view.hpp:58
bool is_open() const
Check whether underlying socket is open.
socket & operator=(const socket &)=delete
Buffer & read(size_t bytes_count, Buffer &, std::error_code &) noexcept
Read up to bytes_count bytes from socket into buffer passed by reference.
Definition: socket.hpp:380
endpoint remote_endpoint() const noexcept
Get address and port of remote end of connection.
System call interrupted by signal (POSIX-specific).
Definition: error.hpp:160
endpoint local_endpoint() const noexcept
Get address and port of local end of connection.
void set_option(const Option &, Args &&...args) noexcept
Set socket option value specified by type tag Option to value value.
Definition: socket.hpp:182
Buffer & read_until(uint8_t delimiter, Buffer &buf, std::error_code &, size_t max_size=0) noexcept
Read from socket until until gives byte is found or max_size bytes read.
Definition: socket.hpp:429
void close() noexcept
Close and destroy underlying socket.
This header defines set of enumerations for platform-independent error code handling.
Descriptor wrapper for TCP socket.
Definition: socket.hpp:59
void clear() noexcept
Same as resize (0).
Disconnected, either forcibly or gracefully.
void connect(endpoint target, std::error_code &ec) noexcept
Initialize underlying socket and connect to remote endpoint.
void shutdown(bool read=true, bool write=true) noexcept
Shutdown reading/writing part of full-duplex connection (or both if read and write is true)...