/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
    QC_Socket.qpp

    Socket class definition

    Qore Programming Language

    Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.

    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.

    Note that the Qore library is released under a choice of three open-source
    licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
    information.
*/

#include "qore/Qore.h"
#include "qore/intern/QC_Socket.h"
#include "qore/intern/QC_SocketPollOperation.h"
#include "qore/intern/QC_Queue.h"
#include "qore/intern/ssl_constants.h"
#include "qore/intern/qore_socket_private.h"
#include "qore/QoreSSLCertificate.h"
#include "qore/QoreSSLPrivateKey.h"

#include <cerrno>
#include <cstring>

static void hash_set_int_key(QoreHashNode& h, int k, const char* str) {
    char buf[15];
    sprintf(buf, "%d", k);
    h.setKeyValue(buf, new QoreStringNode(str), nullptr);
}

static QoreHashNode* get_network_address_family_map() {
    QoreHashNode* rv = new QoreHashNode(autoTypeInfo);

    hash_set_int_key(*rv, AF_INET, "AF_INET");
    hash_set_int_key(*rv, AF_INET6, "AF_INET6");
    hash_set_int_key(*rv, AF_UNIX, "AF_UNIX");
    hash_set_int_key(*rv, AF_UNSPEC, "AF_UNSPEC");

    return rv;
}

static int64 checkOpenResult(int rc, const char *method_name, ExceptionSink *xsink) {
    if (*xsink)
        return 0;
    if (rc == -2) {
        xsink->raiseException("SOCKET-NOT-OPEN", "socket must be open before Socket::%s() call", method_name);
        return 0;
    }
    return rc;
}

static int check_response_code(int status_code, ExceptionSink* xsink) {
    if (status_code < 100 || status_code >= 600) {
        xsink->raiseException("SOCKET-SENDHTTPRESPONSE-STATUS-ERROR", "expecting valid HTTP status code " \
            "between 100 and 599 as first parameter of Socket::sendHTTPResponse() call, got value %d instead",
            status_code);
        return -1;
    }
    return 0;
}

//! Socket or other pollable object poll info hash
/** for use with @ref Qore::Socket::poll() "Socket::poll()""
*/
hashdecl Qore::SocketPollInfo {
    //! The poll type; see @ref socket_poll_constants for possible values to be combined with binary or
    /** On input, these are the events to check, on output, this value gives the events found in the timeout period
    */
    int events;

    //! the pollable object to monitor
    Qore::AbstractPollableIoObject socket;
}

/** @defgroup socket_poll_constants Socket Poll Constants
*/
///@{
//! (input and output) for polling for read events
const SOCK_POLLIN = SOCK_POLLIN;

//! (input and output) for polling for write events
const SOCK_POLLOUT = SOCK_POLLOUT;

//! (output only) indicates that the socket or connection is closed
const SOCK_POLLERR = SOCK_POLLERR;
///@}

/** @defgroup ssl_mode_constants SSL Verification Mode Constants
    These are integer contants to be used with @ref Qore::Socket::setSslVerifyMode() "Socket::setSslVerifyMode()"
    and are returned by @ref Qore::Socket::getSslVerifyMode() "Socket::getSslVerifyMode()"
*/
///@{
//! Do not verify the peer's certificate
/** @par Server Mode
    The server will not send a client certificate request to the client, so the client will not send a certificate

    @par Client Mode
    If not using an anonymous cipher (by default disabled), the server will send a certificate which will be
    checked.  The handshake will be continued regardless of the verification result.
*/
const SSL_VERIFY_NONE = SSL_VERIFY_NONE;

//! Verify the peer's certificate
/** @par Server Mode
    The server sends a client certificate request to the client.  The certificate returned (if any) is checked.
    If the verification process fails, the TLS/SSL handshake is immediately terminated with an alert message
    containing the reason for the verification failure.  The behaviour can be controlled by the additional
    @ref Qore::SSL_VERIFY_FAIL_IF_NO_PEER_CERT and @ref Qore::SSL_VERIFY_CLIENT_ONCE flags.

    @par Client Mode
    The server certificate is verified. If the verification process fails, the TLS/SSL handshake is immediately
    terminated with an alert message containing the reason for the verification failure. If no server
    certificate is sent, because an anonymous cipher is used, @ref Qore::SSL_VERIFY_PEER is ignored.

    @see
    - @ref Qore::SSL_VERIFY_FAIL_IF_NO_PEER_CERT
    - @ref Qore::SSL_VERIFY_CLIENT_ONCE
*/
const SSL_VERIFY_PEER = SSL_VERIFY_PEER;

//! Require a client certificate in server mode
/** @par Server Mode
    If the client did not return a certificate, the TLS/SSL handshake is immediately terminated with a
    "handshake failure" alert.  This flag must be used together with @ref Qore::SSL_VERIFY_PEER (combined
    with binary or).

    @par Client Mode
    Ignored

    @note must be used with @ref Qore::SSL_VERIFY_PEER (combined with binary or)
*/
const SSL_VERIFY_FAIL_IF_NO_PEER_CERT = SSL_VERIFY_FAIL_IF_NO_PEER_CERT;

//! Only request a client certificate once in server mode
/** @par Server Mode
    Only request a client certificate on the initial TLS/SSL handshake. Do not ask for a client certificate
    again in case of a renegotiation. This flag must be used together with @ref Qore::SSL_VERIFY_PEER
    (combined with binary or).

    @par Client Mode
    Ignored

    @note must be used with @ref Qore::SSL_VERIFY_PEER (combined with binary or)
*/
const SSL_VERIFY_CLIENT_ONCE = SSL_VERIFY_CLIENT_ONCE;
///@}

/** @defgroup x509_verification_constants X.509 Verification Constants
    These are string contants for values returned by the following methods:
    - FtpClient::verifyPeerCertificate()
    - HTTPClient::verifyPeerCertificate()
    - Socket::verifyPeerCertificate()
*/
///@{
//! Verification OK
const X509_V_OK = "X509_V_OK";

//! Unable to get issuer certificate
const X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT = "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT";

//! Unable to get certificate CRL
const X509_V_ERR_UNABLE_TO_GET_CRL = "X509_V_ERR_UNABLE_TO_GET_CRL";

//! Unable to decrypt certificate's signature. This means that the actual signature value could not be determined rather than it not matching the expected value; this is only meaningful for RSA
const X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE = "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE";

//! Unable to decrypt CRL's signature
const X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE = "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE";

//! Unable to decode issuer public key (SubjectPublicKeyInfo)
const X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY = "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY";

//! Certificate signature failure; the signature of the certificate is invalid
const X509_V_ERR_CERT_SIGNATURE_FAILURE = "X509_V_ERR_CERT_SIGNATURE_FAILURE";

//! CRL signature failure; the signature of the certificate is invalid
const X509_V_ERR_CRL_SIGNATURE_FAILURE = "X509_V_ERR_CRL_SIGNATURE_FAILURE";

//! Certificate is not yet valid
const X509_V_ERR_CERT_NOT_YET_VALID = "X509_V_ERR_CERT_NOT_YET_VALID";

//! Certificate has expired
const X509_V_ERR_CERT_HAS_EXPIRED = "X509_V_ERR_CERT_HAS_EXPIRED";

//! CRL is not yet valid
const X509_V_ERR_CRL_NOT_YET_VALID = "X509_V_ERR_CRL_NOT_YET_VALID";

//! CRL has expired
const X509_V_ERR_CRL_HAS_EXPIRED = "X509_V_ERR_CRL_HAS_EXPIRED";

//! Format error in certificate's notBefore field (invalid time)
const X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD = "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD";

//! Format error in certificate's notAfter field (invalid time)
const X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD = "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD";

//! Format error in CRL's lastUpdate field (invalid time)
const X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD = "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD";

//! Format error in CRL's nextUpdate field (invalid time)
const X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD = "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD";

//! Out of memory error
const X509_V_ERR_OUT_OF_MEM = "X509_V_ERR_OUT_OF_MEM";

//! Certificate is self-signed and cannot be found in the trusted list
const X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT = "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT";

//! Self signed certificate in certificate chain
const X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN = "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN";

//! Unable to get local issuer certificate. This normally means the list of trusted certificates is not complete
const X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY = "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY";

//! Unable to verify the first certificate
const X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE";

//! Certificate chain too long
const X509_V_ERR_CERT_CHAIN_TOO_LONG = "X509_V_ERR_CERT_CHAIN_TOO_LONG";

//! Certificate has been revoked
const X509_V_ERR_CERT_REVOKED = "X509_V_ERR_CERT_REVOKED";

//! Invalid CA certificate
const X509_V_ERR_INVALID_CA = "X509_V_ERR_INVALID_CA";

//! The basicConstraints pathlength parameter has been exceeded
const X509_V_ERR_PATH_LENGTH_EXCEEDED = "X509_V_ERR_PATH_LENGTH_EXCEEDED";

//! The certificate cannot be used for the specified purpose
const X509_V_ERR_INVALID_PURPOSE = "X509_V_ERR_INVALID_PURPOSE";

//! Root CA is not marked as trusted for the specified purpose
const X509_V_ERR_CERT_UNTRUSTED = "X509_V_ERR_CERT_UNTRUSTED";

//! Root CA is marked to reject the specified purpose
const X509_V_ERR_CERT_REJECTED = "X509_V_ERR_CERT_REJECTED";

//! The current candidate issuer certificate was rejected because its subject name did not match the issuer name of the current certificate
const X509_V_ERR_SUBJECT_ISSUER_MISMATCH = "X509_V_ERR_SUBJECT_ISSUER_MISMATCH";

//! The current candidate issuer certificate was rejected because its subject key identifier was present and did not match the authority key identifier of the current certificate
const X509_V_ERR_AKID_SKID_MISMATCH = "X509_V_ERR_AKID_SKID_MISMATCH";

//! Issuer name and serial number of candidate certificate do not match the authority key identifier of the current certificate
const X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH = "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH";

//! The keyUsage extension does not permit certificate signing
const X509_V_ERR_KEYUSAGE_NO_CERTSIGN = "X509_V_ERR_KEYUSAGE_NO_CERTSIGN";

//! Verification failure
const X509_V_ERR_APPLICATION_VERIFICATION = "X509_V_ERR_APPLICATION_VERIFICATION";

//! maps from varification strings to verification code descriptions
const X509_VerificationReasons = (
    "X509_V_OK": "OK",
    "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT": "Unable to get issuer certificate",
    "X509_V_ERR_UNABLE_TO_GET_CRL": "Unable to get certificate CRL",
    "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE": "Unable to decrypt certificate's signature. This means that the actual signature value could not be determined rather than it not matching the expected value; this is only meaningful for RSA",
    "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE": "Unable to decrypt CRL's signature",
    "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY": "Unable to decode issuer public key (SubjectPublicKeyInfo)",
    "X509_V_ERR_CERT_SIGNATURE_FAILURE": "Certificate signature failure; the signature of the certificate is invalid",
    "X509_V_ERR_CRL_SIGNATURE_FAILURE": "CRL signature failure; the signature of the certificate is invalid",
    "X509_V_ERR_CERT_NOT_YET_VALID": "Certificate is not yet valid",
    "X509_V_ERR_CERT_HAS_EXPIRED": "Certificate has expired",
    "X509_V_ERR_CRL_NOT_YET_VALID": "CRL is not yet valid",
    "X509_V_ERR_CRL_HAS_EXPIRED": "CRL has expired",
    "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD": "Format error in certificate's notBefore field (invalid time)",
    "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD": "Format error in certificate's notAfter field (invalid time)",
    "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD": "Format error in CRL's lastUpdate field (invalid time)",
    "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD": "Format error in CRL's nextUpdate field (invalid time)",
    "X509_V_ERR_OUT_OF_MEM": "Out of memory error",
    "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT": "Certificate is self-signed and cannot be found in the trusted list",
    "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN": "Self signed certificate in certificate chain",
    "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY": "Unable to get local issuer certificate. This normally means the list of trusted certificates is not complete",
    "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE": "Unable to verify the first certificate",
    "X509_V_ERR_CERT_CHAIN_TOO_LONG": "Certificate chain too long",
    "X509_V_ERR_CERT_REVOKED": "Certificate has been revoked",
    "X509_V_ERR_INVALID_CA": "Invalid CA certificate",
    "X509_V_ERR_PATH_LENGTH_EXCEEDED": "The basicConstraints pathlength parameter has been exceeded",
    "X509_V_ERR_INVALID_PURPOSE": "The certificate cannot be used for the specified purpose",
    "X509_V_ERR_CERT_UNTRUSTED": "Root CA is not marked as trusted for the specified purpose",
    "X509_V_ERR_CERT_REJECTED": "Root CA is marked to reject the specified purpose",
    "X509_V_ERR_SUBJECT_ISSUER_MISMATCH": "The current candidate issuer certificate was rejected because its subject name did not match the issuer name of the current certificate",
    "X509_V_ERR_AKID_SKID_MISMATCH": "The current candidate issuer certificate was rejected because its subject key identifier was present and did not match the authority key identifier of the current certificate",
    "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH": "Issuer name and serial number of candidate certificate do not match the authority key identifier of the current certificate",
    "X509_V_ERR_KEYUSAGE_NO_CERTSIGN": "The keyUsage extension does not permit certificate signing",
    "X509_V_ERR_APPLICATION_VERIFICATION": "Verification failure",
);
///@}

/** @defgroup network_address_family_constants Network Address Family Constants
    These are the possible network address family constants
*/
///@{
//! IPv4 address family
const AF_INET = AF_INET;

//! IPv6 address family
const AF_INET6 = AF_INET6;

//! UNIX domain address family (UNIX socket files)
const AF_UNIX = AF_UNIX;

#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif

//! POSIX synonym for AF_UNIX
const AF_LOCAL = AF_LOCAL;

//! unspecified address family
const AF_UNSPEC = AF_UNSPEC;

//! mapping from @ref network_address_family_constants to string codes
const AFMap = qore(get_network_address_family_map());

//! mapping from network address family string codes to @ref network_address_family_constants
const AFStrMap = (
    "AF_INET": AF_INET,
    "AF_INET6": AF_INET6,
    "AF_UNIX": AF_UNIX,
    "AF_LOCAL": AF_UNIX,
    "AF_UNSPEC": AF_UNSPEC,
);

///@}

/** @defgroup network_address_information_constants Network Address Information Constants
    These are the possible network address information constants for the getaddrinfo() function;
    if any of these constants are not defined on the current platform, they will be assigned to 0 in %Qore
*/
///@{
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0
#endif
#ifndef AI_ALL
#define AI_ALL 0
#endif
#ifndef AI_CANONNAME
#define AI_CANONNAME 0
#endif
#ifndef AI_NUMERICHOST
#define AI_NUMERICHOST 0
#endif
#ifndef AI_NUMERICSERV
#define AI_NUMERICSERV 0
#endif
#ifndef AI_PASSIVE
#define AI_PASSIVE 0
#endif
#ifndef AI_V4MAPPED
#define AI_V4MAPPED 0
#endif

//! if this bit is set, addresses of each family are returned only if they are configured on the system
const AI_ADDRCONFIG = AI_ADDRCONFIG;

//! If this bit is set along with AI_V4MAPPED then all matching IPv6 and IPv4 addresses are returned
const AI_ALL = AI_ALL;

//! If this bit is set, then getaddrinfo() will return the canonical name of the hostname in the \c "canonname" key of the first element returned
const AI_CANONNAME = AI_CANONNAME;

//! If this bit is set, then the host is assumed to be an address and no hostname lookup will be preformed
const AI_NUMERICHOST = AI_NUMERICHOST;

//! If this bit is set, then the service is assumed to be a numeric port string, and no service lookup will be performed
/** If the AI_NUMERICSERV bit is set, then any service name string supplied will be treated as a numeric port string.
    Otherwise, a \c QOREADDRINFO-GETINFO-ERROR exception will be thrown due to the \c EAI_NONAME error raised
    internally. This bit prevents any type of name resolution service (for example, NIS+) from being invoked (this
    constant is operating system dependent; it is not available on all operating systems).
*/
const AI_NUMERICSERV = AI_NUMERICSERV;

//! If this bit is set, then the returned information should be usable for a call to Socket::bind()
/** In this case, if the hostname is not given, then the return value will be usable for binding on all addresses for
    the given family.

    If the AI_PASSIVE bit is not set, the returned socket address structure will be ready for use in a call to
    Socket::connect().

    The IP address portion of the socket address structure will be set to the loopback address if hostname is
    @ref nothing and AI_PASSIVE is not set.
*/
const AI_PASSIVE = AI_PASSIVE;

//! If this bit is set, getaddrinfo() will return IPv4-mapped IPv6 addresses on finding no matching IPv6 addresses
/** The AI_V4MAPPED flag is ignored unless family is AF_INET6
*/
const AI_V4MAPPED = AI_V4MAPPED;
///@}

/** @defgroup protocol_constants Network Protocol Constants
    These are the possible network protocol constants
*/
///@{
//! for the UDP protocol
const IPPROTO_UDP = IPPROTO_UDP;

//! for the TCP protocol
const IPPROTO_TCP = IPPROTO_TCP;
///@}

/** @defgroup socket_type_constants Socket Type Constants
    These are the possible socket type constants
*/
///@{
//! for sequenced, reliable, two-way connection-based byte streams (the default)
const SOCK_STREAM = SOCK_STREAM;

