first embryo

This commit is contained in:
JCash
2020-08-25 14:25:29 +02:00
parent e36953369b
commit e781c84283
35 changed files with 9528 additions and 151 deletions

View File

@@ -0,0 +1,174 @@
// Copyright 2020 The Defold Foundation
// Licensed under the Defold License version 1.0 (the "License"); you may not use
// this file except in compliance with the License.
//
// You may obtain a copy of the License, together with FAQs at
// https://www.defold.com/license
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef DMSDK_CONNECTION_POOL
#define DMSDK_CONNECTION_POOL
#include <stdint.h>
#include <dlib/socket.h>
#include <dlib/dns.h>
/**
* Connection pooling
*/
namespace dmConnectionPool
{
/**
* Connection pool handle
*/
typedef struct ConnectionPool* HPool;
/**
* Connection handle
*/
typedef uint32_t HConnection;
/**
* Result codes
*/
enum Result
{
RESULT_OK = 0, //!< RESULT_OK
RESULT_OUT_OF_RESOURCES = -1,//!< RESULT_OUT_OF_RESOURCES
RESULT_SOCKET_ERROR = -2, //!< RESULT_SOCKET_ERROR
RESULT_HANDSHAKE_FAILED = -3,//!< RESULT_HANDSHAKE_FAILED
RESULT_SHUT_DOWN = -4, //<! RESULT_SHUT_DOWN
};
/**
* Stats
*/
struct Stats
{
uint32_t m_Free;
uint32_t m_Connected;
uint32_t m_InUse;
};
/**
* Parameters
*/
struct Params
{
Params()
{
m_MaxConnections = 64;
m_MaxKeepAlive = 10;
}
/// Max connection in pool
uint32_t m_MaxConnections;
/// Default max-keep-alive time in seconds
uint32_t m_MaxKeepAlive;
};
/**
* Create a new connection pool
* @param params
* @param pool
* @return RESULT_OK on success
*/
Result New(const Params* params, HPool* pool);
/**
* Delete connnection pool
* @param pool
* @return RESULT_OK on success
*/
Result Delete(HPool pool);
/**
* Set max keep-alive for sockets in seconds. Sockets older than max_keep_alive
* are not reused and hence closed
* @param pool
* @param max_keep_alive
*/
void SetMaxKeepAlive(HPool pool, uint32_t max_keep_alive);
/**
* Get statistics
* @param pool
* @param stats
*/
void GetStats(HPool pool, Stats* stats);
/**
* Connection to a host/port
* @param pool pool
* @param host host
* @param port port
* @param dns_channel The DNS channel that will be used for translating the host to an address
* @param ssl true for ssl connection
* @param timeout The timeout (micro seconds) for the connection and ssl handshake
* @param connection connection (out)
* @param sock_res socket-result code on failure
* @return RESULT_OK on success
*/
Result Dial(HPool pool, const char* host, uint16_t port, dmDNS::HChannel dns_channel, bool ssl, int timeout, HConnection* connection, dmSocket::Result* sock_res);
/**
* Return connection to pool
* @param pool
* @param connection
*/
void Return(HPool pool, HConnection connection);
/**
* Close connection. Use this function whenever an error occur in eg http.
* @param pool
* @param connection
*/
void Close(HPool pool, HConnection connection);
/**
* Get socket for connection
* @param pool
* @param connection
* @return RESULT_OK on success
*/
dmSocket::Socket GetSocket(HPool pool, HConnection connection);
/**
* Get ssl-handle. The returned value is an mbedtls_ssl_context* (mbedtls)
* @param pool
* @param connection
* @return An mbedtls_ssl_context* pointer on success
*/
void* GetSSLConnection(HPool pool, HConnection connection);
/**
* Get reuse count for a connection
* @param pool
* @param connection
* @return reuse count
*/
uint32_t GetReuseCount(HPool pool, HConnection connection);
/**
* Shuts down all open sockets in the pool and block new connection attempts. The function can be
* called repeatedly on the same pool until it returns no more connections in use.
*
* @param pool pool
* @param how shutdown type to pass to socket shutdown function
* @return current number of connections in use
*/
uint32_t Shutdown(HPool pool, dmSocket::ShutdownType how);
/**
* Reopen the pool from a Shutdown call so it allows Dialing again. This function is here so the pool can be reset
* during testing, or subsequent tests will break when the pool has been put in shutdown mode.
*/
void Reopen(HPool pool);
}
#endif // #ifndef DMSDK_CONNECTION_POOL

93
websocket/src/dns.h Normal file
View File

@@ -0,0 +1,93 @@
// Copyright 2020 The Defold Foundation
// Licensed under the Defold License version 1.0 (the "License"); you may not use
// this file except in compliance with the License.
//
// You may obtain a copy of the License, together with FAQs at
// https://www.defold.com/license
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef DM_DNS_H
#define DM_DNS_H
namespace dmSocket
{
struct Address;
}
namespace dmDNS
{
/**
* A channel roughly translates to a socket on which to put the name lookup requests on.
* Internal implementation resides in dns.cpp.
*/
struct Channel;
typedef Channel* HChannel;
enum Result
{
RESULT_OK = 0,
RESULT_INIT_ERROR = -1,
RESULT_HOST_NOT_FOUND = -2,
RESULT_CANCELLED = -3,
RESULT_UNKNOWN_ERROR = -4
};
/**
* Initialize the DNS system. Some platforms require socket initialization to be called
* before this function. I.e: call dmSocket::Initialize() before dmDNS::Initialize() on win32.
* @return RESULT_OK on success
*/
Result Initialize();
/**
* Finalize DNS system.
* @return RESULT_OK on success
*/
Result Finalize();
/**
* Creates a new channel that can be used for DNS queries.
* @param channel Pointer to the created channel if successfull, will be left alone otherwise
* @return RESULT_OK on succcess
*/
Result NewChannel(HChannel* channel);
/**
* Stops the current request (if available) on a channel.
* @param channel Handle to the channel
*/
void StopChannel(HChannel channel);
/**
* Deletes the current channel and cancels all requests.
* Note: You must always make sure to call StopChannel(channel) before calling this function.
* @param channel Handle to the channel
*/
void DeleteChannel(HChannel channel);
/**
* Refreshes the channel configuration so that the latest DNS servers are used.
* @param channel Handle to the channel
*/
Result RefreshChannel(HChannel channel);
/**
* Get host by name. Note that this function is not entirely thread-safe, even though it is used in a threaded environment.
* @param name Hostname to resolve
* @param address Host address result
* @param channel Channel handle to put the request on. The function does not handle invalid handles - the channel handle must exist.
* @param ipv4 Whether or not to search for IPv4 addresses
* @param ipv6 Whether or not to search for IPv6 addresses
* @return RESULT_OK on success
*/
Result GetHostByName(const char* name, dmSocket::Address* address, HChannel channel, bool ipv4 = true, bool ipv6 = true);
// Used for unit testing
const char* ResultToString(Result r);
}
#endif //DM_DSTRINGS_H

