[wpinet] Move network portions of wpiutil into new wpinet library (#4077)

This commit is contained in:
Peter Johnson
2022-05-07 10:54:14 -07:00
committed by GitHub
parent b33715db15
commit d673ead481
327 changed files with 1783 additions and 1179 deletions

View File

@@ -1,55 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <memory>
#include <string>
#include <string_view>
#include "wpi/Signal.h"
namespace wpi {
class Logger;
namespace uv {
class Loop;
class Tcp;
class Timer;
} // namespace uv
class DsClient : public std::enable_shared_from_this<DsClient> {
struct private_init {};
public:
static std::shared_ptr<DsClient> Create(wpi::uv::Loop& loop,
wpi::Logger& logger) {
return std::make_shared<DsClient>(loop, logger, private_init{});
}
DsClient(wpi::uv::Loop& loop, wpi::Logger& logger, const private_init&);
~DsClient();
DsClient(const DsClient&) = delete;
DsClient& operator=(const DsClient&) = delete;
void Close();
sig::Signal<std::string_view> setIp;
sig::Signal<> clearIp;
private:
void Connect();
void HandleIncoming(std::string_view in);
void ParseJson();
wpi::Logger& m_logger;
std::shared_ptr<wpi::uv::Tcp> m_tcp;
std::shared_ptr<wpi::uv::Timer> m_timer;
std::string m_json;
};
} // namespace wpi

View File

@@ -1,62 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_EVENTLOOPRUNNER_H_
#define WPIUTIL_WPI_EVENTLOOPRUNNER_H_
#include <functional>
#include <memory>
#include "wpi/SafeThread.h"
#include "wpi/uv/Loop.h"
namespace wpi {
/**
* Executes an event loop on a separate thread.
*/
class EventLoopRunner {
public:
using LoopFunc = std::function<void(uv::Loop&)>;
EventLoopRunner();
virtual ~EventLoopRunner();
/**
* Stop the loop. Once the loop is stopped it cannot be restarted.
* This function does not return until the loop has exited.
*/
void Stop();
/**
* Run a function asynchronously (once) on the loop.
* This is safe to call from any thread, but is NOT safe to call from the
* provided function (it will deadlock).
* @param func function to execute on the loop
*/
void ExecAsync(LoopFunc func);
/**
* Run a function synchronously (once) on the loop.
* This is safe to call from any thread, but is NOT safe to call from the
* provided function (it will deadlock).
* This does not return until the function finishes executing.
* @param func function to execute on the loop
*/
void ExecSync(LoopFunc func);
/**
* Get the loop. If the loop thread is not running, returns nullptr.
* @return The loop
*/
std::shared_ptr<uv::Loop> GetLoop();
private:
class Thread;
SafeThreadOwner<Thread> m_owner;
};
} // namespace wpi
#endif // WPIUTIL_WPI_EVENTLOOPRUNNER_H_

View File

@@ -1,227 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HTTPPARSER_H_
#define WPIUTIL_WPI_HTTPPARSER_H_
#include <stdint.h>
#include <string_view>
#include "wpi/Signal.h"
#include "wpi/SmallString.h"
#include "wpi/http_parser.h"
namespace wpi {
/**
* HTTP protocol parser. Performs incremental parsing with callbacks for each
* part of the HTTP protocol. As this is incremental, it's suitable for use
* with event based frameworks that provide arbitrary chunks of data.
*/
class HttpParser {
public:
enum Type {
kRequest = HTTP_REQUEST,
kResponse = HTTP_RESPONSE,
kBoth = HTTP_BOTH
};
/**
* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
*/
static uint32_t GetParserVersion();
/**
* Constructor.
* @param type Type of parser (request or response or both)
*/
explicit HttpParser(Type type);
/**
* Reset the parser to initial state.
* This allows reusing the same parser object from request to request.
* @param type Type of parser (request or response or both)
*/
void Reset(Type type);
/**
* Set the maximum accepted length for URLs, field names, and field values.
* The default is 1024.
* @param len maximum length
*/
void SetMaxLength(size_t len) { m_maxLength = len; }
/**
* Executes the parser. An empty input is treated as EOF.
* @param in input data
* @return Trailing input data after the parse.
*/
std::string_view Execute(std::string_view in) {
in.remove_prefix(
http_parser_execute(&m_parser, &m_settings, in.data(), in.size()));
return in;
}
/**
* Get HTTP major version.
*/
unsigned int GetMajor() const { return m_parser.http_major; }
/**
* Get HTTP minor version.
*/
unsigned int GetMinor() const { return m_parser.http_minor; }
/**
* Get HTTP status code. Valid only on responses. Valid in and after
* the OnStatus() callback has been called.
*/
unsigned int GetStatusCode() const { return m_parser.status_code; }
/**
* Get HTTP method. Valid only on requests.
*/
http_method GetMethod() const {
return static_cast<http_method>(m_parser.method);
}
/**
* Determine if an error occurred.
* @return False if no error.
*/
bool HasError() const { return m_parser.http_errno != HPE_OK; }
/**
* Get error number.
*/
http_errno GetError() const {
return static_cast<http_errno>(m_parser.http_errno);
}
/**
* Abort the parse. Call this from a callback handler to indicate an error.
* This will result in GetError() returning one of the callback-related
* errors (e.g. HPE_CB_message_begin).
*/
void Abort() { m_aborted = true; }
/**
* Determine if an upgrade header was present and the parser has exited
* because of that. Should be checked when Execute() returns in addition to
* checking GetError().
* @return True if upgrade header, false otherwise.
*/
bool IsUpgrade() const { return m_parser.upgrade; }
/**
* If this returns false in the headersComplete or messageComplete
* callback, then this should be the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
bool ShouldKeepAlive() const { return http_should_keep_alive(&m_parser); }
/**
* Pause the parser.
* @param paused True to pause, false to unpause.
*/
void Pause(bool paused) { http_parser_pause(&m_parser, paused); }
/**
* Checks if this is the final chunk of the body.
*/
bool IsBodyFinal() const { return http_body_is_final(&m_parser); }
/**
* Get URL. Valid in and after the url callback has been called.
*/
std::string_view GetUrl() const { return m_urlBuf.str(); }
/**
* Message begin callback.
*/
sig::Signal<> messageBegin;
/**
* URL callback.
*
* The parameter to the callback is the complete URL string.
*/
sig::Signal<std::string_view> url;
/**
* Status callback.
*
* The parameter to the callback is the complete status string.
* GetStatusCode() can be used to get the numeric status code.
*/
sig::Signal<std::string_view> status;
/**
* Header field callback.
*
* The parameters to the callback are the field name and field value.
*/
sig::Signal<std::string_view, std::string_view> header;
/**
* Headers complete callback.
*
* The parameter to the callback is whether the connection should be kept
* alive. If this is false, then this should be the last message on the
* connection. If you are the server, respond with the "Connection: close"
* header. If you are the client, close the connection.
*/
sig::Signal<bool> headersComplete;
/**
* Body data callback.
*
* The parameters to the callback is the data chunk and whether this is the
* final chunk of data in the message. Note this callback will be called
* multiple times arbitrarily (e.g. it's possible that it may be called with
* just a few characters at a time).
*/
sig::Signal<std::string_view, bool> body;
/**
* Headers complete callback.
*
* The parameter to the callback is whether the connection should be kept
* alive. If this is false, then this should be the last message on the
* connection. If you are the server, respond with the "Connection: close"
* header. If you are the client, close the connection.
*/
sig::Signal<bool> messageComplete;
/**
* Chunk header callback.
*
* The parameter to the callback is the chunk size.
*/
sig::Signal<uint64_t> chunkHeader;
/**
* Chunk complete callback.
*/
sig::Signal<> chunkComplete;
private:
http_parser m_parser;
http_parser_settings m_settings;
size_t m_maxLength = 1024;
enum { kStart, kUrl, kStatus, kField, kValue } m_state = kStart;
SmallString<128> m_urlBuf;
SmallString<32> m_fieldBuf;
SmallString<128> m_valueBuf;
bool m_aborted = false;
};
} // namespace wpi
#endif // WPIUTIL_WPI_HTTPPARSER_H_

View File

@@ -1,152 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HTTPSERVERCONNECTION_H_
#define WPIUTIL_WPI_HTTPSERVERCONNECTION_H_
#include <memory>
#include <string_view>
#include "wpi/HttpParser.h"
#include "wpi/span.h"
#include "wpi/uv/Stream.h"
namespace wpi {
class raw_ostream;
class HttpServerConnection {
public:
explicit HttpServerConnection(std::shared_ptr<uv::Stream> stream);
virtual ~HttpServerConnection() = default;
protected:
/**
* Process an incoming HTTP request. This is called after the incoming
* message completes (e.g. from the HttpParser::messageComplete callback).
*
* The implementation should read request details from m_request and call the
* appropriate Send() functions to send a response back to the client.
*/
virtual void ProcessRequest() = 0;
/**
* Build common response headers.
*
* Called by SendHeader() to send headers common to every response.
* Each line must be terminated with "\r\n".
*
* The default implementation sends the following:
* "Server: WebServer/1.0\r\n"
* "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, "
* "post-check=0, max-age=0\r\n"
* "Pragma: no-cache\r\n"
* "Expires: Mon, 3 Jan 2000 12:34:56 GMT\r\n"
*
* These parameters should ensure the browser does not cache the response.
* A browser should connect for each file and not serve files from its cache.
*
* @param os response stream
*/
virtual void BuildCommonHeaders(raw_ostream& os);
/**
* Build HTTP response header, along with other header information like
* mimetype. Calls BuildCommonHeaders().
*
* @param os response stream
* @param code HTTP response code (e.g. 200)
* @param codeText HTTP response code text (e.g. "OK")
* @param contentType MIME content type (e.g. "text/plain")
* @param contentLength Length of content. If 0 is provided, m_keepAlive will
* be set to false.
* @param extra Extra HTTP headers to send, including final "\r\n"
*/
virtual void BuildHeader(raw_ostream& os, int code, std::string_view codeText,
std::string_view contentType, uint64_t contentLength,
std::string_view extra = {});
/**
* Send data to client.
*
* This is a convenience wrapper around m_stream.Write() to provide
* auto-close functionality.
*
* @param bufs Buffers to write. Deallocate() will be called on each
* buffer after the write completes. If different behavior
* is desired, call m_stream.Write() directly instead.
* @param closeAfter close the connection after the write completes
*/
void SendData(span<const uv::Buffer> bufs, bool closeAfter = false);
/**
* Send HTTP response, along with other header information like mimetype.
* Calls BuildHeader().
*
* @param code HTTP response code (e.g. 200)
* @param codeText HTTP response code text (e.g. "OK")
* @param contentType MIME content type (e.g. "text/plain")
* @param content Response message content
* @param extraHeader Extra HTTP headers to send, including final "\r\n"
*/
virtual void SendResponse(int code, std::string_view codeText,
std::string_view contentType,
std::string_view content,
std::string_view extraHeader = {});
/**
* Send HTTP response from static data, along with other header information
* like mimetype. Calls BuildHeader(). Supports gzip pre-compressed data
* (it will decompress if the client does not accept gzip encoded data).
* Unlike SendResponse(), content is not copied and its contents must remain
* valid for an unspecified lifetime.
*
* @param code HTTP response code (e.g. 200)
* @param codeText HTTP response code text (e.g. "OK")
* @param contentType MIME content type (e.g. "text/plain")
* @param content Response message content
* @param gzipped True if content is gzip compressed
* @param extraHeader Extra HTTP headers to send, including final "\r\n"
*/
virtual void SendStaticResponse(int code, std::string_view codeText,
std::string_view contentType,
std::string_view content, bool gzipped,
std::string_view extraHeader = {});
/**
* Send error header and message.
* This provides standard code responses for 400, 401, 403, 404, 500, and 503.
* Other codes will be reported as 501. For arbitrary code handling, use
* SendResponse() instead.
*
* @param code HTTP error code (e.g. 404)
* @param message Additional message text
*/
virtual void SendError(int code, std::string_view message = {});
/** The HTTP request. */
HttpParser m_request{HttpParser::kRequest};
/** Whether the connection should be kept alive. */
bool m_keepAlive = false;
/** If gzip is an acceptable encoding for responses. */
bool m_acceptGzip = false;
/** The underlying stream for the connection. */
uv::Stream& m_stream;
/** The header reader connection. */
sig::ScopedConnection m_dataConn;
/** The end stream connection. */
sig::ScopedConnection m_endConn;
/** The message complete connection. */
sig::Connection m_messageCompleteConn;
};
} // namespace wpi
#endif // WPIUTIL_WPI_HTTPSERVERCONNECTION_H_

View File

@@ -1,422 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HTTPUTIL_H_
#define WPIUTIL_WPI_HTTPUTIL_H_
#include <initializer_list>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "wpi/NetworkStream.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
#include "wpi/StringMap.h"
#include "wpi/raw_istream.h"
#include "wpi/raw_socket_istream.h"
#include "wpi/raw_socket_ostream.h"
#include "wpi/span.h"
namespace wpi {
// Unescape a %xx-encoded URI.
// @param buf Buffer for output
// @param error Set to true if an error occurred
// @return Escaped string
std::string_view UnescapeURI(std::string_view str, SmallVectorImpl<char>& buf,
bool* error);
// Escape a string with %xx-encoding.
// @param buf Buffer for output
// @param spacePlus If true, encodes spaces to '+' rather than "%20"
// @return Escaped string
std::string_view EscapeURI(std::string_view str, SmallVectorImpl<char>& buf,
bool spacePlus = true);
// Parse a set of HTTP headers. Saves just the Content-Type and Content-Length
// fields.
// @param is Input stream
// @param contentType If not null, Content-Type contents are saved here.
// @param contentLength If not null, Content-Length contents are saved here.
// @return False if error occurred in input stream
bool ParseHttpHeaders(raw_istream& is, SmallVectorImpl<char>* contentType,
SmallVectorImpl<char>* contentLength);
// Look for a MIME multi-part boundary. On return, the input stream will
// be located at the character following the boundary (usually "\r\n").
// @param is Input stream
// @param boundary Boundary string to scan for (not including "--" prefix)
// @param saveBuf If not null, all scanned characters up to but not including
// the boundary are saved to this string
// @return False if error occurred on input stream, true if boundary found.
bool FindMultipartBoundary(wpi::raw_istream& is, std::string_view boundary,
std::string* saveBuf);
/**
* Map for looking up elements of the query portion of a URI. Does not
* handle multiple elements with the same name. This is a reference type;
* it does not make a copy of the query string, so it is important that the
* query string has a lifetime at least as long as this object.
*/
class HttpQueryMap {
public:
/**
* Constructs an empty map (with no entries).
*/
HttpQueryMap() = default;
/**
* Constructs from an escaped query string. Note: does not make a copy of
* the query string, so it must not be a temporary.
*
* @param query query string
*/
explicit HttpQueryMap(std::string_view query);
/**
* Gets an element of the query string. Both the name and the returned
* value are unescaped strings.
*
* @param name name (unescaped)
* @param buf result buffer for value
* @return Optional unescaped value. Returns an empty optional if the
* name is not present in the query map.
*/
std::optional<std::string_view> Get(std::string_view name,
SmallVectorImpl<char>& buf) const;
private:
StringMap<std::string_view> m_elems;
};
class HttpPathRef;
/**
* Class for HTTP path matching. A root path is represented as a single
* empty element, otherwise the path consists of each non-empty element
* between the '/' characters:
* - "" -> []
* - "/" -> [""]
* - "/foo" -> ["foo"]
* - "/foo/bar" -> ["foo", "bar"]
* - "/foo//bar/" -> ["foo", "bar"]
*
* All path elements are unescaped.
*/
class HttpPath {
public:
/**
* Constructs an empty HTTP path.
*/
HttpPath() = default;
/**
* Constructs a HTTP path from an escaped path string. Makes a copy of the
* path, so it's safe to be a temporary.
*/
explicit HttpPath(std::string_view path);
/**
* Evaluates to true if the path is not empty.
*/
explicit operator bool() const { return !empty(); }
/**
* Returns true if the path has no elements.
*/
bool empty() const { return m_pathEnds.empty(); }
/**
* Returns number of elements in the path.
*/
size_t size() const { return m_pathEnds.size(); }
/**
* Returns true if the path exactly matches the provided match list.
*
* @param match match list
* @return True if path equals match list
*/
bool equals(std::initializer_list<std::string_view> match) const {
return equals(0, {match.begin(), match.end()});
}
bool equals(span<const std::string_view> match) const {
return equals(0, match);
}
bool equals(std::string_view match) const { return equals(0, {match}); }
/**
* Returns true if the elements of the path starting at the "start" element
* match the provided match list, and there are no additional elements.
*
* @param start element to start matching at
* @param match match list
* @return True if match
*/
bool equals(size_t start,
std::initializer_list<std::string_view> match) const {
return equals(start, {match.begin(), match.end()});
}
bool equals(size_t start, span<const std::string_view> match) const {
if (m_pathEnds.size() != (start + match.size())) {
return false;
}
return startswith(start, match);
}
bool equals(size_t start, std::string_view match) const {
return equals(start, {match});
}
/**
* Returns true if the first elements of the path match the provided match
* list. The path may have additional elements.
*
* @param match match list
* @return True if path starts with match list
*/
bool startswith(std::initializer_list<std::string_view> match) const {
return startswith(0, {match.begin(), match.end()});
}
bool startswith(span<const std::string_view> match) const {
return startswith(0, match);
}
bool startswith(std::string_view match) const {
return startswith(0, {match});
}
/**
* Returns true if the elements of the path starting at the "start" element
* match the provided match list. The path may have additional elements.
*
* @param start element to start matching at
* @param match match list
* @return True if path starting at the start element matches the match list
*/
bool startswith(size_t start,
std::initializer_list<std::string_view> match) const {
return startswith(start, {match.begin(), match.end()});
}
bool startswith(size_t start, span<const std::string_view> match) const;
bool startswith(size_t start, std::string_view match) const {
return startswith(start, {match});
}
/**
* Gets a single element of the path.
*/
std::string_view operator[](size_t n) const;
/**
* Returns a path reference with the first N elements of the path removed.
*/
HttpPathRef drop_front(size_t n) const;
private:
SmallString<128> m_pathBuf;
SmallVector<size_t, 16> m_pathEnds;
};
/**
* Proxy reference object for a portion of a HttpPath.
*/
class HttpPathRef {
public:
HttpPathRef() = default;
/*implicit*/ HttpPathRef(const HttpPath& path, // NOLINT
size_t start = 0)
: m_path(&path), m_start(start) {}
explicit operator bool() const { return !empty(); }
bool empty() const { return m_path && m_path->size() == m_start; }
size_t size() const { return m_path ? m_path->size() - m_start : 0; }
bool equals(std::initializer_list<std::string_view> match) const {
return equals(0, {match.begin(), match.end()});
}
bool equals(span<const std::string_view> match) const {
return equals(0, match);
}
bool equals(std::string_view match) const { return equals(0, {match}); }
bool equals(size_t start,
std::initializer_list<std::string_view> match) const {
return equals(start, {match.begin(), match.end()});
}
bool equals(size_t start, span<const std::string_view> match) const {
return m_path ? m_path->equals(m_start + start, match) : false;
}
bool equals(size_t start, std::string_view match) const {
return equals(start, {match});
}
bool startswith(std::initializer_list<std::string_view> match) const {
return startswith(0, {match.begin(), match.end()});
}
bool startswith(span<const std::string_view> match) const {
return startswith(0, match);
}
bool startswith(std::string_view match) const {
return startswith(0, {match});
}
bool startswith(size_t start,
std::initializer_list<std::string_view> match) const {
return startswith(start, {match.begin(), match.end()});
}
bool startswith(size_t start, span<const std::string_view> match) const {
return m_path ? m_path->startswith(m_start + start, match) : false;
}
bool startswith(size_t start, std::string_view match) const {
return startswith(start, {match});
}
std::string_view operator[](size_t n) const {
return m_path ? m_path->operator[](m_start + n) : std::string_view{};
}
HttpPathRef drop_front(size_t n) const {
return m_path ? m_path->drop_front(m_start + n) : HttpPathRef{};
}
private:
const HttpPath* m_path = nullptr;
size_t m_start = 0;
};
class HttpLocation {
public:
HttpLocation() = default;
HttpLocation(std::string_view url_, bool* error, std::string* errorMsg);
std::string url; // retain copy
std::string user; // unescaped
std::string password; // unescaped
std::string host;
int port;
std::string path; // escaped, not including leading '/'
std::vector<std::pair<std::string, std::string>> params; // unescaped
std::string fragment;
};
class HttpRequest {
public:
HttpRequest() = default;
explicit HttpRequest(const HttpLocation& loc)
: host{loc.host}, port{loc.port} {
SetPath(loc.path, loc.params);
SetAuth(loc);
}
template <typename T>
HttpRequest(const HttpLocation& loc, const T& extraParams);
HttpRequest(const HttpLocation& loc, std::string_view path_)
: host{loc.host}, port{loc.port}, path{path_} {
SetAuth(loc);
}
template <typename T>
HttpRequest(const HttpLocation& loc, std::string_view path_, const T& params)
: host{loc.host}, port{loc.port} {
SetPath(path_, params);
SetAuth(loc);
}
SmallString<128> host;
int port;
std::string auth;
SmallString<128> path;
private:
void SetAuth(const HttpLocation& loc);
template <typename T>
void SetPath(std::string_view path_, const T& params);
template <typename T>
static std::string_view GetFirst(const T& elem) {
return elem.first;
}
template <typename T>
static std::string_view GetFirst(const StringMapEntry<T>& elem) {
return elem.getKey();
}
template <typename T>
static std::string_view GetSecond(const T& elem) {
return elem.second;
}
};
class HttpConnection {
public:
HttpConnection(std::unique_ptr<wpi::NetworkStream> stream_, int timeout)
: stream{std::move(stream_)}, is{*stream, timeout}, os{*stream, true} {}
bool Handshake(const HttpRequest& request, std::string* warnMsg);
std::unique_ptr<wpi::NetworkStream> stream;
wpi::raw_socket_istream is;
wpi::raw_socket_ostream os;
// Valid after Handshake() is successful
SmallString<64> contentType;
SmallString<64> contentLength;
explicit operator bool() const { return stream && !is.has_error(); }
};
class HttpMultipartScanner {
public:
explicit HttpMultipartScanner(std::string_view boundary,
bool saveSkipped = false) {
Reset(saveSkipped);
SetBoundary(boundary);
}
// Change the boundary. This is only safe to do when IsDone() is true (or
// immediately after construction).
void SetBoundary(std::string_view boundary);
// Reset the scanner. This allows reuse of internal buffers.
void Reset(bool saveSkipped = false);
// Execute the scanner. Will automatically call Reset() on entry if IsDone()
// is true.
// @param in input data
// @return the input not consumed; empty if all input consumed
std::string_view Execute(std::string_view in);
// Returns true when the boundary has been found.
bool IsDone() const { return m_state == kDone; }
// Get the skipped data. Will be empty if saveSkipped was false.
std::string_view GetSkipped() const {
return m_saveSkipped ? std::string_view{m_buf} : std::string_view{};
}
private:
SmallString<64> m_boundaryWith, m_boundaryWithout;
// Internal state
enum State { kBoundary, kPadding, kDone };
State m_state;
size_t m_posWith, m_posWithout;
enum Dashes { kUnknown, kWith, kWithout };
Dashes m_dashes;
// Buffer
bool m_saveSkipped;
std::string m_buf;
};
} // namespace wpi
#include "HttpUtil.inc"
#endif // WPIUTIL_WPI_HTTPUTIL_H_

View File

@@ -1,55 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HTTPUTIL_INC_
#define WPIUTIL_WPI_HTTPUTIL_INC_
#include <utility>
#include "wpi/HttpUtil.h"
namespace wpi {
inline HttpPathRef HttpPath::drop_front(size_t n) const {
return HttpPathRef(*this, n);
}
template <typename T>
HttpRequest::HttpRequest(const HttpLocation& loc, const T& extraParams)
: host{loc.host}, port{loc.port} {
StringMap<std::string_view> params;
for (const auto& p : loc.params) {
params.insert(std::make_pair(GetFirst(p), GetSecond(p)));
}
for (const auto& p : extraParams) {
params.insert(std::make_pair(GetFirst(p), GetSecond(p)));
}
SetPath(loc.path, params);
SetAuth(loc);
}
template <typename T>
void HttpRequest::SetPath(std::string_view path_, const T& params) {
// Build location including query string
raw_svector_ostream pathOs{path};
pathOs << path_;
bool first = true;
for (const auto& param : params) {
if (first) {
pathOs << '?';
first = false;
} else {
pathOs << '&';
}
SmallString<64> escapeBuf;
pathOs << EscapeURI(GetFirst(param), escapeBuf, false);
if (!GetSecond(param).empty()) {
pathOs << '=' << EscapeURI(GetSecond(param), escapeBuf, false);
}
}
}
} // namespace wpi
#endif // WPIUTIL_WPI_HTTPUTIL_INC_

View File

@@ -1,92 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_H_
#define WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_H_
#include <initializer_list>
#include <memory>
#include <string>
#include <string_view>
#include "wpi/HttpServerConnection.h"
#include "wpi/SmallVector.h"
#include "wpi/WebSocket.h"
#include "wpi/WebSocketServer.h"
#include "wpi/span.h"
#include "wpi/uv/Stream.h"
namespace wpi {
/**
* A server-side HTTP connection that also accepts WebSocket upgrades.
*
* @tparam Derived derived class for std::enable_shared_from_this.
*/
template <typename Derived>
class HttpWebSocketServerConnection
: public HttpServerConnection,
public std::enable_shared_from_this<Derived> {
public:
/**
* Constructor.
*
* @param stream network stream
* @param protocols Acceptable subprotocols
*/
HttpWebSocketServerConnection(std::shared_ptr<uv::Stream> stream,
span<const std::string_view> protocols);
/**
* Constructor.
*
* @param stream network stream
* @param protocols Acceptable subprotocols
*/
HttpWebSocketServerConnection(
std::shared_ptr<uv::Stream> stream,
std::initializer_list<std::string_view> protocols)
: HttpWebSocketServerConnection(stream,
{protocols.begin(), protocols.end()}) {}
protected:
/**
* Check that an incoming WebSocket upgrade is okay. This is called prior
* to accepting the upgrade (so prior to ProcessWsUpgrade()).
*
* The implementation should check other headers and return true if the
* WebSocket connection should be accepted.
*
* @param protocol negotiated subprotocol
*/
virtual bool IsValidWsUpgrade(std::string_view protocol) { return true; }
/**
* Process an incoming WebSocket upgrade. This is called after the header
* reader has been disconnected and the websocket has been accepted.
*
* The implementation should set up appropriate callbacks on the websocket
* object to continue communication.
*
* @note When a WebSocket upgrade occurs, the stream user data is replaced
* with the websocket, and the websocket user data points to "this".
* Replace the websocket user data with caution!
*/
virtual void ProcessWsUpgrade() = 0;
/**
* WebSocket connection; not valid until ProcessWsUpgrade is called.
*/
WebSocket* m_websocket = nullptr;
private:
WebSocketServerHelper m_helper;
SmallVector<std::string, 2> m_protocols;
};
} // namespace wpi
#include "HttpWebSocketServerConnection.inc"
#endif // WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_H_

View File

@@ -1,56 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_INC_
#define WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_INC_
#include <memory>
#include "wpi/HttpWebSocketServerConnection.h"
namespace wpi {
template <typename Derived>
HttpWebSocketServerConnection<Derived>::HttpWebSocketServerConnection(
std::shared_ptr<uv::Stream> stream, span<const std::string_view> protocols)
: HttpServerConnection{stream},
m_helper{m_request},
m_protocols{protocols.begin(), protocols.end()} {
// Handle upgrade event
m_helper.upgrade.connect([this] {
// Negotiate sub-protocol
SmallVector<std::string_view, 2> protocols{m_protocols.begin(),
m_protocols.end()};
std::string_view protocol = m_helper.MatchProtocol(protocols).second;
// Check that the upgrade is valid
if (!IsValidWsUpgrade(protocol)) {
return;
}
// Disconnect HttpServerConnection header reader
m_dataConn.disconnect();
m_messageCompleteConn.disconnect();
// Accepting the stream may destroy this (as it replaces the stream user
// data), so grab a shared pointer first.
auto self = this->shared_from_this();
// Accept the upgrade
auto ws = m_helper.Accept(m_stream, protocol);
// Set this as the websocket user data to keep it around
ws->SetData(self);
// Store in member
m_websocket = ws.get();
// Call derived class function
ProcessWsUpgrade();
});
}
} // namespace wpi
#endif // WPIUTIL_WPI_HTTPWEBSOCKETSERVERCONNECTION_INC_

View File

@@ -1,16 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_MIMETYPES_H_
#define WPIUTIL_WPI_MIMETYPES_H_
#include <string_view>
namespace wpi {
std::string_view MimeTypeFromPath(std::string_view path);
} // namespace wpi
#endif // WPIUTIL_WPI_MIMETYPES_H_

View File

@@ -1,61 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <stdint.h>
#ifdef __cplusplus
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include "wpi/span.h"
namespace wpi {
class MulticastServiceAnnouncer {
public:
MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port,
wpi::span<const std::pair<std::string, std::string>> txt);
MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port,
wpi::span<const std::pair<std::string_view, std::string_view>> txt);
~MulticastServiceAnnouncer() noexcept;
void Start();
void Stop();
bool HasImplementation() const;
struct Impl;
private:
std::unique_ptr<Impl> pImpl;
};
} // namespace wpi
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned int WPI_MulticastServiceAnnouncerHandle; // NOLINT
WPI_MulticastServiceAnnouncerHandle WPI_CreateMulticastServiceAnnouncer(
const char* serviceName, const char* serviceType, int32_t port,
int32_t txtCount, const char** keys, const char** values);
void WPI_FreeMulticastServiceAnnouncer(
WPI_MulticastServiceAnnouncerHandle handle);
void WPI_StartMulticastServiceAnnouncer(
WPI_MulticastServiceAnnouncerHandle handle);
void WPI_StopMulticastServiceAnnouncer(
WPI_MulticastServiceAnnouncerHandle handle);
int32_t WPI_GetMulticastServiceAnnouncerHasImplementation(
WPI_MulticastServiceAnnouncerHandle handle);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -1,102 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include "wpi/Synchronization.h"
#ifdef __cplusplus
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "wpi/mutex.h"
#include "wpi/span.h"
namespace wpi {
class MulticastServiceResolver {
public:
explicit MulticastServiceResolver(std::string_view serviceType);
~MulticastServiceResolver() noexcept;
struct ServiceData {
unsigned int ipv4Address;
int port;
std::string serviceName;
std::string hostName;
std::vector<std::pair<std::string, std::string>> txt;
};
void Start();
void Stop();
WPI_EventHandle GetEventHandle() const { return event.GetHandle(); }
std::vector<ServiceData> GetData() {
std::scoped_lock lock{mutex};
event.Reset();
if (queue.empty()) {
return {};
}
std::vector<ServiceData> ret;
queue.swap(ret);
return ret;
}
bool HasImplementation() const;
struct Impl;
private:
void PushData(ServiceData&& data) {
std::scoped_lock lock{mutex};
queue.emplace_back(std::forward<ServiceData>(data));
event.Set();
}
wpi::Event event{true};
std::vector<ServiceData> queue;
wpi::mutex mutex;
std::unique_ptr<Impl> pImpl;
};
} // namespace wpi
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned int WPI_MulticastServiceResolverHandle; // NOLINT
WPI_MulticastServiceResolverHandle WPI_CreateMulticastServiceResolver(
const char* serviceType);
void WPI_FreeMulticastServiceResolver(
WPI_MulticastServiceResolverHandle handle);
void WPI_StartMulticastServiceResolver(
WPI_MulticastServiceResolverHandle handle);
void WPI_StopMulticastServiceResolver(
WPI_MulticastServiceResolverHandle handle);
int32_t WPI_GetMulticastServiceResolverHasImplementation(
WPI_MulticastServiceResolverHandle handle);
WPI_EventHandle WPI_GetMulticastServiceResolverEventHandle(
WPI_MulticastServiceResolverHandle handle);
typedef struct WPI_ServiceData { // NOLINT
uint32_t ipv4Address;
int32_t port;
const char* serviceName;
const char* hostName;
int32_t txtCount;
const char** txtKeys;
const char** txtValues;
} WPI_ServiceData;
WPI_ServiceData* WPI_GetMulticastServiceResolverData(
WPI_MulticastServiceResolverHandle handle, int32_t* dataCount);
void WPI_FreeServiceData(WPI_ServiceData* serviceData, int32_t length);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -1,29 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_NETWORKACCEPTOR_H_
#define WPIUTIL_WPI_NETWORKACCEPTOR_H_
#include <memory>
#include "wpi/NetworkStream.h"
namespace wpi {
class NetworkAcceptor {
public:
NetworkAcceptor() = default;
virtual ~NetworkAcceptor() = default;
virtual int start() = 0;
virtual void shutdown() = 0;
virtual std::unique_ptr<NetworkStream> accept() = 0;
NetworkAcceptor(const NetworkAcceptor&) = delete;
NetworkAcceptor& operator=(const NetworkAcceptor&) = delete;
};
} // namespace wpi
#endif // WPIUTIL_WPI_NETWORKACCEPTOR_H_

