mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-25 01:41:43 +00:00
[wpinet] Move network portions of wpiutil into new wpinet library (#4077)
This commit is contained in:
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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.
|
||||
*
|
||||
* It’s 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().
|
||||
*
|
||||
* It’s 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_
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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.
|
||||
*
|
||||
* It’s 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.
|
||||
*
|
||||
* It’s 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_
|
||||
@@ -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.
|
||||
*
|
||||
* It’s 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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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 it’s 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_
|
||||
@@ -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_
|
||||
@@ -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 won’t 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 loop’s 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.
|
||||
* Don’t 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 loop’s 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 won’t 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 don’t 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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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 won’t queue a write request if it can’t 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 won’t queue a write request if it can’t 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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
Reference in New Issue
Block a user