//! for datagrams (connectionless, unreliable messages of a fixed (typically small) maximum length
const SOCK_DGRAM = SOCK_DGRAM;

//! raw socket interface, only available to the superuser, untested
const SOCK_RAW = SOCK_RAW;
///@}

//! The Socket class allows %Qore programs safe access to network sockets
/** @note This class is not available with the @ref PO_NO_NETWORK parse option.

    Non-blocking socket I/O can be performed by appending a timeout value in milliseconds all Socket methods
    performing I/O operations, or by using the Socket::isDataAvailable() method with a timeout value in milliseconds
    (1000 ms = 1 second). Note that as with all %Qore functions and methods accepting a timeout value, relative
    date/time values can be given instead of integers to make the source more readable, for example:

    @code{.py}
    bool rc = socket.isDataAvailable(1250ms); # times out in 1.25 seconds
    @endcode

    Socket objects can automatically convert character encodings if desired when sending string data with
    Socket::send(). Use the Socket::setEncoding() method to set the character encoding for the socket. If a character
    encoding is set, and string data is read with the Socket::recv() method, then it will be tagged with the encoding
    of the socket as well.

    Client applications should call Socket::connect() to connect to a remote port or a UNIX domain socket (socket file
    on the local server). However, if the remote end is expecting a TLS/SSL connection, use Socket::connectSSL()
    instead.

    Server applications should call Socket::bind(), Socket::listen(), and Socket::accept() in this order to accept
    incoming connections. Normally a new thread should be started after the Socket::accept() call to handle the new
    connection in a separate thread (Socket::accept() returns a new Socket object for the accepted connection).

    To support TLS/SSL server connections, first set the certificate and private key with the Socket::setCertificate()
    and Socket::setPrivateKey() methods (see the SSLCertificate Class and the SSLPrivateKey Class for more information
    on the parameters required for these methods). Then Socket::acceptSSL() should be called after the socket is in a
    listening state to accept client connections and negotiate a TLS/SSL connection.

    This class supports posting events to a @ref Qore::Thread::Queue "Queue". See @ref event_handling for more
    information.

    The events raised by this object are listed in the following table:

    @anchor socket_events
    <b>Socket Events</b>
    |!Name|!Description
    |@ref EVENT_PACKET_READ|Raised when a network packet is received.
    |@ref EVENT_PACKET_SENT|Raised when a network packet is sent.
    |@ref EVENT_CHANNEL_CLOSED|Raised when a socket is closed.
    |@ref EVENT_DELETED|Raised when the object being monitored is deleted.
    |@ref EVENT_HOSTNAME_LOOKUP|Raised when a hostname lookup is attempted.
    |@ref EVENT_HOSTNAME_RESOLVED|Raised when a hostname lookup is resolved.
    |@ref EVENT_HTTP_SEND_MESSAGE|Raised when an HTTP message is sent.
    |@ref EVENT_HTTP_MESSAGE_RECEIVED|Raised when an HTTP message is received.
    |@ref EVENT_CONNECTING|Raised right before a socket connection attempt is made.
    |@ref EVENT_CONNECTED|Raised when the socket connection has been established.
    |@ref EVENT_START_SSL|Raised when socket SSL negotiation starts.
    |@ref EVENT_SSL_ESTABLISHED|Raised when SSL communication has been negotiated and established.

    @anchor socket_info_hash
    <b>Socket Information Hash</b>
    |!Key|!Description
    |\c hostname|The interface name if available (ex: \c "localhost"; note that this key is not present when \
        retrieving information about UNIX sockets)
    |\c hostname_desc|A descriptive string giving the hostname and the address family if the hostname is available \
        (ex: \c "ipv6[localhost]"; note that this key is not present when retrieving information about UNIX sockets)
    |\c address|A string giving the address (ex: \c "::ffff:0.0.0.0")
    |\c address_desc|A descriptive string giving the address and the address family (ex: \c "ipv6[::ffff:0.0.0.0]")
    |\c port|An integer port number if available (note that this key is not present when retrieving information \
        about UNIX sockets)
    |\c family|The network address family (see @ref network_address_family_constants)
    |\c familystr|A string describing the network address family (ex: \c "ipv4")
*/
qclass Socket [public_members=*string source,*string source_host; flags=unsetPublicMemberFlag; arg=QoreSocketObject* s; dom=NETWORK; vparent=AbstractPollableIoObjectBase];

//! Creates the socket object
/** @par Example:
    @code{.py} Socket sock(); @endcode
*/
Socket::constructor() {
    self->setPrivate(CID_SOCKET, new QoreSocketObject);
}

//! Creates a new Socket object, not based on the source being copied
/** @par Example:
    @code{.py} Socket newsock = sock.copy(); @endcode
*/
Socket::copy() {
    self->setPrivate(CID_SOCKET, new QoreSocketObject);
}

//! Deletes the object
/**
*/
Socket::destructor() {
    s->invalidate(xsink);
    s->deref(xsink);
}

//! Returns an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to poll the socket for a standard connection
/** @param target the URL target

    @return an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to poll the socket for a standard
    connection

    @since %Qore 1.12
*/
AbstractPollOperation Socket::startPollConnect(string target) {
    s->ref();
    ReferenceHolder<SocketPollOperationBase> poller(new SocketConnectPollOperation(xsink, false, target->c_str(), s),
        xsink);
    ReferenceHolder<QoreObject> rv(new QoreObject(QC_SOCKETPOLLOPERATION, getProgram(), *poller), xsink);
    poller->setSelf(*rv);
    poller.release();

    if (!*xsink) {
        rv->setValue("sock", self->objectRefSelf(), xsink);
        rv->setValue("goal", new QoreStringNode("connect"), xsink);
    }
    return rv.release();
}

//! Returns an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to poll the socket for an SSL connection
/** @param target the URL target

    @return an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to poll the socket for an SSL
    connection

    The poll operation returned will perform the connection and an immediate upgrade to an SSL/TLS connection

    @since %Qore 1.12
*/
AbstractPollOperation Socket::startPollConnectSsl(string target) {
    s->ref();
    ReferenceHolder<SocketPollOperationBase> poller(new SocketConnectPollOperation(xsink, true, target->c_str(), s),
        xsink);
    ReferenceHolder<QoreObject> rv(new QoreObject(QC_SOCKETPOLLOPERATION, getProgram(), *poller), xsink);
    poller->setSelf(*rv);
    poller.release();

    if (!*xsink) {
        rv->setValue("sock", self->objectRefSelf(), xsink);
        rv->setValue("goal", new QoreStringNode("connect-ssl"), xsink);
    }
    return rv.release();
}

//! Returns an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to send data in non-blocking mode
/** @param data the data to send

    @return an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to send data in non-blocking mode

    @since %Qore 1.12
*/
AbstractPollOperation Socket::startPollSend(string data) {
    SimpleRefHolder<QoreStringNode> tmp;
    if (data->getEncoding() != s->getEncoding()) {
        tmp = data->convertEncoding(s->getEncoding(), xsink);
        if (*xsink) {
            return QoreValue();
        }
    }

    s->ref();
    ReferenceHolder<SocketPollOperationBase> poller(new SocketSendPollOperation(xsink,
        tmp ? tmp.release() : data->stringRefSelf(), s), xsink);
    ReferenceHolder<QoreObject> rv(new QoreObject(QC_SOCKETPOLLOPERATION, getProgram(), *poller), xsink);
    poller->setSelf(*rv);
    poller.release();

    if (!*xsink) {
        rv->setValue("sock", self->objectRefSelf(), xsink);
        rv->setValue("goal", new QoreStringNode("send"), xsink);
    }
    return rv.release();
}

//! Returns an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to send data in non-blocking mode
/** @param data the data to send

    @return an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to send data in non-blocking mode

    @since %Qore 1.12
*/
AbstractPollOperation Socket::startPollSend(binary data) {
    s->ref();
    ReferenceHolder<SocketPollOperationBase> poller(new SocketSendPollOperation(xsink, data->binRefSelf(), s), xsink);
    ReferenceHolder<QoreObject> rv(new QoreObject(QC_SOCKETPOLLOPERATION, getProgram(), *poller), xsink);
    poller->setSelf(*rv);
    poller.release();

    if (!*xsink) {
        rv->setValue("sock", self->objectRefSelf(), xsink);
        rv->setValue("goal", new QoreStringNode("send"), xsink);
    }
    return rv.release();
}

//! Returns an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to read data in non-blocking mode
/** @param size the size of the string to receive

    @return an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to read data in non-blocking mode

    The @ref Qore::AbstractPollOperation::getOutput() "AbstractPollOperation::getOutput()" call will return a string
    after a successful poll for inbound socket data

    @since %Qore 1.12
*/
AbstractPollOperation Socket::startPollRecvString(int size) {
    s->ref();
    ReferenceHolder<SocketPollOperationBase> poller(new SocketRecvPollOperation(xsink, size, s, true), xsink);
    ReferenceHolder<QoreObject> rv(new QoreObject(QC_SOCKETPOLLOPERATION, getProgram(), *poller), xsink);
    poller->setSelf(*rv);
    poller.release();

    if (!*xsink) {
        rv->setValue("sock", self->objectRefSelf(), xsink);
        rv->setValue("goal", new QoreStringNode("send"), xsink);
    }
    return rv.release();
}

//! Returns an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to read data in non-blocking mode
/** @param size the size of the string to receive

    @return an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to read data in non-blocking mode

    The @ref Qore::AbstractPollOperation::getOutput() "AbstractPollOperation::getOutput()" call will return a binary
    object after a successful poll for inbound socket data

    @since %Qore 1.12
*/
AbstractPollOperation Socket::startPollRecvBinary(int size) {
    s->ref();
    ReferenceHolder<SocketPollOperationBase> poller(new SocketRecvPollOperation(xsink, size, s, false), xsink);
    ReferenceHolder<QoreObject> rv(new QoreObject(QC_SOCKETPOLLOPERATION, getProgram(), *poller), xsink);
    poller->setSelf(*rv);
    poller.release();

    if (!*xsink) {
        rv->setValue("sock", self->objectRefSelf(), xsink);
        rv->setValue("goal", new QoreStringNode("received"), xsink);
    }
    return rv.release();
}

//! Returns an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to read data in non-blocking mode
/** @param pattern the pattern to use to signify the end of the socket data to read

    @return an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to read data in non-blocking mode

    The @ref Qore::AbstractPollOperation::getOutput() "AbstractPollOperation::getOutput()" call will return a string
    after a successful poll for inbound socket data

    @since %Qore 1.12
*/
AbstractPollOperation Socket::startPollRecvUntilBytesString(string pattern) {
    s->ref();
    ReferenceHolder<SocketPollOperationBase> poller(new SocketRecvUntilBytesPollOperation(xsink, pattern, s, true), xsink);
    ReferenceHolder<QoreObject> rv(new QoreObject(QC_SOCKETPOLLOPERATION, getProgram(), *poller), xsink);
    poller->setSelf(*rv);
    poller.release();

    if (!*xsink) {
        rv->setValue("sock", self->objectRefSelf(), xsink);
        rv->setValue("goal", new QoreStringNode("received"), xsink);
    }
    return rv.release();
}

//! Returns an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to read data in non-blocking mode
/** @param pattern the pattern to use to signify the end of the socket data to read

    @return an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to read data in non-blocking mode

    The @ref Qore::AbstractPollOperation::getOutput() "AbstractPollOperation::getOutput()" call will return a binary
    object after a successful poll for inbound socket data

    @since %Qore 1.12
*/
AbstractPollOperation Socket::startPollRecvUntilBytesBinary(string pattern) {
    s->ref();
    ReferenceHolder<SocketPollOperationBase> poller(new SocketRecvUntilBytesPollOperation(xsink, pattern, s, false), xsink);
    ReferenceHolder<QoreObject> rv(new QoreObject(QC_SOCKETPOLLOPERATION, getProgram(), *poller), xsink);
    poller->setSelf(*rv);
    poller.release();

    if (!*xsink) {
        rv->setValue("sock", self->objectRefSelf(), xsink);
        rv->setValue("goal", new QoreStringNode("received"), xsink);
    }
    return rv.release();
}

//! Returns an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to upgrade a connected client socket to SSL
/** @return an @ref Qore::AbstractPollOperation "AbstractPollOperation" object to upgrade a connected client socket to
    SSL

    The poll operation returned will upgrade a client connection to an SSL/TLS connection using any certificate and
    private key set for the socket.

    @since %Qore 1.12
*/
AbstractPollOperation Socket::startPollUpgradeClientToSSL() {
    s->ref();
    ReferenceHolder<SocketPollOperationBase> poller(new SocketUpgradeClientSslPollOperation(xsink, s), xsink);
    ReferenceHolder<QoreObject> rv(new QoreObject(QC_SOCKETPOLLOPERATION, getProgram(), *poller), xsink);
    poller->setSelf(*rv);
    poller.release();

    if (!*xsink) {
        rv->setValue("sock", self->objectRefSelf(), xsink);
        rv->setValue("goal", new QoreStringNode("upgrade-client-ssl"), xsink);
    }
    return rv.release();
}

//! Connects to a remote port (if the string has a format "host:port") or UNIX domain socket file
/** Connects the socket to a remote (or local) port or UNIX domain socket file, for network (ipv4 and ipv6)
    connections, accepts an optional timeout value in milliseconds (@ref relative_dates "relative date/time values"
    can be given instead of an integer in milliseconds to make the source more readable; ex: \c 20s). If any errors
    occur, an exception is thrown.

    @par Examples:
    @code{.py}# connect to ipv4 adress 192.168.1.45 port 8080 with a 30 second timeout
sock.connect("192.168.1.45:8080", 30s);@endcode
    @code{.py}# connect to ipv6 adress 2001:0db8:85a3:0000:0000:8a2e:0370:7334 port 80 with a 10 second timeout
sock.connect("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80", 10s);@endcode
    @code{.py}# connect to localhost using ipv6 (::1) port 80 with a 10 second timeout
sock.connect("[localhost]:80", 15s);@endcode
    @code{.py}# connect to UNIX domain socket file "/tmp/socket"
sock.connect("/tmp/socket");@endcode

    @par Events:
    @ref EVENT_CONNECTING, @ref EVENT_CONNECTED, @ref EVENT_HOSTNAME_LOOKUP, @ref EVENT_HOSTNAME_RESOLVED

    @param target The target address interpreted with the following rules:
    - If a colon appears in the string, the string will be assumed to be a \c "hostname:port" specification to connect
      to; where \c "port" can also be a service name (ex: \c "example.com:https")
    - If the string contains no colon, the socket will try to connect to a UNIX domain socket file on the local
      filesystem with the given name.
    - Enclose ipv6 addresses in square brackets (ex: \c "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80", will connect
      to port 80 on the given ipv6 address); also if a hostname is enclosed in square brackets, it will be resolved as
      an IPv6 address (ex: \c "[localhost]:80" will connect to port \c 80 on \c ::1, assuming \c "localhost" can be
      resolved to the IPv6 loopback address on the system).
    @param timeout_ms If a timeout value is passed and the connection takes longer to establish than the timeout, a
    \c "SOCKET-CONNECT-ERROR" exception is thrown. Note that like all %Qore functions and methods taking timeout
    values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two
    minutes, etc.)

    @throw SOCKET-CONNECT-ERROR An error occured connecting to the remote socket (cannot resolve hostname, no
    listener, timeout exceeded, etc).

    @see Socket::connectUNIX(), Socket::connectUNIX(), Socket::connectSSL(), Socket::connectINETSSL(), and
    Socket::connectUNIXSSL()
*/
nothing Socket::connect(string target, timeout timeout_ms = -1) {
    s->connect(target->c_str(), timeout_ms, xsink);
}

//! Connects to the given host and port with an optional timeout value with a millisecond resolution
/** Connects the socket to a remote (or local) port; accepts an optional timeout value in milliseconds (@ref relative_dates "relative date/time values" can be given instead of an integer in milliseconds to make the source more readable; ex: \c 20s). If any errors occur, an exception is thrown.

    Do not use square brackets to designate ipv6 addresses with this method; just give the address in its normal form (ex: \c "2001:0db8:85a3:0000:0000:8a2e:0370:7334").

    @par Examples:
    @code{.py}# connect to ipv4 address 192.168.1.45 port 8080 with a 30 second timeout
sock.connectINET("192.168.1.45", 8080, 30s);@endcode
    @code{.py}# connect to ipv6 address 2001:0db8:85a3:0000:0000:8a2e:0370:7334 port 80 with a 20 second timeout
sock.connectINET("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 80, 20s);@endcode

    @par Events:
    @ref EVENT_CONNECTING, @ref EVENT_CONNECTED, @ref EVENT_HOSTNAME_LOOKUP, @ref EVENT_HOSTNAME_RESOLVED

    @param host The host name or IP address to connect to
    @param service The service name (ex: \c "http" or port number (given as or converted to a string) to connect to
    @param timeout_ms If a timeout value is passed and the connection takes longer to establish than the timeout, a \c "SOCKET-CONNECT-ERROR" exception is thrown. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)
    @param family The address family to use to connect to the remote socket; see @ref network_address_family_constants
    @param socktype The type of socket; see @ref socket_type_constants; typically @ref SOCK_STREAM for TCP sockets
    @param protocol The protocol number for the socket (if not sure leave this 0)

    @throw SOCKET-CONNECT-ERROR An error occured connecting to the remote socket (cannot resolve hostname, no listener, timeout exceeded, etc)

    @see Socket::connect(), Socket::connectUNIX(), Socket::connectSSL(), Socket::connectINETSSL(), and Socket::connectUNIXSSL()
*/
nothing Socket::connectINET(string host, softstring service, timeout timeout_ms = -1, softint family = AF_UNSPEC, softint socktype = SOCK_STREAM, softint protocol = 0) {
    s->connectINET2(host->c_str(), service->c_str(), family, socktype, protocol, timeout_ms, xsink);
}