View File

@@ -1,44 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_NETWORKSTREAM_H_
#define WPIUTIL_WPI_NETWORKSTREAM_H_
#include <cstddef>
#include <string_view>
namespace wpi {
class NetworkStream {
public:
NetworkStream() = default;
virtual ~NetworkStream() = default;
enum Error {
kConnectionClosed = 0,
kConnectionReset = -1,
kConnectionTimedOut = -2,
kWouldBlock = -3
};
virtual size_t send(const char* buffer, size_t len, Error* err) = 0;
virtual size_t receive(char* buffer, size_t len, Error* err,
int timeout = 0) = 0;
virtual void close() = 0;
virtual std::string_view getPeerIP() const = 0;
virtual int getPeerPort() const = 0;
virtual void setNoDelay() = 0;
// returns false on failure
virtual bool setBlocking(bool enabled) = 0;
virtual int getNativeHandle() const = 0;
NetworkStream(const NetworkStream&) = delete;
NetworkStream& operator=(const NetworkStream&) = delete;
};
} // namespace wpi
#endif // WPIUTIL_WPI_NETWORKSTREAM_H_

View File

@@ -1,119 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <stdint.h>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "wpi/span.h"
#include "wpi/uv/Timer.h"
namespace wpi {
class Logger;
namespace uv {
class GetAddrInfoReq;
class Loop;
class Tcp;
class Timer;
} // namespace uv
/**
* Parallel TCP connector. Attempts parallel resolution and connection to
* multiple servers with automatic retry if none connect.
*
* Each successful TCP connection results in a call to the connected callback.
* For correct operation, the consuming code (either the connected callback or
* e.g. task it starts) must call Succeeded() to indicate if the connection has
* succeeded prior to the reconnect rate timeout. A successful connection
* results in the connector terminating all other connection attempts.
*
* After the reconnect rate times out, all remaining active connection attempts
* are canceled and new ones started.
*/
class ParallelTcpConnector
: public std::enable_shared_from_this<ParallelTcpConnector> {
struct private_init {};
public:
/**
* Create.
*
* @param loop loop
* @param reconnectRate how long to wait after starting connection attempts
* to cancel and attempt connecting again
* @param logger logger
* @param connected callback function when a connection succeeds; may be
* called multiple times if it does not call Succeeded()
* before returning
* @return Parallel connector
*/
static std::shared_ptr<ParallelTcpConnector> Create(
wpi::uv::Loop& loop, wpi::uv::Timer::Time reconnectRate,
wpi::Logger& logger, std::function<void(wpi::uv::Tcp& tcp)> connected) {
return std::make_shared<ParallelTcpConnector>(
loop, reconnectRate, logger, std::move(connected), private_init{});
}
ParallelTcpConnector(wpi::uv::Loop& loop, wpi::uv::Timer::Time reconnectRate,
wpi::Logger& logger,
std::function<void(wpi::uv::Tcp& tcp)> connected,
const private_init&);
~ParallelTcpConnector();
ParallelTcpConnector(const ParallelTcpConnector&) = delete;
ParallelTcpConnector& operator=(const ParallelTcpConnector&) = delete;
/**
* Closes resources, canceling all pending action attempts.
*/
void Close();
/**
* Changes the servers/ports to connect to. Starts connection attempts if not
* already connected.
*
* @param servers array of server/port pairs
*/
void SetServers(
wpi::span<const std::pair<std::string, unsigned int>> servers);
/**
* Tells the parallel connector that the current connection has terminated and
* it is necessary to start reconnection attempts.
*/
void Disconnected();
/**
* Tells the parallel connector that a particular connection has succeeded and
* it should stop trying to connect.
*
* @param tcp connection passed to connected callback
*/
void Succeeded(wpi::uv::Tcp& tcp);
private:
bool IsConnected() const { return m_isConnected || m_servers.empty(); }
void Connect();
void CancelAll(wpi::uv::Tcp* except = nullptr);
wpi::uv::Loop& m_loop;
wpi::Logger& m_logger;
wpi::uv::Timer::Time m_reconnectRate;
std::function<void(wpi::uv::Tcp& tcp)> m_connected;
std::shared_ptr<wpi::uv::Timer> m_reconnectTimer;
std::vector<std::pair<std::string, unsigned int>> m_servers;
std::vector<std::weak_ptr<wpi::uv::GetAddrInfoReq>> m_resolvers;
std::vector<std::weak_ptr<wpi::uv::Tcp>> m_attempts;
bool m_isConnected{false};
};
} // namespace wpi

View File

@@ -1,59 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_PORTFORWARDER_H_
#define WPIUTIL_WPI_PORTFORWARDER_H_
#pragma once
#include <memory>
#include <string_view>
namespace wpi {
/**
* Forward ports to another host. This is primarily useful for accessing
* Ethernet-connected devices from a computer tethered to the RoboRIO USB port.
*/
class PortForwarder {
public:
PortForwarder(const PortForwarder&) = delete;
PortForwarder& operator=(const PortForwarder&) = delete;
/**
* Get an instance of the PortForwarder class.
*
* This is a singleton to guarantee that there is only a single instance
* regardless of how many times GetInstance is called.
*/
static PortForwarder& GetInstance();
/**
* Forward a local TCP port to a remote host and port.
* Note that local ports less than 1024 won't work as a normal user.
*
* @param port local port number
* @param remoteHost remote IP address / DNS name
* @param remotePort remote port number
*/
void Add(unsigned int port, std::string_view remoteHost,
unsigned int remotePort);
/**
* Stop TCP forwarding on a port.
*
* @param port local port number
*/
void Remove(unsigned int port);
private:
PortForwarder();
struct Impl;
std::unique_ptr<Impl> m_impl;
};
} // namespace wpi
#endif // WPIUTIL_WPI_PORTFORWARDER_H_

View File

@@ -1,22 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_SOCKETERROR_H_
#define WPIUTIL_WPI_SOCKETERROR_H_
#include <string>
namespace wpi {
int SocketErrno();
std::string SocketStrerror(int code);
inline std::string SocketStrerror() {
return SocketStrerror(SocketErrno());
}
} // namespace wpi
#endif // WPIUTIL_WPI_SOCKETERROR_H_

View File

@@ -1,58 +0,0 @@
/*
TCPAcceptor.h
TCPAcceptor class interface. TCPAcceptor provides methods to passively
establish TCP/IP connections with clients.
------------------------------------------
Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef WPIUTIL_WPI_TCPACCEPTOR_H_
#define WPIUTIL_WPI_TCPACCEPTOR_H_
#include <atomic>
#include <memory>
#include <string>
#include <string_view>
#include "wpi/NetworkAcceptor.h"
#include "wpi/TCPStream.h"
namespace wpi {
class Logger;
class TCPAcceptor : public NetworkAcceptor {
int m_lsd;
int m_port;
std::string m_address;
bool m_listening;
std::atomic_bool m_shutdown;
Logger& m_logger;
public:
TCPAcceptor(int port, std::string_view address, Logger& logger);
~TCPAcceptor() override;
int start() override;
void shutdown() final;
std::unique_ptr<NetworkStream> accept() override;
};
} // namespace wpi
#endif // WPIUTIL_WPI_TCPACCEPTOR_H_

View File

@@ -1,49 +0,0 @@
/*
TCPConnector.h
TCPConnector class interface. TCPConnector provides methods to actively
establish TCP/IP connections with a server.
------------------------------------------
Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
*/
#ifndef WPIUTIL_WPI_TCPCONNECTOR_H_
#define WPIUTIL_WPI_TCPCONNECTOR_H_
#include <memory>
#include <utility>
#include "wpi/NetworkStream.h"
#include "wpi/span.h"
namespace wpi {
class Logger;
class TCPConnector {
public:
static std::unique_ptr<NetworkStream> connect(const char* server, int port,
Logger& logger,
int timeout = 0);
static std::unique_ptr<NetworkStream> connect_parallel(
span<const std::pair<const char*, int>> servers, Logger& logger,
int timeout = 0);
};
} // namespace wpi
#endif // WPIUTIL_WPI_TCPCONNECTOR_H_

View File