View File

@@ -0,0 +1,98 @@
/**
* \file base64.h
*
* \brief RFC 1521 base64 encoding/decoding
*/
/*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
#ifndef MBEDTLS_BASE64_H
#define MBEDTLS_BASE64_H
#if !defined(MBEDTLS_CONFIG_FILE)
#include "config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#include <stddef.h>
#define MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL -0x002A /**< Output buffer too small. */
#define MBEDTLS_ERR_BASE64_INVALID_CHARACTER -0x002C /**< Invalid character in input. */
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Encode a buffer into base64 format
*
* \param dst destination buffer
* \param dlen size of the destination buffer
* \param olen number of bytes written
* \param src source buffer
* \param slen amount of data to be encoded
*
* \return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
* *olen is always updated to reflect the amount
* of data that has (or would have) been written.
* If that length cannot be represented, then no data is
* written to the buffer and *olen is set to the maximum
* length representable as a size_t.
*
* \note Call this function with dlen = 0 to obtain the
* required buffer size in *olen
*/
int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
const unsigned char *src, size_t slen );
/**
* \brief Decode a base64-formatted buffer
*
* \param dst destination buffer (can be NULL for checking size)
* \param dlen size of the destination buffer
* \param olen number of bytes written
* \param src source buffer
* \param slen amount of data to be decoded
*
* \return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
* MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
* not correct. *olen is always updated to reflect the amount
* of data that has (or would have) been written.
*
* \note Call this function with *dst = NULL or dlen = 0 to obtain
* the required buffer size in *olen
*/
int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
const unsigned char *src, size_t slen );
#if defined(MBEDTLS_SELF_TEST)
/**
* \brief Checkup routine
*
* \return 0 if successful, or 1 if the test failed
*/
int mbedtls_base64_self_test( int verbose );
#endif /* MBEDTLS_SELF_TEST */
#ifdef __cplusplus
}
#endif
#endif /* base64.h */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
#include "script_util.h"
namespace dmWebsocket {
bool luaL_checkbool(lua_State *L, int numArg)
{
bool b = false;
if (lua_isboolean(L, numArg))
{
b = lua_toboolean(L, numArg);
}
else
{
luaL_typerror(L, numArg, lua_typename(L, LUA_TBOOLEAN));
}
return b;
}
bool luaL_checkboold(lua_State *L, int numArg, int def)
{
int type = lua_type(L, numArg);
if (type != LUA_TNONE && type != LUA_TNIL)
{
return luaL_checkbool(L, numArg);
}
return def;
}
lua_Number luaL_checknumberd(lua_State *L, int numArg, lua_Number def)
{
int type = lua_type(L, numArg);
if (type != LUA_TNONE && type != LUA_TNIL)
{
return luaL_checknumber(L, numArg);
}
return def;
}
char* luaL_checkstringd(lua_State *L, int numArg, const char* def)
{
int type = lua_type(L, numArg);
if (type != LUA_TNONE && type != LUA_TNIL)
{
return (char*)luaL_checkstring(L, numArg);
}
return (char*)def;
}
lua_Number luaL_checktable_number(lua_State *L, int numArg, const char* field, lua_Number def)
{
lua_Number result = def;
if(lua_istable(L, numArg))
{
lua_getfield(L, numArg, field);
if(!lua_isnil(L, -1))
{
result = luaL_checknumber(L, -1);
}
lua_pop(L, 1);
}
return result;
}
char* luaL_checktable_string(lua_State *L, int numArg, const char* field, char* def)
{
char* result = def;
if(lua_istable(L, numArg))
{
lua_getfield(L, numArg, field);
if(!lua_isnil(L, -1))
{
result = (char*)luaL_checkstring(L, -1);
}
lua_pop(L, 1);
}
return result;
}
} // namespace

View File

@@ -0,0 +1,12 @@
#pragma once
#include <dmsdk/sdk.h>
namespace dmWebsocket {
bool luaL_checkbool(lua_State *L, int numArg);
bool luaL_checkboold(lua_State *L, int numArg, int def);
lua_Number luaL_checknumberd(lua_State *L, int numArg, lua_Number def);
char* luaL_checkstringd(lua_State *L, int numArg, const char* def);
lua_Number luaL_checktable_number(lua_State *L, int numArg, const char* field, lua_Number def);
char* luaL_checktable_string(lua_State *L, int numArg, const char* field, char* def);
} // namespace

570
websocket/src/socket.h Normal file
View File