//! Connects to a UNIX domain socket file
/** Connects the socket to the given UNIX domain socket file; if any errors occur, an exception is thrown

    @par Example:
    @code{.py} # connect to UNIX domain socket file "/tmp/socket"
sock.connectUNIX("/tmp/socket"); @endcode

    @par Events:
    @ref EVENT_CONNECTING, @ref EVENT_CONNECTED

    @param path The socket will try to connect to a UNIX domain socket file on the local filesystem with the given name
    @param socktype The type of socket; see @ref socket_type_constants; typically @ref SOCK_STREAM for TCP sockets
    @param protocol The protocol number for the socket (if not sure leave this 0)

    @throw SOCKET-CONNECT-ERROR An error occured connecting to the socket

    @see Socket::connect(), Socket::connectINET(), Socket::connectSSL(), Socket::connectINETSSL(), and Socket::connectUNIXSSL()

    @note UNIX domain sockets are not available on native Windows ports
*/
nothing Socket::connectUNIX(string path, softint socktype = SOCK_STREAM, softint protocol = 0) {
    s->connectUNIX(path->c_str(), socktype, protocol, xsink);
}

//! Connects to a remote socket and attempts to establish a TLS/SSL connection; accepts an optional timeout value with a millisecond resolution
/** Connects to a remote socket and attempts to establish a TLS/SSL connection, for network (INET) connections, accepts an optional timeout value in milliseconds (@ref relative_dates "relative date/time values" can be given instead of an integer in milliseconds to make the source more readable; ex: \c 20s). If any errors occur, an exception is thrown.

    Enclose ipv6 addresses in square brackets (ex: \c "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80", will connect to port 80 on the given ipv6 address); also if a hostname is enclosed in square brackets, it will be resolved as an ipv6 adress (ex: \c "[localhost]:80" will connect to port \c 80 on \c ::1, assuming \c "localhost" can be resolved to the ipv6 loopback address on the system).

    @par Examples:
    @code{.py} # connect to ipv4 adress 192.168.1.45 port 8080 with a 30 second timeout
sock.connectSSL("192.168.1.45:8080", 30s); @endcode
    @code{.py} # connect to ipv6 adress 2001:0db8:85a3:0000:0000:8a2e:0370:7334 port 80 with a 10 second timeout
sock.connectSSL("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80", 10s); @endcode
    @code{.py} # connect to localhost using ipv6 (::1) port 80 with a 10 second timeout
sock.connectSSL("[localhost]:80", 15s); @endcode
    @code{.py} # connect to UNIX domain socket file "/tmp/socket"
sock.connectSSL("/tmp/socket"); @endcode

    @par Events:
    @ref EVENT_CONNECTING, @ref EVENT_CONNECTED, @ref EVENT_HOSTNAME_LOOKUP, @ref EVENT_HOSTNAME_RESOLVED, @ref EVENT_START_SSL, @ref EVENT_SSL_ESTABLISHED

    @param target The target address interpreted with the following rules:
    - If a colon appears in the string, the string will be assumed to be a \c "hostname:port" specification to connect to; where \c "port" can also be a service name (ex: \c "example.com:https")
    - If the string contains no colon, the socket will try to connect to a UNIX domain socket file on the local filesystem with the given name.
    - Enclose ipv6 addresses in square brackets (ex: \c "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80", will connect to port 80 on the given ipv6 address); also if a hostname is enclosed in square brackets, it will be resolved as an ipv6 address (ex: \c "[localhost]:80" will connect to port \c 80 on \c ::1, assuming \c "localhost" can be resolved to the ipv6 loopback address on the system).
    @param timeout_ms if a timeout value is passed and the connection takes longer to establish than the timeout, an exception is thrown. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-CONNECT-ERROR An error occured connecting to the remote socket (cannot resolve hostname, no listener, timeout exceeded, etc)
    @throw SOCKET-SSL-ERROR An error occurred establishing the TLS/SSL connection

    @see Socket::connect(), Socket::connectUNIX(), Socket::connectUNIXSSL(), Socket::connectINETSSL(), and Socket::connectINET()
*/
nothing Socket::connectSSL(string target, timeout timeout_ms = -1) {
    s->connectSSL(target->c_str(), timeout_ms, xsink);
}

//! Connects to the given host and port and attempts to establish a TLS/SSL connection; accepts an optional timeout value with a millisecond resolution
/** Connects the socket to a remote (or local) port and attempts to establish a TLS/SSL connection; accepts an optional timeout value in milliseconds (@ref relative_dates "relative date/time values" can be given instead of an integer in milliseconds to make the source more readable; ex: \c 20s). If any errors occur, an exception is thrown.

    Do not use square brackets to designate ipv6 addresses with this method; just give the address in its normal form (ex: \c "2001:0db8:85a3:0000:0000:8a2e:0370:7334").

    @par Examples:
    @code{.py} # connect to 192.168.1.45 port 8080 with a 30 second timeout
sock.connectINETSSL("192.168.1.45", 8080, 30s); @endcode
    @code{.py} # connect to ipv6 address 2001:0db8:85a3:0000:0000:8a2e:0370:7334 port 80 with a 20 second timeout
sock.connectINETSSL("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 80, 20s); @endcode

    @par Events:
    @ref EVENT_CONNECTING, @ref EVENT_CONNECTED, @ref EVENT_HOSTNAME_LOOKUP, @ref EVENT_HOSTNAME_RESOLVED, @ref EVENT_START_SSL, @ref EVENT_SSL_ESTABLISHED

    @param host The host name or IP address to connect to
    @param service The service name (ex: \c "http" or port number (given as or converted to a string) to connect to
    @param timeout_ms If a timeout value is passed and the connection takes longer to establish than the timeout, a \c "SOCKET-CONNECT-ERROR" exception is thrown. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)
    @param family The address family to use to connect to the remote socket; see @ref network_address_family_constants
    @param socktype The type of socket; see @ref socket_type_constants; typically @ref SOCK_STREAM for TCP sockets
    @param protocol The protocol number for the socket (if not sure leave this 0)

    @throw SOCKET-CONNECT-ERROR An error occured connecting to the remote socket (cannot resolve hostname, no listener, timeout exceeded, etc)
    @throw SOCKET-SSL-ERROR An error occurred establishing the TLS/SSL connection

    @see Socket::connect(), Socket::connectUNIX(), Socket::connectSSL(), Socket::connectINET(), and Socket::connectUNIXSSL()
*/
nothing Socket::connectINETSSL(string host, softstring service, timeout timeout_ms = -1, softint family = AF_UNSPEC, softint socktype = SOCK_STREAM, softint protocol = 0) {
    s->connectINET2SSL(host->c_str(), service->c_str(), family, socktype, protocol, timeout_ms, xsink);
}

//! Connects to the given UNIX domain socket file and attempts to establish a TLS/SSL connection
/** Connects the socket to a UNIX domain socket file and attempts to establish a TLS/SSL connection. If any errors occur, an exception is thrown.

    @par Example:
    @code{.py} # connect to UNIX domain socket file "/tmp/socket"
sock.connectUNIXSSL("/tmp/socket"); @endcode

    @par Events:
    @ref EVENT_CONNECTING, @ref EVENT_CONNECTED, @ref EVENT_START_SSL, @ref EVENT_SSL_ESTABLISHED

    @param path The socket will try to connect to a UNIX domain socket file on the local filesystem with the given name
    @param socktype The type of socket; see @ref socket_type_constants; typically @ref SOCK_STREAM for TCP sockets
    @param protocol The protocol number for the socket (if not sure leave this 0)

    @throw SOCKET-CONNECT-ERROR An error occured connecting to the socket
    @throw SOCKET-SSL-ERROR An error occurred establishing the TLS/SSL connection

    @see Socket::connect(), Socket::connectUNIX(), Socket::connectSSL(), Socket::connectINETSSL(), and Socket::connectINET()

    @note UNIX domain sockets are not available on native Windows ports
*/
nothing Socket::connectUNIXSSL(string path, softint socktype = SOCK_STREAM, softint protocol = 0) {
    s->connectUNIXSSL(path->c_str(), socktype, protocol, xsink);
}

//! Opens and binds the socket to a port, interface and port (if the bind_to string has a format "host:port"), or UNIX domain socket file.
/** If the second parameter is @ref True, then the socket will set the \c SO_REUSEADDR option, which will allow the socket to be bound to a port that is not yet closed (for example, in a \c TIME_WAIT state).

    If any errors occur a non-zero error code is returned.

    This method tries to automatically pick the appropriate address family from the arguments; note that a hostname or address in square brackets (ex: \c "[localhost]") will be looked up and bound as an IPv6 address; additionally, the method recognizes ipv6 addresses by embedded colons (:) in the address string and binds them as such.

    Internally, the getaddrinfo() function is used to look up bind addresses; internal \c bind() operations are tried in sequence for each address returned; as soon as a bind operation is successful, the method returns. If none of the addresses can be bound, then an error code is returned.

    @par Example:
    @code{.py} # bind to a UNIX socket on the local system and reuse the address, check return code
if (sock.bind("/tmp/my-socket", True))
    throw "BIND-ERROR", strerror(); @endcode

    @param str If a colon appears in the string, the string will be assumed to be a \c "bind_address:port" specification, and the port on the named IP address will be bound, otherwise, if the string contains no colon, the socket will be bound to a UNIX domain socket file on the local filesystem with the given name.  Note that a hostname or address in square brackets (ex: \c "[localhost]") will be looked up and bound as an IPv6 address; additionally, the method recognizes ipv6 addresses by embedded colons (:) in the address string (if surrounded by square brackets) and binds them as such.
    @param reuseaddr If this optional argument evaluates to @ref True, the \c SO_REUSEADDR option will be set on the socket, which will allow the socket to be bound to a port that is not yet closed (for example, in a \c TIME_WAIT state); note that this only applies to IPv4 (@ref AF_INET) and IPv6 (@ref AF_INET6) sockets; this option is ignored for UNIX (@ref AF_UNIX) sockets.

    @see Socket::bindINET() and Socket::bindUNIX()

    @note UNIX domain sockets are not available on native Windows ports
*/
int Socket::bind(string str, softbool reuseaddr = False) {
    return s->bind(str->c_str(), reuseaddr);
}

//! Opens and binds the socket to an INET port on all interfaces
/** If the second parameter is @ref True, then the socket will set the \c SO_REUSEADDR option, which will allow the socket to be bound to a port that is not yet closed (for example, in a \c TIME_WAIT state).

    If any errors occur a non-zero error code is returned.

    @par Example:
    @code{.py} # bind to port 80 on all interfaces on the local system and reuse the address, check return code
if (sock.bind(80, True))
    throw "BIND-ERROR", strerror(); @endcode

    @param port A port number to bind to on all interfaces
    @param reuseaddr If this optional argument evaluates to @ref True, the \c SO_REUSEADDR option will be set on the socket, which will allow the socket to be bound to a port that is not yet closed (for example, in a \c TIME_WAIT state)

    @see Socket::bindINET() and Socket::bindUNIX()
*/
int Socket::bind(int port, softbool reuseaddr = False) {
    return s->bind(port, reuseaddr);
}

//! Opens and binds the socket to the given UNIX domain socket file as given by the filename argument. If any errors occur, an exception is thrown
/** Opens and binds the socket to the given UNIX domain socket file as given by the filename argument. Note that the socket file is automatically deleted in the destructor when a UNIX socket is closed. If any errors occur, an exception is thrown.

    @par Example:
    @code{.py} # bind to UNIX domain socket file "/tmp/socket"
sock.bindUNIX("/tmp/socket"); @endcode

    @param path The path of the UNIX domain socket to create and bind to
    @param socktype the type of socket; see Socket Type Constants; typically @ref SOCK_STREAM for TCP sockets
    @param protocol the protocol number for the socket; use 0 for the default protocol

    @throw SOCKET-BIND-ERROR Error opening socket for bind; error binding on socket

    @see Socket::bind() and Socket::bindINET()

    @note UNIX domain sockets are not available on native Windows ports
*/
nothing Socket::bindUNIX(string path, softint socktype = SOCK_STREAM, softint protocol = 0) {
    s->bindUNIX(path->c_str(), socktype, protocol, xsink);
}

//! Opens and binds the socket to the given IPv4 or IPv6 interface (or if no interface is given, then to all interfaces on the local system) and port (the port number will be derived from the service name if a numeric port number is not given)
/** Opens and binds the socket to a port, interface and port (if the interface string has a format \c "host:port"), or UNIX domain socket file (if no port or service name appears in the bind string). If the second parameter is @ref True, then the socket will set the \c SO_REUSEADDR option, which will allow the socket to be bound to a port that is not yet closed (for example, in a \c TIME_WAIT state).

    Internally, the getaddrinfo() function is used to look up bind addresses; internal bind() operations are tried in sequence for each address returned; as soon as a bind operation is successful, the method returns. If none of the addresses can be bound, then an error code is returned.

    If any errors occur, an exception is thrown.

    @par Examples:
    @code{.py} # bind to port 80 on all interfaces on the local system and reuse the address
sock.bindINET(NOTHING, 80, True); @endcode
    @code{.py} # bind to interface 192.168.2.23 port 8080 and do not reuse the address
sock.bindINET("192.168.2.23", 8080); @endcode
    @code{.py} # bind to localhost port 8080 with ipv6 and do not reuse the address
sock.bindINET("localhost", 8080, False, AF_INET6); @endcode
    @code{.py} # bind to ipv6 host address fe80::21c:42ff:fe00:8, port 1001, reuse the address
sock.bindINET("fe80::21c:42ff:fe00:8", 1001, True); @endcode

    @param iface the host name or IP address to bind to
    @param service the service name (ex: "http" or port number (given as or converted to a string) to bind to
    @param reuseaddr if this optional argument evaluates to True, the \c SO_REUSEADDR option will be set on the socket, which will allow the socket to be bound to a port that is not yet closed (for example, in a \c TIME_WAIT state)
    @param family the address family to use to bind; see Network Address Family Constants; \c AF_UNSPEC means to try all possible addres families
    @param socktype the type of socket; see @ref socket_type_constants; typically @ref SOCK_STREAM for TCP sockets
    @param protocol the protocol number for the socket; use 0 for the default protocol

    @throw SOCKET-BIND-ERROR Both hostname and service name are empty or not set; error opening socket for bind; error binding on socket.
    @throw QOREADDRINFO-GETINFO-ERROR error looking up either nodename or servicename (or not known)

    @see Socket::bind() and Socket::bindUNIX()
*/
nothing Socket::bindINET(*string iface, *softstring service, softbool reuseaddr = False, softint family = AF_UNSPEC,
        softint socktype = SOCK_STREAM, softint protocol = 0) {
    if ((!iface || iface->empty())
        && (!service || service->empty()))
        return xsink->raiseException("SOCKET-BIND-ERROR", "both interace (first parameter) and service (second "
            "parameter) were either not present or empty strings; at least one of the first 2 parameters must be "
            "present for a successful call to Socket::bindINET()");

    s->bindINET(iface ? iface->c_str() : nullptr, service ? service->c_str() : nullptr, reuseaddr, family,
        socktype, protocol, xsink);
}

//! Accepts connections on a listening socket
/** Accepts connections on a listening socket; if any errors occur, an exception is thrown.

    The new Socket object returned will have the same character encoding as the current object. Once a new connection
    has been accepted, call Socket::getPeerInfo() to get information about the remote socket.

    @par Example:
    @code{.py} Socket new_socket = sock.accept(); @endcode

    @return a new Socket object is returned for the new connection

    @throw SOCKET-NOT-OPEN The socket is not bound
    @throw SOCKET-ACCEPT-ERROR Error in accepting connection

    @see Socket::acceptSSL(), Socket::listen(), Socket::getPeerInfo()
*/
Socket Socket::accept() {
    QoreSocketObject *n = s->accept(-1, xsink);
    if (!n) {
        assert(*xsink);
        return QoreValue();
    }

    assert(n);

    // ensure that a socket object is returned (and not a subclass)
    QoreObject *ns = new QoreObject(QC_SOCKET, getProgram(), n);

    // save backwards-compatible peer parameters as members in new object (deprecated: Socket::getPeerInfo() should
    // be used in the future)
    my_socket_priv::setAccept(*n, ns);

    return ns;
}