@@ -1,72 +0,0 @@
/*
TCPStream.h
TCPStream class interface. TCPStream provides methods to transfer
data between peers over a TCP/IP connection.
------------------------------------------
Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef WPIUTIL_WPI_TCPSTREAM_H_
#define WPIUTIL_WPI_TCPSTREAM_H_
#include <cstddef>
#include <string>
#include <string_view>
#include "wpi/NetworkStream.h"
struct sockaddr_in;
namespace wpi {
class TCPStream : public NetworkStream {
int m_sd;
std::string m_peerIP;
int m_peerPort;
bool m_blocking;
public:
friend class TCPAcceptor;
friend class TCPConnector;
~TCPStream() override;
size_t send(const char* buffer, size_t len, Error* err) override;
size_t receive(char* buffer, size_t len, Error* err,
int timeout = 0) override;
void close() final;
std::string_view getPeerIP() const override;
int getPeerPort() const override;
void setNoDelay() override;
bool setBlocking(bool enabled) override;
int getNativeHandle() const override;
TCPStream(const TCPStream& stream) = delete;
TCPStream& operator=(const TCPStream&) = delete;
private:
bool WaitForReadEvent(int timeout);
TCPStream(int sd, sockaddr_in* address);
TCPStream() = delete;
};
} // namespace wpi
#endif // WPIUTIL_WPI_TCPSTREAM_H_

View File

@@ -1,49 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UDPCLIENT_H_
#define WPIUTIL_WPI_UDPCLIENT_H_
#include <string>
#include <string_view>
#include "wpi/SmallVector.h"
#include "wpi/mutex.h"
#include "wpi/span.h"
namespace wpi {
class Logger;
class UDPClient {
int m_lsd;
int m_port;
std::string m_address;
Logger& m_logger;
public:
explicit UDPClient(Logger& logger);
UDPClient(std::string_view address, Logger& logger);
UDPClient(const UDPClient& other) = delete;
UDPClient(UDPClient&& other);
~UDPClient();
UDPClient& operator=(const UDPClient& other) = delete;
UDPClient& operator=(UDPClient&& other);
int start();
int start(int port);
void shutdown();
// The passed in address MUST be a resolved IP address.
int send(span<const uint8_t> data, std::string_view server, int port);
int send(std::string_view data, std::string_view server, int port);
int receive(uint8_t* data_received, int receive_len);
int receive(uint8_t* data_received, int receive_len,
SmallVectorImpl<char>* addr_received, int* port_received);
int set_timeout(double timeout);
};
} // namespace wpi
#endif // WPIUTIL_WPI_UDPCLIENT_H_

View File

@@ -1,95 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_URLPARSER_H_
#define WPIUTIL_WPI_URLPARSER_H_
#include <string_view>
#include "wpi/StringExtras.h"
#include "wpi/http_parser.h"
namespace wpi {
/**
* Parses a URL into its constiuent components.
* `schema://userinfo@host:port/the/path?query#fragment`
*/
class UrlParser {
public:
/**
* Parse a URL.
* @param in input
* @param isConnect
*/
UrlParser(std::string_view in, bool isConnect) {
m_data = in;
http_parser_url_init(&m_url);
m_error = http_parser_parse_url(in.data(), in.size(), isConnect, &m_url);
}
/**
* Determine if the URL is valid (e.g. the parse was successful).
*/
bool IsValid() const { return !m_error; }
bool HasSchema() const { return (m_url.field_set & (1 << UF_SCHEMA)) != 0; }
bool HasHost() const { return (m_url.field_set & (1 << UF_HOST)) != 0; }
bool HasPort() const { return (m_url.field_set & (1 << UF_PORT)) != 0; }
bool HasPath() const { return (m_url.field_set & (1 << UF_PATH)) != 0; }
bool HasQuery() const { return (m_url.field_set & (1 << UF_QUERY)) != 0; }
bool HasFragment() const {
return (m_url.field_set & (1 << UF_FRAGMENT)) != 0;
}
bool HasUserInfo() const {
return (m_url.field_set & (1 << UF_USERINFO)) != 0;
}
std::string_view GetSchema() const {
return wpi::substr(m_data, m_url.field_data[UF_SCHEMA].off,
m_url.field_data[UF_SCHEMA].len);
}
std::string_view GetHost() const {
return wpi::substr(m_data, m_url.field_data[UF_HOST].off,
m_url.field_data[UF_HOST].len);
}
unsigned int GetPort() const { return m_url.port; }
std::string_view GetPath() const {
return wpi::substr(m_data, m_url.field_data[UF_PATH].off,
m_url.field_data[UF_PATH].len);
}
std::string_view GetQuery() const {
return wpi::substr(m_data, m_url.field_data[UF_QUERY].off,
m_url.field_data[UF_QUERY].len);
}
std::string_view GetFragment() const {
return wpi::substr(m_data, m_url.field_data[UF_FRAGMENT].off,
m_url.field_data[UF_FRAGMENT].len);
}
std::string_view GetUserInfo() const {
return wpi::substr(m_data, m_url.field_data[UF_USERINFO].off,
m_url.field_data[UF_USERINFO].len);
}
private:
bool m_error;
std::string_view m_data;
http_parser_url m_url;
};
} // namespace wpi
#endif // WPIUTIL_WPI_URLPARSER_H_

View File

@@ -1,477 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_WEBSOCKET_H_
#define WPIUTIL_WPI_WEBSOCKET_H_
#include <stdint.h>
#include <functional>
#include <initializer_list>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/SmallVector.h"
#include "wpi/span.h"
#include "wpi/uv/Buffer.h"
#include "wpi/uv/Error.h"
#include "wpi/uv/Timer.h"
namespace wpi {
namespace uv {
class Stream;
} // namespace uv
/**
* RFC 6455 compliant WebSocket client and server implementation.
*/
class WebSocket : public std::enable_shared_from_this<WebSocket> {
struct private_init {};
static constexpr uint8_t kOpCont = 0x00;
static constexpr uint8_t kOpText = 0x01;
static constexpr uint8_t kOpBinary = 0x02;
static constexpr uint8_t kOpClose = 0x08;
static constexpr uint8_t kOpPing = 0x09;
static constexpr uint8_t kOpPong = 0x0A;
static constexpr uint8_t kOpMask = 0x0F;
static constexpr uint8_t kFlagFin = 0x80;
static constexpr uint8_t kFlagMasking = 0x80;
static constexpr uint8_t kLenMask = 0x7f;
public:
WebSocket(uv::Stream& stream, bool server, const private_init&);
WebSocket(const WebSocket&) = delete;
WebSocket(WebSocket&&) = delete;
WebSocket& operator=(const WebSocket&) = delete;
WebSocket& operator=(WebSocket&&) = delete;
~WebSocket();
/**
* Connection states.
*/
enum State {
/** The connection is not yet open. */
CONNECTING = 0,
/** The connection is open and ready to communicate. */
OPEN,
/** The connection is in the process of closing. */
CLOSING,
/** The connection failed. */
FAILED,
/** The connection is closed. */
CLOSED
};
/**
* Client connection options.
*/
struct ClientOptions {
ClientOptions() : handshakeTimeout{(uv::Timer::Time::max)()} {}
/** Timeout for the handshake request. */
uv::Timer::Time handshakeTimeout; // NOLINT
/** Additional headers to include in handshake. */
span<const std::pair<std::string_view, std::string_view>> extraHeaders;
};
/**
* Starts a client connection by performing the initial client handshake.
* An open event is emitted when the handshake completes.
* This sets the stream user data to the websocket.
* @param stream Connection stream
* @param uri The Request-URI to send
* @param host The host or host:port to send
* @param protocols The list of subprotocols
* @param options Handshake options
*/
static std::shared_ptr<WebSocket> CreateClient(
uv::Stream& stream, std::string_view uri, std::string_view host,
span<const std::string_view> protocols = {},
const ClientOptions& options = {});
/**
* Starts a client connection by performing the initial client handshake.
* An open event is emitted when the handshake completes.
* This sets the stream user data to the websocket.
* @param stream Connection stream
* @param uri The Request-URI to send
* @param host The host or host:port to send
* @param protocols The list of subprotocols
* @param options Handshake options
*/
static std::shared_ptr<WebSocket> CreateClient(
uv::Stream& stream, std::string_view uri, std::string_view host,
std::initializer_list<std::string_view> protocols,
const ClientOptions& options = {}) {
return CreateClient(stream, uri, host, {protocols.begin(), protocols.end()},
options);
}
/**
* Starts a server connection by performing the initial server side handshake.
* This should be called after the HTTP headers have been received.
* An open event is emitted when the handshake completes.
* This sets the stream user data to the websocket.
* @param stream Connection stream
* @param key The value of the Sec-WebSocket-Key header field in the client
* request
* @param version The value of the Sec-WebSocket-Version header field in the
* client request
* @param protocol The subprotocol to send to the client (in the
* Sec-WebSocket-Protocol header field).
*/
static std::shared_ptr<WebSocket> CreateServer(
uv::Stream& stream, std::string_view key, std::string_view version,
std::string_view protocol = {});
/**
* Get connection state.
*/
State GetState() const { return m_state; }
/**
* Return if the connection is open. Messages can only be sent on open
* connections.
*/
bool IsOpen() const { return m_state == OPEN; }
/**
* Get the underlying stream.
*/
uv::Stream& GetStream() const { return m_stream; }
/**
* Get the selected sub-protocol. Only valid in or after the open() event.
*/
std::string_view GetProtocol() const { return m_protocol; }
/**
* Set the maximum message size. Default is 128 KB. If configured to combine
* fragments this maximum applies to the entire message (all combined
* fragments).
* @param size Maximum message size in bytes
*/
void SetMaxMessageSize(size_t size) { m_maxMessageSize = size; }
/**
* Set whether or not fragmented frames should be combined. Default is to
* combine. If fragmented frames are combined, the text and binary callbacks
* will always have the second parameter (fin) set to true.
* @param combine True if fragmented frames should be combined.
*/
void SetCombineFragments(bool combine) { m_combineFragments = combine; }
/**
* Initiate a closing handshake.
* @param code A numeric status code (defaults to 1005, no status code)
* @param reason A human-readable string explaining why the connection is
* closing (optional).
*/
void Close(uint16_t code = 1005, std::string_view reason = {});
/**
* Send a text message.
* @param data UTF-8 encoded data to send
* @param callback Callback which is invoked when the write completes.
*/
void SendText(span<const uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
Send(kFlagFin | kOpText, data, std::move(callback));
}
/**
* Send a text message.
* @param data UTF-8 encoded data to send
* @param callback Callback which is invoked when the write completes.
*/
void SendText(std::initializer_list<uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
SendText({data.begin(), data.end()}, std::move(callback));
}
/**
* Send a binary message.
* @param data Data to send
* @param callback Callback which is invoked when the write completes.
*/
void SendBinary(span<const uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
Send(kFlagFin | kOpBinary, data, std::move(callback));
}
/**
* Send a binary message.
* @param data Data to send
* @param callback Callback which is invoked when the write completes.
*/
void SendBinary(std::initializer_list<uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
SendBinary({data.begin(), data.end()}, std::move(callback));
}
/**
* Send a text message fragment. This must be followed by one or more
* SendFragment() calls, where the last one has fin=True, to complete the
* message.
* @param data UTF-8 encoded data to send
* @param callback Callback which is invoked when the write completes.
*/
void SendTextFragment(
span<const uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
Send(kOpText, data, std::move(callback));
}
/**
* Send a text message fragment. This must be followed by one or more
* SendFragment() calls, where the last one has fin=True, to complete the
* message.
* @param data UTF-8 encoded data to send
* @param callback Callback which is invoked when the write completes.
*/
void SendTextFragment(
std::initializer_list<uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
SendTextFragment({data.begin(), data.end()}, std::move(callback));
}
/**
* Send a text message fragment. This must be followed by one or more
* SendFragment() calls, where the last one has fin=True, to complete the
* message.
* @param data Data to send
* @param callback Callback which is invoked when the write completes.
*/
void SendBinaryFragment(
span<const uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
Send(kOpBinary, data, std::move(callback));
}
/**
* Send a text message fragment. This must be followed by one or more
* SendFragment() calls, where the last one has fin=True, to complete the
* message.
* @param data Data to send
* @param callback Callback which is invoked when the write completes.
*/
void SendBinaryFragment(
std::initializer_list<uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
SendBinaryFragment({data.begin(), data.end()}, std::move(callback));
}
/**
* Send a continuation frame. This is used to send additional parts of a
* message started with SendTextFragment() or SendBinaryFragment().
* @param data Data to send
* @param fin Set to true if this is the final fragment of the message
* @param callback Callback which is invoked when the write completes.
*/
void SendFragment(span<const uv::Buffer> data, bool fin,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
Send(kOpCont | (fin ? kFlagFin : 0), data, std::move(callback));
}
/**
* Send a continuation frame. This is used to send additional parts of a
* message started with SendTextFragment() or SendBinaryFragment().
* @param data Data to send
* @param fin Set to true if this is the final fragment of the message
* @param callback Callback which is invoked when the write completes.
*/
void SendFragment(std::initializer_list<uv::Buffer> data, bool fin,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
SendFragment({data.begin(), data.end()}, fin, std::move(callback));
}
/**
* Send a ping frame with no data.
* @param callback Optional callback which is invoked when the ping frame
* write completes.
*/
void SendPing(std::function<void(uv::Error)> callback = nullptr) {
SendPing({}, [f = std::move(callback)](auto bufs, uv::Error err) {
if (f) {
f(err);
}
});
}
/**
* Send a ping frame.
* @param data Data to send in the ping frame
* @param callback Callback which is invoked when the ping frame
* write completes.
*/
void SendPing(span<const uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
Send(kFlagFin | kOpPing, data, std::move(callback));
}
/**
* Send a ping frame.
* @param data Data to send in the ping frame
* @param callback Callback which is invoked when the ping frame
* write completes.
*/
void SendPing(std::initializer_list<uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
SendPing({data.begin(), data.end()}, std::move(callback));
}
/**
* Send a pong frame with no data.
* @param callback Optional callback which is invoked when the pong frame
* write completes.
*/
void SendPong(std::function<void(uv::Error)> callback = nullptr) {
SendPong({}, [f = std::move(callback)](auto bufs, uv::Error err) {
if (f) {
f(err);
}
});
}
/**
* Send a pong frame.
* @param data Data to send in the pong frame
* @param callback Callback which is invoked when the pong frame
* write completes.
*/
void SendPong(span<const uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
Send(kFlagFin | kOpPong, data, std::move(callback));
}
/**
* Send a pong frame.
* @param data Data to send in the pong frame
* @param callback Callback which is invoked when the pong frame
* write completes.
*/
void SendPong(std::initializer_list<uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback) {
SendPong({data.begin(), data.end()}, std::move(callback));
}
/**
* Fail the connection.
*/
void Fail(uint16_t code = 1002, std::string_view reason = "protocol error");
/**
* Forcibly close the connection.
*/
void Terminate(uint16_t code = 1006, std::string_view reason = "terminated");
/**
* Gets user-defined data.
* @return User-defined data if any, nullptr otherwise.
*/
template <typename T = void>
std::shared_ptr<T> GetData() const {
return std::static_pointer_cast<T>(m_data);
}
/**
* Sets user-defined data.
* @param data User-defined arbitrary data.
*/
void SetData(std::shared_ptr<void> data) { m_data = std::move(data); }
/**
* Shuts down and closes the underlying stream.
*/
void Shutdown();
/**
* Open event. Emitted when the connection is open and ready to communicate.
* The parameter is the selected subprotocol.
*/
sig::Signal<std::string_view> open;
/**
* Close event. Emitted when the connection is closed. The first parameter
* is a numeric value indicating the status code explaining why the connection
* has been closed. The second parameter is a human-readable string
* explaining the reason why the connection has been closed.
*/
sig::Signal<uint16_t, std::string_view> closed;
/**
* Text message event. Emitted when a text message is received.
* The first parameter is the data, the second parameter is true if the
* data is the last fragment of the message.
*/
sig::Signal<std::string_view, bool> text;
/**
* Binary message event. Emitted when a binary message is received.
* The first parameter is the data, the second parameter is true if the
* data is the last fragment of the message.
*/
sig::Signal<span<const uint8_t>, bool> binary;
/**
* Ping event. Emitted when a ping message is received.
*/
sig::Signal<span<const uint8_t>> ping;
/**
* Pong event. Emitted when a pong message is received.
*/
sig::Signal<span<const uint8_t>> pong;
private:
// user data
std::shared_ptr<void> m_data;
// constructor parameters
uv::Stream& m_stream;
bool m_server;
// subprotocol, set via constructor (server) or handshake (client)
std::string m_protocol;
// user-settable configuration
size_t m_maxMessageSize = 128 * 1024;
bool m_combineFragments = true;
// operating state
State m_state = CONNECTING;
// incoming message buffers/state
SmallVector<uint8_t, 14> m_header;
size_t m_headerSize = 0;
SmallVector<uint8_t, 1024> m_payload;
size_t m_frameStart = 0;
uint64_t m_frameSize = UINT64_MAX;
uint8_t m_fragmentOpcode = 0;
// temporary data used only during client handshake
class ClientHandshakeData;
std::unique_ptr<ClientHandshakeData> m_clientHandshake;
void StartClient(std::string_view uri, std::string_view host,
span<const std::string_view> protocols,
const ClientOptions& options);
void StartServer(std::string_view key, std::string_view version,
std::string_view protocol);
void SendClose(uint16_t code, std::string_view reason);
void SetClosed(uint16_t code, std::string_view reason, bool failed = false);
void HandleIncoming(uv::Buffer& buf, size_t size);
void Send(uint8_t opcode, span<const uv::Buffer> data,
std::function<void(span<uv::Buffer>, uv::Error)> callback);
};
} // namespace wpi
#endif // WPIUTIL_WPI_WEBSOCKET_H_

View File

@@ -1,177 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_WEBSOCKETSERVER_H_
#define WPIUTIL_WPI_WEBSOCKETSERVER_H_
#include <functional>
#include <initializer_list>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include "wpi/HttpParser.h"
#include "wpi/Signal.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
#include "wpi/WebSocket.h"
#include "wpi/span.h"
namespace wpi {
namespace uv {
class Stream;
} // namespace uv
/**
* WebSocket HTTP server helper. Handles websocket-specific headers. User
* must provide the HttpParser.
*/
class WebSocketServerHelper {
public:
/**
* Constructor.
* @param req HttpParser for request
*/
explicit WebSocketServerHelper(HttpParser& req);
/**
* Get whether or not this was a websocket upgrade.
* Only valid during and after the upgrade event.
*/
bool IsWebsocket() const { return m_websocket; }
/**
* Try to find a match to the list of sub-protocols provided by the client.
* The list is priority ordered, so the first match wins.
* Only valid during and after the upgrade event.
* @param protocols Acceptable protocols
* @return Pair; first item is true if a match was made, false if not.
* Second item is the matched protocol if a match was made, otherwise
* is empty.
*/
std::pair<bool, std::string_view> MatchProtocol(
span<const std::string_view> protocols);
/**
* Try to find a match to the list of sub-protocols provided by the client.
* The list is priority ordered, so the first match wins.
* Only valid during and after the upgrade event.
* @param protocols Acceptable protocols
* @return Pair; first item is true if a match was made, false if not.
* Second item is the matched protocol if a match was made, otherwise
* is empty.
*/
std::pair<bool, std::string_view> MatchProtocol(
std::initializer_list<std::string_view> protocols) {
return MatchProtocol({protocols.begin(), protocols.end()});
}
/**
* Accept the upgrade. Disconnect other readers (such as the HttpParser
* reader) before calling this. See also WebSocket::CreateServer().
* @param stream Connection stream
* @param protocol The subprotocol to send to the client
*/
std::shared_ptr<WebSocket> Accept(uv::Stream& stream,
std::string_view protocol = {}) {
return WebSocket::CreateServer(stream, m_key, m_version, protocol);
}
bool IsUpgrade() const { return m_gotHost && m_websocket; }
/**
* Upgrade event. Call Accept() to accept the upgrade.
*/
sig::Signal<> upgrade;
private:
bool m_gotHost = false;
bool m_websocket = false;
SmallVector<std::string, 2> m_protocols;
SmallString<64> m_key;
SmallString<16> m_version;
};
/**
* Dedicated WebSocket server.
*/
class WebSocketServer : public std::enable_shared_from_this<WebSocketServer> {
struct private_init {};
public:
/**
* Server options.
*/
struct ServerOptions {
/**
* Checker for URL. Return true if URL should be accepted. By default all
* URLs are accepted.
*/
std::function<bool(std::string_view)> checkUrl;
/**
* Checker for Host header. Return true if Host should be accepted. By
* default all hosts are accepted.
*/
std::function<bool(std::string_view)> checkHost;
};
/**
* Private constructor.
*/
WebSocketServer(uv::Stream& stream, span<const std::string_view> protocols,
ServerOptions options, const private_init&);
/**
* Starts a dedicated WebSocket server on the provided connection. The
* connection should be an accepted client stream.
* This also sets the stream user data to the socket server.
* A connected event is emitted when the connection is opened.
* @param stream Connection stream
* @param protocols Acceptable subprotocols
* @param options Handshake options
*/
static std::shared_ptr<WebSocketServer> Create(
uv::Stream& stream, span<const std::string_view> protocols = {},
const ServerOptions& options = {});
/**
* Starts a dedicated WebSocket server on the provided connection. The
* connection should be an accepted client stream.
* This also sets the stream user data to the socket server.
* A connected event is emitted when the connection is opened.
* @param stream Connection stream
* @param protocols Acceptable subprotocols
* @param options Handshake options
*/
static std::shared_ptr<WebSocketServer> Create(
uv::Stream& stream, std::initializer_list<std::string_view> protocols,
const ServerOptions& options = {}) {
return Create(stream, {protocols.begin(), protocols.end()}, options);
}
/**
* Connected event. First parameter is the URL, second is the websocket.
*/
sig::Signal<std::string_view, WebSocket&> connected;
private:
uv::Stream& m_stream;
HttpParser m_req{HttpParser::kRequest};
WebSocketServerHelper m_helper;
SmallVector<std::string, 2> m_protocols;
ServerOptions m_options;
bool m_aborted = false;
sig::ScopedConnection m_dataConn;
sig::ScopedConnection m_errorConn;
sig::ScopedConnection m_endConn;
void Abort(uint16_t code, std::string_view reason);
};
} // namespace wpi
#endif // WPIUTIL_WPI_WEBSOCKETSERVER_H_

View File