@@ -0,0 +1,570 @@
// Copyright 2020 The Defold Foundation
// Licensed under the Defold License version 1.0 (the "License"); you may not use
// this file except in compliance with the License.
//
// You may obtain a copy of the License, together with FAQs at
// https://www.defold.com/license
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef DM_SOCKET_H
#define DM_SOCKET_H
#include <stdint.h>
#include <string.h> // memset, memcmp
#if defined(__linux__) || defined(__MACH__) || defined(ANDROID) || defined(__EMSCRIPTEN__) || defined(__NX__)
#include <sys/select.h>
#elif defined(_WIN32)
#include <winsock2.h>
#else
#error "Unsupported platform"
#endif
/**
* Socket abstraction
* @note For Recv* and Send* function ETIMEDOUT is translated to EWOULDBLOCK
* on win32 for compatibility with BSD sockets.
*/
namespace dmSocket
{
struct Selector
{
fd_set m_FdSets[3];
int m_Nfds;
Selector();
};
/**
* Socket result
*/
enum Result
{
RESULT_OK = 0, //!< RESULT_OK
RESULT_ACCES = -1, //!< RESULT_ACCES
RESULT_AFNOSUPPORT = -2, //!< RESULT_AFNOSUPPORT
RESULT_WOULDBLOCK = -3, //!< RESULT_WOULDBLOCK
RESULT_BADF = -4, //!< RESULT_BADF
RESULT_CONNRESET = -5, //!< RESULT_CONNRESET
RESULT_DESTADDRREQ = -6, //!< RESULT_DESTADDRREQ
RESULT_FAULT = -7, //!< RESULT_FAULT
RESULT_HOSTUNREACH = -8, //!< RESULT_HOSTUNREACH
RESULT_INTR = -9, //!< RESULT_INTR
RESULT_INVAL = -10, //!< RESULT_INVAL
RESULT_ISCONN = -11, //!< RESULT_ISCONN
RESULT_MFILE = -12, //!< RESULT_MFILE
RESULT_MSGSIZE = -13, //!< RESULT_MSGSIZE
RESULT_NETDOWN = -14, //!< RESULT_NETDOWN
RESULT_NETUNREACH = -15, //!< RESULT_NETUNREACH
//RESULT_NFILE = -16,
RESULT_NOBUFS = -17, //!< RESULT_NOBUFS
//RESULT_NOENT = -18,
//RESULT_NOMEM = -19,
RESULT_NOTCONN = -20, //!< RESULT_NOTCONN
//RESULT_NOTDIR = -21,
RESULT_NOTSOCK = -22, //!< RESULT_NOTSOCK
RESULT_OPNOTSUPP = -23, //!< RESULT_OPNOTSUPP
RESULT_PIPE = -24, //!< RESULT_PIPE
RESULT_PROTONOSUPPORT = -25, //!< RESULT_PROTONOSUPPORT
RESULT_PROTOTYPE = -26, //!< RESULT_PROTOTYPE
RESULT_TIMEDOUT = -27, //!< RESULT_TIMEDOUT
RESULT_ADDRNOTAVAIL = -28, //!< RESULT_ADDRNOTAVAIL
RESULT_CONNREFUSED = -29, //!< RESULT_CONNREFUSED
RESULT_ADDRINUSE = -30, //!< RESULT_ADDRINUSE
RESULT_CONNABORTED = -31, //!< RESULT_CONNABORTED
RESULT_INPROGRESS = -32, //!< RESULT_INPROGRESS
// gethostbyname errors
RESULT_HOST_NOT_FOUND = -100, //!< RESULT_HOST_NOT_FOUND
RESULT_TRY_AGAIN = -101, //!< RESULT_TRY_AGAIN
RESULT_NO_RECOVERY = -102, //!< RESULT_NO_RECOVERY
RESULT_NO_DATA = -103, //!< RESULT_NO_DATA
RESULT_UNKNOWN = -1000,//!< RESULT_UNKNOWN
};
/**
* Socket handle
* @note Use INVALID_SOCKET_HANDLE instead of zero for unset values. This is an exception
* from all other handles.
*/
typedef int Socket;
enum SelectorKind
{
SELECTOR_KIND_READ = 0,
SELECTOR_KIND_WRITE = 1,
SELECTOR_KIND_EXCEPT = 2,
};
enum Flags
{
FLAGS_UP = (1 << 0),
FLAGS_RUNNING = (1 << 1),
FLAGS_INET = (1 << 2),
FLAGS_LINK = (1 << 3),
};
/**
* Invalid socket handle
*/
const Socket INVALID_SOCKET_HANDLE = 0xffffffff;
/**
* Domain type
*/
enum Domain
{
DOMAIN_MISSING, //!< DOMAIN_MISSING
DOMAIN_IPV4, //!< DOMAIN_IPV4
DOMAIN_IPV6, //!< DOMAIN_IPV6
DOMAIN_UNKNOWN, //!< DOMAIN_UNKNOWN
};
/**
* Socket type
*/
enum Type
{
TYPE_STREAM, //!< TYPE_STREAM
TYPE_DGRAM, //!< TYPE_DGRAM
};
/**
* Network protocol
*/
enum Protocol
{
PROTOCOL_TCP, //!< PROTOCOL_TCP
PROTOCOL_UDP, //!< PROTOCOL_UDP
};
/**
* Socket shutdown type
*/
enum ShutdownType
{
SHUTDOWNTYPE_READ,
SHUTDOWNTYPE_WRITE,
SHUTDOWNTYPE_READWRITE,
};
/**
* Network address
* Network addresses were previously represented as an uint32_t, but in
* order to support IPv6 the internal representation was changed to a
* struct.
*/
struct Address
{
Address() {
m_family = dmSocket::DOMAIN_MISSING;
memset(m_address, 0x0, sizeof(m_address));
}
Domain m_family;
uint32_t m_address[4];
};
/**
* Comparison operators for dmSocket::Address (network address).
* These operators are required since network code was initially designed
* with the assumption that addresses were stored as uint32_t (IPv4), and
* thus sortable.
*/
inline bool operator==(const Address& lhs, const Address& rhs)
{
return memcmp(lhs.m_address, rhs.m_address, sizeof(lhs.m_address)) == 0;
}
inline bool operator< (const Address& lhs, const Address& rhs)
{
return memcmp(lhs.m_address, rhs.m_address, sizeof(lhs.m_address)) < 0;
}
inline bool operator!=(const Address& lhs, const Address& rhs) { return !operator==(lhs,rhs); }
inline bool operator> (const Address& lhs, const Address& rhs) { return operator< (rhs,lhs); }
inline bool operator<=(const Address& lhs, const Address& rhs) { return !operator> (lhs,rhs); }
inline bool operator>=(const Address& lhs, const Address& rhs) { return !operator< (lhs,rhs); }
struct IfAddr
{
char m_Name[128];
uint32_t m_Flags;
Address m_Address;
uint8_t m_MacAddress[6];
};
/**
* Initialize socket system. Network initialization is required on some platforms.
* @return RESULT_OK on success
*/
Result Initialize();
/**
* Finalize socket system.
* @return RESULT_OK on success
*/
Result Finalize();
/**
* Create a new socket. Corresponds to BSD socket function socket().
* @note SIGPIPE is disabled on applicable platforms. This has the implication
* that Receive can return zero bytes when the connection is closed by remote peer.
* @param type Soccket type
* @param protocol Protocol
* @param socket Pointer to created socket
* @return RESULT_OK on succcess
*/
Result New(Domain domain, Type type, enum Protocol protocol, Socket* socket);
/**
* Delete a socket. Corresponds to BSD socket function close()
* @param socket Socket to close
* @return RESULT_OK on success
*/
Result Delete(Socket socket);
/**
* Get underlying file descriptor
* @param socket socket to get fd for
* @return file-descriptor
*/
int GetFD(Socket socket);
/**
* Set reuse socket address option on socket. Socket option SO_REUSEADDR on most platforms
* @param socket Socket to set reuse address to
* @param reuse True if reuse
* @return RESULT_OK on success
*/
Result SetReuseAddress(Socket socket, bool reuse);
/**
* Set broadcast address option on socket. Socket option SO_BROADCAST on most platforms.
* @param socket Socket to set reuse address to
* @param broadcast True if broadcast
* @return RESULT_OK on success
*/
Result SetBroadcast(Socket socket, bool broadcast);
/**
* Set blocking option on a socket
* @param socket Socket to set blocking on
* @param blocking True to block
* @return RESULT_OK on success
*/
Result SetBlocking(Socket socket, bool blocking);
/**
* Set TCP_NODELAY on socket
* @param socket Socket to set TCP_NODELAY on
* @param no_delay True for no delay
* @return RESULT_OK on success
*/
Result SetNoDelay(Socket socket, bool no_delay);
/**
* Set socket send timeout
* @note Timeout resolution might be in milliseconds, e.g. windows. Use values
* larger than or equal to 1000.
* @param socket socket
* @param timeout timeout in microseconds
* @return RESULT_OK on success
*/
Result SetSendTimeout(Socket socket, uint64_t timeout);
/**
* Set socket receive timeout
* @note Timeout resolution might be in milliseconds, e.g. windows. Use values
* larger than or equal to 1000
* @param socket socket
* @param timeout timeout in microseconds
* @return RESULT_OK on success
*/
Result SetReceiveTimeout(Socket socket, uint64_t timeout);
/**
* Add multicast membership
* @param socket socket to add membership on
* @param multi_addr multicast address
* @param interface_addr interface address
* @param ttl multicast package time to live
* @return RESULT_OK
*/
Result AddMembership(Socket socket, Address multi_addr, Address interface_addr, int ttl);
/**
* Set address for outgoing multicast datagrams
* @param socket socket to set multicast address for
* @param address address of network interface to use
* @return RESULT_OK
*/
Result SetMulticastIf(Socket socket, Address address);
/**
* Accept a connection on a socket
* @param socket Socket to accept connections on
* @param address Result address parameter
* @param accept_socket Pointer to accepted socket (result)
* @return RESULT_OK on success
*/
Result Accept(Socket socket, Address* address, Socket* accept_socket);
/**
* Bind a name to a socket
* @param socket Socket to bind name to
* @param address Address to bind
* @param port Port to bind to
* @return RESULT_OK on success
*/
Result Bind(Socket socket, Address address, int port);
/**
* Initiate a connection on a socket
* @param socket Socket to initiate connection on
* @param address Address to connect to
* @param port Port to connect to
* @return RESULT_OK on success
*/
Result Connect(Socket socket, Address address, int port);
/**
* Listen for connections on a socket
* @param socket Socket to listen on
* @param backlog Maximum length for the queue of pending connections
* @return RESULT_OK on success
*/
Result Listen(Socket socket, int backlog);
/**
* Shutdown part of a socket connection
* @param socket Socket to shutdown connection ow
* @param how Shutdown type
* @return RESULT_OK on success
*/
Result Shutdown(Socket socket, ShutdownType how);
/**
* Send a message on a socket
* @param socket Socket to send a message on
* @param buffer Buffer to send
* @param length Length of buffer to send
* @param sent_bytes Number of bytes sent (result)
* @return RESULT_OK on success
*/
Result Send(Socket socket, const void* buffer, int length, int* sent_bytes);
/**
* Send a message to a specific address
* @param socket Socket to send a message on
* @param buffer Buffer to send
* @param length Length of buffer to send
* @param sent_bytes Number of bytes sent (result)
* @param to_addr To address
* @param to_port From addres
* @return RESULT_OK on success
*/
Result SendTo(Socket socket, const void* buffer, int length, int* sent_bytes, Address to_addr, uint16_t to_port);
/**
* Receive data on a socket
* @param socket Socket to receive data on
* @param buffer Buffer to receive to
* @param length Receive buffer length
* @param received_bytes Number of received bytes (result)
* @return RESULT_OK on success
*/
Result Receive(Socket socket, void* buffer, int length, int* received_bytes);
/**
* Receive from socket
* @param socket Socket to receive data on
* @param buffer Buffer to receive to
* @param length Receive buffer length
* @param received_bytes Number of received bytes (result)
* @param from_addr From address (result)
* @param from_port To address (result)
* @return RESULT_OK on success
*/
Result ReceiveFrom(Socket socket, void* buffer, int length, int* received_bytes,
Address* from_addr, uint16_t* from_port);
/**
* Get name, address and port for socket
* @param socket Socket to get name for
* @param address Address (result)
* @param port Socket (result)
* @return RESULT_OK on success
*/
Result GetName(Socket socket, Address*address, uint16_t* port);
/**
* Get local hostname
* @param hostname hostname buffer
* @param hostname_length hostname buffer length
* @return RESULT_OK on success
*/
Result GetHostname(char* hostname, int hostname_length);
/**
* Get first local IP address
* The function tries to determine the local IP address. If several
* IP addresses are available only a single is returned
* @note This function might fallback to 127.0.0.1 if no adapter is found
* Sometimes it might be appropriate to run this function periodically
* @param address address result
* @return RESULT_OK on success
*/
Result GetLocalAddress(Address* address);
/**
* Get address from ip string
* @param address IP-string
* @return Address
*/
Address AddressFromIPString(const char* address);
/**
* Convert address to ip string
* @param address address to convert
* @return IP string. The caller is responsible to free the string using free()
*/
char* AddressToIPString(Address address);
/**
* Get host by name.
* @param name Hostname to resolve
* @param address Host address result
* @param ipv4 Whether or not to search for IPv4 addresses
* @param ipv6 Whether or not to search for IPv6 addresses
* @return RESULT_OK on success
*/
Result GetHostByName(const char* name, Address* address, bool ipv4 = true, bool ipv6 = true);
/**
* Get information about network adapters (loopback devices are not included)
* @note Make sure that addresses is large enough. If too small
* the result is capped.
* @param addresses array of if-addresses
* @param addresses_count count
* @param count actual count
*/
void GetIfAddresses(IfAddr* addresses, uint32_t addresses_count, uint32_t* count);
/**
* Convert result value to string
* @param result Result to convert
* @return Result as string
*/
const char* ResultToString(Result result);
/**
* Converts a native result (error) to dmSocket::Result
* Also logs the error
* @param filename the file that calls this function
* @param line the line number of this call
* @param r the native result
* @return Result
*/
Result NativeToResult(const char* filename, int line, int r);
/**
* Check if a network address is empty (all zeroes).
* @param address The address to check
* @return True if the address is empty, false otherwise
*/
bool Empty(Address address);
/**
* Return a pointer to the IPv4 buffer of address.
* @note Make sure the address family of address is actually AF_INET before
* attempting to retrieve the IPv4 buffer, otherwise an assert will trigger.
* @param address Pointer to the address containing the buffer
* @return Pointer to the buffer that holds the IPv4 address
*/
uint32_t* IPv4(Address* address);
/**
* Return a pointer to the IPv6 buffer of address.
* @note Make sure the address family of address is actually AF_INET6 before
* attempting to retrieve the IPv6 buffer, otherwise an assert will trigger.
* @param address Pointer to the address containing the buffer
* @return Pointer to the buffer that holds the IPv6 address
*/
uint32_t* IPv6(Address* address);
/**
* Checks if a socket was created for IPv4 (AF_INET).
* @param socket The socket to check
* @return True if the socket was created for IPv4 communication, false otherwise
*/
bool IsSocketIPv4(Socket socket);
/**
* Checks if a socket was created for IPv6 (AF_INET6).
* @param socket The socket to check
* @return True if the socket was created for IPv6 communication, false otherwise
*/
bool IsSocketIPv6(Socket socket);
/**
* Calculate the number of bits that differs between address a and b.
* @note This is used for the Hamming Distance.
* @param a The first address to compare
* @param b The second address to compare
* @return Number of bits that differs between a and b
*/
uint32_t BitDifference(Address a, Address b);
struct Selector;
/**
* Clear selector for socket. Similar to FD_CLR
* @param selector Selector
* @param selector_kind Kind to clear
* @param socket Socket to clear
*/
void SelectorClear(Selector* selector, SelectorKind selector_kind, Socket socket);
/**
* Set selector for socket. Similar to FD_SET
* @param selector Selector
* @param selector_kind Kind to clear
* @param socket Socket to set
*/
void SelectorSet(Selector* selector, SelectorKind selector_kind, Socket socket);
/**
* Check if selector is set. Similar to FD_ISSET
* @param selector Selector
* @param selector_kind Selector kind
* @param socket Socket to check for
* @return True if set.
*/
bool SelectorIsSet(Selector* selector, SelectorKind selector_kind, Socket socket);
/**
* Clear selector (all kinds). Similar to FD_ZERO
* @param selector Selector
*/
void SelectorZero(Selector* selector);
/**
* Select for pending data
* @param selector Selector
* @param timeout Timeout. For blocking pass -1
* @return RESULT_OK on success
*/
Result Select(Selector* selector, int32_t timeout);
}
#endif // DM_SOCKET_H