//! Accepts connections on a listening socket (see Socket::listen()) accepting a timeout value with a millisecond resolution
/** If any errors occur, an exception is thrown.

    The new Socket object returned will have the same character encoding as the current object. Once a new connection has been accepted, call Socket::getPeerInfo() to get information about the remote socket.

    @par Example:
    @code{.py} *Socket new_socket = sock.accept(30s); @endcode

    @param timeout_ms If a timeout value is passed and the connection takes longer to establish than the timeout, then @ref nothing is returned.Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return If no connection is accepted within the timeout period, then @ref nothing is returned, otherwise a Socket object for the new connection is returned.

    @throw SOCKET-NOT-OPEN The socket is not bound
    @throw SOCKET-ACCEPT-ERROR Error in accepting connection

    @see Socket::acceptSSL(), Socket::listen(), Socket::getPeerInfo()
*/
*Socket Socket::accept(timeout timeout_ms) {
    QoreSocketObject *n = s->accept(timeout_ms, xsink);
    if (!n)
        return QoreValue();

    // ensure that a socket object is returned (and not a subclass)
    QoreObject *ns = new QoreObject(QC_SOCKET, getProgram(), n);

    // save backwards-compatible peer parameters as members in new object (deprecated: Socket::getPeerInfo() should be used in the future)
    my_socket_priv::setAccept(*n, ns);

    return ns;
}

//! Accepts connections on a listening socket and attempts to negotiate a TLS/SSL connection
/** Accepts connections on a listening socket and attempts to negotiate a TLS/SSL connection; if any errors occur, an
    exception is thrown.

    The new Socket object returned will have the same character encoding as the current object. Once a new connection
    has been accepted, call Socket::getPeerInfo() to get information about the remote socket.

    @par Example:
    @code{.py} Socket new_socket = sock.acceptSSL(); @endcode

    @return When a new connection is accepted and a TLS/SSL session has been successfully negotiated, a new Socket
    object is returned for the new connectio

    @throw SOCKET-NOT-OPEN The socket is not bound
    @throw SOCKET-ACCEPT-ERROR Error in accepting connection

    @see Socket::accept(), Socket::listen(), Socket::getPeerInfo()
*/
Socket Socket::acceptSSL() {
    QoreSocketObject *n = s->acceptSSL(-1, xsink);
    if (!n) {
        assert(*xsink);
        return QoreValue();
    }

    // ensure that a socket object is returned (and not a subclass)
    QoreObject *ns = new QoreObject(QC_SOCKET, getProgram(), n);

    // save backwards-compatible peer parameters as members in new object (deprecated: Socket::getPeerInfo() should be
    // used in the future)
    my_socket_priv::setAccept(*n, ns);

    return ns;
}

//! Accepts connections on a listening socket and attempts to negotiate a TLS/SSL connection accepting a timeout value with a millisecond resolution
/** If any errors occur, an exception is thrown.

    The new Socket object returned will have the same character encoding as the current object. Once a new connection has been accepted, call Socket::getPeerInfo() to get information about the remote socket.

    @par Example:
    @code{.py} *Socket new_socket = sock.acceptSSL(30s); @endcode

    @param timeout_ms If a timeout value is passed and the connection takes longer to establish than the timeout, then @ref nothing is returned. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return If no connection is accepted within the timeout period, then @ref nothing is returned, otherwise a Socket object for the new connection is returned.

    @throw SOCKET-NOT-OPEN The socket is not bound
    @throw SOCKET-ACCEPT-ERROR Error in accepting connection

    @see Socket::acceptSSL(), Socket::listen(), Socket::getPeerInfo()
*/
*Socket Socket::acceptSSL(timeout timeout_ms) {
    QoreSocketObject* n = s->acceptSSL(timeout_ms, xsink);
    if (!n)
        return QoreValue();

    // ensure that a socket object is returned (and not a subclass)
    QoreObject* ns = new QoreObject(QC_SOCKET, getProgram(), n);

    // save backwards-compatible peer parameters as members in new object (deprecated: Socket::getPeerInfo() should be used in the future)
    my_socket_priv::setAccept(*n, ns);

    return ns;
}

//! Listens for connections on a bound socket; sets the socket in a listening state
/** Listens for new connections on a bound socket.

    @par Example:
    @code{.py} sock.listen(); @endcode

    @param backlog the size of the queue for pending connections

    @return Returns 0 for success, -1 for error

    @throw SOCKET-NOT-OPEN The socket is not bound

    @since %Qore 0.8.8 the \a backlog parameter was added
*/
int Socket::listen(int backlog = 20) {
    return checkOpenResult(s->listen((int)backlog), "listen", xsink);
}

//! Sends binary data over the socket; if any errors occur, an exception is thrown
/**
    @par Example:
    @code{.py}
sock.send(data, 20s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param bin Sends the binary data over the socket
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @see
    - Socket::sendBinary()

    @note this method is as of %Qore 0.8.6+ equivalent to Socket::send2() except for the return value

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error like Socket::send2(); this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::send(binary bin, timeout timeout_ms = -1) {
    return s->send(bin, timeout_ms, xsink);
}

//! Sends string data over the socket; string data is converted to the socket's encoding if necessary; if any errors occur, an exception is thrown
/** String data will be converted to the encoding set for the socket if necessary.

    @par Example:
    @code{.py}
sock.send(str, 20s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param str sends the string data over the socket without the trailing null ('\0') character; the string's encoding is converted to the socket's encoding if necessary
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @see
    - Socket::sendBinary2()

    @note this method is as of %Qore 0.8.6+ equivalent to Socket::send2() except for the return value

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error like Socket::send2(); this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::send(string str, timeout timeout_ms = -1) {
    return s->send(*str, timeout_ms, xsink);
}

//! Sends string data over the socket without converting the string to the socket's encoding, but instead is sent exactly as-is; if any errors occur, an exception is thrown
/**
    @par Example:
    @code{.py}
sock.sendBinary(str);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param str string data to be sent (without any conversion to the socket's encoding) over the socket without the trailing null ('\0') character
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @see Socket::send2()

    @note this method is as of %Qore 0.8.6+ equivalent to Socket::sendBinary2() except for the return value

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error like Socket::send2(); this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::sendBinary(string str, timeout timeout_ms = -1) {
    return s->send(str->c_str(), str->strlen(), timeout_ms, xsink);
}

//! Sends binary data over the socket; if any errors occur, an exception is thrown
/**
    @par Example:
    @code{.py}
sock.sendBinary2(data);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param bin Sends the binary data over the socket
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @see Socket::send2()

    @note this method is as of %Qore 0.8.6+ equivalent to Socket::sendBinary2() except for the return value

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error like Socket::send2(); this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::sendBinary(binary bin, timeout timeout_ms = -1) {
    return s->send(bin, timeout_ms, xsink);
}

//! Sends binary data over the socket; if any errors occur, an exception is thrown
/**
    @par Example:
    @code{.py}
sock.send2(data);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param bin Sends the binary data over the socket
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @see
    - Socket::sendBinary2()

    @note this method is as of %Qore 0.8.6+ equivalent to Socket::send() except for the lack of a return value (since errors cause exceptions to be thrown, no return value is necessary)

    @since %Qore 0.8.4
*/
nothing Socket::send2(binary bin, timeout timeout_ms = -1) {
    s->send(bin, timeout_ms, xsink);
}

//! Sends string data over the socket; string data is converted to the socket's encoding if necessary; if any errors occur, an exception is thrown
/** String data will be converted to the encoding set for the socket if necessary.

    @par Example:
    @code{.py}
sock.send2(str);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param str sends the string data over the socket without the trailing null ('\0') character; the string's encoding is converted to the socket's encoding if necessary
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @see
    - Socket::sendBinary2()

    @note this method is as of %Qore 0.8.6+ equivalent to Socket::send() except for the lack of a return value (since errors cause exceptions to be thrown, no return value is necessary)

    @since %Qore 0.8.4
*/
nothing Socket::send2(string str, timeout timeout_ms = -1) {
    s->send(*str, timeout_ms, xsink);
}

//! Sends string data over the socket without converting the string to the socket's encoding, but instead is sent exactly as-is; if any errors occur, an exception is thrown
/**
    @par Example:
    @code{.py}
sock.sendBinary2(str);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param str string data to be sent (without any conversion to the socket's encoding) over the socket without the trailing null ('\0') character
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @see Socket::send2()

    @note this method is as of %Qore 0.8.6+ equivalent to Socket::sendBinary() except for the lack of a return value (since errors cause exceptions to be thrown, no return value is necessary)

    @since %Qore 0.8.4
*/
nothing Socket::sendBinary2(string str, timeout timeout_ms = -1) {
    s->send(str->c_str(), str->strlen(), timeout_ms, xsink);
}

//! Sends binary data over the socket; if any errors occur, an exception is thrown
/**
    @par Example:
    @code{.py}
sock.sendBinary2(data);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param bin Sends the binary data over the socket
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @see Socket::send2()

    @note this method is as of %Qore 0.8.6+ equivalent to Socket::sendBinary() except for the lack of a return value (since errors cause exceptions to be thrown, no return value is necessary)

    @since %Qore 0.8.4
*/
nothing Socket::sendBinary2(binary bin, timeout timeout_ms = -1) {
    s->send(bin, timeout_ms, xsink);
}

//! Reads data from an @ref InputStream and sends the bytes over the socket
/** If any errors occur writing to the socket, an exception is raised

    @par Example:
    @code{.py}
sock.sendFromInputStream(input_stream, -1); # write all data available
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param input_stream the @ref InputStream providing the data to send
    @param size the amount of data to send in bytes; to send all data available in the InputStream, use -1
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @since %Qore 0.8.13
*/
nothing Socket::sendFromInputStream(Qore::InputStream[InputStream] input_stream, softint size = -1,
        timeout timeout_ms = -1) {
    SimpleRefHolder<InputStream> isHolder(input_stream);
    s->sendFromInputStream(input_stream, size, timeout_ms, xsink);
}

//! Sends a 1-byte integer over the socket
/** If any errors occur, an exception is thrown

    @par Example:
    @code{.py}
sock.sendi1(val, 20s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param i the integer to send; only the least-significant byte will be sent
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error; this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::sendi1(softint i = 0, timeout timeout_ms = -1) {
    return s->sendi1((char)i, timeout_ms, xsink);
}

//! Sends a 2-byte (16-bit) integer in big-endian format (network byte order) over the socket
/** If any errors occur, an exception is thrown

    @par Example:
    @code{.py}
sock.sendi2(val, 20s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param i the integer to send; only the least-significant 2 bytes will be sent
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error; this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::sendi2(softint i = 0, timeout timeout_ms = -1) {
    return s->sendi2((short)i, timeout_ms, xsink);
}

//! Sends a 4-byte (32-bit) integer in big-endian format (network byte order) over the socket
/** If any errors occur, an exception is thrown

    @par Example:
    @code{.py}
sock.sendi4(val, 20s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param i the integer to send; only the least-significant 4 bytes will be sent
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error; this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::sendi4(softint i = 0, timeout timeout_ms = -1) {
    return s->sendi4((int)i, timeout_ms, xsink);
}

//! Sends an 8-byte (64-bit) integer in big-endian format (network byte order) over the socket
/** If any errors occur, an exception is thrown

    @par Example:
    @code{.py}
sock.sendi8(val, 20s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param i the integer to send
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error; this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::sendi8(softint i = 0, timeout timeout_ms = -1) {
    return s->sendi8(i, timeout_ms, xsink);
}

//! Sends a 2-byte (16-bit) integer in little-endian format over the socket
/** If any errors occur, an exception is thrown

    @par Example:
    @code{.py}
sock.sendi2LSB(val, 20s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param i the integer to send; only the least-significant 2 bytes will be sent
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error; this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::sendi2LSB(softint i = 0, timeout timeout_ms = -1) {
    return s->sendi2LSB((short)i, timeout_ms, xsink);
}

//! Sends a 4-byte (32-bit) integer in little-endian format over the socket
/** If any errors occur, an exception is thrown

    @par Example:
    @code{.py}
sock.sendi4LSB(val, 20s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param i the integer to send; only the least-significant 4 bytes will be sent
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error; this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::sendi4LSB(softint i = 0, timeout timeout_ms = -1) {
    return s->sendi4LSB((int)i, timeout_ms, xsink);
}

//! Sends an 8-byte (64-bit) integer in little-endian format over the socket
/** If any errors occur, an exception is thrown

    @par Example:
    @code{.py}
sock.sendi8LSB(val, 20s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param i the integer to send
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection; the timeout value is the longest value that a single send() operation can take with non-blocking I/O. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return always returns 0 for backwards compatibility; if an error occurs, an exception is thrown

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-TIMEOUT a single send() operation exceeded the given timeout period
    @throw SOCKET-SEND-ERROR an error occurred sending the socket data
    @throw SOCKET-SSL-ERROR there was an SSL error while writing data to the socket

    @since %Qore 0.8.6 this method takes a timeout value and throws exceptions on error; this change was
    made because it was not possible to catch SSL errors with this method with the previous implementation
*/
int Socket::sendi8LSB(softint i = 0, timeout timeout_ms = -1) {
    return s->sendi8LSB(i, timeout_ms, xsink);
}

//! Receives data from the socket and returns a string tagged with the Socket's character encoding
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
string data = sock.recv(-1); # read all data available
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param size the amount of data to read in bytes; to read until the remote closes the connection, use -1; a value of 0 means to read all data currently available or available up until the timeout period (if non-negative)
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return the data read, returned as a string tagged with the Socket's character encoding

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection.
    @throw SOCKET-RECV-ERROR there was an error receiving the data.
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see
    - Socket::setEncoding()
    - Socket::recvBinary()
*/
string Socket::recv(softint size = 0, timeout timeout_ms = -1) {
    if (size > 0)
        return s->recv(size, timeout_ms, xsink);

    QoreStringNode* str = s->recv(timeout_ms, xsink);
    return !*xsink && !str ? new QoreStringNode : str;
}

//! Receives data from the socket and returns a binary object
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
binary data = sock.recvBinary(-1); # read all data available
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param size the amount of data to read in bytes; to read until the remote closes the connection, use -1; a value of 0 means to read all data currently available or available up until the timeout period (if non-negative)
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return the data read, returned as a binary object

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection.
    @throw SOCKET-RECV-ERROR there was an error receiving the data.
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recv()
*/
binary Socket::recvBinary(softint size = 0, timeout timeout_ms = -1) {
    if (size > 0)
        return s->recvBinary(size, timeout_ms, xsink);

    BinaryNode* str = s->recvBinary(timeout_ms, xsink);
    return !*xsink && !str ? new BinaryNode : str;
}

//! Receives data from the socket and writes the bytes to an @ref OutputStream
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
sock.recvToOutputStream(os, -1); # read all data available
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param os The output stream to write the response body to
    @param size the amount of data to read in bytes; to read until the remote closes the connection, use -1
    @param timeout_ms the timeout in milliseconds (1/1000 second). If no timeout is passed, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection.
    @throw SOCKET-RECV-ERROR there was an error receiving the data.
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recv()
    @since %Qore 0.8.13
*/
nothing Socket::recvToOutputStream(Qore::OutputStream[OutputStream] os, softint size = -1, timeout timeout_ms = -1) {
    SimpleRefHolder<OutputStream> osHolder(os);
    s->recvToOutputStream(os, size, timeout_ms, xsink);
}

//! Receives a 1-byte signed integer from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvi1();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 1-byte signed integer read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvu1()
*/
int Socket::recvi1(timeout timeout_ms = -1) {
    char b;
    s->recvi1(timeout_ms, &b, xsink);
    return b;
}

//! Receives a 2-byte (16-bit) signed integer in big-endian format (network byte order) from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvi2();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 2-byte signed integer in big-endian format (network byte order) read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvu2(), Socket::recvi2LSB(), Socket::recvu2LSB()
*/
int Socket::recvi2(timeout timeout_ms = -1) {
    short b;
    s->recvi2(timeout_ms, &b, xsink);
    return b;
}

//! Receives a 4-byte (32-bit) signed integer in big-endian format (network byte order) from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvi4();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the
    call will not time out and will not return until all the data has been read or the remote end closes the
    connection. Note that like all %Qore functions and methods taking timeout values, a
    @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes,
    etc.)

    @return The 4-byte signed integer in big-endian format (network byte order) read; if any errors occur reading from
    the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvu4(), Socket::recvi4LSB(), Socket::recvu4LSB()
*/
int Socket::recvi4(timeout timeout_ms = -1) {
    int b;
    s->recvi4(timeout_ms, &b, xsink);
    return b;
}

//! Receives an 8-byte (64-bit) signed integer in big-endian format (network byte order) from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvi8();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 8-byte signed integer in big-endian format (network byte order) read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvi8LSB()
*/
int Socket::recvi8(timeout timeout_ms = -1) {
    int64 b;
    s->recvi8(timeout_ms, &b, xsink);
    return b;
}

//! Receives a 2-byte (16-bit) signed integer in little-endian format from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvi2LSB();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 2-byte signed integer in little-endian format read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvi2(), Socket::recvu2(), Socket::recvu2LSB()
*/
int Socket::recvi2LSB(timeout timeout_ms = -1) {
    short b;
    s->recvi2LSB(timeout_ms, &b, xsink);
    return b;
}

//! Receives a 4-byte (32-bit) signed integer in little-endian format from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvi4LSB();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 4-byte signed integer in little-endian format read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvi4(), Socket::recvu4(), Socket::recvu4LSB()
*/
int Socket::recvi4LSB(timeout timeout_ms = -1) {
    int b;
    s->recvi4LSB(timeout_ms, &b, xsink);
    return b;
}