@@ -1,285 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_WORKERTHREAD_H_
#define WPIUTIL_WPI_WORKERTHREAD_H_
#include <functional>
#include <memory>
#include <tuple>
#include <utility>
#include <vector>
#include "wpi/SafeThread.h"
#include "wpi/future.h"
#include "wpi/uv/Async.h"
namespace wpi {
namespace detail {
template <typename R>
struct WorkerThreadAsync {
using AfterWorkFunction = std::function<void(R)>;
~WorkerThreadAsync() { UnsetLoop(); }
void SetLoop(uv::Loop& loop) {
auto async = uv::Async<AfterWorkFunction, R>::Create(loop);
async->wakeup.connect(
[](AfterWorkFunction func, R result) { func(result); });
m_async = async;
}
void UnsetLoop() {
if (auto async = m_async.lock()) {
async->Close();
m_async.reset();
}
}
std::weak_ptr<uv::Async<AfterWorkFunction, R>> m_async;
};
template <>
struct WorkerThreadAsync<void> {
using AfterWorkFunction = std::function<void()>;
~WorkerThreadAsync() { RemoveLoop(); }
void SetLoop(uv::Loop& loop) {
auto async = uv::Async<AfterWorkFunction>::Create(loop);
async->wakeup.connect([](AfterWorkFunction func) { func(); });
m_async = async;
}
void RemoveLoop() {
if (auto async = m_async.lock()) {
async->Close();
m_async.reset();
}
}
std::weak_ptr<uv::Async<AfterWorkFunction>> m_async;
};
template <typename R, typename... T>
struct WorkerThreadRequest {
using WorkFunction = std::function<R(T...)>;
using AfterWorkFunction = typename WorkerThreadAsync<R>::AfterWorkFunction;
WorkerThreadRequest() = default;
WorkerThreadRequest(uint64_t promiseId_, WorkFunction work_,
std::tuple<T...> params_)
: promiseId(promiseId_),
work(std::move(work_)),
params(std::move(params_)) {}
WorkerThreadRequest(WorkFunction work_, AfterWorkFunction afterWork_,
std::tuple<T...> params_)
: promiseId(0),
work(std::move(work_)),
afterWork(std::move(afterWork_)),
params(std::move(params_)) {}
uint64_t promiseId;
WorkFunction work;
AfterWorkFunction afterWork;
std::tuple<T...> params;
};
template <typename R, typename... T>
class WorkerThreadThread : public SafeThread {
public:
using Request = WorkerThreadRequest<R, T...>;
void Main() override;
std::vector<Request> m_requests;
PromiseFactory<R> m_promises;
detail::WorkerThreadAsync<R> m_async;
};
template <typename R, typename... T>
void RunWorkerThreadRequest(WorkerThreadThread<R, T...>& thr,
WorkerThreadRequest<R, T...>& req) {
R result = std::apply(req.work, std::move(req.params));
if (req.afterWork) {
if (auto async = thr.m_async.m_async.lock()) {
async->Send(std::move(req.afterWork), std::move(result));
}
} else {
thr.m_promises.SetValue(req.promiseId, std::move(result));
}
}
template <typename... T>
void RunWorkerThreadRequest(WorkerThreadThread<void, T...>& thr,
WorkerThreadRequest<void, T...>& req) {
std::apply(req.work, req.params);
if (req.afterWork) {
if (auto async = thr.m_async.m_async.lock()) {
async->Send(std::move(req.afterWork));
}
} else {
thr.m_promises.SetValue(req.promiseId);
}
}
template <typename R, typename... T>
void WorkerThreadThread<R, T...>::Main() {
std::vector<Request> requests;
while (m_active) {
std::unique_lock lock(m_mutex);
m_cond.wait(lock, [&] { return !m_active || !m_requests.empty(); });
if (!m_active) {
break;
}
// don't want to hold the lock while executing the callbacks
requests.swap(m_requests);
lock.unlock();
for (auto&& req : requests) {
if (!m_active) {
break; // requests may be long-running
}
RunWorkerThreadRequest(*this, req);
}
requests.clear();
m_promises.Notify();
}
}
} // namespace detail
template <typename T>
class WorkerThread;
template <typename R, typename... T>
class WorkerThread<R(T...)> final {
using Thread = detail::WorkerThreadThread<R, T...>;
public:
using WorkFunction = std::function<R(T...)>;
using AfterWorkFunction =
typename detail::WorkerThreadAsync<R>::AfterWorkFunction;
WorkerThread() { m_owner.Start(); }
/**
* Set the loop. This must be called from the loop thread.
* Subsequent calls to QueueWorkThen will run afterWork on the provided
* loop (via an async handle).
*
* @param loop the loop to use for running afterWork routines
*/
void SetLoop(uv::Loop& loop) {
if (auto thr = m_owner.GetThread()) {
thr->m_async.SetLoop(loop);
}
}
/**
* Set the loop. This must be called from the loop thread.
* Subsequent calls to QueueWorkThen will run afterWork on the provided
* loop (via an async handle).
*
* @param loop the loop to use for running afterWork routines
*/
void SetLoop(std::shared_ptr<uv::Loop> loop) { SetLoop(*loop); }
/**
* Unset the loop. This must be called from the loop thread.
* Subsequent calls to QueueWorkThen will no longer run afterWork.
*/
void UnsetLoop() {
if (auto thr = m_owner.GetThread()) {
thr->m_async.UnsetLoop();
}
}
/**
* Get the handle used by QueueWorkThen() to run afterWork.
* This handle is set by SetLoop().
* Calling Close() on this handle is the same as calling UnsetLoop().
*
* @return The handle (if nullptr, no handle is set)
*/
std::shared_ptr<uv::Handle> GetHandle() const {
if (auto thr = m_owner.GetThread()) {
return thr->m_async.m_async.lock();
} else {
return nullptr;
}
}
/**
* Wakeup the worker thread, call the work function, and return a future for
* the result.
*
* Its safe to call this function from any thread.
* The work function will be called on the worker thread.
*
* The future will return a default-constructed result if this class is
* destroyed while waiting for a result.
*
* @param work Work function (called on worker thread)
* @param u Arguments to work function
*/
template <typename... U>
future<R> QueueWork(WorkFunction work, U&&... u) {
if (auto thr = m_owner.GetThread()) {
// create the future
uint64_t req = thr->m_promises.CreateRequest();
// add the parameters to the input queue
thr->m_requests.emplace_back(
req, std::move(work), std::forward_as_tuple(std::forward<U>(u)...));
// signal the thread
thr->m_cond.notify_one();
// return future
return thr->m_promises.CreateFuture(req);
}
// XXX: is this the right thing to do?
return future<R>();
}
/**
* Wakeup the worker thread, call the work function, and call the afterWork
* function with the result on the loop set by SetLoop().
*
* Its safe to call this function from any thread.
* The work function will be called on the worker thread, and the afterWork
* function will be called on the loop thread.
*
* SetLoop() must be called prior to calling this function for afterWork to
* be called.
*
* @param work Work function (called on worker thread)
* @param afterWork After work function (called on loop thread)
* @param u Arguments to work function
*/
template <typename... U>
void QueueWorkThen(WorkFunction work, AfterWorkFunction afterWork, U&&... u) {
if (auto thr = m_owner.GetThread()) {
// add the parameters to the input queue
thr->m_requests.emplace_back(
std::move(work), std::move(afterWork),
std::forward_as_tuple(std::forward<U>(u)...));
// signal the thread
thr->m_cond.notify_one();
}
}
private:
SafeThreadOwner<Thread> m_owner;
};
} // namespace wpi
#endif // WPIUTIL_WPI_WORKERTHREAD_H_

View File

@@ -1,19 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_HOSTNAME_H_
#define WPIUTIL_WPI_HOSTNAME_H_
#include <string>
#include <string_view>
namespace wpi {
template <typename T>
class SmallVectorImpl;
std::string GetHostname();
std::string_view GetHostname(SmallVectorImpl<char>& name);
} // namespace wpi
#endif // WPIUTIL_WPI_HOSTNAME_H_

View File

@@ -1,421 +0,0 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef wpi_http_parser_h
#define wpi_http_parser_h
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 8
#define HTTP_PARSER_VERSION_PATCH 1
#include <stddef.h>
#include <stdint.h>
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#endif
/* Maximium header size allowed. If the macro is not defined
* before including this header then the default is used. To
* change the maximum header size, define the macro in the build
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
* the effective limit on the size of the header, define the macro
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
*/
#ifndef HTTP_MAX_HEADER_SIZE
# define HTTP_MAX_HEADER_SIZE (80*1024)
#endif
namespace wpi {
struct http_parser;
struct http_parser_settings;
/* Callbacks should return non-zero to indicate an error. The parser will
* then halt execution.
*
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
* returning '1' from on_headers_complete will tell the parser that it
* should not expect a body. This is used when receiving a response to a
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any further responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
*/
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
/* Status Codes */
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
enum http_status
{
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
};
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
/* icecast */ \
XX(33, SOURCE, SOURCE) \
enum http_method
{
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
, F_CONNECTION_UPGRADE = 1 << 3
, F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
};
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((::wpi::http_errno) (p)->http_errno)
struct http_parser {
/** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned int upgrade : 1;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
* Usage example:
*
* unsigned long version = http_parser_version();
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings);
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
/* If http_should_keep_alive() in the on_headers_complete or
* on_message_complete callback returns 0, then this should be
* the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
int http_should_keep_alive(const http_parser *parser);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);
/* Returns a string version of the HTTP status code. */
const char *http_status_str(enum http_status s);
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);
} // namespace wpi
#endif

View File

@@ -1,31 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_RAW_SOCKET_ISTREAM_H_
#define WPIUTIL_WPI_RAW_SOCKET_ISTREAM_H_
#include "wpi/raw_istream.h"
namespace wpi {
class NetworkStream;
class raw_socket_istream : public raw_istream {
public:
explicit raw_socket_istream(NetworkStream& stream, int timeout = 0)
: m_stream(stream), m_timeout(timeout) {}
void close() override;
size_t in_avail() const override;
private:
void read_impl(void* data, size_t len) override;
NetworkStream& m_stream;
int m_timeout;
};
} // namespace wpi
#endif // WPIUTIL_WPI_RAW_SOCKET_ISTREAM_H_

View File

@@ -1,39 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_RAW_SOCKET_OSTREAM_H_
#define WPIUTIL_WPI_RAW_SOCKET_OSTREAM_H_
#include "wpi/raw_ostream.h"
namespace wpi {
class NetworkStream;
class raw_socket_ostream : public raw_ostream {
public:
raw_socket_ostream(NetworkStream& stream, bool shouldClose)
: m_stream(stream), m_shouldClose(shouldClose) {}
~raw_socket_ostream() override;
void close();
bool has_error() const { return m_error; }
void clear_error() { m_error = false; }
protected:
void error_detected() { m_error = true; }
private:
void write_impl(const char* data, size_t len) override;
uint64_t current_pos() const override;
NetworkStream& m_stream;
bool m_error = false;
bool m_shouldClose;
};
} // namespace wpi
#endif // WPIUTIL_WPI_RAW_SOCKET_OSTREAM_H_

View File

@@ -1,74 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_RAW_UV_OSTREAM_H_
#define WPIUTIL_WPI_RAW_UV_OSTREAM_H_
#include <functional>
#include <utility>
#include "wpi/SmallVector.h"
#include "wpi/raw_ostream.h"
#include "wpi/span.h"
#include "wpi/uv/Buffer.h"
namespace wpi {
/**
* raw_ostream style output to a SmallVector of uv::Buffer buffers. Fixed-size
* buffers are allocated and appended as necessary to fit the data being output.
* The SmallVector need not be empty at start.
*/
class raw_uv_ostream : public raw_ostream {
public:
/**
* Construct a new raw_uv_ostream.
* @param bufs Buffers vector. NOT cleared on construction.
* @param allocSize Size to allocate for each buffer; allocation will be
* performed using Buffer::Allocate().
*/
raw_uv_ostream(SmallVectorImpl<uv::Buffer>& bufs, size_t allocSize)
: m_bufs(bufs), m_alloc([=] { return uv::Buffer::Allocate(allocSize); }) {
SetUnbuffered();
}
/**
* Construct a new raw_uv_ostream.
* @param bufs Buffers vector. NOT cleared on construction.
* @param alloc Allocator.
*/
raw_uv_ostream(SmallVectorImpl<uv::Buffer>& bufs,
std::function<uv::Buffer()> alloc)
: m_bufs(bufs), m_alloc(std::move(alloc)) {
SetUnbuffered();
}
~raw_uv_ostream() override = default;
/**
* Returns an span to the buffers.
*/
span<uv::Buffer> bufs() { return m_bufs; }
void flush() = delete;
/**
* Resets the amount of allocated space.
*/
void reset() { m_left = 0; }
private:
void write_impl(const char* data, size_t len) override;
uint64_t current_pos() const override;
SmallVectorImpl<uv::Buffer>& m_bufs;
std::function<uv::Buffer()> m_alloc;
// How much allocated space is left in the current buffer.
size_t m_left = 0;
};
} // namespace wpi
#endif // WPIUTIL_WPI_RAW_UV_OSTREAM_H_

View File