85
websocket/src/uri.h Normal file
View File

@@ -0,0 +1,85 @@
// Copyright 2020 The Defold Foundation
// Licensed under the Defold License version 1.0 (the "License"); you may not use
// this file except in compliance with the License.
//
// You may obtain a copy of the License, together with FAQs at
// https://www.defold.com/license
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef DM_URI_H
#define DM_URI_H
namespace dmURI
{
/**
* URI parsing result
*/
enum Result
{
RESULT_OK,//!< RESULT_OK
};
const uint32_t MAX_SCHEME_LEN = 8;
const uint32_t MAX_LOCATION_LEN = 64;
const uint32_t MAX_PATH_LEN = 2048;
// Maximum length of an URI
// scheme :// location / path
const uint32_t MAX_URI_LEN = MAX_SCHEME_LEN + 3 + MAX_LOCATION_LEN + 1 + MAX_PATH_LEN;
/**
* URI parsing result parts
*/
struct Parts
{
/// Scheme parts, eg http
char m_Scheme[MAX_SCHEME_LEN];
/// Location part, eg foo.com:80
char m_Location[MAX_LOCATION_LEN];
/// Hostname part of location, eg foo.com
char m_Hostname[MAX_LOCATION_LEN];
/// Port part of location, eg 80. -1 if not present
int m_Port;
/// Path part, eg index.html
// Increased from 512 to 2048 (DEF-3410). 2048 seems like a reasonable
// number based on the following SO answer: https://stackoverflow.com/a/417184/1266551
char m_Path[MAX_PATH_LEN];
};
/**
* Parse URI and split in three parts. (scheme, location, path)
* @note This is a simplified URI parser and does not conform to rfc2396.
* Missing features are: parameters, query, fragment part of URI and support for escaped sequences
* @note For http m_Port is set to 80 if not specified in uri.
* @param uri URI to parse
* @param parts Result
* @return RESULT_OK on success
*/
Result Parse(const char* uri, Parts* parts);
/**
* Performs URL encoding of the supplied buffer
* @param src String to encode
* @param dst Encoded string
* @param dst_size size of the provided out buffer
*/
void Encode(const char* src, char* dst, uint32_t dst_size);
/**
* Undoes URL decoding on a buffer.
* @note The output will never be larger than the input.
* @param src Input
* @param dst Decoded output
*/
void Decode(const char* src, char* dst);
}
#endif // DM_URI_H