//! Receives an 8-byte (64-bit) signed integer in little-endian format from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvi8LSB();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 8-byte signed integer in little-endian format read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvi8()
*/
int Socket::recvi8LSB(timeout timeout_ms = -1) {
    int64 b;
    s->recvi8LSB(timeout_ms, &b, xsink);
    return b;
}

//! Receives a 1-byte unsigned integer from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvu1();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 1-byte unsigned integer read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvi1()
*/
int Socket::recvu1(timeout timeout_ms = -1) {
    unsigned char b;
    s->recvu1(timeout_ms, &b, xsink);
    return b;
}

//! Receives a 2-byte (16-bit) unsigned integer in big-endian format (network byte order) from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvu2();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 2-byte unsigned integer in big-endian format (network byte order) read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvi2(), Socket::recvi2LSB(), Socket::recvu2LSB()
*/
int Socket::recvu2(timeout timeout_ms = -1) {
    unsigned short b;
    s->recvu2(timeout_ms, &b, xsink);
    return b;
}

//! Receives a 4-byte (32-bit) unsigned integer in big-endian format (network byte order) from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvu4();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 4-byte unsigned integer in big-endian format (network byte order) read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvi4(), Socket::recvi4LSB(), Socket::recvu4LSB()
*/
int Socket::recvu4(timeout timeout_ms = -1) {
    unsigned int b;
    s->recvu4(timeout_ms, &b, xsink);
    return b;
}

//! Receives a 2-byte (16-bit) unsigned integer in little-endian format from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvi2LSB();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 2-byte unsigned integer in little-endian format read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvi2(), Socket::recvu2(), Socket::recvi2LSB()
*/
int Socket::recvu2LSB(timeout timeout_ms = -1) {
    unsigned short b;
    s->recvu2LSB(timeout_ms, &b, xsink);
    return b;
}

//! Receives a 4-byte (32-bit) unsigned integer in little-endian format from the socket
/** If any errors occur reading from the socket, an exception is raised

    @par Example:
    @code{.py}
int val = sock.recvu4LSB();
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been read or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return The 4-byte unsigned integer in little-endian format read; if any errors occur reading from the socket, an exception is raised

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-CLOSED the remote end has closed the connection
    @throw SOCKET-RECV-ERROR there was an error receiving the data
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR there was an SSL error while reading data from the socket

    @see Socket::recvi4(), Socket::recvu4(), Socket::recvi4LSB()
*/
int Socket::recvu4LSB(timeout timeout_ms = -1) {
    unsigned b;
    s->recvu4LSB(timeout_ms, &b, xsink);
    return b;
}

//! Sends an HTTP message with a method and user-defined headers given as a hash and an optional message body
/** Creates a properly-formatted HTTP message and sends it over the Socket.
    To send a string without converting the encoding to the Socket's encoding, convert the string to a binary object
    using the binary() function and use the variant of this method that takes a binary object for the data argument.

    @par Example:
    @code{.py}
sock.sendHTTPMessage("POST", "/RPC2", "1.1", ("Content-Type" : "text/xml"), xml);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT, @ref EVENT_HTTP_SEND_MESSAGE

    @param method the HTTP method name to send (i.e. POST, HEAD, etc)
    @param path the path component of the URL
    @param http_version the HTTP protocol version (\c "1.0" or \c "1.1")
    @param headers a hash of additional headers to send (key-value pairs)
    @param body the body to be sent with the message (if present, the \c "Content-Length" header will be automatically
    set); string data will be automatically converted to the Socket's encoding if necessary
    @param info An optional reference to an lvalue that can accept a hash that will be used as an output variable with
    the following hash key:
    - \c request-uri: The request URI as sent in the output message
    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the
    call will not time out and will not return until all the data has been sent or the remote end closes the
    connection. Note that like all %Qore functions and methods taking timeout values, a
    @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes,
    etc.)

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw ENCODING-CONVERSION-ERROR the given string could not be converted to the socket's character encoding
    @throw SOCKET-SEND-ERROR Send failed
    @throw SOCKET-SSL-ERROR there was an error sending SSL data

    @see Socket::sendHTTPResponse(), Socket::readHTTPHeader()
*/
nothing Socket::sendHTTPMessage(string method, string path, string http_version, hash<auto> headers, string body,
        *reference<hash> info, timeout timeout_ms = -1) {
    OptHashRefHelper ohrh(info, xsink);
    s->sendHTTPMessage(xsink, *ohrh, method->c_str(), path->c_str(), http_version->c_str(), headers, *body,
        QORE_SOURCE_SOCKET, timeout_ms);
}

//! Sends an HTTP message with a method and user-defined headers given as a hash and an optional message body
/** Creates a properly-formatted HTTP message and sends it over the Socket

    @par Example:
    @code{.py}
sock.sendHTTPMessage("POST", "/RPC2", "1.1", ("Content-Type" : "text/xml"), xml);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT, @ref EVENT_HTTP_SEND_MESSAGE

    @param method the HTTP method name to send (i.e. POST, HEAD, etc)
    @param path the path component of the URL
    @param http_version the HTTP protocol version (\c "1.0" or \c "1.1")
    @param headers a hash of additional headers to send (key-value pairs)
    @param body if it does not have a length of zero, the body to be sent with the message (if present, the
    \c "Content-Length" header will be automatically set)
    @param info An optional reference to an lvalue that can accept a hash that will be used as an output variable with
    the following hash key:
    - \c request-uri: The request URI as sent in the output message
    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the
    call will not time out and will not return until all the data has been sent or the remote end closes the
    connection. Note that like all %Qore functions and methods taking timeout values, a
    @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes,
    etc.)

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR Send failed
    @throw SOCKET-SSL-ERROR there was an error sending SSL data

    @see Socket::sendHTTPResponse(), Socket::readHTTPHeader()
*/
nothing Socket::sendHTTPMessage(string method, string path, string http_version, hash<auto> headers, *binary body,
        *reference<hash<auto>> info, timeout timeout_ms = -1) {
    OptHashRefHelper ohrh(info, xsink);
    s->sendHTTPMessage(xsink, *ohrh, method->c_str(), path->c_str(), http_version->c_str(), headers,
        body ? body->getPtr() : nullptr, body ? body->size() : 0, QORE_SOURCE_SOCKET, timeout_ms);
}

//! Sends an HTTP message with a method and user-defined headers given as a hash and an optional message body
/** Creates a properly-formatted HTTP message and sends it over the Socket

    @par Example:
    @code{.py}
sock.sendHTTPMessageWithCallback(send_callback, "POST", "/RPC2", "1.1", ("Content-Type" : "text/xml"));
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT, @ref EVENT_HTTP_SEND_MESSAGE

    @param scb The callback giving the chunked HTTP data to send; this callback must return either a string or a binary value each time it is called to give the chunked data to send; when all data has been sent, then a hash of message trailers can be sent or simply @ref nothing which will close the chunked message
    @param method the HTTP method name to send (i.e. POST, HEAD, etc)
    @param path the path component of the URL
    @param http_version the HTTP protocol version (\c "1.0" or \c "1.1")
    @param headers a hash of additional headers to send (key-value pairs)
    @param info An optional reference to an lvalue that can accept a hash that will be used as an output variable with the following hash key:
    - \c request-uri: The request URI as sent in the output message
    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR Send failed
    @throw SOCKET-SSL-ERROR there was an error sending SSL data

    @see Socket::sendHTTPResponse(), Socket::readHTTPHeader()

    @since %Qore 0.8.10
*/
nothing Socket::sendHTTPMessageWithCallback(code scb, string method, string path, string http_version,
        hash<auto> headers, *reference<hash<auto>> info, timeout timeout_ms = -1) {
    OptHashRefHelper ohrh(info, xsink);
    s->sendHTTPMessageWithCallback(xsink, *ohrh, method->c_str(), path->c_str(), http_version->c_str(), headers, *scb,
        QORE_SOURCE_SOCKET, timeout_ms);
}

//! Sends an HTTP response with user-defined headers given as a hash and an optional message body
/** Creates a properly-formatted HTTP response message and sends it over the Socket (must already be connected).  If any errors occur, an exception is raised.
    To send a string without converting the encoding to the Socket's encoding, convert the string to a binary object using the binary() function and use the variant of this method that takes a binary object for the data argument.

    @par Example:
    @code{.py}
sock.sendHTTPResponse(200, "OK", "1.1", ("Connection":"Keep-Alive"));
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT, @ref EVENT_HTTP_SEND_MESSAGE

    @param status_code the HTTP status code to send (i.e. \c 200, \c 404, etc)
    @param status_desc the descriptive text for the status code.
    @param http_version the HTTP protocol version (normally \c "1.0" or \c "1.1", however this method allows any string to be sent)
    @param headers a hash of additional headers to send (key-value pairs)
    @param body if present (and does not have a length of zero), the body to be sent with the message (if present, the \c "Content-Length" header will be automatically set); string data will be automatically converted to the Socket's encoding if necessary
    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the call will not time out and will not return until all the data has been sent or the remote end closes the connection. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-SENDHTTPRESPONSE-STATUS-ERROR raised if the status_code (first argument) is < 100 or > 599
    @throw ENCODING-CONVERSION-ERROR the given string could not be converted to the socket's character encoding
    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR send failed
*/
nothing Socket::sendHTTPResponse(softint status_code, string status_desc, string http_version, hash<auto> headers,
        string body, timeout timeout_ms = -1) {
    if (check_response_code(status_code, xsink)) {
        return QoreValue();
    }

    s->sendHTTPResponse(xsink, nullptr, status_code, status_desc->c_str(), http_version->c_str(), headers, *body,
        QORE_SOURCE_SOCKET, timeout_ms);
}

//! Sends an HTTP response with user-defined headers given as a hash and an optional message body
/** Creates a properly-formatted HTTP response message and sends it over the Socket (must already be connected).  If
    any errors occur, an exception is raised.
    To send a string without converting the encoding to the Socket's encoding, convert the string to a binary object
    using the binary() function and use the variant of this method that takes a binary object for the data argument.

    @par Example:
    @code{.py}
sock.sendHTTPResponse(200, "OK", "1.1", ("Connection":"Keep-Alive"));
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT, @ref EVENT_HTTP_SEND_MESSAGE

    @param status_code the HTTP status code to send (i.e. \c 200, \c 404, etc)
    @param status_desc the descriptive text for the status code.
    @param http_version the HTTP protocol version (normally \c "1.0" or \c "1.1", however this method allows any
    string to be sent)
    @param headers a hash of additional headers to send (key-value pairs)
    @param body if present (and does not have a length of zero), the body to be sent with the message (if present, the
    \c "Content-Length" header will be automatically set); string data will be automatically converted to the Socket's
    encoding if necessary
    @param info An optional reference to an lvalue that can accept a hash that will be used as an output variable with
    the following hash key:
    - \c response-uri: The response URI as sent in the output message
    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the
    call will not time out and will not return until all the data has been sent or the remote end closes the
    connection. Note that like all %Qore functions and methods taking timeout values, a
    @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes,
    etc.)

    @throw SOCKET-SENDHTTPRESPONSE-STATUS-ERROR raised if the status_code (first argument) is < 100 or > 599
    @throw ENCODING-CONVERSION-ERROR the given string could not be converted to the socket's character encoding
    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR send failed

    @since %Qore 0.9.4
*/
nothing Socket::sendHTTPResponse(softint status_code, string status_desc, string http_version, hash<auto> headers,
        string body, *reference<hash<auto>> info, timeout timeout_ms = -1) {
    if (check_response_code(status_code, xsink)) {
        return QoreValue();
    }

    OptHashRefHelper ohrh(info, xsink);
    s->sendHTTPResponse(xsink, *ohrh, status_code, status_desc->c_str(), http_version->c_str(), headers, *body,
        QORE_SOURCE_SOCKET, timeout_ms);
}

//! Sends an HTTP response with user-defined headers given as a hash and a message body as literal binary data
/** Creates a properly-formatted HTTP response message and sends it over the Socket (must already be connected).  If
    any errors occur, an exception is raised.

    @par Example:
    @code{.py}
sock.sendHTTPResponse(200, "OK", "1.1", {"Connection":"Keep-Alive"}, bin);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT, @ref EVENT_HTTP_SEND_MESSAGE

    @param status_code the HTTP status code to send (i.e. \c 200, \c 404, etc)
    @param status_desc the descriptive text for the status code.
    @param http_version the HTTP protocol version (normally \c "1.0" or \c "1.1", however this method allows any
    string to be sent)
    @param headers a hash of additional headers to send (key-value pairs)
    @param body if the binary object given does not have a length of zero, the body to be sent with the message (if
    present, the \c "Content-Length" header will be automatically set)
    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the
    call will not time out and will not return until all the data has been sent or the remote end closes the
    connection. Note that like all %Qore functions and methods taking timeout values, a
    @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes,
    etc.)

    @throw SOCKET-SENDHTTPRESPONSE-STATUS-ERROR raised if the status_code (first argument) is < 100 or > 599
    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR send failed
*/
nothing Socket::sendHTTPResponse(softint status_code, string status_desc, string http_version, hash<auto> headers,
        *binary body, timeout timeout_ms = -1) {
    if (check_response_code(status_code, xsink)) {
        return QoreValue();
    }

    s->sendHTTPResponse(xsink, nullptr, status_code, status_desc->c_str(), http_version->c_str(), headers,
        body ? body->getPtr() : nullptr, body ? body->size() : 0, QORE_SOURCE_SOCKET, timeout_ms);
}

//! Sends an HTTP response with user-defined headers given as a hash and a message body as literal binary data
/** Creates a properly-formatted HTTP response message and sends it over the Socket (must already be connected).  If
    any errors occur, an exception is raised.

    @par Example:
    @code{.py}
sock.sendHTTPResponse(200, "OK", "1.1", {"Connection":"Keep-Alive"}, bin);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT, @ref EVENT_HTTP_SEND_MESSAGE

    @param status_code the HTTP status code to send (i.e. \c 200, \c 404, etc)
    @param status_desc the descriptive text for the status code.
    @param http_version the HTTP protocol version (normally \c "1.0" or \c "1.1", however this method allows any
    string to be sent)
    @param headers a hash of additional headers to send (key-value pairs)
    @param body if the binary object given does not have a length of zero, the body to be sent with the message (if
    present, the \c "Content-Length" header will be automatically set)
    @param info An optional reference to an lvalue that can accept a hash that will be used as an output variable with
    the following hash key:
    - \c response-uri: The response URI as sent in the output message
    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the
    call will not time out and will not return until all the data has been sent or the remote end closes the
    connection. Note that like all %Qore functions and methods taking timeout values, a
    @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes,
    etc.)

    @throw SOCKET-SENDHTTPRESPONSE-STATUS-ERROR raised if the status_code (first argument) is < 100 or > 599
    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR send failed

    @since %Qore 0.9.4
*/
nothing Socket::sendHTTPResponse(softint status_code, string status_desc, string http_version, hash<auto> headers,
        *binary body, *reference<hash<auto>> info, timeout timeout_ms = -1) {
    if (check_response_code(status_code, xsink)) {
        return QoreValue();
    }

    OptHashRefHelper ohrh(info, xsink);
    s->sendHTTPResponse(xsink, *ohrh, status_code, status_desc->c_str(), http_version->c_str(), headers,
        body ? body->getPtr() : nullptr, body ? body->size() : 0, QORE_SOURCE_SOCKET, timeout_ms);
}

//! Sends an HTTP response with user-defined headers and a message body from an input stream in chunked transfer encoding
/** If any errors are encountered, an exception is raised.

    @par Example:
    @code{.py}
sock.sendHTTPResponse(200, "OK", "1.1", {"Connection": "Keep-Alive", "Transfer-Encoding": "chunked"}, inputStream);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param status_code the HTTP status code to send (i.e. \c 200, \c 404, etc)
    @param status_desc the descriptive text for the status code.
    @param http_version the HTTP protocol version (normally \c "1.0" or \c "1.1", however this method allows any string to be sent)
    @param headers a hash of additional headers to send (key-value pairs); note that the \c "Transfer-Encoding" header
    must be set to \c "chunked" manually with this call
    @param input_stream The input stream to read data from
    @param max_chunk_size the maximum chunk size as the read block size for the @ref InputStream read block size; the
    actual size of the data read will determine the size of each HTTP chunk
    @param info An optional reference to an lvalue that can accept a hash that will be used as an output variable with
    the following hash key:
    - \c response-uri: The response URI as sent in the output message
    @param timeout_ms The timeout in milliseconds (1/1000 second). If no timeout or if a negative timeout is passed,
    then the call will not time out and will not return until all the data has been read or the remote end closes the
    connection.  If a timeout occurs, a \c "SOCKET-TIMEOUT" exception is raised. Note that like all %Qore functions
    and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units
    clear (i.e. \c 2m = two minutes, etc.)
    @param tcb An optional trailer callback for message trailers to write in the outgoing chunked message; the
    callback takes no arguments and must return either @ref nothing or @ref hash_type "hash" giving a hash of trailers
    for the final chunk

    @note the \c "Transfer-Encoding" header must be set to \c "chunked" manually in the \a headers argument as in the
    example

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR send failed
    @throw HTTP-CHUNK-ERROR the chunk size given is not greater than 0
    @throw HTTP-TRAILER-ERROR the trailer callback must return either @ref nothing or @ref hash_type "hash"; this
    exception is thrown is another type is returned

    @since %Qore 0.9.4
*/
nothing Socket::sendHTTPResponse(softint status_code, string status_desc, string http_version, hash<auto> headers,
        Qore::InputStream[InputStream] input_stream, int max_chunk_size = 4096, *reference<hash<auto>> info, timeout timeout_ms = -1, *code tcb) {
    SimpleRefHolder<InputStream> isHolder(input_stream);
    if (check_response_code(status_code, xsink)) {
        return QoreValue();
    }
    if (max_chunk_size < 1) {
        xsink->raiseException("HTTP-CHUNK-ERROR", "the max chunk size must be greater than 0; value given is: " QLLD,
            max_chunk_size);
        return QoreValue();
    }

    OptHashRefHelper ohrh(info, xsink);
    s->sendHTTPResponse(xsink, *ohrh, status_code, status_desc->c_str(), http_version->c_str(), headers, input_stream,
        max_chunk_size, tcb, QORE_SOURCE_SOCKET, timeout_ms);
}