@@ -1,174 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_ASYNC_H_
#define WPIUTIL_WPI_UV_ASYNC_H_
#include <uv.h>
#include <memory>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>
#include "wpi/Signal.h"
#include "wpi/mutex.h"
#include "wpi/uv/Handle.h"
#include "wpi/uv/Loop.h"
namespace wpi::uv {
/**
* Async handle.
* Async handles allow the user to "wakeup" the event loop and have a signal
* generated from another thread.
*
* Data may be passed into the callback called on the event loop by using
* template parameters. If data parameters are used, the async callback will
* be called once for every call to Send(). If no data parameters are used,
* the async callback may or may not be called for every call to Send() (e.g.
* the calls may be coaleasced).
*/
template <typename... T>
class Async final : public HandleImpl<Async<T...>, uv_async_t> {
struct private_init {};
public:
Async(const std::shared_ptr<Loop>& loop, const private_init&)
: m_loop{loop} {}
~Async() noexcept override {
if (auto loop = m_loop.lock()) {
this->Close();
} else {
this->ForceClosed();
}
}
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Async> Create(Loop& loop) {
return Create(loop.shared_from_this());
}
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Async> Create(const std::shared_ptr<Loop>& loop) {
auto h = std::make_shared<Async>(loop, private_init{});
int err =
uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
auto& h = *static_cast<Async*>(handle->data);
std::scoped_lock lock(h.m_mutex);
for (auto&& v : h.m_data) {
std::apply(h.wakeup, v);
}
h.m_data.clear();
});
if (err < 0) {
loop->ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
/**
* Wakeup the event loop and emit the event.
*
* Its safe to call this function from any thread including the loop thread.
* An async event will be emitted on the loop thread.
*/
template <typename... U>
void Send(U&&... u) {
auto loop = m_loop.lock();
if (loop && loop->GetThreadId() == std::this_thread::get_id()) {
// called from within the loop, just call the function directly
wakeup(std::forward<U>(u)...);
return;
}
{
std::scoped_lock lock(m_mutex);
m_data.emplace_back(std::forward_as_tuple(std::forward<U>(u)...));
}
if (loop) {
this->Invoke(&uv_async_send, this->GetRaw());
}
}
/**
* Signal generated (on event loop thread) when the async event occurs.
*/
sig::Signal<T...> wakeup;
private:
wpi::mutex m_mutex;
std::vector<std::tuple<T...>> m_data;
std::weak_ptr<Loop> m_loop;
};
/**
* Async specialization for no data parameters. The async callback may or may
* not be called for every call to Send() (e.g. the calls may be coaleasced).
*/
template <>
class Async<> final : public HandleImpl<Async<>, uv_async_t> {
struct private_init {};
public:
Async(const std::shared_ptr<Loop>& loop, const private_init&)
: m_loop(loop) {}
~Async() noexcept override;
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Async> Create(Loop& loop) {
return Create(loop.shared_from_this());
}
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Async> Create(const std::shared_ptr<Loop>& loop);
/**
* Wakeup the event loop and emit the event.
*
* Its safe to call this function from any thread.
* An async event will be emitted on the loop thread.
*/
void Send() {
if (auto loop = m_loop.lock()) {
if (loop->GetThreadId() == std::this_thread::get_id()) {
// called from within the loop, just call the function directly
wakeup();
} else {
Invoke(&uv_async_send, GetRaw());
}
}
}
/**
* Signal generated (on event loop thread) when the async event occurs.
*/
sig::Signal<> wakeup;
private:
std::weak_ptr<Loop> m_loop;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_ASYNC_H_

View File

@@ -1,167 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_ASYNCFUNCTION_H_
#define WPIUTIL_WPI_UV_ASYNCFUNCTION_H_
#include <stdint.h>
#include <uv.h>
#include <functional>
#include <memory>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>
#include "wpi/future.h"
#include "wpi/mutex.h"
#include "wpi/uv/Handle.h"
#include "wpi/uv/Loop.h"
namespace wpi::uv {
template <typename T>
class AsyncFunction;
/**
* Function async handle.
* Async handles allow the user to "wakeup" the event loop and have a function
* called from another thread that returns a result to the calling thread.
*/
template <typename R, typename... T>
class AsyncFunction<R(T...)> final
: public HandleImpl<AsyncFunction<R(T...)>, uv_async_t> {
struct private_init {};
public:
AsyncFunction(const std::shared_ptr<Loop>& loop,
std::function<void(promise<R>, T...)> func, const private_init&)
: wakeup{std::move(func)}, m_loop{loop} {}
~AsyncFunction() noexcept override {
if (auto loop = m_loop.lock()) {
this->Close();
} else {
this->ForceClosed();
}
}
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
* @param func wakeup function to be called (sets wakeup value); the function
* needs to return void, and its first parameter is the promise
* for the result. If no value is set on the promise by the
* wakeup function, a default-constructed value is "returned".
*/
static std::shared_ptr<AsyncFunction> Create(
Loop& loop, std::function<void(promise<R>, T...)> func = nullptr) {
return Create(loop.shared_from_this(), std::move(func));
}
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
* @param func wakeup function to be called (sets wakeup value); the function
* needs to return void, and its first parameter is the promise
* for the result. If no value is set on the promise by the
* wakeup function, a default-constructed value is "returned".
*/
static std::shared_ptr<AsyncFunction> Create(
const std::shared_ptr<Loop>& loop,
std::function<void(promise<R>, T...)> func = nullptr) {
auto h =
std::make_shared<AsyncFunction>(loop, std::move(func), private_init{});
int err =
uv_async_init(loop->GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
auto& h = *static_cast<AsyncFunction*>(handle->data);
std::unique_lock lock(h.m_mutex);
if (!h.m_params.empty()) {
// for each set of parameters in the input queue, call the wakeup
// function and put the result in the output queue if the caller is
// waiting for it
for (auto&& v : h.m_params) {
auto p = h.m_promises.CreatePromise(v.first);
if (h.wakeup) {
std::apply(h.wakeup,
std::tuple_cat(std::make_tuple(std::move(p)),
std::move(v.second)));
}
}
h.m_params.clear();
// wake up any threads that might be waiting for the result
lock.unlock();
h.m_promises.Notify();
}
});
if (err < 0) {
loop->ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
/**
* Wakeup the event loop, call the async function, and return a future for
* the result.
*
* Its safe to call this function from any thread including the loop thread.
* The async function will be called on the loop thread.
*
* The future will return a default-constructed result if this handle is
* destroyed while waiting for a result.
*/
template <typename... U>
future<R> Call(U&&... u) {
// create the future
uint64_t req = m_promises.CreateRequest();
auto loop = m_loop.lock();
if (loop && loop->GetThreadId() == std::this_thread::get_id()) {
// called from within the loop, just call the function directly
wakeup(m_promises.CreatePromise(req), std::forward<U>(u)...);
return m_promises.CreateFuture(req);
}
// add the parameters to the input queue
{
std::scoped_lock lock(m_mutex);
m_params.emplace_back(std::piecewise_construct,
std::forward_as_tuple(req),
std::forward_as_tuple(std::forward<U>(u)...));
}
// signal the loop
if (loop) {
this->Invoke(&uv_async_send, this->GetRaw());
}
// return future
return m_promises.CreateFuture(req);
}
template <typename... U>
future<R> operator()(U&&... u) {
return Call(std::forward<U>(u)...);
}
/**
* Function called (on event loop thread) when the async is called.
*/
std::function<void(promise<R>, T...)> wakeup;
private:
wpi::mutex m_mutex;
std::vector<std::pair<uint64_t, std::tuple<T...>>> m_params;
PromiseFactory<R> m_promises;
std::weak_ptr<Loop> m_loop;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_ASYNCFUNCTION_H_

View File

@@ -1,163 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_BUFFER_H_
#define WPIUTIL_WPI_UV_BUFFER_H_
#include <uv.h>
#include <cstring>
#include <initializer_list>
#include <string_view>
#include <utility>
#include "wpi/SmallVector.h"
#include "wpi/span.h"
namespace wpi::uv {
/**
* Data buffer. Convenience wrapper around uv_buf_t.
*/
class Buffer : public uv_buf_t {
public:
Buffer() {
base = nullptr;
len = 0;
}
/*implicit*/ Buffer(const uv_buf_t& oth) { // NOLINT
base = oth.base;
len = oth.len;
}
/*implicit*/ Buffer(std::string_view str) // NOLINT
: Buffer{str.data(), str.size()} {}
/*implicit*/ Buffer(span<const uint8_t> arr) // NOLINT
: Buffer{reinterpret_cast<const char*>(arr.data()), arr.size()} {}
Buffer(char* base_, size_t len_) {
base = base_;
len = static_cast<decltype(len)>(len_);
}
Buffer(const char* base_, size_t len_) {
base = const_cast<char*>(base_);
len = static_cast<decltype(len)>(len_);
}
span<const char> data() const { return {base, len}; }
span<char> data() { return {base, len}; }
operator span<const char>() const { return data(); } // NOLINT
operator span<char>() { return data(); } // NOLINT
static Buffer Allocate(size_t size) { return Buffer{new char[size], size}; }
static Buffer Dup(std::string_view in) {
Buffer buf = Allocate(in.size());
std::memcpy(buf.base, in.data(), in.size());
return buf;
}
static Buffer Dup(span<const uint8_t> in) {
Buffer buf = Allocate(in.size());
std::memcpy(buf.base, in.begin(), in.size());
return buf;
}
Buffer Dup() const {
Buffer buf = Allocate(len);
std::memcpy(buf.base, base, len);
return buf;
}
void Deallocate() {
delete[] base;
base = nullptr;
len = 0;
}
Buffer Move() {
Buffer buf = *this;
base = nullptr;
len = 0;
return buf;
}
friend void swap(Buffer& a, Buffer& b) {
using std::swap;
swap(a.base, b.base);
swap(a.len, b.len);
}
};
/**
* A simple pool allocator for Buffers.
* Buffers are allocated individually but are reused rather than returned
* to the heap.
* @tparam DEPTH depth of pool
*/
template <size_t DEPTH = 4>
class SimpleBufferPool {
public:
/**
* Constructor.
* @param size Size of each buffer to allocate.
*/
explicit SimpleBufferPool(size_t size = 4096) : m_size{size} {}
~SimpleBufferPool() { Clear(); }
SimpleBufferPool(const SimpleBufferPool& other) = delete;
SimpleBufferPool& operator=(const SimpleBufferPool& other) = delete;
/**
* Allocate a buffer.
*/
Buffer Allocate() {
if (m_pool.empty()) {
return Buffer::Allocate(m_size);
}
auto buf = m_pool.back();
m_pool.pop_back();
buf.len = m_size;
return buf;
}
/**
* Allocate a buffer.
*/
Buffer operator()() { return Allocate(); }
/**
* Release allocated buffers back into the pool.
* This is NOT safe to use with arbitrary buffers unless they were
* allocated with the same size as the buffer pool allocation size.
*/
void Release(span<Buffer> bufs) {
for (auto& buf : bufs) {
m_pool.emplace_back(buf.Move());
}
}
/**
* Clear the pool, releasing all buffers.
*/
void Clear() {
for (auto& buf : m_pool) {
buf.Deallocate();
}
m_pool.clear();
}
/**
* Get number of buffers left in the pool before a new buffer will be
* allocated from the heap.
*/
size_t Remaining() const { return m_pool.size(); }
private:
SmallVector<Buffer, DEPTH> m_pool;
size_t m_size; // NOLINT
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_BUFFER_H_

View File

@@ -1,65 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_CHECK_H_
#define WPIUTIL_WPI_UV_CHECK_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi::uv {
class Loop;
/**
* Check handle.
* Check handles will generate a signal once per loop iteration, right
* after polling for I/O.
*/
class Check final : public HandleImpl<Check, uv_check_t> {
struct private_init {};
public:
explicit Check(const private_init&) {}
~Check() noexcept override = default;
/**
* Create a check handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Check> Create(Loop& loop);
/**
* Create a check handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Check> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Start the handle.
*/
void Start();
/**
* Stop the handle. The signal will no longer be generated.
*/
void Stop() { Invoke(&uv_check_stop, GetRaw()); }
/**
* Signal generated once per loop iteration after polling for I/O.
*/
sig::Signal<> check;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_CHECK_H_

View File

@@ -1,46 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_ERROR_H_
#define WPIUTIL_WPI_UV_ERROR_H_
#include <uv.h>
namespace wpi::uv {
/**
* Error code.
*/
class Error {
public:
Error() = default;
explicit Error(int err) : m_err(err) {}
/**
* Boolean conversion. Returns true if error, false if ok.
*/
explicit operator bool() const { return m_err < 0; }
/**
* Returns the error code.
*/
int code() const { return m_err; }
/**
* Returns the error message.
*/
const char* str() const { return uv_strerror(m_err); }
/**
* Returns the error name.
*/
const char* name() const { return uv_err_name(m_err); }
private:
int m_err{UV_UNKNOWN};
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_ERROR_H_

View File

@@ -1,79 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_FSEVENT_H_
#define WPIUTIL_WPI_UV_FSEVENT_H_
#include <uv.h>
#include <memory>
#include <string>
#include <string_view>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi::uv {
class Loop;
/**
* Filesystem Event handle.
*/
class FsEvent final : public HandleImpl<FsEvent, uv_fs_event_t> {
struct private_init {};
public:
explicit FsEvent(const private_init&) {}
~FsEvent() noexcept override = default;
/**
* Create a filesystem event handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<FsEvent> Create(Loop& loop);
/**
* Create a filesystem event handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<FsEvent> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Start watching the specified path for changes.
*
* @param path Path to watch for changes
* @param events Bitmask of event flags. Only UV_FS_EVENT_RECURSIVE is
* supported (and only on OSX and Windows).
*/
void Start(std::string_view path, unsigned int flags = 0);
/**
* Stop watching for changes.
*/
void Stop() { Invoke(&uv_fs_event_stop, GetRaw()); }
/**
* Get the path being monitored. Signals error and returns empty string if
* an error occurs.
* @return Monitored path.
*/
std::string GetPath();
/**
* Signal generated when a filesystem change occurs. The first parameter
* is the filename (if a directory was passed to Start(), the filename is
* relative to that directory). The second parameter is an ORed mask of
* UV_RENAME and UV_CHANGE.
*/
sig::Signal<const char*, int> fsEvent;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_FSEVENT_H_

View File

@@ -1,119 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_GETADDRINFO_H_
#define WPIUTIL_WPI_UV_GETADDRINFO_H_
#include <uv.h>
#include <functional>
#include <memory>
#include <string_view>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/uv/Request.h"
namespace wpi::uv {
class Loop;
/**
* GetAddrInfo request.
* For use with `GetAddrInfo()` function family.
*/
class GetAddrInfoReq : public RequestImpl<GetAddrInfoReq, uv_getaddrinfo_t> {
public:
GetAddrInfoReq();
Loop& GetLoop() const { return *static_cast<Loop*>(GetRaw()->loop->data); }
/**
* Resolved lookup signal.
* Parameter is resolved address info.
*/
sig::Signal<const addrinfo&> resolved;
};
/**
* Asynchronous getaddrinfo(3). HandleResolvedAddress() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* Either node or service may be empty but not both.
*
* @param loop Event loop
* @param req request
* @param node Either a numerical network address or a network hostname.
* @param service Either a service name or a port number as a string.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
*/
void GetAddrInfo(Loop& loop, const std::shared_ptr<GetAddrInfoReq>& req,
std::string_view node, std::string_view service = {},
const addrinfo* hints = nullptr);
/**
* Asynchronous getaddrinfo(3). HandleResolvedAddress() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* Either node or service may be empty but not both.
*
* @param loop Event loop
* @param req request
* @param node Either a numerical network address or a network hostname.
* @param service Either a service name or a port number as a string.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
*/
inline void GetAddrInfo(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetAddrInfoReq>& req,
std::string_view node, std::string_view service = {},
const addrinfo* hints = nullptr) {
GetAddrInfo(*loop, req, node, service, hints);
}
/**
* Asynchronous getaddrinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
* Either node or service may be empty but not both.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param node Either a numerical network address or a network hostname.
* @param service Either a service name or a port number as a string.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
*/
void GetAddrInfo(Loop& loop, std::function<void(const addrinfo&)> callback,
std::string_view node, std::string_view service = {},
const addrinfo* hints = nullptr);
/**
* Asynchronous getaddrinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
* Either node or service may be empty but not both.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param node Either a numerical network address or a network hostname.
* @param service Either a service name or a port number as a string.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
*/
inline void GetAddrInfo(const std::shared_ptr<Loop>& loop,
std::function<void(const addrinfo&)> callback,
std::string_view node, std::string_view service = {},
const addrinfo* hints = nullptr) {
GetAddrInfo(*loop, std::move(callback), node, service, hints);
}
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_GETADDRINFO_H_

View File

@@ -1,228 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_GETNAMEINFO_H_
#define WPIUTIL_WPI_UV_GETNAMEINFO_H_
#include <uv.h>
#include <functional>
#include <memory>
#include <string_view>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/uv/Request.h"
namespace wpi::uv {
class Loop;
/**
* GetNameInfo request.
* For use with `GetNameInfo()` function family.
*/
class GetNameInfoReq : public RequestImpl<GetNameInfoReq, uv_getnameinfo_t> {
public:
GetNameInfoReq();
Loop& GetLoop() const { return *static_cast<Loop*>(GetRaw()->loop->data); }
/**
* Resolved lookup signal.
* Parameters are hostname and service.
*/
sig::Signal<const char*, const char*> resolved;
};
/**
* Asynchronous getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
const sockaddr& addr, int flags = 0);
/**
* Asynchronous getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
inline void GetNameInfo(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetNameInfoReq>& req,
const sockaddr& addr, int flags = 0) {
GetNameInfo(*loop, req, addr, flags);
}
/**
* Asynchronous getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo(Loop& loop,
std::function<void(const char*, const char*)> callback,
const sockaddr& addr, int flags = 0);
/**
* Asynchronous getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional flags to modify the behavior of `getnameinfo`.
* @return Connection object for the callback
*/
inline void GetNameInfo(const std::shared_ptr<Loop>& loop,
std::function<void(const char*, const char*)> callback,
const sockaddr& addr, int flags = 0) {
GetNameInfo(*loop, std::move(callback), addr, flags);
}
/**
* Asynchronous IPv4 getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param ip A valid IPv4 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo4(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
std::string_view ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv4 getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param ip A valid IPv4 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
inline void GetNameInfo4(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetNameInfoReq>& req,
std::string_view ip, unsigned int port,
int flags = 0) {
return GetNameInfo4(*loop, req, ip, port, flags);
}
/**
* Asynchronous IPv4 getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param ip A valid IPv4 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo4(Loop& loop,
std::function<void(const char*, const char*)> callback,
std::string_view ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv4 getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
* @param loop Event loop
* @param ip A valid IPv4 address
* @param port A valid port number
* @param callback Callback function to call when resolution completes
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
inline void GetNameInfo4(const std::shared_ptr<Loop>& loop,
std::function<void(const char*, const char*)> callback,
std::string_view ip, unsigned int port,
int flags = 0) {
return GetNameInfo4(*loop, std::move(callback), ip, port, flags);
}
/**
* Asynchronous IPv6 getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param ip A valid IPv6 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo6(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
std::string_view ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv6 getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param ip A valid IPv6 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
inline void GetNameInfo6(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetNameInfoReq>& req,
std::string_view ip, unsigned int port,
int flags = 0) {
GetNameInfo6(*loop, req, ip, port, flags);
}
/**
* Asynchronous IPv6 getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param ip A valid IPv6 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo6(Loop& loop,
std::function<void(const char*, const char*)> callback,
std::string_view ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv6 getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param ip A valid IPv6 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
inline void GetNameInfo6(const std::shared_ptr<Loop>& loop,
std::function<void(const char*, const char*)> callback,
std::string_view ip, unsigned int port,
int flags = 0) {
return GetNameInfo6(*loop, std::move(callback), ip, port, flags);
}
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_GETNAMEINFO_H_

View File

@@ -1,297 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_HANDLE_H_
#define WPIUTIL_WPI_UV_HANDLE_H_
#include <uv.h>
#include <cstdlib>
#include <functional>
#include <memory>
#include <string_view>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/uv/Buffer.h"
#include "wpi/uv/Error.h"
#include "wpi/uv/Loop.h"
namespace wpi::uv {
/**
* Handle.
* Handles are not moveable or copyable and cannot be directly constructed.
* This class provides shared_ptr ownership and shared_from_this.
* Use the specific handle type Create() functions to create handles.
*/
class Handle : public std::enable_shared_from_this<Handle> {
public:
using Type = uv_handle_type;
Handle(const Handle&) = delete;
Handle(Handle&&) = delete;
Handle& operator=(const Handle&) = delete;
Handle& operator=(Handle&&) = delete;
virtual ~Handle() noexcept;
/**
* Get the type of the handle.
*
* A base handle offers no functionality to promote it to the actual handle
* type. By means of this function, the type of the underlying handle as
* specified by Type is made available.
*
* @return The actual type of the handle.
*/
Type GetType() const noexcept { return m_uv_handle->type; }
/**
* Get the name of the type of the handle. E.g. "pipe" for pipe handles.
*/
std::string_view GetTypeName() const noexcept {
return uv_handle_type_name(m_uv_handle->type);
}
/**
* Get the loop where this handle runs.
*
* @return The loop.
*/
std::shared_ptr<Loop> GetLoop() const noexcept {
return GetLoopRef().shared_from_this();
}
/**
* Get the loop where this handle runs.
*
* @return The loop.
*/
Loop& GetLoopRef() const noexcept {
return *static_cast<Loop*>(m_uv_handle->loop->data);
}
/**
* Check if the handle is active.
*
* What _active_ means depends on the type of handle:
*
* * An AsyncHandle handle is always active and cannot be deactivated,
* except by closing it with uv_close().
* * A PipeHandle, TcpHandle, UDPHandle, etc. handle - basically any handle
* that deals with I/O - is active when it is doing something that involves
* I/O, like reading, writing, connecting, accepting new connections, etc.
* * A CheckHandle, IdleHandle, TimerHandle, etc. handle is active when it
* has been started with a call to `Start()`.
*
* Rule of thumb: if a handle of type `FooHandle` has a `Start()` member
* method, then its active from the moment that method is called. Likewise,
* `Stop()` deactivates the handle again.
*
* @return True if the handle is active, false otherwise.
*/
bool IsActive() const noexcept { return uv_is_active(m_uv_handle) != 0; }
/**
* Check if a handle is closing or closed.
*
* This function should only be used between the initialization of the
* handle and the arrival of the close callback.
*
* @return True if the handle is closing or closed, false otherwise.
*/
bool IsClosing() const noexcept {
return m_closed || uv_is_closing(m_uv_handle) != 0;
}
/**
* Request handle to be closed.
*
* This **must** be called on each handle before memory is released.
* In-progress requests are cancelled and this can result in error() being
* emitted.
*
* The handle will emit closed() when finished.
*/
void Close() noexcept;
/**
* Set if the loop is closing.
*
* This is set during EventLoopRunner.Stop(), and can be used for other cases
* to indicate the loop should be closing. For instance for a uv_walk loop can
* use this to close existing handles.
*
* @param loopClosing true to set the loop currently in closing stages.
*/
void SetLoopClosing(bool loopClosing) noexcept {
m_loopClosing = loopClosing;
}
/**
* Get the loop closing status.
*
* This can be used from closed() in order to tell if a closing loop is the
* reason for the close, or another reason.
*
* @return true if the loop is closing, otherwise false.
*/
bool IsLoopClosing() const noexcept { return m_loopClosing; }
/**
* Reference the given handle.
*
* References are idempotent, that is, if a handle is already referenced
* calling this function again will have no effect.
*/
void Reference() noexcept { uv_ref(m_uv_handle); }
/**
* Unreference the given handle.
*
* References are idempotent, that is, if a handle is not referenced calling
* this function again will have no effect.
*/
void Unreference() noexcept { uv_unref(m_uv_handle); }
/**
* Check if the given handle is referenced.
* @return True if the handle is referenced, false otherwise.
*/
bool HasReference() const noexcept { return uv_has_ref(m_uv_handle) != 0; }
/**
* Return the size of the underlying handle type.
* @return The size of the underlying handle type.
*/
size_t RawSize() const noexcept { return uv_handle_size(m_uv_handle->type); }
/**
* Get the underlying handle data structure.
*
* @return The underlying handle data structure.
*/
uv_handle_t* GetRawHandle() const noexcept { return m_uv_handle; }
/**
* Set the functions used for allocating and releasing buffers. The size
* passed to the allocator function is a "suggested" size--it's just an
* indication, not related in any way to the pending data to be read. The
* user is free to allocate the amount of memory they decide. For example,
* applications with custom allocation schemes may decide to use a different
* size which matches the memory chunks they already have for other purposes.
*
* @warning Be very careful changing the allocator after the loop has started
* running; there are no interlocks between this and buffers currently in
* flight.
*
* @param alloc Allocation function
* @param dealloc Deallocation function
*/
void SetBufferAllocator(std::function<Buffer(size_t)> alloc,
std::function<void(Buffer&)> dealloc) {
m_allocBuf = std::move(alloc);
m_freeBuf = std::move(dealloc);
}
/**
* Free a buffer. Uses the function provided to SetBufFree() or
* Buffer::Deallocate by default.
*
* @param buf The buffer
*/
void FreeBuf(Buffer& buf) const noexcept { m_freeBuf(buf); }
/**
* Gets user-defined data.
* @return User-defined data if any, nullptr otherwise.
*/
template <typename T = void>
std::shared_ptr<T> GetData() const {
return std::static_pointer_cast<T>(m_data);
}
/**
* Sets user-defined data.
* @param data User-defined arbitrary data.
*/
void SetData(std::shared_ptr<void> data) { m_data = std::move(data); }
/**
* Error signal
*/
sig::Signal<Error> error;
/**
* Closed signal
*/
sig::Signal<> closed;
/**
* Report an error.
* @param err Error code
*/
void ReportError(int err) const { error(Error(err)); }
protected:
explicit Handle(uv_handle_t* uv_handle) : m_uv_handle{uv_handle} {
m_uv_handle->data = this;
}
void Keep() noexcept { m_self = shared_from_this(); }
void Release() noexcept { m_self.reset(); }
void ForceClosed() noexcept { m_closed = true; }
static void AllocBuf(uv_handle_t* handle, size_t size, uv_buf_t* buf);
static void DefaultFreeBuf(Buffer& buf);
template <typename F, typename... Args>
bool Invoke(F&& f, Args&&... args) const {
auto err = std::forward<F>(f)(std::forward<Args>(args)...);
if (err < 0) {
ReportError(err);
}
return err == 0;
}
private:
std::shared_ptr<Handle> m_self;
uv_handle_t* m_uv_handle;
bool m_closed = false;
bool m_loopClosing = false;
std::function<Buffer(size_t)> m_allocBuf{&Buffer::Allocate};
std::function<void(Buffer&)> m_freeBuf{&DefaultFreeBuf};
std::shared_ptr<void> m_data;
};
/**
* Handle.
*/
template <typename T, typename U>
class HandleImpl : public Handle {
public:
std::shared_ptr<T> shared_from_this() {
return std::static_pointer_cast<T>(Handle::shared_from_this());
}
std::shared_ptr<const T> shared_from_this() const {
return std::static_pointer_cast<const T>(Handle::shared_from_this());
}
/**
* Get the underlying handle data structure.
*
* @return The underlying handle data structure.
*/
U* GetRaw() const noexcept {
return reinterpret_cast<U*>(this->GetRawHandle());
}
protected:
HandleImpl() : Handle{static_cast<uv_handle_t*>(std::malloc(sizeof(U)))} {}
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_HANDLE_H_

View File

@@ -1,74 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_IDLE_H_
#define WPIUTIL_WPI_UV_IDLE_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi::uv {
class Loop;
/**
* Idle handle.
*
* Idle handles will generate a signal once per loop iteration, right
* before the Prepare handles.
*
* The notable difference with Prepare handles is that when there are active
* idle handles, the loop will perform a zero timeout poll instead of blocking
* for I/O.
*
* @warning Despite the name, idle handles will signal every loop iteration,
* not when the loop is actually "idle". This also means they can easly become
* CPU hogs.
*/
class Idle final : public HandleImpl<Idle, uv_idle_t> {
struct private_init {};
public:
explicit Idle(const private_init&) {}
~Idle() noexcept override = default;
/**
* Create an idle handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Idle> Create(Loop& loop);
/**
* Create an idle handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Idle> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Start the handle.
*/
void Start();
/**
* Stop the handle. The signal will no longer be generated.
*/
void Stop() { Invoke(&uv_idle_stop, GetRaw()); }
/**
* Signal generated once per loop iteration prior to Prepare signals.
*/
sig::Signal<> idle;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_IDLE_H_

View File

@@ -1,253 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_LOOP_H_
#define WPIUTIL_WPI_UV_LOOP_H_
#include <uv.h>
#include <atomic>
#include <chrono>
#include <functional>
#include <memory>
#include <thread>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/function_ref.h"
#include "wpi/uv/Error.h"
namespace wpi::uv {
class Handle;
/**
* Event loop.
*
* The event loop is the central part of uv functionality. It takes care of
* polling for I/O and scheduling signals to be generated based on different
* sources of events.
*
* The event loop is not moveable, copyable, or directly constructible. Use
* Create() to create an event loop, or GetDefault() to get the default loop
* if you know your program will only have a single one.
*/
class Loop final : public std::enable_shared_from_this<Loop> {
struct private_init {};
public:
using Time = std::chrono::duration<uint64_t, std::milli>;
enum Mode {
kDefault = UV_RUN_DEFAULT,
kOnce = UV_RUN_ONCE,
kNoWait = UV_RUN_NOWAIT
};
explicit Loop(const private_init&) noexcept;
Loop(const Loop&) = delete;
Loop& operator=(const Loop&) = delete;
Loop(Loop&& oth) = delete;
Loop& operator=(Loop&& oth) = delete;
~Loop() noexcept;
/**
* Create a new event loop. The created loop is not the default event loop.
*
* @return The newly created loop. May return nullptr if a failure occurs.
*/
static std::shared_ptr<Loop> Create();
/**
* Create the default event loop. Only use this event loop if a single loop
* is needed for the entire application.
*
* @return The newly created loop. May return nullptr if a failure occurs.
*/
static std::shared_ptr<Loop> GetDefault();
/**
* Release all internal loop resources.
*
* Call this function only when the loop has finished executing and all open
* handles and requests have been closed, or the loop will emit an error.
*
* error() will be emitted in case of errors.
*/
void Close();
/**
* Run the event loop.
*
* Available modes are:
*
* * `Loop::kDefault`: Run the event loop until there are no
* active and referenced handles or requests.
* * `Loop::kOnce`: Run a single event loop iteration. Note that this
* function blocksif there are no pending callbacks.
* * `Loop::kNoWait`: Run a single event loop iteration, but don't block
* if there are no pending callbacks.
*
* @return True when done, false in all other cases.
*/
bool Run(Mode mode = kDefault) {
m_tid = std::this_thread::get_id();
int rv = uv_run(m_loop, static_cast<uv_run_mode>(static_cast<int>(mode)));
m_tid = std::thread::id{};
return rv == 0;
}
/**
* Check if there are active resources.
*
* @return True if there are active resources in the loop.
*/
bool IsAlive() const noexcept { return uv_loop_alive(m_loop) != 0; }
/**
* Stop the event loop.
*
* This will cause Run() to end as soon as possible.
* This will happen not sooner than the next loop iteration.
* If this function was called before blocking for I/O, the loop wont block
* for I/O on this iteration.
*/
void Stop() noexcept { uv_stop(m_loop); }
/**
* Get backend file descriptor.
*
* Only kqueue, epoll and event ports are supported.
* This can be used in conjunction with `run(Loop::kNoWait)` to poll
* in one thread and run the event loops callbacks in another.
*
* @return The backend file descriptor.
*/
int GetDescriptor() const noexcept { return uv_backend_fd(m_loop); }
/**
* Get the poll timeout.
*
* @return A `std::pair` composed of a boolean value that is true in case of
* valid timeout, false otherwise, and the timeout
* (`std::chrono::duration<uint64_t, std::milli>`).
*/
std::pair<bool, Time> GetTimeout() const noexcept {
auto to = uv_backend_timeout(m_loop);
return std::make_pair(to == -1, Time{to});
}
/**
* Return the current timestamp in milliseconds.
*
* The timestamp is cached at the start of the event loop tick.
* The timestamp increases monotonically from some arbitrary point in
* time.
* Dont make assumptions about the starting point, you will only get
* disappointed.
*
* @return The current timestamp in milliseconds (actual type is
* `std::chrono::duration<uint64_t, std::milli>`).
*/
Time Now() const noexcept { return Time{uv_now(m_loop)}; }
/**
* Update the event loops concept of _now_.
*
* The current time is cached at the start of the event loop tick in order
* to reduce the number of time-related system calls.
* You wont normally need to call this function unless you have callbacks
* that block the event loop for longer periods of time, where _longer_ is
* somewhat subjective but probably on the order of a millisecond or more.
*/
void UpdateTime() noexcept { uv_update_time(m_loop); }
/**
* Walk the list of handles.
*
* The callback will be executed once for each handle that is still active.
*
* @param callback A function to be invoked once for each active handle.
*/
void Walk(function_ref<void(Handle&)> callback);
/**
* Reinitialize any kernel state necessary in the child process after
* a fork(2) system call.
*
* Previously started watchers will continue to be started in the child
* process.
*
* It is necessary to explicitly call this function on every event loop
* created in the parent process that you plan to continue to use in the
* child, including the default loop (even if you dont continue to use it
* in the parent). This function must be called before calling any API
* function using the loop in the child. Failure to do so will result in
* undefined behaviour, possibly including duplicate events delivered to
* both parent and child or aborting the child process.
*
* When possible, it is preferred to create a new loop in the child process
* instead of reusing a loop created in the parent. New loops created in the
* child process after the fork should not use this function.
*
* Note that this function is not implemented on Windows.
* Note also that this function is experimental in `libuv`. It may contain
* bugs, and is subject to change or removal. API and ABI stability is not
* guaranteed.
*
* error() will be emitted in case of errors.
*/
void Fork();
/**
* Get the underlying event loop data structure.
*
* @return The underlying event loop data structure.
*/
uv_loop_t* GetRaw() const noexcept { return m_loop; }
/**
* Gets user-defined data.
* @return User-defined data if any, nullptr otherwise.
*/
template <typename T = void>
std::shared_ptr<T> GetData() const {
return std::static_pointer_cast<T>(m_data);
}
/**
* Sets user-defined data.
* @param data User-defined arbitrary data.
*/
void SetData(std::shared_ptr<void> data) { m_data = std::move(data); }
/**
* Get the thread id of the loop thread. If the loop is not currently
* running, returns default-constructed thread id.
*/
std::thread::id GetThreadId() const { return m_tid; }
/**
* Error signal
*/
sig::Signal<Error> error;
/**
* Reports error.
* @param err Error code
*/
void ReportError(int err) { error(Error(err)); }
private:
std::shared_ptr<void> m_data;
uv_loop_t* m_loop;
uv_loop_t m_loopStruct;
std::atomic<std::thread::id> m_tid;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_LOOP_H_

View File

@@ -1,153 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_NETWORKSTREAM_H_
#define WPIUTIL_WPI_UV_NETWORKSTREAM_H_
#include <uv.h>
#include <cstdlib>
#include <functional>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Stream.h"
namespace wpi::uv {
class NetworkStream;
/**
* Connection request.
*/
class ConnectReq : public RequestImpl<ConnectReq, uv_connect_t> {
public:
ConnectReq();
NetworkStream& GetStream() const {
return *static_cast<NetworkStream*>(GetRaw()->handle->data);
}
/**
* Connection completed signal.
*/
sig::Signal<> connected;
};
/**
* Network stream handle.
* This is an abstract type; there are two network stream implementations (Tcp
* and Pipe).
*/
class NetworkStream : public Stream {
public:
static constexpr int kDefaultBacklog = 128;
std::shared_ptr<NetworkStream> shared_from_this() {
return std::static_pointer_cast<NetworkStream>(Handle::shared_from_this());
}
std::shared_ptr<const NetworkStream> shared_from_this() const {
return std::static_pointer_cast<const NetworkStream>(
Handle::shared_from_this());
}
/**
* Start listening for incoming connections. When a new incoming connection
* is received the connection signal is generated.
* @param backlog the number of connections the kernel might queue, same as
* listen(2).
*/
void Listen(int backlog = kDefaultBacklog);
/**
* Start listening for incoming connections. This is a convenience wrapper
* around `Listen(int)` that also connects a callback to the connection
* signal. When a new incoming connection is received the connection signal
* is generated (and the callback is called).
* @param callback the callback to call when a connection is received.
* `Accept()` should be called from this callback.
* @param backlog the number of connections the kernel might queue, same as
* listen(2).
*/
void Listen(std::function<void()> callback, int backlog = kDefaultBacklog);
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a ListenEvent event to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @return The stream handle for the accepted connection, or nullptr on error.
*/
std::shared_ptr<NetworkStream> Accept() {
return DoAccept()->shared_from_this();
}
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a connection signal to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @param client Client stream object.
* @return False on error.
*/
bool Accept(const std::shared_ptr<NetworkStream>& client) {
return Invoke(&uv_accept, GetRawStream(), client->GetRawStream());
}
/**
* Signal generated when an incoming connection is received.
*/
sig::Signal<> connection;
protected:
explicit NetworkStream(uv_stream_t* uv_stream) : Stream{uv_stream} {}
virtual NetworkStream* DoAccept() = 0;
};
template <typename T, typename U>
class NetworkStreamImpl : public NetworkStream {
public:
std::shared_ptr<T> shared_from_this() {
return std::static_pointer_cast<T>(Handle::shared_from_this());
}
std::shared_ptr<const T> shared_from_this() const {
return std::static_pointer_cast<const T>(Handle::shared_from_this());
}
/**
* Get the underlying handle data structure.
*
* @return The underlying handle data structure.
*/
U* GetRaw() const noexcept {
return reinterpret_cast<U*>(this->GetRawHandle());
}
protected:
NetworkStreamImpl()
: NetworkStream{static_cast<uv_stream_t*>(std::malloc(sizeof(U)))} {}
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_NETWORKSTREAM_H_

View File

@@ -1,208 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_PIPE_H_
#define WPIUTIL_WPI_UV_PIPE_H_
#include <uv.h>
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include "wpi/uv/NetworkStream.h"
namespace wpi::uv {
class Loop;
class PipeConnectReq;
/**
* Pipe handle.
* Pipe handles provide an abstraction over local domain sockets on Unix and
* named pipes on Windows.
*/
class Pipe final : public NetworkStreamImpl<Pipe, uv_pipe_t> {
struct private_init {};
public:
explicit Pipe(const private_init&) {}
~Pipe() noexcept override = default;
/**
* Create a pipe handle.
*
* @param loop Loop object where this handle runs.
* @param ipc Indicates if this pipe will be used for handle passing between
* processes.
*/
static std::shared_ptr<Pipe> Create(Loop& loop, bool ipc = false);
/**
* Create a pipe handle.
*
* @param loop Loop object where this handle runs.
* @param ipc Indicates if this pipe will be used for handle passing between
* processes.
*/
static std::shared_ptr<Pipe> Create(const std::shared_ptr<Loop>& loop,
bool ipc = false) {
return Create(*loop, ipc);
}
/**
* Reuse this handle. This closes the handle, and after the close completes,
* reinitializes it (identically to Create) and calls the provided callback.
* Unlike Close(), it does NOT emit the closed signal, however, IsClosing()
* will return true until the callback is called. This does nothing if
* IsClosing() is true (e.g. if Close() was called).
*
* @param ipc IPC
* @param callback Callback
*/
void Reuse(std::function<void()> callback, bool ipc = false);
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a ListenEvent event to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @return The stream handle for the accepted connection, or nullptr on error.
*/
std::shared_ptr<Pipe> Accept();
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a connection signal to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @param client Client stream object.
* @return False on error.
*/
bool Accept(const std::shared_ptr<Pipe>& client) {
return NetworkStream::Accept(client);
}
/**
* Open an existing file descriptor or HANDLE as a pipe.
*
* @note The passed file descriptor or HANDLE is not checked for its type, but
* it's required that it represents a valid pipe.
*
* @param file A valid file handle (either a file descriptor or a HANDLE).
*/
void Open(uv_file file) { Invoke(&uv_pipe_open, GetRaw(), file); }
/**
* Bind the pipe to a file path (Unix) or a name (Windows).
*
* @note Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes,
* typically between 92 and 108 bytes.
*
* @param name File path (Unix) or name (Windows).
*/
void Bind(std::string_view name);
/**
* Connect to the Unix domain socket or the named pipe.
*
* @note Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes,
* typically between 92 and 108 bytes.
*
* HandleConnected() is called on the request when the connection has been
* established.
* HandleError() is called on the request in case of errors during the
* connection.
*
* @param name File path (Unix) or name (Windows).
* @param req connection request
*/
void Connect(std::string_view name,
const std::shared_ptr<PipeConnectReq>& req);
/**
* Connect to the Unix domain socket or the named pipe.
*
* @note Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes,
* typically between 92 and 108 bytes.
*
* The callback is called when the connection has been established. Errors
* are reported to the stream error handler.
*
* @param name File path (Unix) or name (Windows).
* @param callback Callback function to call when connection established
*/
void Connect(std::string_view name, std::function<void()> callback);
/**
* Get the name of the Unix domain socket or the named pipe.
* @return The name (will be empty if an error occurred).
*/
std::string GetSock();
/**
* Get the name of the Unix domain socket or the named pipe to which the
* handle is connected.
* @return The name (will be empty if an error occurred).
*/
std::string GetPeer();
/**
* Set the number of pending pipe instance handles when the pipe server is
* waiting for connections.
* @note This setting applies to Windows only.
* @param count Number of pending handles.
*/
void SetPendingInstances(int count) {
uv_pipe_pending_instances(GetRaw(), count);
}
/**
* Alters pipe permissions, allowing it to be accessed from processes run
* by different users. Makes the pipe writable or readable by all users.
* Mode can be UV_WRITABLE, UV_READABLE, or both. This function is blocking.
* @param flags chmod flags
*/
void Chmod(int flags) { Invoke(&uv_pipe_chmod, GetRaw(), flags); }
private:
Pipe* DoAccept() override;
struct ReuseData {
std::function<void()> callback;
bool ipc;
};
std::unique_ptr<ReuseData> m_reuseData;
};
/**
* Pipe connection request.
*/
class PipeConnectReq : public ConnectReq {
public:
Pipe& GetStream() const {
return *static_cast<Pipe*>(&ConnectReq::GetStream());
}
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_PIPE_H_

View File

@@ -1,121 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_POLL_H_
#define WPIUTIL_WPI_UV_POLL_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi::uv {
class Loop;
/**
* Poll handle.
*/
class Poll final : public HandleImpl<Poll, uv_poll_t> {
struct private_init {};
public:
explicit Poll(const private_init&) {}
~Poll() noexcept override = default;
/**
* Create a poll handle using a file descriptor.
*
* @param loop Loop object where this handle runs.
* @param fd File descriptor.
*/
static std::shared_ptr<Poll> Create(Loop& loop, int fd);
/**
* Create a poll handle using a file descriptor.
*
* @param loop Loop object where this handle runs.
* @param fd File descriptor.
*/
static std::shared_ptr<Poll> Create(const std::shared_ptr<Loop>& loop,
int fd) {
return Create(*loop, fd);
}
/**
* Create a poll handle using a socket descriptor.
*
* @param loop Loop object where this handle runs.
* @param sock Socket descriptor.
*/
static std::shared_ptr<Poll> CreateSocket(Loop& loop, uv_os_sock_t sock);
/**
* Create a poll handle using a socket descriptor.
*
* @param loop Loop object where this handle runs.
* @param sock Socket descriptor.
*/
static std::shared_ptr<Poll> CreateSocket(const std::shared_ptr<Loop>& loop,
uv_os_sock_t sock) {
return CreateSocket(*loop, sock);
}
/**
* Reuse this handle. This closes the handle, and after the close completes,
* reinitializes it (identically to Create) and calls the provided callback.
* Unlike Close(), it does NOT emit the closed signal, however, IsClosing()
* will return true until the callback is called. This does nothing if
* IsClosing() is true (e.g. if Close() was called).
*
* @param fd File descriptor
* @param callback Callback
*/
void Reuse(int fd, std::function<void()> callback);
/**
* Reuse this handle. This closes the handle, and after the close completes,
* reinitializes it (identically to CreateSocket) and calls the provided
* callback. Unlike Close(), it does NOT emit the closed signal, however,
* IsClosing() will return true until the callback is called. This does
* nothing if IsClosing() is true (e.g. if Close() was called).
*
* @param sock Socket descriptor.
* @param callback Callback
*/
void ReuseSocket(uv_os_sock_t sock, std::function<void()> callback);
/**
* Start polling the file descriptor.
*
* @param events Bitmask of events (UV_READABLE, UV_WRITEABLE, UV_PRIORITIZED,
* and UV_DISCONNECT).
*/
void Start(int events);
/**
* Stop polling the file descriptor.
*/
void Stop() { Invoke(&uv_poll_stop, GetRaw()); }
/**
* Signal generated when a poll event occurs.
*/
sig::Signal<int> pollEvent;
private:
struct ReuseData {
std::function<void()> callback;
bool isSocket;
int fd;
uv_os_sock_t sock;
};
std::unique_ptr<ReuseData> m_reuseData;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_POLL_H_

View File

@@ -1,65 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_PREPARE_H_
#define WPIUTIL_WPI_UV_PREPARE_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi::uv {
class Loop;
/**
* Prepare handle.
* Prepare handles will generate a signal once per loop iteration, right
* before polling for I/O.
*/
class Prepare final : public HandleImpl<Prepare, uv_prepare_t> {
struct private_init {};
public:
explicit Prepare(const private_init&) {}
~Prepare() noexcept override = default;
/**
* Create a prepare handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Prepare> Create(Loop& loop);
/**
* Create a prepare handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Prepare> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Start the handle.
*/
void Start();
/**
* Stop the handle. The signal will no longer be generated.
*/
void Stop() { Invoke(&uv_prepare_stop, GetRaw()); }
/**
* Signal generated once per loop iteration prior to polling for I/O.
*/
sig::Signal<> prepare;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_PREPARE_H_

View File

@@ -1,310 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_PROCESS_H_
#define WPIUTIL_WPI_UV_PROCESS_H_
#include <uv.h>
#include <initializer_list>
#include <memory>
#include <string>
#include <string_view>
#include "wpi/Signal.h"
#include "wpi/SmallVector.h"
#include "wpi/span.h"
#include "wpi/uv/Handle.h"
namespace wpi::uv {
class Loop;
class Pipe;
/**
* Process handle.
* Process handles will spawn a new process and allow the user to control it
* and establish communication channels with it using streams.
*/
class Process final : public HandleImpl<Process, uv_process_t> {
struct private_init {};
public:
explicit Process(const private_init&) {}
~Process() noexcept override = default;
/**
* Structure for Spawn() option temporaries. This is a reference type, so if
* this value is stored outside of a temporary, be careful about overwriting
* what it points to.
*/
struct Option {
enum Type {
kNone,
kArg,
kEnv,
kCwd,
kUid,
kGid,
kSetFlags,
kClearFlags,
kStdioIgnore,
kStdioInheritFd,
kStdioInheritPipe,
kStdioCreatePipe
};
Option() : m_type(kNone) {}
/*implicit*/ Option(const char* arg) { // NOLINT
m_data.str = arg;
}
/*implicit*/ Option(const std::string& arg) { // NOLINT
m_data.str = arg.data();
}
/*implicit*/ Option(std::string_view arg) // NOLINT
: m_strData(arg) {
m_data.str = m_strData.c_str();
}
/*implicit*/ Option(const SmallVectorImpl<char>& arg) // NOLINT
: m_strData(arg.data(), arg.size()) {
m_data.str = m_strData.c_str();
}
explicit Option(Type type) : m_type(type) {}
Type m_type = kArg;
std::string m_strData;
union {
const char* str;
uv_uid_t uid;
uv_gid_t gid;
unsigned int flags;
struct {
size_t index;
union {
int fd;
Pipe* pipe;
};
unsigned int flags;
} stdio;
} m_data;
};
/**
* Set environment variable for the subprocess. If not set, the parent's
* environment is used.
* @param env environment variable
*/
static Option Env(std::string_view env) {
Option o(Option::kEnv);
o.m_strData = env;
o.m_data.str = o.m_strData.c_str();
return o;
}
/**
* Set the current working directory for the subprocess.
* @param cwd current working directory
*/
static Option Cwd(std::string_view cwd) {
Option o(Option::kCwd);
o.m_strData = cwd;
o.m_data.str = o.m_strData.c_str();
return o;
}
/**
* Set the child process' user id.
* @param uid user id
*/
static Option Uid(uv_uid_t uid) {
Option o(Option::kUid);
o.m_data.uid = uid;
return o;
}
/**
* Set the child process' group id.
* @param gid group id
*/
static Option Gid(uv_gid_t gid) {
Option o(Option::kGid);
o.m_data.gid = gid;
return o;
}
/**
* Set spawn flags.
* @param flags Bitmask values from uv_process_flags.
*/
static Option SetFlags(unsigned int flags) {
Option o(Option::kSetFlags);
o.m_data.flags = flags;
return o;
}
/**
* Clear spawn flags.
* @param flags Bitmask values from uv_process_flags.
*/
static Option ClearFlags(unsigned int flags) {
Option o(Option::kClearFlags);
o.m_data.flags = flags;
return o;
}
/**
* Explicitly ignore a stdio.
* @param index stdio index
*/
static Option StdioIgnore(size_t index) {
Option o(Option::kStdioIgnore);
o.m_data.stdio.index = index;
return o;
}
/**
* Inherit a file descriptor from the parent process.
* @param index stdio index
* @param fd parent file descriptor
*/
static Option StdioInherit(size_t index, int fd) {
Option o(Option::kStdioInheritFd);
o.m_data.stdio.index = index;
o.m_data.stdio.fd = fd;
return o;
}
/**
* Inherit a pipe from the parent process.
* @param index stdio index
* @param pipe pipe
*/
static Option StdioInherit(size_t index, Pipe& pipe) {
Option o(Option::kStdioInheritPipe);
o.m_data.stdio.index = index;
o.m_data.stdio.pipe = &pipe;
return o;
}
/**
* Create a pipe between the child and the parent.
* @param index stdio index
* @param pipe pipe
* @param flags Some combination of UV_READABLE_PIPE, UV_WRITABLE_PIPE, and
* UV_OVERLAPPED_PIPE (Windows only, ignored on Unix).
*/
static Option StdioCreatePipe(size_t index, Pipe& pipe, unsigned int flags) {
Option o(Option::kStdioCreatePipe);
o.m_data.stdio.index = index;
o.m_data.stdio.pipe = &pipe;
o.m_data.stdio.flags = flags;
return o;
}
/**
* Disables inheritance for file descriptors / handles that this process
* inherited from its parent. The effect is that child processes spawned
* by this process don't accidentally inherit these handles.
*
* It is recommended to call this function as early in your program as
* possible, before the inherited file descriptors can be closed or
* duplicated.
*/
static void DisableStdioInheritance() { uv_disable_stdio_inheritance(); }
/**
* Starts a process. If the process is not successfully spawned, an error
* is generated on the loop and this function returns nullptr.
*
* Possible reasons for failing to spawn would include (but not be limited to)
* the file to execute not existing, not having permissions to use the setuid
* or setgid specified, or not having enough memory to allocate for the new
* process.
*
* @param loop Loop object where this handle runs.
* @param file Path pointing to the program to be executed
* @param options Process options
*/
static std::shared_ptr<Process> SpawnArray(Loop& loop, std::string_view file,
span<const Option> options);
static std::shared_ptr<Process> SpawnArray(
Loop& loop, std::string_view file,
std::initializer_list<Option> options) {
return SpawnArray(loop, file, {options.begin(), options.end()});
}
template <typename... Args>
static std::shared_ptr<Process> Spawn(Loop& loop, std::string_view file,
const Args&... options) {
return SpawnArray(loop, file, {options...});
}
/**
* Starts a process. If the process is not successfully spawned, an error
* is generated on the loop and this function returns nullptr.
*
* Possible reasons for failing to spawn would include (but not be limited to)
* the file to execute not existing, not having permissions to use the setuid
* or setgid specified, or not having enough memory to allocate for the new
* process.
*
* @param loop Loop object where this handle runs.
* @param file Path pointing to the program to be executed
* @param options Process options
*/
static std::shared_ptr<Process> SpawnArray(const std::shared_ptr<Loop>& loop,
std::string_view file,
span<const Option> options) {
return SpawnArray(*loop, file, options);
}
static std::shared_ptr<Process> SpawnArray(
const std::shared_ptr<Loop>& loop, std::string_view file,
std::initializer_list<Option> options) {
return SpawnArray(*loop, file, options);
}
template <typename... Args>
static std::shared_ptr<Process> Spawn(const std::shared_ptr<Loop>& loop,
std::string_view file,
const Args&... options) {
return SpawnArray(*loop, file, {options...});
}
/**
* Sends the specified signal to the process.
* @param signum signal number
*/
void Kill(int signum) { Invoke(&uv_process_kill, GetRaw(), signum); }
/**
* Sends the specified signal to the given PID.
* @param pid process ID
* @param signum signal number
* @return 0 on success, otherwise error code.
*/
static int Kill(int pid, int signum) noexcept { return uv_kill(pid, signum); }
/**
* Get the process ID.
* @return Process ID.
*/
uv_pid_t GetPid() const noexcept { return GetRaw()->pid; }
/**
* Signal generated when the process exits. The parameters are the exit
* status and the signal that caused the process to terminate, if any.
*/
sig::Signal<int64_t, int> exited;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_PROCESS_H_

View File

@@ -1,166 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_REQUEST_H_
#define WPIUTIL_WPI_UV_REQUEST_H_
#include <uv.h>
#include <functional>
#include <memory>
#include "wpi/uv/Error.h"
namespace wpi::uv {
/**
* Request. Requests are not moveable or copyable.
* This class provides shared_ptr ownership and shared_from_this.
*/
class Request : public std::enable_shared_from_this<Request> {
public:
using Type = uv_req_type;
Request(const Request&) = delete;
Request(Request&&) = delete;
Request& operator=(const Request&) = delete;
Request& operator=(Request&&) = delete;
virtual ~Request() noexcept = default;
/**
* Get the type of the request.
*
* A base request offers no functionality to promote it to the actual request
* type. By means of this function, the type of the underlying request as
* specified by Type is made available.
*
* @return The actual type of the request.
*/
Type GetType() const noexcept { return m_uv_req->type; }
/**
* Get the name of the type of the request. E.g. "connect" for connect.
*/
const char* GetTypeName() const noexcept {
return uv_req_type_name(m_uv_req->type);
}
/**
* Cancel a pending request.
*
* This method fails if the request is executing or has finished
* executing.
* It can emit an error signal in case of errors.
*
* @return True in case of success, false otherwise.
*/
bool Cancel() { return uv_cancel(m_uv_req) == 0; }
/**
* Return the size of the underlying request type.
* @return The size of the underlying request type.
*/
size_t RawSize() const noexcept { return uv_req_size(m_uv_req->type); }
/**
* Get the underlying request data structure.
*
* @return The underlying request data structure.
*/
uv_req_t* GetRawReq() noexcept { return m_uv_req; }
/**
* Get the underlying request data structure.
*
* @return The underlying request data structure.
*/
const uv_req_t* GetRawReq() const noexcept { return m_uv_req; }
/**
* Keep this request in memory even if no outside shared_ptr references
* remain. To release call Release().
*
* Derived classes can override this method for different memory management
* approaches (e.g. pooled storage of requests).
*/
virtual void Keep() noexcept { m_self = shared_from_this(); }
/**
* No longer force holding this request in memory. Does not immediately
* destroy the object unless no outside shared_ptr references remain.
*
* Derived classes can override this method for different memory management
* approaches (e.g. pooled storage of requests).
*/
virtual void Release() noexcept { m_self.reset(); }
/**
* Error callback. By default, this is set up to report errors to the handle
* that created this request.
* @param err error code
*/
std::function<void(Error)> error;
/**
* Report an error.
* @param err Error code
*/
void ReportError(int err) { error(Error(err)); }
protected:
/**
* Constructor.
*/
explicit Request(uv_req_t* uv_req) : m_uv_req{uv_req} {
m_uv_req->data = this;
}
private:
std::shared_ptr<Request> m_self;
uv_req_t* m_uv_req;
};
/**
* Request. Requests are not moveable or copyable.
* @tparam T CRTP derived class
* @tparam U underlying libuv request type
*/
template <typename T, typename U>
class RequestImpl : public Request {
public:
std::shared_ptr<T> shared_from_this() {
return std::static_pointer_cast<T>(this->shared_from_this());
}
std::shared_ptr<const T> shared_from_this() const {
return std::static_pointer_cast<const T>(this->shared_from_this());
}
/**
* Get the underlying request data structure.
*
* @return The underlying request data structure.
*/
U* GetRaw() noexcept { return &m_uv_req; }
/**
* Get the underlying request data structure.
*
* @return The underlying request data structure.
*/
const U* GetRaw() const noexcept { return &m_uv_req; }
protected:
/**
* Constructor.
*/
RequestImpl() : Request{reinterpret_cast<uv_req_t*>(&m_uv_req)} {}
private:
U m_uv_req;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_REQUEST_H_

View File

@@ -1,79 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_SIGNAL_H_
#define WPIUTIL_WPI_UV_SIGNAL_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi::uv {
class Loop;
/**
* Signal handle.
*/
class Signal final : public HandleImpl<Signal, uv_signal_t> {
struct private_init {};
public:
explicit Signal(const private_init&) {}
~Signal() noexcept override = default;
/**
* Create a signal handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Signal> Create(Loop& loop);
/**
* Create a signal handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Signal> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Start watching for the given signal.
*
* @param signum Signal to watch for.
*/
void Start(int signum);
/**
* Start watching for the given signal. Same as Start() but the signal
* handler is reset the moment the signal is received.
*
* @param signum Signal to watch for.
*/
void StartOneshot(int signum);
/**
* Stop watching for the signal.
*/
void Stop() { Invoke(&uv_signal_stop, GetRaw()); }
/**
* Get the signal being monitored.
* @return Signal number.
*/
int GetSignal() const { return GetRaw()->signum; }
/**
* Signal generated when a signal occurs.
*/
sig::Signal<int> signal;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_SIGNAL_H_

View File

@@ -1,302 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_STREAM_H_
#define WPIUTIL_WPI_UV_STREAM_H_
#include <uv.h>
#include <cstdlib>
#include <functional>
#include <initializer_list>
#include <memory>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/span.h"
#include "wpi/uv/Buffer.h"
#include "wpi/uv/Handle.h"
#include "wpi/uv/Request.h"
namespace wpi::uv {
class Stream;
/**
* Shutdown request.
*/
class ShutdownReq : public RequestImpl<ShutdownReq, uv_shutdown_t> {
public:
ShutdownReq();
Stream& GetStream() const {
return *static_cast<Stream*>(GetRaw()->handle->data);
}
/**
* Shutdown completed signal.
*/
sig::Signal<> complete;
};
/**
* Write request.
*/
class WriteReq : public RequestImpl<WriteReq, uv_write_t> {
public:
WriteReq();
Stream& GetStream() const {
return *static_cast<Stream*>(GetRaw()->handle->data);
}
/**
* Write completed signal. This is called even if an error occurred.
* @param err error value
*/
sig::Signal<Error> finish;
};
/**
* Stream handle.
* Stream handles provide an abstraction of a duplex communication channel.
* This is an abstract type; there are three stream implementations (Tcp,
* Pipe, and Tty).
*/
class Stream : public Handle {
public:
std::shared_ptr<Stream> shared_from_this() {
return std::static_pointer_cast<Stream>(Handle::shared_from_this());
}
std::shared_ptr<const Stream> shared_from_this() const {
return std::static_pointer_cast<const Stream>(Handle::shared_from_this());
}
/**
* Shutdown the outgoing (write) side of a duplex stream. It waits for pending
* write requests to complete. HandleShutdownComplete() is called on the
* request after shutdown is complete.
*
* @param req shutdown request
*/
void Shutdown(const std::shared_ptr<ShutdownReq>& req);
/**
* Shutdown the outgoing (write) side of a duplex stream. It waits for pending
* write requests to complete. The callback is called after shutdown is
* complete. Errors will be reported to the stream error handler.
*
* @param callback Callback function to call when shutdown completes
* @return Connection object for the callback
*/
void Shutdown(std::function<void()> callback = nullptr);
/**
* Start reading data from an incoming stream.
*
* This will only succeed after a connection has been established.
*
* A data signal will be emitted several times until there is no more
* data to read or `StopRead()` is called.
* An end signal will be emitted when there is no more data to read.
*/
void StartRead();
/**
* Stop reading data from the stream.
*
* This function is idempotent and may be safely called on a stopped stream.
*/
void StopRead() { Invoke(&uv_read_stop, GetRawStream()); }
/**
* Write data to the stream.
*
* Data are written in order. The lifetime of the data pointers passed in
* the `bufs` parameter must exceed the lifetime of the write request.
* An easy way to ensure this is to have the write request keep track of
* the data and use either its Complete() function or destructor to free the
* data.
*
* The finish signal will be emitted on the request object when the data
* has been written (or if an error occurs).
* The error signal will be emitted on the request object in case of errors.
*
* @param bufs The buffers to be written to the stream.
* @param req write request
*/
void Write(span<const Buffer> bufs, const std::shared_ptr<WriteReq>& req);
/**
* Write data to the stream.
*
* Data are written in order. The lifetime of the data pointers passed in
* the `bufs` parameter must exceed the lifetime of the write request.
* An easy way to ensure this is to have the write request keep track of
* the data and use either its Complete() function or destructor to free the
* data.
*
* The finish signal will be emitted on the request object when the data
* has been written (or if an error occurs).
* The error signal will be emitted on the request object in case of errors.
*
* @param bufs The buffers to be written to the stream.
* @param req write request
*/
void Write(std::initializer_list<Buffer> bufs,
const std::shared_ptr<WriteReq>& req) {
Write({bufs.begin(), bufs.end()}, req);
}
/**
* Write data to the stream.
*
* Data are written in order. The lifetime of the data pointers passed in
* the `bufs` parameter must exceed the lifetime of the write request.
* The callback can be used to free data after the request completes.
*
* The callback will be called when the data has been written (even if an
* error occurred). Errors will be reported to the stream error handler.
*
* @param bufs The buffers to be written to the stream.
* @param callback Callback function to call when the write completes
*/
void Write(span<const Buffer> bufs,
std::function<void(span<Buffer>, Error)> callback);
/**
* Write data to the stream.
*
* Data are written in order. The lifetime of the data pointers passed in
* the `bufs` parameter must exceed the lifetime of the write request.
* The callback can be used to free data after the request completes.
*
* The callback will be called when the data has been written (even if an
* error occurred). Errors will be reported to the stream error handler.
*
* @param bufs The buffers to be written to the stream.
* @param callback Callback function to call when the write completes
*/
void Write(std::initializer_list<Buffer> bufs,
std::function<void(span<Buffer>, Error)> callback) {
Write({bufs.begin(), bufs.end()}, std::move(callback));
}
/**
* Queue a write request if it can be completed immediately.
*
* Same as `Write()`, but wont queue a write request if it cant be
* completed immediately.
* An error signal will be emitted in case of errors.
*
* @param bufs The buffers to be written to the stream.
* @return Number of bytes written.
*/
int TryWrite(span<const Buffer> bufs);
/**
* Queue a write request if it can be completed immediately.
*
* Same as `Write()`, but wont queue a write request if it cant be
* completed immediately.
* An error signal will be emitted in case of errors.
*
* @param bufs The buffers to be written to the stream.
* @return Number of bytes written.
*/
int TryWrite(std::initializer_list<Buffer> bufs) {
return TryWrite({bufs.begin(), bufs.end()});
}
/**
* Check if the stream is readable.
* @return True if the stream is readable, false otherwise.
*/
bool IsReadable() const noexcept {
return uv_is_readable(GetRawStream()) == 1;
}
/**
* @brief Checks if the stream is writable.
* @return True if the stream is writable, false otherwise.
*/
bool IsWritable() const noexcept {
return uv_is_writable(GetRawStream()) == 1;
}
/**
* Enable or disable blocking mode for a stream.
*
* When blocking mode is enabled all writes complete synchronously. The
* interface remains unchanged otherwise, e.g. completion or failure of the
* operation will still be reported through events which are emitted
* asynchronously.
*
* @param enable True to enable blocking mode, false otherwise.
* @return True in case of success, false otherwise.
*/
bool SetBlocking(bool enable) noexcept {
return uv_stream_set_blocking(GetRawStream(), enable) == 0;
}
/**
* Gets the amount of queued bytes waiting to be sent.
* @return Amount of queued bytes waiting to be sent.
*/
size_t GetWriteQueueSize() const noexcept {
return GetRawStream()->write_queue_size;
}
/**
* Get the underlying stream data structure.
*
* @return The underlying stream data structure.
*/
uv_stream_t* GetRawStream() const noexcept {
return reinterpret_cast<uv_stream_t*>(GetRawHandle());
}
/**
* Signal generated when data was read on a stream.
*/
sig::Signal<Buffer&, size_t> data;
/**
* Signal generated when no more read data is available.
*/
sig::Signal<> end;
protected:
explicit Stream(uv_stream_t* uv_stream)
: Handle{reinterpret_cast<uv_handle_t*>(uv_stream)} {}
};
template <typename T, typename U>
class StreamImpl : public Stream {
public:
std::shared_ptr<T> shared_from_this() {
return std::static_pointer_cast<T>(Handle::shared_from_this());
}
std::shared_ptr<const T> shared_from_this() const {
return std::static_pointer_cast<const T>(Handle::shared_from_this());
}
/**
* Get the underlying handle data structure.
*
* @return The underlying handle data structure.
*/
U* GetRaw() const noexcept {
return reinterpret_cast<U*>(this->GetRawHandle());
}
protected:
StreamImpl() : Stream{static_cast<uv_stream_t*>(std::malloc(sizeof(U)))} {}
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_STREAM_H_

View File

@@ -1,363 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_TCP_H_
#define WPIUTIL_WPI_UV_TCP_H_
#include <uv.h>
#include <chrono>
#include <functional>
#include <memory>
#include <string_view>
#include <utility>
#include "wpi/uv/NetworkStream.h"
namespace wpi::uv {
class Loop;
class TcpConnectReq;
/**
* TCP handle.
* TCP handles are used to represent both TCP streams and servers.
*/
class Tcp final : public NetworkStreamImpl<Tcp, uv_tcp_t> {
struct private_init {};
public:
using Time = std::chrono::duration<uint64_t, std::milli>;
explicit Tcp(const private_init&) {}
~Tcp() noexcept override = default;
/**
* Create a TCP handle.
*
* @param loop Loop object where this handle runs.
* @param flags Flags
*/
static std::shared_ptr<Tcp> Create(Loop& loop,
unsigned int flags = AF_UNSPEC);
/**
* Create a TCP handle.
*
* @param loop Loop object where this handle runs.
* @param flags Flags
*/
static std::shared_ptr<Tcp> Create(const std::shared_ptr<Loop>& loop,
unsigned int flags = AF_UNSPEC) {
return Create(*loop, flags);
}
/**
* Reuse this handle. This closes the handle, and after the close completes,
* reinitializes it (identically to Create) and calls the provided callback.
* Unlike Close(), it does NOT emit the closed signal, however, IsClosing()
* will return true until the callback is called. This does nothing if
* IsClosing() is true (e.g. if Close() was called).
*
* @param flags Flags
* @param callback Callback
*/
void Reuse(std::function<void()> callback, unsigned int flags = AF_UNSPEC);
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a ListenEvent event to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @return The stream handle for the accepted connection, or nullptr on error.
*/
std::shared_ptr<Tcp> Accept();
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a connection signal to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @param client Client stream object.
* @return False on error.
*/
bool Accept(const std::shared_ptr<Tcp>& client) {
return NetworkStream::Accept(client);
}
/**
* Open an existing file descriptor or SOCKET as a TCP handle.
*
* @note The passed file descriptor or SOCKET is not checked for its type, but
* it's required that it represents a valid stream socket.
*
* @param sock A valid socket handle (either a file descriptor or a SOCKET).
*/
void Open(uv_os_sock_t sock) { Invoke(&uv_tcp_open, GetRaw(), sock); }
/**
* Enable no delay operation (turns off Nagle's algorithm).
* @param enable True to enable it, false otherwise.
* @return True in case of success, false otherwise.
*/
bool SetNoDelay(bool enable) { return uv_tcp_nodelay(GetRaw(), enable) == 0; }
/**
* Enable/Disable TCP keep-alive.
* @param enable True to enable it, false otherwise.
* @param time Initial delay in seconds (use
* `std::chrono::duration<unsigned int>`).
* @return True in case of success, false otherwise.
*/
bool SetKeepAlive(bool enable, Time time = Time{0}) {
return uv_tcp_keepalive(GetRaw(), enable,
static_cast<unsigned>(time.count())) == 0;
}
/**
* Enable/Disable simultaneous asynchronous accept requests.
*
* Enable/Disable simultaneous asynchronous accept requests that are
* queued by the operating system when listening for new TCP
* connections.
* This setting is used to tune a TCP server for the desired performance.
* Having simultaneous accepts can significantly improve the rate of
* accepting connections (which is why it is enabled by default) but may
* lead to uneven load distribution in multi-process setups.
*
* @param enable True to enable it, false otherwise.
* @return True in case of success, false otherwise.
*/
bool SetSimultaneousAccepts(bool enable) {
return uv_tcp_simultaneous_accepts(GetRaw(), enable) == 0;
}
/**
* Bind the handle to an IPv4 or IPv6 address and port.
*
* A successful call to this function does not guarantee that the call to
* `Listen()` or `Connect()` will work properly.
* An error signal can be emitted because of either this function or the
* ones mentioned above.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional additional flags.
*/
void Bind(const sockaddr& addr, unsigned int flags = 0) {
Invoke(&uv_tcp_bind, GetRaw(), &addr, flags);
}
void Bind(const sockaddr_in& addr, unsigned int flags = 0) {
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
}
void Bind(const sockaddr_in6& addr, unsigned int flags = 0) {
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
}
/**
* Bind the handle to an IPv4 address and port.
*
* A successful call to this function does not guarantee that the call to
* `Listen()` or `Connect()` will work properly.
* An error signal can be emitted because of either this function or the
* ones mentioned above.
*
* Available flags are:
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
void Bind(std::string_view ip, unsigned int port, unsigned int flags = 0);
/**
* Bind the handle to an IPv6 address and port.
*
* A successful call to this function does not guarantee that the call to
* `Listen()` or `Connect()` will work properly.
* An error signal can be emitted because of either this function or the
* ones mentioned above.
*
* Available flags are:
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
void Bind6(std::string_view ip, unsigned int port, unsigned int flags = 0);
/**
* Get the current address to which the handle is bound.
* @return The address (will be zeroed if an error occurred).
*/
sockaddr_storage GetSock();
/**
* Get the address of the peer connected to the handle.
* @return The address (will be zeroed if an error occurred).
*/
sockaddr_storage GetPeer();
/**
* Establish an IPv4 or IPv6 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* The connected signal is emitted on the request when the connection has been
* established.
* The error signal is emitted on the request in case of errors during the
* connection.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param req connection request
*/
void Connect(const sockaddr& addr, const std::shared_ptr<TcpConnectReq>& req);
void Connect(const sockaddr_in& addr,
const std::shared_ptr<TcpConnectReq>& req) {
Connect(reinterpret_cast<const sockaddr&>(addr), req);
}
void Connect(const sockaddr_in6& addr,
const std::shared_ptr<TcpConnectReq>& req) {
Connect(reinterpret_cast<const sockaddr&>(addr), req);
}
/**
* Establish an IPv4 or IPv6 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* The callback is called when the connection has been established. Errors
* are reported to the stream error handler.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param callback Callback function to call when connection established
*/
void Connect(const sockaddr& addr, std::function<void()> callback);
void Connect(const sockaddr_in& addr, std::function<void()> callback) {
Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
}
void Connect(const sockaddr_in6& addr, std::function<void()> callback) {
Connect(reinterpret_cast<const sockaddr&>(addr), std::move(callback));
}
/**
* Establish an IPv4 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* The connected signal is emitted on the request when the connection has been
* established.
* The error signal is emitted on the request in case of errors during the
* connection.
*
* @param ip The address to which to connect to.
* @param port The port to which to connect to.
* @param req connection request
*/
void Connect(std::string_view ip, unsigned int port,
const std::shared_ptr<TcpConnectReq>& req);
/**
* Establish an IPv4 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* The callback is called when the connection has been established. Errors
* are reported to the stream error handler.
*
* @param ip The address to which to connect to.
* @param port The port to which to connect to.
* @param callback Callback function to call when connection established
*/
void Connect(std::string_view ip, unsigned int port,
std::function<void()> callback);
/**
* Establish an IPv6 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* The connected signal is emitted on the request when the connection has been
* established.
* The error signal is emitted on the request in case of errors during the
* connection.
*
* @param ip The address to which to connect to.
* @param port The port to which to connect to.
* @param req connection request
*/
void Connect6(std::string_view ip, unsigned int port,
const std::shared_ptr<TcpConnectReq>& req);
/**
* Establish an IPv6 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* The callback is called when the connection has been established. Errors
* are reported to the stream error handler.
*
* @param ip The address to which to connect to.
* @param port The port to which to connect to.
* @param callback Callback function to call when connection established
*/
void Connect6(std::string_view ip, unsigned int port,
std::function<void()> callback);
private:
Tcp* DoAccept() override;
struct ReuseData {
std::function<void()> callback;
unsigned int flags;
};
std::unique_ptr<ReuseData> m_reuseData;
};
/**
* TCP connection request.
*/
class TcpConnectReq : public ConnectReq {
public:
Tcp& GetStream() const {
return *static_cast<Tcp*>(&ConnectReq::GetStream());
}
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_TCP_H_

View File

@@ -1,135 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_TIMER_H_
#define WPIUTIL_WPI_UV_TIMER_H_
#include <uv.h>
#include <chrono>
#include <functional>
#include <memory>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi::uv {
class Loop;
/**
* Timer handle.
* Timer handles are used to schedule signals to be called in the future.
*/
class Timer final : public HandleImpl<Timer, uv_timer_t> {
struct private_init {};
public:
using Time = std::chrono::duration<uint64_t, std::milli>;
explicit Timer(const private_init&) {}
~Timer() noexcept override = default;
/**
* Create a timer handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Timer> Create(Loop& loop);
/**
* Create a timer handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Timer> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Create a timer that calls a functor after a given time interval.
*
* @param loop Loop object where the timer should run.
* @param timeout Time interval
* @param func Functor
*/
static void SingleShot(Loop& loop, Time timeout, std::function<void()> func);
/**
* Create a timer that calls a functor after a given time interval.
*
* @param loop Loop object where the timer should run.
* @param timeout Time interval
* @param func Functor
*/
static void SingleShot(const std::shared_ptr<Loop>& loop, Time timeout,
std::function<void()> func) {
return SingleShot(*loop, timeout, std::move(func));
}
/**
* Start the timer.
*
* If timeout is zero, an event is emitted on the next event loop
* iteration. If repeat is non-zero, an event is emitted first
* after timeout milliseconds and then repeatedly after repeat milliseconds.
*
* @param timeout Milliseconds before to emit an event (use
* `std::chrono::duration<uint64_t, std::milli>`).
* @param repeat Milliseconds between successive events (use
* `std::chrono::duration<uint64_t, std::milli>`).
*/
void Start(Time timeout, Time repeat = Time{0});
/**
* Stop the timer.
*/
void Stop() { Invoke(&uv_timer_stop, GetRaw()); }
/**
* Stop the timer and restart it if it was repeating.
*
* Stop the timer, and if it is repeating restart it using the repeat value
* as the timeout.
* If the timer has never been started before it emits sigError.
*/
void Again() { Invoke(&uv_timer_again, GetRaw()); }
/**
* Set the repeat interval value.
*
* The timer will be scheduled to run on the given interval and will follow
* normal timer semantics in the case of a time-slice overrun.
* For example, if a 50ms repeating timer first runs for 17ms, it will be
* scheduled to run again 33ms later. If other tasks consume more than the
* 33ms following the first timer event, then another event will be emitted
* as soon as possible.
*
* If the repeat value is set from a listener bound to an event, it does
* not immediately take effect. If the timer was non-repeating before, it
* will have been stopped. If it was repeating, then the old repeat value
* will have been used to schedule the next timeout.
*
* @param repeat Repeat interval in milliseconds (use
* `std::chrono::duration<uint64_t, std::milli>`).
*/
void SetRepeat(Time repeat) { uv_timer_set_repeat(GetRaw(), repeat.count()); }
/**
* Get the timer repeat value.
* @return Timer repeat value in milliseconds (as a
* `std::chrono::duration<uint64_t, std::milli>`).
*/
Time GetRepeat() const { return Time{uv_timer_get_repeat(GetRaw())}; }
/**
* Signal generated when the timeout event occurs.
*/
sig::Signal<> timeout;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_TIMER_H_

View File

@@ -1,85 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_TTY_H_
#define WPIUTIL_WPI_UV_TTY_H_
#include <uv.h>
#include <memory>
#include <utility>
#include "wpi/uv/Stream.h"
namespace wpi::uv {
class Loop;
class Tty;
/**
* TTY handle.
* TTY handles represent a stream for the console.
*/
class Tty final : public StreamImpl<Tty, uv_tty_t> {
struct private_init {};
public:
explicit Tty(const private_init&) {}
~Tty() noexcept override = default;
/**
* Create a TTY handle.
*
* @param loop Loop object where this handle runs.
* @param fd File descriptor, usually 0=stdin, 1=stdout, 2=stderr
* @param readable Specifies if you plan on calling StartRead(). stdin is
* readable, stdout is not.
*/
static std::shared_ptr<Tty> Create(Loop& loop, uv_file fd, bool readable);
/**
* Create a TTY handle.
*
* @param loop Loop object where this handle runs.
* @param fd File descriptor, usually 0=stdin, 1=stdout, 2=stderr
* @param readable Specifies if you plan on calling StartRead(). stdin is
* readable, stdout is not.
*/
static std::shared_ptr<Tty> Create(const std::shared_ptr<Loop>& loop,
uv_file fd, bool readable) {
return Create(*loop, fd, readable);
}
/**
* Set the TTY using the specified terminal mode.
*
* @param mode terminal mode
*/
void SetMode(uv_tty_mode_t mode) {
int err = uv_tty_set_mode(GetRaw(), mode);
if (err < 0) {
ReportError(err);
}
}
/**
* Reset TTY settings to default values for the next process to take over.
* Typically called when the program exits.
*/
void ResetMode() { Invoke(&uv_tty_reset_mode); }
/**
* Gets the current window size.
* @return Window size (pair of width and height).
*/
std::pair<int, int> GetWindowSize() {
int width = 0, height = 0;
Invoke(&uv_tty_get_winsize, GetRaw(), &width, &height);
return std::make_pair(width, height);
}
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_TTY_H_

View File

@@ -1,378 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_UDP_H_
#define WPIUTIL_WPI_UV_UDP_H_
#include <uv.h>
#include <functional>
#include <memory>
#include <string_view>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/span.h"
#include "wpi/uv/Handle.h"
#include "wpi/uv/Request.h"
namespace wpi::uv {
class Loop;
class Udp;
/**
* UDP send request.
*/
class UdpSendReq : public RequestImpl<UdpSendReq, uv_udp_send_t> {
public:
UdpSendReq();
Udp& GetUdp() const { return *static_cast<Udp*>(GetRaw()->handle->data); }
/**
* Send completed signal. This is called even if an error occurred.
* @param err error value
*/
sig::Signal<Error> complete;
};
/**
* UDP handle.
* UDP handles encapsulate UDP communication for both clients and servers.
*/
class Udp final : public HandleImpl<Udp, uv_udp_t> {
struct private_init {};
public:
explicit Udp(const private_init&) {}
~Udp() noexcept override = default;
/**
* Create a UDP handle.
*
* @param loop Loop object where this handle runs.
* @param flags Flags
*/
static std::shared_ptr<Udp> Create(Loop& loop,
unsigned int flags = AF_UNSPEC);
/**
* Create a UDP handle.
*
* @param loop Loop object where this handle runs.
* @param flags Flags
*/
static std::shared_ptr<Udp> Create(const std::shared_ptr<Loop>& loop,
unsigned int flags = AF_UNSPEC) {
return Create(*loop, flags);
}
/**
* Open an existing file descriptor or SOCKET as a UDP handle.
*
* @param sock A valid socket handle (either a file descriptor or a SOCKET).
*/
void Open(uv_os_sock_t sock) { Invoke(&uv_udp_open, GetRaw(), sock); }
/**
* Bind the handle to an IPv4 or IPv6 address and port.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional additional flags.
*/
void Bind(const sockaddr& addr, unsigned int flags = 0) {
Invoke(&uv_udp_bind, GetRaw(), &addr, flags);
}
void Bind(const sockaddr_in& addr, unsigned int flags = 0) {
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
}
void Bind(const sockaddr_in6& addr, unsigned int flags = 0) {
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
}
/**
* Bind the handle to an IPv4 address and port.
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
void Bind(std::string_view ip, unsigned int port, unsigned int flags = 0);
/**
* Bind the handle to an IPv6 address and port.
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
void Bind6(std::string_view ip, unsigned int port, unsigned int flags = 0);
/**
* Associate the handle to a remote address and port, so every message sent
* by this handle is automatically sent to that destination.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
*/
void Connect(const sockaddr& addr) {
Invoke(&uv_udp_connect, GetRaw(), &addr);
}
void Connect(const sockaddr_in& addr) {
Connect(reinterpret_cast<const sockaddr&>(addr));
}
void Connect(const sockaddr_in6& addr) {
Connect(reinterpret_cast<const sockaddr&>(addr));
}
/**
* Associate the handle to an IPv4 address and port, so every message sent
* by this handle is automatically sent to that destination.
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
*/
void Connect(std::string_view ip, unsigned int port);
/**
* Associate the handle to an IPv6 address and port, so every message sent
* by this handle is automatically sent to that destination.
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
void Connect6(std::string_view ip, unsigned int port);
/**
* Get the remote IP and port on connected UDP handles.
* @return The address (will be zeroed if an error occurred).
*/
sockaddr_storage GetPeer();
/**
* Get the current address to which the handle is bound.
* @return The address (will be zeroed if an error occurred).
*/
sockaddr_storage GetSock();
/**
* Set membership for a multicast address.
*
* @param multicastAddr Multicast address to set membership for
* @param interfaceAddr Interface address
* @param membership Should be UV_JOIN_GROUP or UV_LEAVE_GROUP
*/
void SetMembership(std::string_view multicastAddr,
std::string_view interfaceAddr, uv_membership membership);
/**
* Set IP multicast loop flag. Makes multicast packets loop back to local
* sockets.
*
* @param enabled True for enabled, false for disabled
*/
void SetMulticastLoop(bool enabled) {
Invoke(&uv_udp_set_multicast_loop, GetRaw(), enabled ? 1 : 0);
}
/**
* Set the multicast TTL.
*
* @param ttl Time to live (1-255)
*/
void SetMulticastTtl(int ttl) {
Invoke(&uv_udp_set_multicast_ttl, GetRaw(), ttl);
}
/**
* Set the multicast interface to send or receive data on.
*
* @param interfaceAddr Interface address
*/
void SetMulticastInterface(std::string_view interfaceAddr);
/**
* Set broadcast on or off.
*
* @param enabled True for enabled, false for disabled
*/
void SetBroadcast(bool enabled) {
Invoke(&uv_udp_set_broadcast, GetRaw(), enabled ? 1 : 0);
}
/**
* Set the time to live (TTL).
*
* @param ttl Time to live (1-255)
*/
void SetTtl(int ttl) { Invoke(&uv_udp_set_ttl, GetRaw(), ttl); }
/**
* Send data over the UDP socket. If the socket has not previously been bound
* with Bind() it will be bound to 0.0.0.0 (the "all interfaces" IPv4 address)
* and a random port number.
*
* Data are written in order. The lifetime of the data pointers passed in
* the `bufs` parameter must exceed the lifetime of the send request.
* The callback can be used to free data after the request completes.
*
* HandleSendComplete() will be called on the request object when the data
* has been written. HandleSendComplete() is called even if an error occurs.
* HandleError() will be called on the request object in case of errors.
*
* @param addr sockaddr_in or sockaddr_in6 with the address and port of the
* remote peer.
* @param bufs The buffers to be written to the stream.
* @param req write request
*/
void Send(const sockaddr& addr, span<const Buffer> bufs,
const std::shared_ptr<UdpSendReq>& req);
void Send(const sockaddr_in& addr, span<const Buffer> bufs,
const std::shared_ptr<UdpSendReq>& req) {
Send(reinterpret_cast<const sockaddr&>(addr), bufs, req);
}
void Send(const sockaddr_in6& addr, span<const Buffer> bufs,
const std::shared_ptr<UdpSendReq>& req) {
Send(reinterpret_cast<const sockaddr&>(addr), bufs, req);
}
/**
* Variant of Send() for connected sockets. Cannot be used with
* connectionless sockets.
*
* @param bufs The buffers to be written to the stream.
* @param req write request
*/
void Send(span<const Buffer> bufs, const std::shared_ptr<UdpSendReq>& req);
/**
* Send data over the UDP socket. If the socket has not previously been bound
* with Bind() it will be bound to 0.0.0.0 (the "all interfaces" IPv4 address)
* and a random port number.
*
* Data are written in order. The lifetime of the data pointers passed in
* the `bufs` parameter must exceed the lifetime of the send request.
* The callback can be used to free data after the request completes.
*
* The callback will be called when the data has been sent. Errors will
* be reported via the error signal.
*
* @param addr sockaddr_in or sockaddr_in6 with the address and port of the
* remote peer.
* @param bufs The buffers to be sent.
* @param callback Callback function to call when the data has been sent.
*/
void Send(const sockaddr& addr, span<const Buffer> bufs,
std::function<void(span<Buffer>, Error)> callback);
void Send(const sockaddr_in& addr, span<const Buffer> bufs,
std::function<void(span<Buffer>, Error)> callback) {
Send(reinterpret_cast<const sockaddr&>(addr), bufs, std::move(callback));
}
void Send(const sockaddr_in6& addr, span<const Buffer> bufs,
std::function<void(span<Buffer>, Error)> callback) {
Send(reinterpret_cast<const sockaddr&>(addr), bufs, std::move(callback));
}
/**
* Variant of Send() for connected sockets. Cannot be used with
* connectionless sockets.
*
* @param bufs The buffers to be written to the stream.
* @param callback Callback function to call when the data has been sent.
*/
void Send(span<const Buffer> bufs,
std::function<void(span<Buffer>, Error)> callback);
/**
* Same as Send(), but won't queue a send request if it can't be completed
* immediately.
*
* @param addr sockaddr_in or sockaddr_in6 with the address and port of the
* remote peer.
* @param bufs The buffers to be send.
* @return Number of bytes sent.
*/
int TrySend(const sockaddr& addr, span<const Buffer> bufs) {
int val = uv_udp_try_send(GetRaw(), bufs.data(),
static_cast<unsigned>(bufs.size()), &addr);
if (val < 0) {
this->ReportError(val);
return 0;
}
return val;
}
int TrySend(const sockaddr_in& addr, span<const Buffer> bufs) {
return TrySend(reinterpret_cast<const sockaddr&>(addr), bufs);
}
int TrySend(const sockaddr_in6& addr, span<const Buffer> bufs) {
return TrySend(reinterpret_cast<const sockaddr&>(addr), bufs);
}
/**
* Variant of TrySend() for connected sockets. Cannot be used with
* connectionless sockets.
*
* @param bufs The buffers to be written to the stream.
* @return Number of bytes sent.
*/
int TrySend(span<const Buffer> bufs) {
int val = uv_udp_try_send(GetRaw(), bufs.data(),
static_cast<unsigned>(bufs.size()), nullptr);
if (val < 0) {
this->ReportError(val);
return 0;
}
return val;
}
/**
* Prepare for receiving data. If the socket has not previously been bound
* with Bind() it is bound to 0.0.0.0 (the "all interfaces" IPv4 address) and
* a random port number.
*
* A received signal will be emitted for each received data packet until
* `StopRecv()` is called.
*/
void StartRecv();
/**
* Stop listening for incoming datagrams.
*/
void StopRecv() { Invoke(&uv_udp_recv_stop, GetRaw()); }
/**
* Gets the amount of queued bytes waiting to be sent.
* @return Amount of queued bytes waiting to be sent.
*/
size_t GetSendQueueSize() const noexcept { return GetRaw()->send_queue_size; }
/**
* Gets the amount of queued packets waiting to be sent.
* @return Amount of queued packets waiting to be sent.
*/
size_t GetSendQueueCount() const noexcept {
return GetRaw()->send_queue_count;
}
/**
* Signal generated for each incoming datagram. Parameters are the buffer,
* the number of bytes received, the address of the sender, and flags.
*/
sig::Signal<Buffer&, size_t, const sockaddr&, unsigned> received;
};
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_UDP_H_

View File

@@ -1,93 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_WORK_H_
#define WPIUTIL_WPI_UV_WORK_H_
#include <uv.h>
#include <functional>
#include <memory>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/uv/Request.h"
namespace wpi::uv {
class Loop;
/**
* Work request.
* For use with `QueueWork()` function family.
*/
class WorkReq : public RequestImpl<WorkReq, uv_work_t> {
public:
WorkReq();
Loop& GetLoop() const { return *static_cast<Loop*>(GetRaw()->loop->data); }
/**
* Function(s) that will be run on the thread pool.
*/
sig::Signal<> work;
/**
* Function(s) that will be run on the loop thread after the work on the
* thread pool has been completed by the work callback.
*/
sig::Signal<> afterWork;
};
/**
* Initializes a work request which will run on the thread pool.
*
* @param loop Event loop
* @param req request
*/
void QueueWork(Loop& loop, const std::shared_ptr<WorkReq>& req);
/**
* Initializes a work request which will run on the thread pool.
*
* @param loop Event loop
* @param req request
*/
inline void QueueWork(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<WorkReq>& req) {
QueueWork(*loop, req);
}
/**
* Initializes a work request which will run on the thread pool. The work
* callback will be run on a thread from the thread pool, and the afterWork
* callback will be called on the loop thread after the work completes. Any
* errors are forwarded to the loop.
*
* @param loop Event loop
* @param work Work callback (called from separate thread)
* @param afterWork After work callback (called on loop thread)
*/
void QueueWork(Loop& loop, std::function<void()> work,
std::function<void()> afterWork);
/**
* Initializes a work request which will run on the thread pool. The work
* callback will be run on a thread from the thread pool, and the afterWork
* callback will be called on the loop thread after the work completes. Any
* errors are forwarded to the loop.
*
* @param loop Event loop
* @param work Work callback (called from separate thread)
* @param afterWork After work callback (called on loop thread)
*/
inline void QueueWork(const std::shared_ptr<Loop>& loop,
std::function<void()> work,
std::function<void()> afterWork) {
QueueWork(*loop, std::move(work), std::move(afterWork));
}
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_WORK_H_

View File

@@ -1,147 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#ifndef WPIUTIL_WPI_UV_UTIL_H_
#define WPIUTIL_WPI_UV_UTIL_H_
#include <uv.h>
#include <cstring>
#include <string_view>
namespace wpi::uv {
/**
* Convert a binary structure containing an IPv4 address to a string.
* @param addr Binary structure
* @param ip Output string (any type that has `assign(char*, char*)`)
* @param port Output port number
* @return Error (same as `uv_ip4_name()`).
*/
template <typename T>
int AddrToName(const sockaddr_in& addr, T* ip, unsigned int* port) {
char name[128];
int err = uv_ip4_name(&addr, name, 128);
if (err == 0) {
ip->assign(name, name + std::strlen(name));
*port = ntohs(addr.sin_port);
} else {
ip->assign(name, name);
}
return err;
}
/**
* Convert a binary structure containing an IPv6 address to a string.
* @param addr Binary structure
* @param ip Output string (any type that has `assign(char*, char*)`)
* @param port Output port number
* @return Error (same as `uv_ip6_name()`).
*/
template <typename T>
int AddrToName(const sockaddr_in6& addr, T* ip, unsigned int* port) {
char name[128];
int err = uv_ip6_name(&addr, name, 128);
if (err == 0) {
ip->assign(name, name + std::strlen(name));
*port = ntohs(addr.sin6_port);
} else {
ip->assign(name, name);
}
return err;
}
/**
* Convert a binary structure containing an IPv4 or IPv6 address to a string.
* @param addr Binary structure
* @param ip Output string (any type that has `assign(char*, char*)`)
* @param port Output port number
* @return Error (same as `uv_ip6_name()`).
*/
template <typename T>
int AddrToName(const sockaddr_storage& addr, T* ip, unsigned int* port) {
if (addr.ss_family == AF_INET) {
return AddrToName(reinterpret_cast<const sockaddr_in&>(addr), ip, port);
}
if (addr.ss_family == AF_INET6) {
return AddrToName(reinterpret_cast<const sockaddr_in6&>(addr), ip, port);
}
char name[1];
ip->assign(name, name);
return -1;
}
/**
* Convert a binary IPv4 address to a string.
* @param addr Binary address
* @param ip Output string (any type that has `assign(char*, char*)`)
* @return Error (same as `uv_inet_ntop()`).
*/
template <typename T>
int AddrToName(const in_addr& addr, T* ip) {
char name[128];
int err = uv_inet_ntop(AF_INET, &addr, name, 128);
if (err == 0) {
ip->assign(name, name + std::strlen(name));
} else {
ip->assign(name, name);
}
return err;
}
/**
* Convert a binary IPv6 address to a string.
* @param addr Binary address
* @param ip Output string (any type that has `assign(char*, char*)`)
* @return Error (same as `uv_inet_ntop()`).
*/
template <typename T>
int AddrToName(const in6_addr& addr, T* ip) {
char name[128];
int err = uv_inet_ntop(AF_INET6, &addr, name, 128);
if (err == 0) {
ip->assign(name, name + std::strlen(name));
} else {
ip->assign(name, name);
}
return err;
}
/**
* Convert a string containing an IPv4 address to a binary structure.
* @param ip IPv4 address string
* @param port Port number
* @param addr Output binary structure
* @return Error (same as `uv_ip4_addr()`).
*/
int NameToAddr(std::string_view ip, unsigned int port, sockaddr_in* addr);
/**
* Convert a string containing an IPv6 address to a binary structure.
* @param ip IPv6 address string
* @param port Port number
* @param addr Output binary structure
* @return Error (same as `uv_ip6_addr()`).
*/
int NameToAddr(std::string_view ip, unsigned int port, sockaddr_in6* addr);
/**
* Convert a string containing an IPv4 address to binary format.
* @param ip IPv4 address string
* @param addr Output binary
* @return Error (same as `uv_inet_pton()`).
*/
int NameToAddr(std::string_view ip, in_addr* addr);
/**
* Convert a string containing an IPv6 address to binary format.
* @param ip IPv6 address string
* @param addr Output binary
* @return Error (same as `uv_inet_pton()`).
*/
int NameToAddr(std::string_view ip, in6_addr* addr);
} // namespace wpi::uv
#endif // WPIUTIL_WPI_UV_UTIL_H_