1056
websocket/src/websocket.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,340 @@
/*
* Wslay - The WebSocket Library
*
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
*
* 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.
*/
#include "wslay_frame.h"
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include "wslay_net.h"
#define wslay_min(A, B) (((A) < (B)) ? (A) : (B))
int wslay_frame_context_init(wslay_frame_context_ptr *ctx,
const struct wslay_frame_callbacks *callbacks,
void *user_data)
{
*ctx = (wslay_frame_context_ptr)malloc(sizeof(struct wslay_frame_context));
if(*ctx == NULL) {
return -1;
}
memset(*ctx, 0, sizeof(struct wslay_frame_context));
(*ctx)->istate = RECV_HEADER1;
(*ctx)->ireqread = 2;
(*ctx)->ostate = PREP_HEADER;
(*ctx)->user_data = user_data;
(*ctx)->ibufmark = (*ctx)->ibuflimit = (*ctx)->ibuf;
(*ctx)->callbacks = *callbacks;
return 0;
}
void wslay_frame_context_free(wslay_frame_context_ptr ctx)
{
free(ctx);
}
ssize_t wslay_frame_send(wslay_frame_context_ptr ctx,
struct wslay_frame_iocb *iocb)
{
if(iocb->data_length > iocb->payload_length) {
return WSLAY_ERR_INVALID_ARGUMENT;
}
if(ctx->ostate == PREP_HEADER) {
uint8_t *hdptr = ctx->oheader;
memset(ctx->oheader, 0, sizeof(ctx->oheader));
*hdptr |= (iocb->fin << 7) & 0x80u;
*hdptr |= (iocb->rsv << 4) & 0x70u;
*hdptr |= iocb->opcode & 0xfu;
++hdptr;
*hdptr |= (iocb->mask << 7) & 0x80u;
if(wslay_is_ctrl_frame(iocb->opcode) && iocb->payload_length > 125) {
return WSLAY_ERR_INVALID_ARGUMENT;
}
if(iocb->payload_length < 126) {
*hdptr |= iocb->payload_length;
++hdptr;
} else if(iocb->payload_length < (1 << 16)) {
uint16_t len = htons(iocb->payload_length);
*hdptr |= 126;
++hdptr;
memcpy(hdptr, &len, 2);
hdptr += 2;
} else if(iocb->payload_length < (1ull << 63)) {
uint64_t len = hton64(iocb->payload_length);
*hdptr |= 127;
++hdptr;
memcpy(hdptr, &len, 8);
hdptr += 8;
} else {
/* Too large payload length */
return WSLAY_ERR_INVALID_ARGUMENT;
}
if(iocb->mask) {
if(ctx->callbacks.genmask_callback(ctx->omaskkey, 4,
ctx->user_data) != 0) {
return WSLAY_ERR_INVALID_CALLBACK;
} else {
ctx->omask = 1;
memcpy(hdptr, ctx->omaskkey, 4);
hdptr += 4;
}
}
ctx->ostate = SEND_HEADER;
ctx->oheadermark = ctx->oheader;
ctx->oheaderlimit = hdptr;
ctx->opayloadlen = iocb->payload_length;
ctx->opayloadoff = 0;
}
if(ctx->ostate == SEND_HEADER) {
ptrdiff_t len = ctx->oheaderlimit-ctx->oheadermark;
ssize_t r;
int flags = 0;
if(iocb->data_length > 0) {
flags |= WSLAY_MSG_MORE;
};
r = ctx->callbacks.send_callback(ctx->oheadermark, len, flags,
ctx->user_data);
if(r > 0) {
if(r > len) {
return WSLAY_ERR_INVALID_CALLBACK;
} else {
ctx->oheadermark += r;
if(ctx->oheadermark == ctx->oheaderlimit) {
ctx->ostate = SEND_PAYLOAD;
} else {
return WSLAY_ERR_WANT_WRITE;
}
}
} else {
return WSLAY_ERR_WANT_WRITE;
}
}
if(ctx->ostate == SEND_PAYLOAD) {
size_t totallen = 0;
if(iocb->data_length > 0) {
if(ctx->omask) {
uint8_t temp[4096];
const uint8_t *datamark = iocb->data,
*datalimit = iocb->data+iocb->data_length;
while(datamark < datalimit) {
size_t datalen = datalimit - datamark;
const uint8_t *writelimit = datamark+
wslay_min(sizeof(temp), datalen);
size_t writelen = writelimit-datamark;
ssize_t r;
size_t i;
for(i = 0; i < writelen; ++i) {
temp[i] = datamark[i]^ctx->omaskkey[(ctx->opayloadoff+i)%4];
}
r = ctx->callbacks.send_callback(temp, writelen, 0, ctx->user_data);
if(r > 0) {
if((size_t)r > writelen) {
return WSLAY_ERR_INVALID_CALLBACK;
} else {
datamark += r;
ctx->opayloadoff += r;
totallen += r;
}
} else {
if(totallen > 0) {
break;
} else {
return WSLAY_ERR_WANT_WRITE;
}
}
}
} else {
ssize_t r;
r = ctx->callbacks.send_callback(iocb->data, iocb->data_length, 0,
ctx->user_data);
if(r > 0) {
if((size_t)r > iocb->data_length) {
return WSLAY_ERR_INVALID_CALLBACK;
} else {
ctx->opayloadoff += r;
totallen = r;
}
} else {
return WSLAY_ERR_WANT_WRITE;
}
}
}
if(ctx->opayloadoff == ctx->opayloadlen) {
ctx->ostate = PREP_HEADER;
}
return totallen;
}
return WSLAY_ERR_INVALID_ARGUMENT;
}
static void wslay_shift_ibuf(wslay_frame_context_ptr ctx)
{
ptrdiff_t len = ctx->ibuflimit-ctx->ibufmark;
memmove(ctx->ibuf, ctx->ibufmark, len);
ctx->ibuflimit = ctx->ibuf+len;
ctx->ibufmark = ctx->ibuf;
}
static ssize_t wslay_recv(wslay_frame_context_ptr ctx)
{
ssize_t r;
if(ctx->ibufmark != ctx->ibuf) {
wslay_shift_ibuf(ctx);
}
r = ctx->callbacks.recv_callback
(ctx->ibuflimit, ctx->ibuf+sizeof(ctx->ibuf)-ctx->ibuflimit,
0, ctx->user_data);
if(r > 0) {
ctx->ibuflimit += r;
} else {
r = WSLAY_ERR_WANT_READ;
}
return r;
}
#define WSLAY_AVAIL_IBUF(ctx) ((size_t)(ctx->ibuflimit - ctx->ibufmark))
ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx,
struct wslay_frame_iocb *iocb)
{
ssize_t r;
if(ctx->istate == RECV_HEADER1) {
uint8_t fin, opcode, rsv, payloadlen;
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
if((r = wslay_recv(ctx)) <= 0) {
return r;
}
}
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
return WSLAY_ERR_WANT_READ;
}
fin = (ctx->ibufmark[0] >> 7) & 1;
rsv = (ctx->ibufmark[0] >> 4) & 7;
opcode = ctx->ibufmark[0] & 0xfu;
ctx->iom.opcode = opcode;
ctx->iom.fin = fin;
ctx->iom.rsv = rsv;
++ctx->ibufmark;
ctx->imask = (ctx->ibufmark[0] >> 7) & 1;
payloadlen = ctx->ibufmark[0] & 0x7fu;
++ctx->ibufmark;
if(wslay_is_ctrl_frame(opcode) && (payloadlen > 125 || !fin)) {
return WSLAY_ERR_PROTO;
}
if(payloadlen == 126) {
ctx->istate = RECV_EXT_PAYLOADLEN;
ctx->ireqread = 2;
} else if(payloadlen == 127) {
ctx->istate = RECV_EXT_PAYLOADLEN;
ctx->ireqread = 8;
} else {
ctx->ipayloadlen = payloadlen;
ctx->ipayloadoff = 0;
if(ctx->imask) {
ctx->istate = RECV_MASKKEY;
ctx->ireqread = 4;
} else {
ctx->istate = RECV_PAYLOAD;
}
}
}
if(ctx->istate == RECV_EXT_PAYLOADLEN) {
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
if((r = wslay_recv(ctx)) <= 0) {
return r;
}
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
return WSLAY_ERR_WANT_READ;
}
}
ctx->ipayloadlen = 0;
ctx->ipayloadoff = 0;
memcpy((uint8_t*)&ctx->ipayloadlen+(8-ctx->ireqread),
ctx->ibufmark, ctx->ireqread);
ctx->ipayloadlen = ntoh64(ctx->ipayloadlen);
ctx->ibufmark += ctx->ireqread;
if(ctx->ireqread == 8) {
if(ctx->ipayloadlen < (1 << 16) ||
ctx->ipayloadlen & (1ull << 63)) {
return WSLAY_ERR_PROTO;
}
} else if(ctx->ipayloadlen < 126) {
return WSLAY_ERR_PROTO;
}
if(ctx->imask) {
ctx->istate = RECV_MASKKEY;
ctx->ireqread = 4;
} else {
ctx->istate = RECV_PAYLOAD;
}
}
if(ctx->istate == RECV_MASKKEY) {
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
if((r = wslay_recv(ctx)) <= 0) {
return r;
}
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
return WSLAY_ERR_WANT_READ;
}
}
memcpy(ctx->imaskkey, ctx->ibufmark, 4);
ctx->ibufmark += 4;
ctx->istate = RECV_PAYLOAD;
}
if(ctx->istate == RECV_PAYLOAD) {
uint8_t *readlimit, *readmark;
uint64_t rempayloadlen = ctx->ipayloadlen-ctx->ipayloadoff;
if(WSLAY_AVAIL_IBUF(ctx) == 0 && rempayloadlen > 0) {
if((r = wslay_recv(ctx)) <= 0) {
return r;
}
}
readmark = ctx->ibufmark;
readlimit = WSLAY_AVAIL_IBUF(ctx) < rempayloadlen ?
ctx->ibuflimit : ctx->ibufmark+rempayloadlen;
if(ctx->imask) {
for(; ctx->ibufmark != readlimit;
++ctx->ibufmark, ++ctx->ipayloadoff) {
ctx->ibufmark[0] ^= ctx->imaskkey[ctx->ipayloadoff % 4];
}
} else {
ctx->ibufmark = readlimit;
ctx->ipayloadoff += readlimit-readmark;
}
iocb->fin = ctx->iom.fin;
iocb->rsv = ctx->iom.rsv;
iocb->opcode = ctx->iom.opcode;
iocb->payload_length = ctx->ipayloadlen;
iocb->mask = ctx->imask;
iocb->data = readmark;
iocb->data_length = ctx->ibufmark-readmark;
if(ctx->ipayloadlen == ctx->ipayloadoff) {
ctx->istate = RECV_HEADER1;
ctx->ireqread = 2;
}
return iocb->data_length;
}
return WSLAY_ERR_INVALID_ARGUMENT;
}