//! Sends an HTTP response with user-defined headers given as a hash and a message body to send with chunked transfer encoding provided by a callback
/** Creates a properly-formatted HTTP response message and sends it over the Socket (must already be connected).  If any errors occur, an exception is raised.

    @par Example:
    @code{.py}
sock.sendHTTPResponseWithCallback(send_callback, 200, "OK", "1.1", {"Connection": "Keep-Alive", "Transfer-Encoding": "chunked"});
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT, @ref EVENT_HTTP_SEND_MESSAGE

    @param scb The callback giving the chunked HTTP data to send; this callback must return either a string or a
    binary value each time it is called to give the chunked data to send; when all data has been sent, then a hash of
    message trailers can be sent or simply @ref nothing which will close the chunked message
    @param status_code the HTTP status code to send (i.e. \c 200, \c 404, etc)
    @param status_desc the descriptive text for the status code.
    @param http_version the HTTP protocol version (normally \c "1.0" or \c "1.1", however this method allows any
    string to be sent)
    @param headers a hash of additional headers to send (key-value pairs); note that the \c "Transfer-Encoding" header
    must be set to \c "chunked" manually with this call
    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the
    call will not time out and will not return until all the data has been sent or the remote end closes the
    connection. Note that like all %Qore functions and methods taking timeout values, a
    @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes,
    etc.)

    @note the \c "Transfer-Encoding" header must be set to \c "chunked" manually in the \a headers argument as in the
    example

    @throw SOCKET-SENDHTTPRESPONSE-STATUS-ERROR raised if the status_code (first argument) is < 100 or > 599
    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR send failed

    @since %Qore 0.8.10
*/
nothing Socket::sendHTTPResponseWithCallback(code scb, softint status_code, string status_desc, string http_version,
        hash<auto> headers, timeout timeout_ms = -1) {
    if (check_response_code(status_code, xsink)) {
        return QoreValue();
    }

    s->sendHTTPResponseWithCallback(xsink, nullptr, status_code, status_desc->c_str(), http_version->c_str(), headers,
        *scb, QORE_SOURCE_SOCKET, timeout_ms);
}

//! Sends an HTTP response with user-defined headers given as a hash and a message body to send with chunked transfer encoding provided by a callback
/** Creates a properly-formatted HTTP response message and sends it over the Socket (must already be connected).  If any errors occur, an exception is raised.

    @par Example:
    @code{.py}
sock.sendHTTPResponseWithCallback(send_callback, 200, "OK", "1.1", {"Connection": "Keep-Alive", "Transfer-Encoding": "chunked"});
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT, @ref EVENT_HTTP_SEND_MESSAGE

    @param scb The callback giving the chunked HTTP data to send; this callback must return either a string or a
    binary value each time it is called to give the chunked data to send; when all data has been sent, then a hash of
    message trailers can be sent or simply @ref nothing which will close the chunked message
    @param status_code the HTTP status code to send (i.e. \c 200, \c 404, etc)
    @param status_desc the descriptive text for the status code.
    @param http_version the HTTP protocol version (normally \c "1.0" or \c "1.1", however this method allows any
    string to be sent)
    @param headers a hash of additional headers to send (key-value pairs); note that the \c "Transfer-Encoding" header
    must be set to \c "chunked" manually with this call
    @param info An optional reference to an lvalue that can accept a hash that will be used as an output variable with
    the following hash key:
    - \c response-uri: The response URI as sent in the output message
    @param timeout_ms the timeout in milliseconds (1/1000 second); If no timeout is passed or is negative, then the
    call will not time out and will not return until all the data has been sent or the remote end closes the
    connection. Note that like all %Qore functions and methods taking timeout values, a
    @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes,
    etc.)

    @note the \c "Transfer-Encoding" header must be set to \c "chunked" manually in the \a headers argument as in the
    example

    @throw SOCKET-SENDHTTPRESPONSE-STATUS-ERROR raised if the status_code (first argument) is < 100 or > 599
    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR send failed

    @since %Qore 0.9.4
*/
nothing Socket::sendHTTPResponseWithCallback(code scb, softint status_code, string status_desc, string http_version,
        hash<auto> headers, *reference<hash<auto>> info, timeout timeout_ms = -1) {
    if (check_response_code(status_code, xsink)) {
        return QoreValue();
    }

    OptHashRefHelper ohrh(info, xsink);
    s->sendHTTPResponseWithCallback(xsink, *ohrh, status_code, status_desc->c_str(), http_version->c_str(), headers,
        *scb, QORE_SOURCE_SOCKET, timeout_ms);
}

//! Sends an HTTP message body in chunked transfer encoding
/** If any errors are encountered, an exception is raised.

    @par Example:
    @code{.py}
sock.sendHTTPChunkedBodyFromInputStream(inputStream);
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param input_stream The input stream to read data from
    @param max_chunk_size the maximum chunk size as the read block size for the @ref InputStream read block size; the
    actual size of the data read will determine the size of each HTTP chunk
    @param timeout_ms The timeout in milliseconds (1/1000 second). If no timeout or if a negative timeout is passed,
    then the call will not time out and will not return until all the data has been read or the remote end closes the
    connection.  If a timeout occurs, a \c "SOCKET-TIMEOUT" exception is raised. Note that like all %Qore functions
    and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units
    clear (i.e. \c 2m = two minutes, etc.)
    @param tcb An optional trailer callback for message trailers to write in the outgoing chunked message; the
    callback takes no arguments and must return either @ref nothing or @ref hash_type "hash" giving a hash of trailers
    for the final chunk

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR send failed
    @throw HTTP-CHUNK-ERROR the chunk size given is not greater than 0
    @throw HTTP-TRAILER-ERROR the trailer callback must return either @ref nothing or @ref hash_type "hash"; this
    exception is thrown is another type is returned

    @since %Qore 0.8.13
*/
nothing Socket::sendHTTPChunkedBodyFromInputStream(Qore::InputStream[InputStream] input_stream, int max_chunk_size = 4096,
        timeout timeout_ms = -1, *code tcb) {
    SimpleRefHolder<InputStream> isHolder(input_stream);
    if (max_chunk_size < 1) {
        xsink->raiseException("HTTP-CHUNK-ERROR", "the max chunk size must be greater than 0; value given is: " QLLD,
            max_chunk_size);
        return QoreValue();
    }
    s->sendHTTPChunkedBodyFromInputStream(input_stream, max_chunk_size, timeout_ms, tcb, xsink);
}

//! Sends the trialer of an HTTP message body in chunked transfer encoding
/** If any errors are encountered, an exception is raised

    @par Example:
    @code{.py}
sock.sendHTTPChunkedBodyTrailer({});
    @endcode

    @par Events:
    @ref EVENT_PACKET_SENT

    @param trailer optional trailer data
    @param timeout_ms The timeout in milliseconds (1/1000 second). If no timeout or if a negative timeout is passed,
    then the call will not time out and will not return until all the data has been read or the remote end closes the
    connection.  If a timeout occurs, a \c "SOCKET-TIMEOUT" exception is raised. Note that like all %Qore functions
    and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units
    clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN the socket is not connected
    @throw SOCKET-TIMEOUT the data requested was not received in the timeout period
    @throw SOCKET-SEND-ERROR send failed

    @since %Qore 0.8.13
*/
nothing Socket::sendHTTPChunkedBodyTrailer(*hash<auto> trailer, timeout timeout_ms = -1) {
    s->sendHTTPChunkedBodyTrailer(trailer, timeout_ms, xsink);
}

//! Retuns a hash representing the data in the HTTP header read, or, if the data cannot be parsed as an HTTP header, then an exception is thrown, and the data read is returned as a string in the \c arg key of the exception hash
/** If any errors occur reading from the socket or if invalid HTTP data is received, an exception is raised. Accepts an optional timeout value in milliseconds.

    @par Example:
    @code{.py}
hash<auto> ans = sock.readHTTPHeader(10s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ, @ref EVENT_HTTP_MESSAGE_RECEIVED

    @param timeout_ms The timeout in milliseconds (1/1000 second). If no timeout or if a negative timeout is passed, then the call will not time out and will not return until all the data has been read or the remote end closes the connection.  If a timeout occurs, a \c "SOCKET-TIMEOUT" exception is raised. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)
    @param info An optional reference to an lvalue that can accept a hash that will be used as an output variable with one of the following two keys:
    - \c headers-raw: the raw headers read without any case conversions or other processing
    - \c request-uri: (only set when parsing a request header) gives the request URI in an HTTP request
    - \c response-uri: (only set when parsing a response header) gives the response URI in an HTTP response
    - \c body-content-type: this is the  \c "Content-Type" header without any charset declaration
    - \c charset: if there is a charset declaration in the \c "Content-Type" header, the value is returned in this key
    - \c close: (only set when parsing a request header) set to @ref True "True" if the connection should be closed after responding, @ref False "False" if not; see notes below about how this value is calculated
    - \c accept-charset: this key will be set to an appropriate value from any \c "Accept-Charset" header; if any of \c "*", \c "utf8", or \c "utf-8" are present, then this will be set to \c "utf8", otherwise it will be set to the first requested character encoding in the list
    - \c accept-encoding: this key will be set to a list of values from any \c "Accept-Encoding" header

    @return a hash of headers (where each header key is converted to lower-case) also including the following keys giving additional information about the HTTP header received:
    - \c http_version: a string giving the HTTP version set in the header
    - \c status_code: (HTTP responses only) an integer giving the status code
    - \c status_message: (HTTP responses only) if present in an HTTP response, this key will be set to the message after the status code
    - \c method: (HTTP requests only) a string giving the HTTP method (i.e. \c "GET", \c "POST", etc)
    - \c path: (HTTP requests only) a string giving the path in a request without any decoding; use decode_url() to decode if necessary

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-CLOSED The remote end closed the connection without sending any data
    @throw SOCKET-RECV-ERROR There was an error receiving the data
    @throw SOCKET-TIMEOUT The data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR There was an SSL error while reading data from the socket
    @throw SOCKET-HTTP-ERROR Invalid HTTP data was received, in the case of invalid header info received, the \c arg key of the exception hash will have the invalid data received

    @note
    - if the header claims a certain character encoding via a \c charset declaration in the \c "Content-Type" header, the Socket's @ref character_encoding "character encoding" is automatically set accordingly
    - <a href="http://tools.ietf.org/html/rfc2616#section-3.7.1">RFC 2616 3.7.1</a>: if no encoding is specified, then set \c "iso-8859-1"
    - <a href="http://tools.ietf.org/html/rfc2068#section-19.7.1">RFC 2068 19.7.1</a>: Persistent connections in HTTP/1.0 must be explicitly negotiated as they are not the default behavior
    - <a href="http://tools.ietf.org/html/rfc1945#section-1.3">RFC 1945 1.3</a>: Except for experimental applications, current practice requires that the connection be established by the client prior to each request and closed by the server after sending the response.
    - the conclusion is that server applications MUST close the connection when a request is received by an HTTP 1.0 client without an explicit request to keep the connection open

    @since
    - %Qore 0.8.4 this method always returns a hash and raises a \c SOCKET-HTTP-ERROR if invalid HTTP data is received
    - %Qore 0.8.8 added the \c close, \c charset, \c body-content-type, and \c accept-charset info keys as well as the encoding handling based on the detected charset
    - %Qore 0.9.3 added the \c "headers-raw" key in the output \c info hash
*/
hash<auto> Socket::readHTTPHeader(timeout timeout_ms = -1, *reference<hash<auto>> info) {
    OptHashRefHelper ohrh(info, xsink);
    return s->readHTTPHeader(xsink, *ohrh, timeout_ms);
}

//! Retuns a string representing the data in the HTTP header read (reads until \c "\r\n\r\n")
/** If any errors occur reading from the socket, an exception is raised. Accepts an optional timeout value in milliseconds.

    @par Example:
    @code{.py}
string str = sock.readHTTPHeaderString(10s);
    @endcode

    @par Events:
    @ref EVENT_PACKET_READ

    @param timeout_ms The timeout in milliseconds (1/1000 second). If no timeout or if a negative timeout is passed, then the call will not time out and will not return until all the data has been read or the remote end closes the connection.  If a timeout occurs, a \c "SOCKET-TIMEOUT" exception is raised. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return a string representing the header data read

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-CLOSED The remote end closed the connection without sending any data
    @throw SOCKET-RECV-ERROR There was an error receiving the data
    @throw SOCKET-TIMEOUT The data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR There was an SSL error while reading data from the socket
    @throw SOCKET-HTTP-ERROR maximum header size was exceeded

    @since %Qore 0.8.8
*/
string Socket::readHTTPHeaderString(timeout timeout_ms = -1) {
    return s->readHTTPHeaderString(xsink, timeout_ms);
}

//! Reads in an HTTP message body sent in chunked transfer encoding and returns it with any footers received as a string in the \c "body" key of a hash (including footers received)
/** If any errors are encountered, an exception is raised

    @par Example:
    @code{.py}
hash<auto> ans = sock.readHTTPChunkedBody(20s);
    @endcode

    @par Events:
    @ref EVENT_HTTP_CHUNK_SIZE, @ref EVENT_HTTP_FOOTERS_RECEIVED

    @param timeout_ms The timeout in milliseconds (1/1000 second). If no timeout or if a negative timeout is passed, then the call will not time out and will not return until all the data has been read or the remote end closes the connection.  If a timeout occurs, a \c "SOCKET-TIMEOUT" exception is raised. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return a hash of any HTTP footers received (with footer keys converted to lower case), with the body returned in the \c "body" key as a string in the Socket's encoding

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-CLOSED The remote end has closed the connection
    @throw SOCKET-RECV-ERROR There was an error receiving the data
    @throw SOCKET-TIMEOUT The data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR There was an SSL error while reading data from the socket
    @throw READ-HTTP-CHUNK-ERROR negative value given for chunk size by server
*/
hash<auto> Socket::readHTTPChunkedBody(timeout timeout_ms = -1) {
    // when rc = -3 it's a timeout, but rv will be NULL anyway, so we do nothing
    return s->readHTTPChunkedBody(timeout_ms, xsink);
}

//! Reads in an HTTP message body sent in chunked transfer encoding and returns it with any footers received as a binary object in the \c "body" key of a hash (including footers received)
/** If any errors are encountered, an exception is raised

    @par Example:
    @code{.py}
hash<auto> ans = sock.readHTTPChunkedBodyBinary(20s);
    @endcode

    @par Events:
    @ref EVENT_HTTP_CHUNK_SIZE, @ref EVENT_HTTP_FOOTERS_RECEIVED

    @param timeout_ms The timeout in milliseconds (1/1000 second). If no timeout or if a negative timeout is passed, then the call will not time out and will not return until all the data has been read or the remote end closes the connection.  If a timeout occurs, a \c "SOCKET-TIMEOUT" exception is raised. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return a hash of any HTTP footers received (with footer keys converted to lower case), with the body returned in the \c "body" key as a binary object

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-CLOSED The remote end has closed the connection
    @throw SOCKET-RECV-ERROR There was an error receiving the data
    @throw SOCKET-TIMEOUT The data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR There was an SSL error while reading data from the socket
    @throw READ-HTTP-CHUNK-ERROR negative value given for chunk size by server
*/
hash<auto> Socket::readHTTPChunkedBodyBinary(timeout timeout_ms = -1) {
    // when rc = -3 it's a timeout, but rv will be NULL anyway, so we do nothing
    return s->readHTTPChunkedBodyBinary(timeout_ms, xsink);
}

//! Reads in an HTTP message body sent in chunked transfer encoding, writes it in an @ref OutputStream and returns any footers received
/** If any errors are encountered, an exception is raised

    @par Example:
    @code{.py}
hash<auto> ans = sock.readHTTPChunkedBodyToOutputStream(outputStream, 20s);
    @endcode

    @par Events:
    @ref EVENT_HTTP_CHUNK_SIZE, @ref EVENT_HTTP_FOOTERS_RECEIVED

    @param os The output stream to write the response body to
    @param timeout_ms The timeout in milliseconds (1/1000 second). If no timeout or if a negative timeout is passed, then the call will not time out and will not return until all the data has been read or the remote end closes the connection.  If a timeout occurs, a \c "SOCKET-TIMEOUT" exception is raised. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @return a hash of any HTTP footers received (with footer keys converted to lower case)

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-CLOSED The remote end has closed the connection
    @throw SOCKET-RECV-ERROR There was an error receiving the data
    @throw SOCKET-TIMEOUT The data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR There was an SSL error while reading data from the socket
    @throw READ-HTTP-CHUNK-ERROR negative value given for chunk size by server
*/
hash<auto> Socket::readHTTPChunkedBodyToOutputStream(Qore::OutputStream[OutputStream] os, timeout timeout_ms = -1) {
    SimpleRefHolder<OutputStream> osHolder(os);
    return s->readHTTPChunkedBodyToOutputStream(os, timeout_ms, xsink);
}

//! Reads in an HTTP message body sent in chunked transfer encoding and returns it with any footers received as a string in the \c "body" key of a hash (including footers received)
/** If any errors are encountered, an exception is raised

    @par Example:
    @code{.py}
sock.readHTTPChunkedBodyWithCallback(recv_callback, 20s);
    @endcode

    @par Events:
    @ref EVENT_HTTP_CHUNK_SIZE, @ref EVENT_HTTP_FOOTERS_RECEIVED

    @param rcb The receive callback for the data received; first this method is called with a hash of the message headers, and then with any message body; if a chunked HTTP message is received, then the callback is called once for each chunk; when the message has been received, then the receive callback is called with a hash representing any trailer data received in a chunked transfer or @ref nothing if the data was received in a normal message body or if there was no trailer data in a chunked transfer.  The argument to this callback is always a hash; data calls have the following keys:
    - \c "data": the string or binary data
    - \c "chunked": True if the data was received with chunked transfer encoding, False if not
    .
    Header or trailer data is placed in a hash with the following keys:
    - \c "hdr": this can be assigned to @ref nothing for the trailer hash if the data was not sent chunked or no trailers were included in a chunked message
    - \c "obj": this is the owning object (so socket parameters can be changed based on headers received, such as, for example, socket character encoding)
    @param timeout_ms The timeout in milliseconds (1/1000 second). If no timeout or if a negative timeout is passed, then the call will not time out and will not return until all the data has been read or the remote end closes the connection.  If a timeout occurs, a \c "SOCKET-TIMEOUT" exception is raised. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-CLOSED The remote end has closed the connection
    @throw SOCKET-RECV-ERROR There was an error receiving the data
    @throw SOCKET-TIMEOUT The data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR There was an SSL error while reading data from the socket
    @throw READ-HTTP-CHUNK-ERROR negative value given for chunk size by server

    @since %Qore 0.8.10
*/
Socket::readHTTPChunkedBodyWithCallback(code rcb, timeout timeout_ms = -1) {
    s->readHTTPChunkedBodyWithCallback(*rcb, self, timeout_ms, xsink);
}

//! Reads in an HTTP message body sent in chunked transfer encoding and returns it with any footers received as a string in the \c "body" key of a hash (including footers received)
/** If any errors are encountered, an exception is raised

    @par Example:
    @code{.py}
sock.readHTTPChunkedBodyBinaryWithCallback(recv_callback, 20s);
    @endcode

    @par Events:
    @ref EVENT_HTTP_CHUNK_SIZE, @ref EVENT_HTTP_FOOTERS_RECEIVED

    @param rcb The receive callback for the data received; first this method is called with a hash of the message headers, and then with any message body; if a chunked HTTP message is received, then the callback is called once for each chunk; when the message has been received, then the receive callback is called with a hash representing any trailer data received in a chunked transfer or @ref nothing if the data was received in a normal message body or if there was no trailer data in a chunked transfer.  The argument to this callback is always a hash; data calls have the following keys:
    - \c "data": the string or binary data
    - \c "chunked": True if the data was received with chunked transfer encoding, False if not
    .
    Header or trailer data is placed in a hash with the following keys:
    - \c "hdr": this can be assigned to @ref nothing for the trailer hash if the data was not sent chunked or no trailers were included in a chunked message
    - \c "obj": this is the owning object (so socket parameters can be changed based on headers received, such as, for example, socket character encoding)
    @param timeout_ms The timeout in milliseconds (1/1000 second). If no timeout or if a negative timeout is passed, then the call will not time out and will not return until all the data has been read or the remote end closes the connection.  If a timeout occurs, a \c "SOCKET-TIMEOUT" exception is raised. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-CLOSED The remote end has closed the connection
    @throw SOCKET-RECV-ERROR There was an error receiving the data
    @throw SOCKET-TIMEOUT The data requested was not received in the timeout period
    @throw SOCKET-SSL-ERROR There was an SSL error while reading data from the socket
    @throw READ-HTTP-CHUNK-ERROR negative value given for chunk size by server

    @since %Qore 0.8.10
*/
Socket::readHTTPChunkedBodyBinaryWithCallback(code rcb, timeout timeout_ms = -1) {
    s->readHTTPChunkedBodyBinaryWithCallback(*rcb, self, timeout_ms, xsink);
}

//! Closes an open socket
/** Also deletes the UNIX domain socket file if it was created by a call to Socket::bind(). Returns 0 for success, -1 for error; in this case check errno() for the error number

    @par Example:
    @code{.py}
if (sock.close())
    stderr.printf("Error closing socket: %s\n", strerror(errno()));
    @endcode

    @par Events:
    @ref EVENT_CHANNEL_CLOSED

    @return 0 for success, -1 for error; in this case check errno() for the error number
*/
int Socket::close() {
    return s->close();
}

//! Ensures that a socket will be closed even if the file descriptor is shared with other processes (for example, after a call to fork())
/** Does not throw any exceptions; returns an error code in case of an error

    @par Example:
    @code{.py}
if (sock.shutdown())
    stderr.printf("Error calling Socket::shutdown(): %s\n", strerror(errno()));
    @endcode

    @return 0 for success, -1 for error; in this case check errno() for the error number
*/
int Socket::shutdown() {
    return s->shutdown();
}

//! Shuts down the SSL connection on a secure connection
/** If any errors occur, an exception is raised

    @par Example:
    @code{.py}
sock.shutdownSSL();
    @endcode

    @throw SOCKET-SSL-ERROR an error occurred shutting down the TLS/SSL connection
*/
nothing Socket::shutdownSSL() {
    s->shutdownSSL(xsink);
}

//! Returns the port number of the socket for INET sockets
/** @par Example:
    @code{.py}
int port = sock.getPort();
    @endcode

    @return the port number for an INET connection, -1 if no INET connection has been established
*/
int Socket::getPort() [flags=CONSTANT] {
    return s->getPort();
}

//! Returns the socket file descriptor number
/** @par Example:
    @code{.py}
int sock = sock.getSocket();
    @endcode

    @return the socket file descriptor number or -1 if the socket is not open
*/
int Socket::getSocket() [flags=CONSTANT] {
    return s->getSocket();
}

//! sets the send timeout as a socket option
/** Returns 0 for success, -1 for error (check errno() for the actual error in this case)

    @par Example:
    @code{.py}
if (sock.setSendTimeout(20s))
    printf("error setting timeout on socket: %s\n", strerror(errno()));
    @endcode

    @param timeout_ms the send timeout to set with a resolution of milliseconds

    @return 0 for success, -1 for error (check errno() for the actual error in this case)
*/
int Socket::setSendTimeout(timeout timeout_ms) {
    return s->setSendTimeout(timeout_ms);
}

//! sets the receive timeout as a socket option
/** Returns 0 for success, -1 for error (check errno() for the actual error in this case)

    @par Example:
    @code{.py}
if (sock.setRecvTimeout(20s))
    printf("error setting timeout on socket: %s\n", strerror(errno()));
    @endcode

    @param timeout_ms the receive timeout to set with a resolution of milliseconds; Note that like all %Qore functions
    and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units
    clear (i.e. \c 2m = two minutes, etc.)

    @return 0 for success, -1 for error (check errno() for the actual error in this case)
*/
int Socket::setRecvTimeout(timeout timeout_ms) {
    return s->setRecvTimeout(timeout_ms);
}

//! Returns the send timeout socket option value as an integer in milliseconds
/** @par Example:
    @code{.py}
int to = sock.getSendTimeout();
    @endcode

    @return the send timeout socket option value as an integer in milliseconds
*/
int Socket::getSendTimeout() [flags=CONSTANT] {
    return s->getSendTimeout();
}

//! Returns the receive timeout socket option value as an integer in milliseconds
/** @par Example:
    @code{.py}
int to = sock.getRecvTimeout();
    @endcode

    @return the receive timeout socket option value as an integer in milliseconds
*/
int Socket::getRecvTimeout() [flags=CONSTANT] {
    return s->getRecvTimeout();
}

//! Returns the @ref character_encoding "character encoding" for the socket
/** @return the @ref character_encoding "character encoding" for the socket
*/
string Socket::getEncoding() [flags=CONSTANT] {
    return new QoreStringNode(s->getEncoding()->getCode());
}

//! Sets the @ref character_encoding "character encoding" for the socket
/** @param encoding the @ref character_encoding "character encoding" for the socket
*/
nothing Socket::setEncoding(string encoding) {
    s->setEncoding(QEM.findCreate(encoding));
}

//! Returns the @ref character_encoding "character encoding" for the socket
/** A method synonym for Socket::getEncoding(), kept for backwards-compatibility
    @return the @ref character_encoding "character encoding" for the socket
*/
string Socket::getCharset() [flags=CONSTANT] {
    return new QoreStringNode(s->getEncoding()->getCode());
}

//! Sets the @ref character_encoding "character encoding" for the socket
/** A method synonym for Socket::setEncoding(), kept for backwards-compatibility
    @param encoding The @ref character_encoding "character encoding" for the socket
*/
nothing Socket::setCharset(string encoding) {
    s->setEncoding(QEM.findCreate(encoding));
}

//! Returns @ref True or @ref False depending on whether there is data to be read on the socket
/** With a timeout of zero this method returns immediately and can be used for non-blocking polling the socket for
    data (can also be used to poll for new connections before Socket::accept()).

    @par Example:
    @code{.py}
bool b = sock.isDataAvailable();
    @endcode

    @param timeout_ms an optional timeout in milliseconds (1/1000 second); if no timeout is given, the method returns
    immediately; Note that like all %Qore functions and methods taking timeout values, a
    @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes,
    etc.)

    @return @ref True if data is available on the Socket within the timeout period, @ref False if not

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-CLOSED The remote end closed the connection without sending any data
*/
bool Socket::isDataAvailable(timeout timeout_ms = 0) {
    return s->isDataAvailable(xsink, (int)timeout_ms);
}

//! Returns @ref True or @ref False depending on whether all the data has been written to the socket
/** With a timeout of zero this method returns immediately.

    @par Example:
    @code{.py}
bool b = sock.isWriteFinished();
    @endcode

    @param timeout_ms an optional timeout in milliseconds (1/1000 second); if no timeout is given, the method returns
    immediately; Note that like all %Qore functions and methods taking timeout values, a
    @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes,
    etc.)

    @return @ref True if the send action completes within the timeout period, @ref False if not

    @throw SOCKET-NOT-OPEN The socket is not connected
    @throw SOCKET-CLOSED The remote end closed the connection without sending any data
*/
bool Socket::isWriteFinished(timeout timeout_ms = 0) {
    return s->isWriteFinished(xsink, (int)timeout_ms);
}

//! Returns @ref True if the socket is open
/** @par Example:
    @code{.py}
bool b = sock.isOpen();
    @endcode

    @return @ref True if the socket is open, @ref False if not
*/
bool Socket::isOpen() [flags=CONSTANT] {
    return s->isOpen();
}

//! Returns the name of the cipher for an encrypted connection or @ref nothing if a secure connection has not been established
/** @par Example:
    @code{.py}
*string str = sock.getSSLCipherName();
    @endcode

    @return the name of the cipher for an encrypted connection or @ref nothing if a secure connection has not been
    established
*/
*string Socket::getSSLCipherName() [flags=CONSTANT] {
    const char *str = s->getSSLCipherName();
    return str ? new QoreStringNode(str) : 0;
}

//! Returns the version string of the cipher for an encrypted connection or @ref nothing if a secure connection has not been established
/** @par Example:
    @code{.py}
*string str = sock.getSSLCipherVersion();
    @endcode

    @return the version string of the cipher for an encrypted connection or @ref nothing if a secure connection has
    not been established
*/
*string Socket::getSSLCipherVersion() [flags=CONSTANT] {
    const char *str = s->getSSLCipherVersion();
    return str ? new QoreStringNode(str) : 0;
}

//! Returns @ref True if the connection is a secure TLS/SSL connection
/** @par Example:
    @code{.py}
bool b = sock.isSecure();
    @endcode

    @return @ref True if the connection is encrypted, @ref False if not
*/
bool Socket::isSecure() [flags=CONSTANT] {
    return s->isSecure();
}

//! Returns a string code giving the result of verifying the remote certificate or @ref nothing if an encrypted connection is not currently established
/** @par Example:
    @code{.py}
*string str = sock.verifyPeerCertificate();
    @endcode

    @return A string code giving the result of verifying the peer's certificate. No value is returned if a secure connection has not been established.  The set of possible return values is made up of the keys of the @ref X509_VerificationReasons hash. This hash can also be used to generate a textual description of the verification result.
*/
*string Socket::verifyPeerCertificate() [flags=CONSTANT] {
    const char *c = getSSLCVCode(s->verifyPeerCertificate());
    return c ? new QoreStringNode(c) : 0;
}

//! Sets the X.509 certificate to use for negotiating encrypted connections
/** @par Example:
    @code{.py}
sock.setCertificate(cert);
    @endcode

    @param cert This must be an SSLCertificate object
*/
nothing Socket::setCertificate(SSLCertificate[QoreSSLCertificate] cert) {
    // pass reference from QoreObject::getReferencedPrivateData() to QoreSocketObject::setCertificate()
    s->setCertificate(cert);
}

//! Sets the X.509 certificate to use for negotiating encrypted connections from the PEM-encoded string representing the X.509 certificate
/** @par Example:
    @code{.py}
sock.setCertificate(cert_string);
    @endcode

    @param cert_pem the PEM-encoded string representing the X.509 certificate
*/
nothing Socket::setCertificate(string cert_pem) {
    SimpleRefHolder<QoreSSLCertificate> cert(new QoreSSLCertificate(cert_pem, xsink));
    if (*xsink)
        return QoreValue();

    // QoreSocketObject::setCertificate() takes over ownership of certificate reference
    s->setCertificate(cert.release());
}

//! Sets the X.509 certificate to use for negotiating encrypted connections from the DER-encoded binary object representing the X.509 certificate
/** @par Example:
    @code{.py}
sock.setCertificate(cert_der);
    @endcode

    @param cert_der the DER-encoded binary object representing the X.509 certificate
*/
nothing Socket::setCertificate(binary cert_der) {
    SimpleRefHolder<QoreSSLCertificate> cert(new QoreSSLCertificate(cert_der, xsink));
    if (*xsink)
        return QoreValue();

    // QoreSocketObject::setCertificate() takes over ownership of certificate reference
    s->setCertificate(cert.release());
}

//! Sets the private key to use for negotiating encrypted connections along with the X.509 certificate
/** @par Example:
    @code{.py}
sock.setPrivateKey(key);
    @endcode

    @param key the private key for the X.509 certificate to use when establishing TLS/SSL encrypted connections
*/
nothing Socket::setPrivateKey(SSLPrivateKey[QoreSSLPrivateKey] key) {
    // pass reference from QoreObject::getReferencedPrivateData() to QoreSocketObject::setPrivateKey()
    s->setPrivateKey(key);
}

//! Sets the private key to use for negotiating encrypted connections along with the X.509 certificate from a PEM-encoded string representing the private key and an optional password
/** @par Example:
    @code{.py}
sock.setPrivateKey(key_pem, password);
    @endcode

    @param key_pem the PEM-encoded string representing the private key for the X.509 certificate to use when establishing TLS/SSL encrypted connections
    @param pass the password for the private key, if needed
*/
nothing Socket::setPrivateKey(string key_pem, *string pass) {
    SimpleRefHolder<QoreSSLPrivateKey> key(new QoreSSLPrivateKey(key_pem, pass ? pass->c_str() : 0, xsink));
    if (*xsink)
        return QoreValue();

    s->setPrivateKey(key.release());
}

//! Sets the private key to use for negotiating encrypted connections along with the X.509 certificate from a DER-encoded binary object representing the private key
/** @par Example:
    @code{.py}
sock.setPrivateKey(key_der);
    @endcode

    @param key_der the DER-encoded binary object representing the private key for the X.509 certificate to use when establishing TLS/SSL encrypted connections
*/
nothing Socket::setPrivateKey(binary key_der) {
    SimpleRefHolder<QoreSSLPrivateKey> key(new QoreSSLPrivateKey(key_der, xsink));
    if (*xsink)
        return QoreValue();

    s->setPrivateKey(key.release());
}

//! Sets the X.509 certificate and private key in one atomic operation to use for negotiating encrypted connections
/** @par Example:
    @code{.py}
sock.setCertificateAndPrivateKey(cert, key);
    @endcode

    @param cert This must be an SSLCertificate object
    @param key This must be an QoreSSLPrivateKey object

    @since %Qore 1.11.0
*/
nothing Socket::setCertificateAndPrivateKey(SSLCertificate[QoreSSLCertificate] cert, SSLPrivateKey[QoreSSLPrivateKey] key) {
    // pass reference from QoreObject::getReferencedPrivateData() to QoreSocketObject::setCertificate()
    s->setCertificateAndPrivateKey(cert, key);
}