View File

@@ -0,0 +1,36 @@
/*
* Wslay - The WebSocket Library
*
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
*
* 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.
*/
#include "wslay_net.h"
#ifndef WORDS_BIGENDIAN
uint64_t wslay_byteswap64(uint64_t x)
{
uint64_t u = ntohl(x & 0xffffffffllu);
uint64_t l = ntohl(x >> 32);
return (u << 32) | l;
}
#endif /* !WORDS_BIGENDIAN */

View File

@@ -0,0 +1,117 @@
/*
* Wslay - The WebSocket Library
*
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
*
* 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.
*/
#include "wslay_queue.h"
#include <string.h>
#include <assert.h>
struct wslay_queue* wslay_queue_new(void)
{
struct wslay_queue *queue = (struct wslay_queue*)malloc
(sizeof(struct wslay_queue));
if(!queue) {
return NULL;
}
queue->top = queue->tail = NULL;
return queue;
}
void wslay_queue_free(struct wslay_queue *queue)
{
if(!queue) {
return;
} else {
struct wslay_queue_cell *p = queue->top;
while(p) {
struct wslay_queue_cell *next = p->next;
free(p);
p = next;
}
free(queue);
}
}
int wslay_queue_push(struct wslay_queue *queue, void *data)
{
struct wslay_queue_cell *new_cell = (struct wslay_queue_cell*)malloc
(sizeof(struct wslay_queue_cell));
if(!new_cell) {
return WSLAY_ERR_NOMEM;
}
new_cell->data = data;
new_cell->next = NULL;
if(queue->tail) {
queue->tail->next = new_cell;
queue->tail = new_cell;
} else {
queue->top = queue->tail = new_cell;
}
return 0;
}
int wslay_queue_push_front(struct wslay_queue *queue, void *data)
{
struct wslay_queue_cell *new_cell = (struct wslay_queue_cell*)malloc
(sizeof(struct wslay_queue_cell));
if(!new_cell) {
return WSLAY_ERR_NOMEM;
}
new_cell->data = data;
new_cell->next = queue->top;
queue->top = new_cell;
if(!queue->tail) {
queue->tail = queue->top;
}
return 0;
}
void wslay_queue_pop(struct wslay_queue *queue)
{
struct wslay_queue_cell *top = queue->top;
assert(top);
queue->top = top->next;
if(top == queue->tail) {
queue->tail = NULL;
}
free(top);
}
void* wslay_queue_top(struct wslay_queue *queue)
{
assert(queue->top);
return queue->top->data;
}
void* wslay_queue_tail(struct wslay_queue *queue)
{
assert(queue->tail);
return queue->tail->data;
}
int wslay_queue_empty(struct wslay_queue *queue)
{
return queue->top == NULL;
}

View File

@@ -0,0 +1,86 @@
/*
* Wslay - The WebSocket Library
*
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
*
* 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.
*/
#include "wslay_stack.h"
#include <string.h>
#include <assert.h>
struct wslay_stack* wslay_stack_new()
{
struct wslay_stack *stack = (struct wslay_stack*)malloc
(sizeof(struct wslay_stack));
if(!stack) {
return NULL;
}
stack->top = NULL;
return stack;
}
void wslay_stack_free(struct wslay_stack *stack)
{
struct wslay_stack_cell *p;
if(!stack) {
return;
}
p = stack->top;
while(p) {
struct wslay_stack_cell *next = p->next;
free(p);
p = next;
}
free(stack);
}
int wslay_stack_push(struct wslay_stack *stack, void *data)
{
struct wslay_stack_cell *new_cell = (struct wslay_stack_cell*)malloc
(sizeof(struct wslay_stack_cell));
if(!new_cell) {
return WSLAY_ERR_NOMEM;
}
new_cell->data = data;
new_cell->next = stack->top;
stack->top = new_cell;
return 0;
}
void wslay_stack_pop(struct wslay_stack *stack)
{
struct wslay_stack_cell *top = stack->top;
assert(top);
stack->top = top->next;
free(top);
}
void* wslay_stack_top(struct wslay_stack *stack)
{
assert(stack->top);
return stack->top->data;
}
int wslay_stack_empty(struct wslay_stack *stack)
{
return stack->top == NULL;
}