//! Removes any @ref Qore::Thread::Queue "Queue" object from the Socket object so that @ref socket_events "socket events" are no longer added to the @ref Qore::Thread::Queue "Queue"
/** @par Example:
    @code{.py}
sock.setEventQueue();
    @endcode

    @see @ref event_handling for more information
*/
nothing Socket::setEventQueue() {
    s->setEventQueue(xsink, nullptr, QoreValue(), false);
}

//! Sets a @ref Qore::Thread::Queue "Queue" object to receive @ref socket_events "socket events"
/** @par Example:
    @code{.py}
sock.setEventQueue(queue);
    @endcode

    @param queue the @ref Qore::Thread::Queue "Queue" object to receive @ref socket_events "socket events"; note that
    the Queue passed cannot have any maximum size set or a QUEUE-ERROR will be thrown
    @param arg an argument that will be included in each event hash in the \a arg key
    @param with_data if @ref True, then the actual raw data transferred / received is also included in the events

    @throw QUEUE-ERROR the Queue passed has a maximum size set

    @see @ref event_handling for more information

    @since %Qore 0.9.4 added the \a arg and \a with_data options
*/
nothing Socket::setEventQueue(Qore::Thread::Queue[Queue] queue, auto arg, *bool with_data) {
    ReferenceHolder<Queue> q(queue, xsink);
    if (queue->getMax() != -1)
        return xsink->raiseException("QUEUE-ERROR", "the Queue object passed as a maximum size of %d entr%s, which " \
            "could cause internal I/O to freeze in the Socket object if used; use a Queue object with no maximum " \
            "size for I/O monitoring", queue->getMax(), queue->getMax() == 1 ? "y" : "ies");

    // pass reference from QoreObject::getReferencedPrivateData() to function
    s->setEventQueue(xsink, q.release(), arg.refSelf(), with_data);
}

//! Sets the boolean \c TCP_NODELAY setting for the socket
/** When this setting is @ref True, then data will be immediately sent out over the socket, when it is @ref False, then data transmission may be delayed to be packaged with other data for the same target.

    Delayed data transmissions may cause problems when the sender immediately closes the socket after sending data; in this case the receiver may not get the data even though the send succeeded.

    Note that if no value is given to the method, the argument will be assumed to be @ref True, and output buffering will be turned off for the socket.

    @par Example:
    @code{.py}
if (sock.setNoDelay(True))
    printf("error setting TCP_NODELAY: %s\n", strerror(errno()));
    @endcode

    @param nd the boolean \c TCP_NODELAY setting for the socket

    @return 0 for success, non-zero for errors; to get error information, see errno() and strerror()

    @see Socket::getNoDelay()
*/
int Socket::setNoDelay(bool nd = True) {
    return s->setNoDelay(nd);
}

//! Returns the \c TCP_NODELAY setting for the socket
/** @par Example:
    @code{.py}
bool b = sock.getNoDelay();
    @endcode

    @return the \c TCP_NODELAY setting for the socket

    @see Socket::setNoDelay() for a description of the \c TCP_NODELAY setting
*/
bool Socket::getNoDelay() [flags=CONSTANT] {
    return s->getNoDelay();
}

//! Returns a @ref socket_info_hash "hash of information" about the remote end for connected sockets
/** If the socket is not connected, an exception is thrown

    @par Example:
    @code{.py}
hash<auto> h = sock.getPeerInfo();
    @endcode

    @param host_lookup do a lookup of the hostname; if this is @ref False "False" then \c "hostname" and \c "hostname_desc" are not present in the response hash

    @return a @ref socket_info_hash "hash of information" about the remote end for connected sockets

    @throw SOCKET-NOT-OPEN The socket is not open
    @throw SOCKET-GETPEERINFO-ERROR error in getpeername()

    @since %Qore 0.8.8 added the \a host_lookup parameter
*/
hash<auto> Socket::getPeerInfo(bool host_lookup = True) {
    return s->getPeerInfo(xsink, host_lookup);
}

//! Returns information about the local socket as a hash
/** If the socket is not open, an exception is thrown

    @par Example:
    @code{.py}
hash<auto> h = sock.getSocketInfo();
    @endcode

    @param host_lookup do a lookup of the hostname; if this is @ref False "False" then \c "hostname" and \c "hostname_desc" are not present in the response hash

    @return a @ref socket_info_hash "hash of information" about the remote end for connected sockets

    @throw SOCKET-NOT-OPEN The socket is not open
    @throw SOCKET-GETSOCKETINFO-ERROR error in getsockname()

    @since %Qore 0.8.8 added the \a host_lookup parameter
*/
hash<auto> Socket::getSocketInfo(bool host_lookup = True) {
    return s->getSocketInfo(xsink, host_lookup);
}

//! Upgrades a client socket connection to a TLS/SSL connection
/** @par Example:
    @code{.py}
sock.upgradeClientToSSL();
    @endcode

    @param timeout_ms If a positive timeout value is passed and the SSL upgrade takes longer to establish than the timeout, an \c "SSL-TIMEOUT-ERROR" exception is thrown. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN the socket must be open before making this call
    @throw SSL-TIMEOUT-ERROR timeout error negotiating SSL connection

    @note if an SSL connection has already been established, this method call returns immediately without taking any further action

    @since %Qore 0.8.12 the timeout_ms parameter was added
*/
nothing Socket::upgradeClientToSSL(timeout timeout_ms = -1) {
    s->upgradeClientToSSL(timeout_ms, xsink);
}

//! Upgrades a server socket connection to a TLS/SSL connection
/** @par Example:
    @code{.py}
sock.upgradeServerToSSL();
    @endcode

    @param timeout_ms If a positive timeout value is passed and the SSL upgrade takes longer to establish than the timeout, an \c "SSL-TIMEOUT-ERROR" exception is thrown. Note that like all %Qore functions and methods taking timeout values, a @ref relative_dates "relative date/time value" can be used to make the units clear (i.e. \c 2m = two minutes, etc.)

    @throw SOCKET-NOT-OPEN the socket must be open before making this call
    @throw SSL-TIMEOUT-ERROR timeout error negotiating SSL connection

    @note if an SSL connection has already been established, this method call returns immediately without taking any further action

    @since %Qore 0.8.12 the timeout_ms parameter was added
*/
nothing Socket::upgradeServerToSSL(timeout timeout_ms = -1) {
    s->upgradeServerToSSL(timeout_ms, xsink);
}

//! Removes any warning @ref Qore::Thread::Queue "Queue" object from the Socket
/** @par Example:
    @code{.py}
sock.clearWarningQueue();
    @endcode

    @see Socket::setWarningQueue()

    @since %Qore 0.8.9
*/
nothing Socket::clearWarningQueue() {
    s->clearWarningQueue(xsink);
}

//! Sets a @ref Qore::Thread::Queue "Queue" object to receive socket warnings
/** @par Example:
    @code{.py}
sock.setWarningQueue(5000, 5000, queue, "socket-1");
    @endcode

    @param warning_ms the threshold in milliseconds for individual socket actions (send, receive, connect), if exceeded, a socket warning is placed on the warning queue with the following keys:
    - \c "type": a string with the constant value \c "SOCKET-OPERATION-WARNING"
    - \c "operation": a string giving the operation that caused the warning (ex: \c "connect")
    - \c "us": an integer giving the number of microseconds for the operation
    - \c "timeout": an integer giving the warning threshold in microseconds
    - \c "arg": if any \c "arg" argument is passed to the @ref Qore::Socket::setWarningQueue() "Socket::setWarningQueue()" method, it will be included in the warning hash here
    @param warning_bs value in bytes per second; if any call has performance below this threshold, a socket warning is placed on the warning queue with the following keys:
    - \c "type": a string with the constant value \c "SOCKET-THROUGHPUT-WARNING"
    - \c "dir": either \c "send" or \c "recv" depending on the direction of the data flow
    - \c "bytes": the amount of bytes sent
    - \c "us": an integer giving the number of microseconds for the operation
    - \c "bytes_sec": a float giving the transfer speed in bytes per second
    - \c "threshold": an integer giving the warning threshold in bytes per second
    - \c "arg": if any \c "arg" argument is passed to the @ref Qore::Socket::setWarningQueue() "Socket::setWarningQueue()" method, it will be included in the warning hash here
    @param queue the @ref Qore::Thread::Queue "Queue" object to receive warning events
    @param arg an optional argument to be placed in the \c "arg" key in each warning hash (could be used to identify the socket for example)
    @param min_ms the minimum transfer time with a resolution of milliseconds for a transfer to be eligible for triggering a warning; transfers that take less than this period of time are not eligible for raising a warning

    @throw QUEUE-ERROR the Queue passed has a maximum size set
    @throw SOCKET-SETWARNINGQUEUE-ERROR at least one of \a warning_ms and \a warning_bs must be > 0

    @see Socket::clearWarningQueue()

    @since
    - %Qore 0.8.9
    - %Qore 0.8.12 the minimum threshold for a warning is a transfer of at least 1024 bytes, smaller transfers will not result in a warning
*/
nothing Socket::setWarningQueue(int warning_ms, int warning_bs, Queue[Queue] queue, auto arg, timeout min_ms = 1s) {
    ReferenceHolder<Queue> q(queue, xsink);
    if (queue->getMax() != -1)
        return xsink->raiseException("QUEUE-ERROR", "the Queue object passed as a maximum size of %d entr%s, which "
            "could cause internal I/O to freeze in the Socket object if used; use a Queue object with no maximum "
            "size for I/O monitoring", queue->getMax(), queue->getMax() == 1 ? "y" : "ies");

    // pass reference from QoreObject::getReferencedPrivateData() to function
    s->setWarningQueue(xsink, warning_ms, warning_bs, q.release(), arg.refSelf(), min_ms);
}

//! Returns performance statistics for the socket
/** @par Example:
    @code{.py}
hash<auto> h = sock.getUsageInfo();
    @endcode

    @return a hash with the following keys:
    - \c "bytes_sent": an integer giving the total amount of bytes sent
    - \c "bytes_recv": an integer giving the total amount of bytes received
    - \c "us_sent": an integer giving the total number of microseconds spent sending data
    - \c "us_recv": an integer giving the total number of microseconds spent receiving data
    - \c "arg": (only if warning values have been set with @ref Qore::Socket::setWarningQueue() "Socket::setWarningQueue()") the optional argument for warning hashes
    - \c "timeout": (only if warning values have been set with @ref Qore::Socket::setWarningQueue() "Socket::setWarningQueue()") the warning timeout in microseconds
    - \c "min_throughput": (only if warning values have been set with @ref Qore::Socket::setWarningQueue() "Socket::setWarningQueue()") the minimum warning throughput in bytes/sec

    @since %Qore 0.8.9

    @see Socket::clearStats()
*/
hash<auto> Socket::getUsageInfo() [flags=CONSTANT] {
    return s->getUsageInfo();
}

//! Clears performance statistics
/** @par Example:
    @code{.py}
sock.clearStats();
    @endcode

    @since %Qore 0.8.9

    @see Socket::getUsageInfo()
*/
Socket::clearStats() {
    s->clearStats();
}

//! returns True if the socket is still connected, and a HTTP header was read indicating chunked transfer encoding, but no chunked body has been read yet
/** @par Example:
    @code{.py}
bool b = sock.pendingHttpChunkedBody();
    @endcode

    @since %Qore 0.8.10
*/
bool Socket::pendingHttpChunkedBody() {
    return s->pendingHttpChunkedBody();
}

//! sets the SSL verification mode
/** @par Example:
    @code{.py}
sock.setSslVerifyMode(SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
    @endcode

    If there is no SSL connection in place, then the mode is saved and will be used automatically
    for future SSL sessions automatically.

    @param mode the SSL verfication mode, see @ref ssl_mode_constants for more information

    @see
    - @ref Qore::Socket::getAcceptAllCertificates() "Socket::getAcceptAllCertificates()"
    - @ref Qore::Socket::getSslVerifyMode() "Socket::getSslVerifyMode()"
    - @ref Qore::Socket::acceptAllCertificates() "Socket::acceptAllCertificates()"

    @since %Qore 0.8.13
*/
Socket::setSslVerifyMode(int mode) {
    s->setSslVerifyMode((int)mode);
}

//! returns the current SSL verification mode
/** @par Example:
    @code{.py}
int mode = sock.getSslVerifyMode();
    @endcode

    @return the SSL verfication mode, see @ref ssl_mode_constants for more information

    @see
    - @ref Qore::Socket::getAcceptAllCertificates() "Socket::getAcceptAllCertificates()"
    - @ref Qore::Socket::setSslVerifyMode() "Socket::setSslVerifyMode()"
    - @ref Qore::Socket::acceptAllCertificates() "Socket::acceptAllCertificates()"

    @since %Qore 0.8.13
*/
int Socket::getSslVerifyMode() [flags=CONSTANT] {
    return s->getSslVerifyMode();
}

//! with peer verification enabled, all certificates are accepted regardless of the validity of the Certificate Authority
/** @par Example:
    @code{.py}
# require a certificate from the peer, but accept all certificates, even self-signed ones
sock.setSslVerifyMode(SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
sock.acceptAllCertificates();
    @endcode

    @param accept_all if @ref True "True", then self-signed certificates are accepted, if @ref False "False", then they are not

    @see
    - @ref Qore::Socket::getAcceptAllCertificates() "Socket::getAcceptAllCertificates()"
    - @ref Qore::Socket::setSslVerifyMode() "Socket::setSslVerifyMode()"
    - @ref Qore::Socket::getSslVerifyMode() "Socket::getSslVerifyMode()"

    @since %Qore 0.8.13
*/
Socket::acceptAllCertificates(bool accept_all = True) {
    s->acceptAllCertificates(accept_all);
}

//! returns the current value of the "accept all certificates" flag
/** @par Example:
    @code{.py}
bool b = sock.getAcceptAllCertificates();
    @endcode

    @return the current value of the "accept all certificates" flag; if @ref True "True", then self-signed certificates are accepted, if @ref False "False", then they are not

    @see
    - @ref Qore::Socket::acceptAllCertificates() "Socket::acceptAllCertificates()"
    - @ref Qore::Socket::setSslVerifyMode() "Socket::setSslVerifyMode()"
    - @ref Qore::Socket::getSslVerifyMode() "Socket::getSslVerifyMode()"

    @since %Qore 0.8.13
*/
bool Socket::getAcceptAllCertificates() [flags=CONSTANT] {
    return s->getAcceptAllCertificates();
}

//! Sets the flag for capturing remote X.509 certificates; by default no capture of remote certificates is performed
/** @par Example:
    @code{.py}
bool b = sock.captureRemoteCertificates();
    @endcode

    @param set the flag for capturing remote X.509 certificates; by default no capture of remote certificates is performed

    @return the old value of the flag

    @see getRemoteCertificate()

    @since %Qore 0.9.3
*/
bool Socket::captureRemoteCertificates(bool set = True) {
    return s->captureRemoteCertificates(set);
}

//! Returns any remote certificate captured or @ref nothing if there is none
/** @par Example:
    @code{.py}
*SSLCertificate cert = sock.getRemoteCertificate();
    @endcode

    @return any remote certificate captured or @ref nothing if there is none

    @see captureRemoteCertificates()

    @since %Qore 0.9.3
*/
*SSLCertificate Socket::getRemoteCertificate() {
    return s->getRemoteCertificate();
}

//! Returns an integer connection ID that is incremented every time the socket is disconnected
/** @par Example:
    @code{.py}
int conn_id = sock.getConnectionId();
    @endcode

    @return an integer connection ID that is incremented every time the socket is disconnected

    This number can be used to identify new connections

    @since %Qore 0.9.3.1
*/
int Socket::getConnectionId() [flags=CONSTANT] {
    return s->getConnectionId();
}

//! polls multiple sockets and returns all sockets with events
/** @par Example:
    @code{.py}
list<hash<SocketPollInfo>> l(
    <SocketPollInfo>{
        "events": SOCK_POLLOUT,
        "socket": sock1,
    },
    <SocketPollInfo>{
        "events": SOCK_POLLIN | SOCK_POLLOUT,
        "socket": sock2,
    },
);
l = Socket::poll(l, 2s);
    @endcode

    @param items list of @ref SocketPollInfo hashes of sockets with events in the timeout period
    @param timeout_ms the poll timeout period; -1 = never timeout

    @return a list of sockets with events in the timeout period

    @throw SOCKET-POLL-ERROR \a socket value in the \a items argument is not assigned or there was an error in the
    poll operation
    @throw SOCKET-NOT-OPEN \a socket value in the \a items argument is closed

    @note If a socket being polled is closed or deleted in a background thread, the poll operation is not interrupted
    on Linux; other platforms behave differently (macOS / Darwin interrupts the polling process in this case); for
    proper operation on all platforms, do not assume that the polling operation will return immediately if a socket
    being polled is deleted in a background thread.

    @since %Qore 0.9.11
*/
static list<hash<SocketPollInfo>> Socket::poll(list<hash<SocketPollInfo>> items, timeout timeout_ms) {
    return qore_socket_private::poll(items, timeout_ms, xsink);
}
