libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit c488cadbc17253178c418f52ba9a321816f0080b
parent 1e147571229c7c2f402c5208171ef3ba78dfd03d
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 26 Apr 2021 14:12:23 +0200

verbatim import of David Gausmann's websocket extension; tests do not yet pass, only enabled with --enable-experimental

Diffstat:
M.gitignore | 1+
MChangeLog | 3+++
Mconfigure.ac | 5+++--
Msrc/Makefile.am | 4++--
Msrc/include/Makefile.am | 6+++++-
Asrc/include/microhttpd_ws.h | 979+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/microhttpd/Makefile.am | 4++++
Asrc/microhttpd_ws/Makefile.am | 39+++++++++++++++++++++++++++++++++++++++
Asrc/microhttpd_ws/mhd_websocket.c | 2072+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/microhttpd_ws/sha1.c | 420+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/microhttpd_ws/sha1.h | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/microhttpd_ws/test_websocket.c | 8983+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
12 files changed, 12656 insertions(+), 5 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -62,3 +62,4 @@ src/examples/suspend_resume_epoll uncrustify.cfg **.dvi **.t2d +src/microhttpd_ws/test_websocket diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,6 @@ +Mon 26 Apr 2021 02:09:46 PM CEST + Importing experimental Websocket support by David Gausmann. -CG + Sun 25 Apr 2021 14:00:00 MSK Releasing GNU libmicrohttpd 0.9.73. -EG diff --git a/configure.ac b/configure.ac @@ -1,5 +1,5 @@ # This file is part of libmicrohttpd. -# (C) 2006-2020 Christian Grothoff (and other contributing authors) +# (C) 2006-2021 Christian Grothoff (and other contributing authors) # # libmicrohttpd is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published @@ -935,7 +935,7 @@ test "x$enable_examples" = "xno" || enable_examples=yes AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$enable_examples" = "xyes"]) AC_ARG_ENABLE([[heavy-tests]], - [AS_HELP_STRING([[--enable-heavy-tests]], [use heavy tests in test-suite. WARNING:] + [AS_HELP_STRING([[--enable-heavy-tests]], [use heavy tests in test-suite. WARNING:] [a dedicated host with minimal number of background processes and no network] [activity is recommended to enable.])], [], [enable_heavy_tests=no]) @@ -2413,6 +2413,7 @@ src/Makefile src/include/Makefile src/lib/Makefile src/microhttpd/Makefile +src/microhttpd_ws/Makefile src/examples/Makefile src/testcurl/Makefile src/testcurl/https/Makefile diff --git a/src/Makefile.am b/src/Makefile.am @@ -16,11 +16,11 @@ endif # Finally (last!) also build experimental lib... if HAVE_EXPERIMENTAL -SUBDIRS += lib +SUBDIRS += microhttpd_ws lib endif EXTRA_DIST = \ datadir/cert-and-key.pem \ - datadir/cert-and-key-for-wireshark.pem + datadir/cert-and-key-for-wireshark.pem .NOTPARALLEL: diff --git a/src/include/Makefile.am b/src/include/Makefile.am @@ -1,7 +1,11 @@ # This Makefile.am is in the public domain SUBDIRS = . -include_HEADERS = microhttpd.h +include_HEADERS = microhttpd.h noinst_HEADERS = microhttpd2.h microhttpd_tls.h +if HAVE_EXPERIMENTAL +include_HEADERS += microhttpd_ws.h +endif + EXTRA_DIST = platform.h autoinit_funcs.h mhd_options.h diff --git a/src/include/microhttpd_ws.h b/src/include/microhttpd_ws.h @@ -0,0 +1,979 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2021 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +/** + * @file microhttpd_ws.h + * @brief interface for experimental web socket extension to libmicrohttpd + * @author David Gausmann + */ +#ifndef MHD_MICROHTTPD_WS_H +#define MHD_MICROHTTPD_WS_H + + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + + +/** + * @brief Handle for the encoding/decoding of websocket data + * (one stream is used per websocket) + * @ingroup websocket + */ +struct MHD_WebSocketStream; + +/** + * @brief Flags for the initialization of a websocket stream + * `struct MHD_WebSocketStream` used by + * #MHD_websocket_stream_init() or + * #MHD_websocket_stream_init2(). + * @ingroup websocket + */ +enum MHD_WEBSOCKET_FLAG +{ + /** + * The websocket is used by the server (default). + * Thus all outgoing payload will not be "masked". + * All incoming payload must be masked. + * This cannot be used together with #MHD_WEBSOCKET_FLAG_CLIENT + */ + MHD_WEBSOCKET_FLAG_SERVER = 0, + /** + * The websocket is used by the client + * (not used if you provide the server). + * Thus all outgoing payload will be "masked" (XOR-ed with random values). + * All incoming payload must be unmasked. + * Please note that this implementation doesn't use a strong random + * number generator for the mask as suggested in RFC6455 10.3, because + * the main intention of this implemention is the use as server + * with MHD, which doesn't need masking. + * Instead a weak random number generator is used (`rand()`). + * You can set the seed for the random number generator + * by calling #MHD_websocket_srand(). + * This cannot be used together with #MHD_WEBSOCKET_FLAG_SERVER + */ + MHD_WEBSOCKET_FLAG_CLIENT = 1, + /** + * You don't want to get fragmented data while decoding. + * Fragmented frames will be internally put together until + * they are complete. + * Whether or not data is fragmented is decided + * by the sender of the data during encoding. + * This cannot be used together with #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS + */ + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS = 0, + /** + * You want fragmented data, if it appears while decoding. + * You will receive the content of the fragmented frame, + * but if you are decoding text, you will never get an unfinished + * UTF-8 sequences (if the sequence appears between two fragments). + * Instead the text will end before the unfinished UTF-8 sequence. + * With the next fragment, which finishes the UTF-8 sequence, + * you will get the complete UTF-8 sequence. + * This cannot be used together with #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS + */ + MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS = 2, + /** + * If the websocket stream becomes invalid during decoding due to + * protocol errors, a matching close frame will automatically + * be generated. + * The close frame will be returned via the parameters + * result and result_len of #MHD_websocket_decode() and + * the return value is negative + * (a value of `enum MHD_WEBSOCKET_STATUS`). + * The generated close frame must be freed by the caller + * with #MHD_websocket_free(). + */ + MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR = 4 +}; + +/** + * @brief Enum to specify the fragmenting behavior + * while encoding with #MHD_websocket_encode_text() or + * #MHD_websocket_encode_binary(). + * @ingroup websocket + */ +enum MHD_WEBSOCKET_FRAGMENTATION +{ + /** + * You don't want to use fragmentation. + * The encoded frame consists of only one frame. + */ + MHD_WEBSOCKET_FRAGMENTATION_NONE = 0, + /** + * You want to use fragmentation. + * The encoded frame is the first frame of + * a series of data frames of the same type + * (text or binary). + * You may send control frames (ping, pong or close) + * between these data frames. + */ + MHD_WEBSOCKET_FRAGMENTATION_FIRST = 1, + /** + * You want to use fragmentation. + * The encoded frame is not the first frame of + * the series of data frames, but also not the last one. + * You may send control frames (ping, pong or close) + * between these data frames. + */ + MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING = 2, + /** + * You want to use fragmentation. + * The encoded frame is the last frame of + * the series of data frames, but also not the first one. + * After this frame, you may send all type of frames again. + */ + MHD_WEBSOCKET_FRAGMENTATION_LAST = 3 +}; + +/** + * @brief Enum of the return value for almost every MHD_websocket function. + * Errors are negative and values equal to or above zero mean a success. + * Positive values are only used by #MHD_websocket_decode(). + * @ingroup websocket + */ +enum MHD_WEBSOCKET_STATUS +{ + /** + * The call succeeded. + * For #MHD_websocket_decode() this means that no error occurred, + * but also no frame has been completed yet. + */ + MHD_WEBSOCKET_STATUS_OK = 0, + /** + * #MHD_websocket_decode() has decoded a text frame. + * The parameters result and result_len are filled with the decoded text + * (if any). + */ + MHD_WEBSOCKET_STATUS_TEXT_FRAME = 0x1, + /** + * #MHD_websocket_decode() has decoded a binary frame. + * The parameters result and result_len are filled with the decoded + * binary data (if any). + */ + MHD_WEBSOCKET_STATUS_BINARY_FRAME = 0x2, + /** + * #MHD_websocket_decode() has decoded a close frame. + * This means you must close the socket using #MHD_upgrade_action() + * with #MHD_UPGRADE_ACTION_CLOSE. + * You may respond with a close frame before closing. + * The parameters result and result_len are filled with + * the close reason (if any). + * The close reason starts with a two byte sequence of close code + * in network byte order (see `enum MHD_WEBSOCKET_CLOSEREASON`). + * After these two bytes a UTF-8 encoded close reason may follow. + * Compare with result_len to decide whether there is any close reason. + */ + MHD_WEBSOCKET_STATUS_CLOSE_FRAME = 0x8, + /** + * #MHD_websocket_decode() has decoded a ping frame. + * You should respond to this with a pong frame. + * The pong frame must contain the same binary data as + * the corresponding ping frame (if it had any). + * The parameters result and result_len are filled with + * the binary ping data (if any). + */ + MHD_WEBSOCKET_STATUS_PING_FRAME = 0x9, + /** + * #MHD_websocket_decode() has decoded a pong frame. + * You should usually only receive pong frames if you sent + * a ping frame before. + * The binary data should be equal to your ping frame and can be + * used to distinguish the response if you sent multiple ping frames. + * The parameters result and result_len are filled with + * the binary pong data (if any). + */ + MHD_WEBSOCKET_STATUS_PONG_FRAME = 0xA, + /** + * #MHD_websocket_decode() has decoded a text frame fragment. + * The parameters result and result_len are filled with the decoded text + * (if any). + * This is like #MHD_WEBSOCKET_STATUS_TEXT_FRAME, but it can only + * appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS during + * the call of #MHD_websocket_stream_init() or + * #MHD_websocket_stream_init2(). + */ + MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT = 0x11, + /** + * #MHD_websocket_decode() has decoded a binary frame fragment. + * The parameters result and result_len are filled with the decoded + * binary data (if any). + * This is like #MHD_WEBSOCKET_STATUS_BINARY_FRAME, but it can only + * appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS during + * the call of #MHD_websocket_stream_init() or + * #MHD_websocket_stream_init2(). + */ + MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT = 0x12, + /** + * #MHD_websocket_decode() has decoded the last text frame fragment. + * The parameters result and result_len are filled with the decoded text + * (if any). + * This is like #MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, but it appears + * only for the last fragment of a series of fragments. + * It can only appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS + * during the call of #MHD_websocket_stream_init() or + * #MHD_websocket_stream_init2(). + */ + MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT = 0x21, + /** + * #MHD_websocket_decode() has decoded the last binary frame fragment. + * The parameters result and result_len are filled with the decoded + * binary data (if any). + * This is like #MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT, but it appears + * only for the last fragment of a series of fragments. + * It can only appear if you specified #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS + * during the call of #MHD_websocket_stream_init() or + * #MHD_websocket_stream_init2(). + */ + MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT = 0x22, + /** + * The call failed and the stream is invalid now for decoding. + * You must close the websocket now using #MHD_upgrade_action() + * with #MHD_UPGRADE_ACTION_CLOSE. + * You can send a close frame before closing. + * This is only used by #MHD_websocket_decode() and happens + * if the stream contains errors (i. e. invalid byte data). + */ + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR = -1, + /** + * You tried to decode something, but the stream has already + * been marked invalid. + * You must close the websocket now using #MHD_upgrade_action() + * with #MHD_UPGRADE_ACTION_CLOSE. + * You can send a close frame before closing. + * This is only used by #MHD_websocket_decode() and happens + * if you call #MDM_websocket_decode() again after is + * has been invalidated. + */ + MHD_WEBSOCKET_STATUS_STREAM_BROKEN = -2, + /** + * A memory allocation failed. The stream remains valid. + * If this occurred while decoding, the decoding could be + * possible later if enough memory is available. + * This could happen while decoding if you received a too big data frame. + * You could try to specify max_payload_size during the call of + * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2() then to + * avoid this and close the frame instead. + */ + MHD_WEBSOCKET_STATUS_MEMORY_ERROR = -3, + /** + * You passed invalid parameters during the function call + * (i. e. a NULL pointer for a required parameter). + * The stream remains valid. + */ + MHD_WEBSOCKET_STATUS_PARAMETER_ERROR = -4, + /** + * The maximum payload size has been exceeded. + * If you got this return code from #MHD_websocket_decode() then + * the stream becomes invalid and the websocket must be closed + * using #MHD_upgrade_action() with #MHD_UPGRADE_ACTION_CLOSE. + * You can send a close frame before closing. + * The maximum payload size is specified during the call of + * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2(). + * This can also appear if you specified 0 as maximum payload size + * when the message is greater than the maximum allocatable memory size + * (i. e. more than 4 GB on 32 bit systems). + * If you got this return code from #MHD_websocket_encode_close(), + * #MHD_websocket_encode_ping() or #MHD_websocket_encode_pong() then + * you passed to much payload data. The stream remains valid then. + */ + MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED = -5, + /** + * An UTF-8 text is invalid. + * If you got this return code from #MHD_websocket_decode() then + * the stream becomes invalid and you must close the websocket + * using #MHD_upgrade_action() with #MHD_UPGRADE_ACTION_CLOSE. + * You can send a close frame before closing. + * If you got this from #MHD_websocket_encode_text() or + * #MHD_websocket_encode_close() then you passed invalid UTF-8 text. + * The stream remains valid then. + */ + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR = -6 +}; + +/** + * @brief Enumeration of possible close reasons for close frames. + * + * The possible values are specified in RFC 6455 7.4.1 + * These close reasons here are the default set specified by RFC 6455, + * but also other close reasons could be used. + * + * The definition is for short: + * 0-999 are never used (if you pass 0 in + * #MHD_websocket_encode_close() then no close reason is used). + * 1000-2999 are specified by RFC 6455. + * 3000-3999 are specified by libraries, etc. but must be registered by IANA. + * 4000-4999 are reserved for private use. + * + * @ingroup websocket + */ +enum MHD_WEBSOCKET_CLOSEREASON +{ + /** + * This value is used as placeholder for #MHD_websocket_encode_close() + * to tell that you don't want to specify any reason. + * If you use this value then no reason text may be used. + * This value cannot a result of decoding, because this value + * is not a valid close reason for the WebSocket protocol. + */ + MHD_WEBSOCKET_CLOSEREASON_NO_REASON = 0, + /** + * You close the websocket fulfilled its purpose and shall + * now be closed in a normal, planned way. + */ + MHD_WEBSOCKET_CLOSEREASON_REGULAR = 1000, + /** + * You close the websocket because are shutting down the server or + * something similar. + */ + MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY = 1001, + /** + * You close the websocket because you a protocol error occurred + * during decoding (i. e. invalid byte data). + */ + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR = 1002, + /** + * You close the websocket because you received data which you don't accept. + * For example if you received a binary frame, + * but your application only expects text frames. + */ + MHD_WEBSOCKET_CLOSEREASON_UNSUPPORTED_DATATYPE = 1003, + /** + * You close the websocket because it contains malformed UTF-8. + * The UTF-8 validity is automatically checked by #MHD_websocket_decode(), + * so you don't need to check it on your own. + * UTF-8 is specified in RFC 3629. + */ + MHD_WEBSOCKET_CLOSEREASON_MALFORMED_UTF8 = 1007, + /** + * You close the websocket because of any reason. + * Usually this close reason is used if no other close reason + * is more specific or if you don't want to use any other close reason. + */ + MHD_WEBSOCKET_CLOSEREASON_POLICY_VIOLATED = 1008, + /** + * You close the websocket because you received a frame which is too big to process. + * You can specify the maximum allowed payload size during the call of + * #MHD_websocket_stream_init() or #MHD_websocket_stream_init2(). + */ + MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED = 1009, + /** + * This status code can be sent by the client if it + * expected a specific extension, but this extension hasn't been negotiated. + */ + MHD_WEBSOCKET_CLOSEREASON_MISSING_EXTENSION = 1010, + /** + * The server closes the websocket because it encountered + * an unexpected condition that prevented it from fulfilling the request. + */ + MHD_WEBSOCKET_CLOSEREASON_UNEXPECTED_CONDITION = 1011 +}; + +/** + * @brief Enumeration of possible UTF-8 check steps + * + * These values are used during the encoding of fragmented text frames + * or for error analysis while encoding text frames. + * Its values specify the next step of the UTF-8 check. + * UTF-8 sequences consist of one to four bytes. + * This enumeration just says how long the current UTF-8 sequence is + * and what is the next expected byte. + * + * @ingroup websocket + */ +enum MHD_WEBSOCKET_UTF8STEP +{ + /** + * There is no open UTF-8 sequence. + * The next byte must be 0x00-0x7F or 0xC2-0xF4. + */ + MHD_WEBSOCKET_UTF8STEP_NORMAL = 0, + /** + * The second byte of a two byte UTF-8 sequence. + * The first byte was 0xC2-0xDF. + * The next byte must be 0x80-0xBF. + */ + MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1 = 1, + /** + * The second byte of a three byte UTF-8 sequence. + * The first byte was 0xE0. + * The next byte must be 0xA0-0xBF. + */ + MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2 = 2, + /** + * The second byte of a three byte UTF-8 sequence. + * The first byte was 0xED. + * The next byte must by 0x80-0x9F. + */ + MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2 = 3, + /** + * The second byte of a three byte UTF-8 sequence. + * The first byte was 0xE1-0xEC or 0xEE-0xEF. + * The next byte must be 0x80-0xBF. + */ + MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2 = 4, + /** + * The third byte of a three byte UTF-8 sequence. + * The next byte must be 0x80-0xBF. + */ + MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2 = 5, + /** + * The second byte of a four byte UTF-8 sequence. + * The first byte was 0xF0. + * The next byte must be 0x90-0xBF. + */ + MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3 = 6, + /** + * The second byte of a four byte UTF-8 sequence. + * The first byte was 0xF4. + * The next byte must be 0x80-0x8F. + */ + MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3 = 7, + /** + * The second byte of a four byte UTF-8 sequence. + * The first byte was 0xF1-0xF3. + * The next byte must be 0x80-0xBF. + */ + MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3 = 8, + /** + * The third byte of a four byte UTF-8 sequence. + * The next byte must be 0x80-0xBF. + */ + MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3 = 9, + /** + * The fourth byte of a four byte UTF-8 sequence. + * The next byte must be 0x80-0xBF. + */ + MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3 = 10 +}; + +/** +* @brief Enumeration of validity values +* +* These values are used for #MHD_websocket_stream_is_valid() +* and specifiy the validity status. +* +* @ingroup websocket +*/ +enum MHD_WEBSOCKET_VALIDITY +{ + /** + * The stream is invalid. + * It cannot be used for decoding anymore. + */ + MHD_WEBSOCKET_VALIDITY_INVALID = 0, + /** + * The stream is valid. + * Decoding works as expected. + */ + MHD_WEBSOCKET_VALIDITY_VALID = 1, + /** + * The stream has received a close frame and + * is partly invalid. + * You can still use the stream for decoding, + * but if a data frame is received an error will be reported. + * After a close frame has been sent, no data frames + * may follow from the sender of the close frame. + */ + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES = 2 +}; +/** + * This method is called by many websocket + * functions for allocating data. + * By default 'malloc' is used. + * This can be used for operating systems like Windows + * where malloc, realloc and free are compiler dependent. + * + * @param len new size + * @return allocated memory + * @ingroup websocket + */ +typedef void* +(*MHD_WebSocketMallocCallback) (size_t len); +/** + * This method is called by many websocket + * functions for reallocating data. + * By default 'realloc' is used. + * This can be used for operating systems like Windows + * where malloc, realloc and free are compiler dependent. + * + * @param cls closure + * @param len new size + * @return reallocated memory + * @ingroup websocket + */ +typedef void* +(*MHD_WebSocketReallocCallback) (void *cls, size_t len); +/** + * This method is called by many websocket + * functions for freeing data. + * By default 'free' is used. + * This can be used for operating systems like Windows + * where malloc, realloc and free are compiler dependent. + * + * @param cls closure + * @ingroup websocket + */ +typedef void +(*MHD_WebSocketFreeCallback) (void *cls); + +/** + * Creates the response value for the incoming 'Sec-WebSocket-Key' header. + * The generated value must be sent to the client as 'Sec-WebSocket-Accept' response header. + * + * @param sec_websocket_key The value of the 'Sec-WebSocket-Key' request header + * @param[out] sec_websocket_accept The response buffer, which will receive + * the generated 'Sec-WebSocket-Accept' header. + * This buffer must be at least 29 bytes long and + * will contain a terminating NUL character. + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * Typically 0 on success or less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_create_accept (const char*sec_websocket_key, + char*sec_websocket_accept); + +/** + * Creates a new websocket stream, used for decoding/encoding. + * + * @param[out] ws The websocket stream + * @param flags Combination of `enum MHD_WEBSOCKET_FLAG` values + * to modify the behavior of the websocket stream. + * @param max_message_size The maximum size for incoming payload + * data in bytes. Use 0 to allow each size. + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * Typically 0 on success or less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_stream_init (struct MHD_WebSocketStream**ws, + int flags, + size_t max_payload_size); + +/** + * Creates a new websocket stream, used for decoding/encoding, + * but with custom memory functions for malloc, realloc and free. + * + * @param[out] ws The websocket stream + * @param flags Combination of `enum MHD_WEBSOCKET_FLAG` values + * to modify the behavior of the websocket stream. + * @param max_message_size The maximum size for incoming payload + * data in bytes. Use 0 to allow each size. + * @param callback_malloc The callback function for 'malloc'. + * @param callback_realloc The callback function for 'realloc'. + * @param callback_free The callback function for 'free'. + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * Typically 0 on success or less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws, + int flags, + size_t max_payload_size, + MHD_WebSocketMallocCallback callback_malloc, + MHD_WebSocketReallocCallback callback_realloc, + MHD_WebSocketFreeCallback callback_free); + +/** + * Frees a websocket stream + * + * @param ws The websocket stream. This value may be NULL. + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * Typically 0 on success or less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_stream_free (struct MHD_WebSocketStream*ws); + +/** + * Invalidates a websocket stream. + * After invalidation a websocket stream cannot be used for decoding anymore. + * Encoding is still possible. + * + * @param ws The websocket stream. + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * Typically 0 on success or less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws); + +/** + * Queries whether a websocket stream is valid. + * Invalidated websocket streams cannot be used for decoding anymore. + * Encoding is still possible. + * + * @param ws The websocket stream. + * @return A value of `enum MHD_WEBSOCKET_VALIDITY`. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws); + +/** + * Decodes a byte sequence via this websocket stream. + * Decoding is done until either a frame is complete or + * the end of the byte sequence is reached. + * + * @param ws The websocket stream. + * @param streambuf The byte sequence for decoding. + * Typically that what you received via `recv()`. + * @param streambuf_len The length of the byte sequence @a streambuf + * @param[out] streambuf_read_len The number of bytes which has been processed + * by this call. This value may be less + * than @a streambuf_len when a frame is decoded + * before the end of the buffer is reached. + * The remaining bytes of @a buf must be passed + * in the following decoding. + * @param[out] payload This variable receives a buffer with the decoded + * payload data. + * If no decoded data is available this is NULL. + * When this variable is not NULL then + * the buffer contains always @a payload_len bytes plus + * one terminating NUL character. + * The caller must free this buffer + * using #MHD_websocket_free(). + * If you passed the flag + * #MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR + * upon creation of this websocket stream and + * a decoding error occurred + * (return value less than 0), then this + * buffer contains a generated close frame + * which must be sent via the socket to the recipient. + * If you passed the flag #MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS + * upon creation of this websocket stream then + * this payload may only be a part of the complete message. + * Only complete UTF-8 sequences are returned + * for fragmented text frames. + * If necessary the UTF-8 sequence will be completed + * with the next text fragment. + * @param[out] payload_len The length of the result payload buffer in bytes. + * + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * This is greater than 0 if a frame has is complete, equal to 0 if more data + * is needed an less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_decode (struct MHD_WebSocketStream*ws, + const char*streambuf, + size_t streambuf_len, + size_t*streambuf_read_len, + char**payload, + size_t*payload_len); + +/** + * Splits the payload of of a decoded close frame. + * + * @param payload The payload of the close frame. + * This parameter may be NULL if @a payload_len is 0. + * @param payload_len The length of @a payload. + * @param[out] reason_code The numeric close reason. + * If there was no close reason, this is + * #MHD_WEBSOCKET_CLOSEREASON_NO_REASON. + * Compare with `enum MHD_WEBSOCKET_CLOSEREASON`. + * This parameter is optional and can be NULL. + * @param[out] reason_utf8 The literal close reason. + * If there was no literal close reason, this is NULL. + * This parameter is optional and can be NULL. + * Please note that no memory is allocated + * in this function. + * If not NULL the returned value of this parameter + * points to a position in the specified @a payload. + * @param[out] reason_utf8_len The length of the literal close reason. + * If there was no literal close reason, this is 0. + * This parameter is optional and can be NULL. + * + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success + * or a value less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_split_close_reason (const char*payload, + size_t payload_len, + unsigned short*reason_code, + const char**reason_utf8, + size_t*reason_utf8_len); + +/** + * Encodes an UTF-8 encoded text into websocket text frame. + * + * @param ws The websocket stream. + * @param payload_utf8 The UTF-8 encoded text to send. + * This can be NULL if payload_utf8_len is 0. + * @param payload_utf8_len The length of the UTF-8 encoded text in bytes. + * @param fragmentation A value of `enum MHD_WEBSOCKET_FRAGMENTATION` + * to specifiy the fragmentation behavior. + * Specify MHD_WEBSOCKET_FRAGMENTATION_NONE + * if you don't want to use fragmentation. + * @param[out] frame This variable receives a buffer with the encoded frame. + * This is what you typically send via `send()` to the recipient. + * If no encoded data is available this is NULL. + * When this variable is not NULL then the buffer contains always + * @a frame_len bytes plus one terminating NUL character. + * The caller must free this buffer using #MHD_websocket_free(). + * @param[out] frame_len The length of the encoded frame in bytes. + * @param[out] utf8_step This parameter is required for fragmentation and + * can be NULL if no fragmentation is used. + * It contains information about the last encoded + * UTF-8 sequence and is required to continue a previous + * UTF-8 sequence when fragmentation is used. + * The `enum MHD_WEBSOCKET_UTF8STEP` is for this. + * If you start a new fragment using + * MHD_WEBSOCKET_FRAGMENTATION_NONE or + * MHD_WEBSOCKET_FRAGMENTATION_FIRST the value + * of this variable will be initialized + * to MHD_WEBSOCKET_UTF8STEP_NORMAL. + * + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success + * or a value less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_encode_text (struct MHD_WebSocketStream*ws, + const char*payload_utf8, + size_t payload_utf8_len, + int fragmentation, + char**frame, + size_t*frame_len, + int*utf8_step); + +/** + * Encodes a binary data into websocket binary frame. + * + * @param ws The websocket stream. + * @param payload The binary data to send. + * @param payload_len The length of the binary data in bytes. + * @param fragmentation A value of `enum MHD_WEBSOCKET_FRAGMENTATION` + * to specifiy the fragmentation behavior. + * Specify MHD_WEBSOCKET_FRAGMENTATION_NONE + * if you don't want to use fragmentation. + * @param[out] frame This variable receives a buffer with + * the encoded binary frame. + * This is what you typically send via `send()` + * to the recipient. + * If no encoded frame is available this is NULL. + * When this variable is not NULL then the allocated buffer + * contains always @a frame_len bytes plus one terminating + * NUL character. + * The caller must free this buffer using #MHD_websocket_free(). + * @param[out] frame_len The length of the result frame buffer in bytes. + * + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success + * or a value less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws, + const char*payload, + size_t payload_len, + int fragmentation, + char**frame, + size_t*frame_len); + +/** + * Encodes a websocket ping frame + * + * @param ws The websocket stream. + * @param payload The binary ping payload data to send. + * This may be NULL if @a payload_len is 0. + * @param payload_len The length of the payload data in bytes. + * This may not exceed 125 bytes. + * @param[out] frame This variable receives a buffer with the encoded ping frame data. + * This is what you typically send via `send()` to the recipient. + * If no encoded frame is available this is NULL. + * When this variable is not NULL then the buffer contains always + * @a frame_len bytes plus one terminating NUL character. + * The caller must free this buffer using #MHD_websocket_free(). + * @param[out] frame_len The length of the result frame buffer in bytes. + * + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success + * or a value less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws, + const char*payload, + size_t payload_len, + char**frame, + size_t*frame_len); + +/** + * Encodes a websocket pong frame + * + * @param ws The websocket stream. + * @param payload The binary pong payload data, which is typically + * the decoded payload from the received ping frame. + * This may be NULL if @a payload_len is 0. + * @param payload_len The length of the payload data in bytes. + * This may not exceed 125 bytes. + * @param[out] frame This variable receives a buffer with + * the encoded pong frame data. + * This is what you typically send via `send()` + * to the recipient. + * If no encoded frame is available this is NULL. + * When this variable is not NULL then the buffer + * contains always @a frame_len bytes plus one + * terminating NUL character. + * The caller must free this buffer + * using #MHD_websocket_free(). + * @param[out] frame_len The length of the result frame buffer in bytes. + * + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success + * or a value less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws, + const char*payload, + size_t payload_len, + char**frame, + size_t*frame_len); + +/** + * Encodes a websocket close frame + * + * @param ws The websocket stream. + * @param reason_code The reason for close. + * You can use `enum MHD_WEBSOCKET_CLOSEREASON` + * for typical reasons, + * but you are not limited to these values. + * The allowed values are specified in RFC 6455 7.4. + * If you don't want to enter a reason, you can specify + * #MHD_WEBSOCKET_CLOSEREASON_NO_REASON then + * no reason is encoded. + * @param reason_utf8 An UTF-8 encoded text reason why the connection is closed. + * This may be NULL if @a reason_utf8_len is 0. + * This must be NULL if @a reason_code is + * #MHD_WEBSOCKET_CLOSEREASON_NO_REASON (= 0). + * @param reason_utf8_len The length of the UTF-8 encoded text reason in bytes. + * This may not exceed 123 bytes. + * @param[out] frame This variable receives a buffer with + * the encoded close frame. + * This is what you typically send via `send()` + * to the recipient. + * If no encoded frame is available this is NULL. + * When this variable is not NULL then the buffer + * contains always @a frame_len bytes plus + * one terminating NUL character. + * The caller must free this buffer + * using #MHD_websocket_free(). + * @param[out] frame_len The length of the result frame buffer in bytes. + * + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success + * or a value less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_encode_close (struct MHD_WebSocketStream*ws, + unsigned short reason_code, + const char*reason_utf8, + size_t reason_utf8_len, + char**frame, + size_t*frame_len); + +/** + * Sets the seed for the random number generated used for + * the generation of masked frames (this is only used for client websockets). + * This seed is used for all websocket streams. + * Internally `srand()` is called. + * Please note that on some situations + * (where `rand()` and `srand()` are shared between your program + * and this library) this could cause unwanted results in your program if + * your program relies on a specific seed. + * + * @param seed The seed used for the initialization of + * the pseudo random number generator. + * Typically `time(NULL)` is used here to + * generate a seed. + * + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success + * or a value less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_srand (unsigned long seed); + +/** + * Allocates memory with the associated 'malloc' function + * of the websocket stream + * + * @param ws The websocket stream. + * @param len The length of the memory to allocate in bytes + * + * @return The allocated memory on success or NULL on failure. + * @ingroup websocket + */ +_MHD_EXTERN void* +MHD_websocket_malloc (struct MHD_WebSocketStream*ws, + size_t len); + +/** + * Reallocates memory with the associated 'realloc' function + * of the websocket stream + * + * @param ws The websocket stream. + * @param cls The previously allocated memory or NULL + * @param len The new length of the memory in bytes + * + * @return The allocated memory on success or NULL on failure. + * If NULL is returned the previously allocated buffer + * remains valid. + * @ingroup websocket + */ +_MHD_EXTERN void* +MHD_websocket_realloc (struct MHD_WebSocketStream*ws, + void*cls, + size_t len); + +/** + * Frees memory with the associated 'free' function + * of the websocket stream + * + * @param ws The websocket stream. + * @param cls The previously allocated memory or NULL + * + * @return A value of `enum MHD_WEBSOCKET_STATUS`. + * This is #MHD_WEBSOCKET_STATUS_OK (= 0) on success + * or a value less than 0 on errors. + * @ingroup websocket + */ +_MHD_EXTERN int +MHD_websocket_free (struct MHD_WebSocketStream*ws, + void*cls); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am @@ -80,6 +80,7 @@ libmicrohttpd_la_SOURCES += \ mhd_locks.h endif + libmicrohttpd_la_CPPFLAGS = \ $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) \ -DBUILDING_MHD_LIB=1 @@ -92,6 +93,9 @@ libmicrohttpd_la_LDFLAGS = \ libmicrohttpd_la_LIBADD = \ $(MHD_LIBDEPS) $(MHD_TLS_LIBDEPS) + + + if HAVE_W32 MHD_DLL_RES_SRC = microhttpd_dll_res.rc MHD_DLL_RES_LO = libmicrohttpd_la-$(MHD_DLL_RES_SRC:.rc=.lo) diff --git a/src/microhttpd_ws/Makefile.am b/src/microhttpd_ws/Makefile.am @@ -0,0 +1,39 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/microhttpd + +AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS) + +noinst_DATA = +MOSTLYCLEANFILES = + +SUBDIRS = . + +lib_LTLIBRARIES = \ + libmicrohttpd_ws.la +libmicrohttpd_ws_la_SOURCES = \ + sha1.c sha1.h \ + mhd_websocket.c +libmicrohttpd_ws_la_CPPFLAGS = \ + $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) \ + -DBUILDING_MHD_LIB=1 +libmicrohttpd_ws_la_CFLAGS = \ + $(AM_CFLAGS) $(MHD_LIB_CFLAGS) +libmicrohttpd_ws_la_LDFLAGS = \ + $(MHD_LIB_LDFLAGS) \ + $(W32_MHD_LIB_LDFLAGS) \ + -version-info 0:0:0 +libmicrohttpd_ws_la_LIBADD = \ + $(MHD_LIBDEPS) + +TESTS = $(check_PROGRAMS) + +check_PROGRAMS = \ + test_websocket + +test_websocket_SOURCES = \ + test_websocket.c +test_websocket_LDADD = \ + $(top_builddir)/src/microhttpd_ws/libmicrohttpd_ws.la \ + $(top_builddir)/src/microhttpd/libmicrohttpd.la diff --git a/src/microhttpd_ws/mhd_websocket.c b/src/microhttpd_ws/mhd_websocket.c @@ -0,0 +1,2072 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2021 David Gausmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file microhttpd_ws/mhd_websocket.c + * @brief Support for the websocket protocol + * @author David Gausmann + */ +#include "platform.h" +#include "microhttpd.h" +#include "microhttpd_ws.h" +#include "sha1.h" + +struct MHD_WebSocketStream +{ + /* The function pointer to malloc for payload (can be used to use different memory managment) */ + MHD_WebSocketMallocCallback malloc; + /* The function pointer to realloc for payload (can be used to use different memory managment) */ + MHD_WebSocketReallocCallback realloc; + /* The function pointer to free for payload (can be used to use different memory managment) */ + MHD_WebSocketFreeCallback free; + /* The flags specified upon initialization. It may alter the behavior of decoding/encoding */ + int flags; + /* The current step for the decoder. 0 means start of a frame. */ + char decode_step; + /* Specifies whether the stream is valid (1) or not (0), + if a close frame has been received this is (-1) to indicate that no data frames are allowed anymore */ + char validity; + /* The current step of the UTF-8 encoding check in the data payload */ + char data_utf8_step; + /* The current step of the UTF-8 encoding check in the control payload */ + char control_utf8_step; + /* if != 0 means that we expect a CONTINUATION frame */ + char data_type; + /* The start of the current frame (may differ from data_payload for CONTINUATION frames) */ + char*data_payload_start; + /* The buffer for the data frame */ + char*data_payload; + /* The buffer for the control frame */ + char*control_payload; + /* Configuration for the maximum allowed buffer size for payload data */ + size_t max_payload_size; + /* The current frame header size */ + size_t frame_header_size; + /* The current data payload size (can be greater than payload_size for fragmented frames) */ + size_t data_payload_size; + /* The size of the payload of the current frame (control or data) */ + size_t payload_size; + /* The processing offset to the start of the payload of the current frame (control or data) */ + size_t payload_index; + /* The frame header of the current frame (control or data) */ + char frame_header[32]; + /* The mask key of the current frame (control or data); this is 0 if no masking used */ + char mask_key[4]; +}; + +#define MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT MHD_WEBSOCKET_FLAG_CLIENT +#define MHD_WEBSOCKET_FLAG_MASK_FRAGMENTATION \ + MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS +#define MHD_WEBSOCKET_FLAG_MASK_GENERATE_CLOSE_FRAMES \ + MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR +#define MHD_WEBSOCKET_FLAG_MASK_ALL \ + (MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT \ + | MHD_WEBSOCKET_FLAG_MASK_FRAGMENTATION \ + | MHD_WEBSOCKET_FLAG_MASK_GENERATE_CLOSE_FRAMES) + +enum MHD_WebSocket_Opcode +{ + MHD_WebSocket_Opcode_Continuation = 0x0, + MHD_WebSocket_Opcode_Text = 0x1, + MHD_WebSocket_Opcode_Binary = 0x2, + MHD_WebSocket_Opcode_Close = 0x8, + MHD_WebSocket_Opcode_Ping = 0x9, + MHD_WebSocket_Opcode_Pong = 0xA +}; + +enum MHD_WebSocket_DecodeStep +{ + MHD_WebSocket_DecodeStep_Start = 0, + MHD_WebSocket_DecodeStep_Length1ofX = 1, + MHD_WebSocket_DecodeStep_Length1of2 = 2, + MHD_WebSocket_DecodeStep_Length2of2 = 3, + MHD_WebSocket_DecodeStep_Length1of8 = 4, + MHD_WebSocket_DecodeStep_Length2of8 = 5, + MHD_WebSocket_DecodeStep_Length3of8 = 6, + MHD_WebSocket_DecodeStep_Length4of8 = 7, + MHD_WebSocket_DecodeStep_Length5of8 = 8, + MHD_WebSocket_DecodeStep_Length6of8 = 9, + MHD_WebSocket_DecodeStep_Length7of8 = 10, + MHD_WebSocket_DecodeStep_Length8of8 = 11, + MHD_WebSocket_DecodeStep_Mask1Of4 = 12, + MHD_WebSocket_DecodeStep_Mask2Of4 = 13, + MHD_WebSocket_DecodeStep_Mask3Of4 = 14, + MHD_WebSocket_DecodeStep_Mask4Of4 = 15, + MHD_WebSocket_DecodeStep_HeaderCompleted = 16, + MHD_WebSocket_DecodeStep_PayloadOfDataFrame = 17, + MHD_WebSocket_DecodeStep_PayloadOfControlFrame = 18, + MHD_WebSocket_DecodeStep_BrokenStream = 99 +}; + +enum MHD_WebSocket_UTF8Result +{ + MHD_WebSocket_UTF8Result_Invalid = 0, + MHD_WebSocket_UTF8Result_Valid = 1, + MHD_WebSocket_UTF8Result_Incomplete = 2 +}; + +#define htonll(x) \ + ((1 == htonl (1)) ? (x) : ((uint64_t) htonl ((x) & 0xFFFFFFFF) << 32) \ + | htonl ((x) >> 32)) +#define ntohll(x) \ + ((1 == ntohl (1)) ? (x) : ((uint64_t) ntohl ((x) & 0xFFFFFFFF) << 32) \ + | ntohl ((x) >> 32)) + +static void +MHD_websocket_copy_payload (char*dst, + const char*src, + size_t len, + unsigned long mask, + unsigned long mask_offset); + +static int +MHD_websocket_check_utf8 (const char*buf, + size_t buf_len, + int*utf8_step, + size_t*buf_offset); + +static int +MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws, + char**payload, + size_t*payload_len); + +static int +MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws, + char**payload, + size_t*payload_len); + +static char +MHD_websocket_encode_is_masked (struct MHD_WebSocketStream *ws); +static char +MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws, + size_t payload_len); + +static int +MHD_websocket_encode_data (struct MHD_WebSocketStream*ws, + const char*payload, + size_t payload_len, + int fragmentation, + char**frame, + size_t*frame_len, + char opcode); + +static int +MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws, + const char*payload, + size_t payload_len, + char**frame, + size_t*frame_len, + char opcode); + +static unsigned long +MHD_websocket_generate_mask (); + +/** + * Creates the response for the Sec-WebSocket-Accept header + */ +_MHD_EXTERN int +MHD_websocket_create_accept (const char*sec_websocket_key, + char*sec_websocket_accept) +{ + /* initialize output variables for errors cases */ + if (NULL != sec_websocket_accept) + *sec_websocket_accept = 0; + + /* validate parameters */ + if ((NULL == sec_websocket_key) || + (NULL == sec_websocket_accept) ) + { + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + } + + /* build SHA1 hash of the given key and the UUID appended */ + char sha1[20]; + const char*suffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + int length = (int) strlen (sec_websocket_key); + struct sha1_ctx ctx; + sha1_init_ctx (&ctx); + sha1_process_bytes (sec_websocket_key, length, &ctx); + sha1_process_bytes (suffix, 36, &ctx); + sha1_finish_ctx (&ctx, sha1); + + /* base64 encode that SHA1 hash */ + /* (simple algorithm here; SHA1 has always 20 bytes, */ + /* which will always result in a 28 bytes base64 hash) */ + const char*base64_encoding_table = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + for (int i = 0, j = 0; i < 20;) + { + uint32_t octet_a = i < 20 ? (unsigned char) sha1[i++] : 0; + uint32_t octet_b = i < 20 ? (unsigned char) sha1[i++] : 0; + uint32_t octet_c = i < 20 ? (unsigned char) sha1[i++] : 0; + uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + sec_websocket_accept[j++] = base64_encoding_table[(triple >> 3 * 6) & 0x3F]; + sec_websocket_accept[j++] = base64_encoding_table[(triple >> 2 * 6) & 0x3F]; + sec_websocket_accept[j++] = base64_encoding_table[(triple >> 1 * 6) & 0x3F]; + sec_websocket_accept[j++] = base64_encoding_table[(triple >> 0 * 6) & 0x3F]; + + } + sec_websocket_accept[27] = '='; + sec_websocket_accept[28] = 0; + + return MHD_WEBSOCKET_STATUS_OK; +} + + +/** + * Initializes a new websocket stream + */ +_MHD_EXTERN int +MHD_websocket_stream_init (struct MHD_WebSocketStream**ws, + int flags, + size_t max_payload_size) +{ + return MHD_websocket_stream_init2 (ws, + flags, + max_payload_size, + malloc, + realloc, + free); +} + + +/** + * Initializes a new websocket stream with + * additional parameters for allocation functions + */ +_MHD_EXTERN int +MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws, + int flags, + size_t max_payload_size, + MHD_WebSocketMallocCallback callback_malloc, + MHD_WebSocketReallocCallback callback_realloc, + MHD_WebSocketFreeCallback callback_free) +{ + /* initialize output variables for errors cases */ + if (NULL != ws) + *ws = NULL; + + /* validate parameters */ + if ((NULL == ws) || + (0 != (flags & ~MHD_WEBSOCKET_FLAG_MASK_ALL)) || + ((uint64_t) 0x7FFFFFFFFFFFFFFF < max_payload_size) || + (NULL == callback_malloc) || + (NULL == callback_realloc) || + (NULL == callback_free) ) + { + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + } + + /* allocate stream */ + struct MHD_WebSocketStream*ws_ = (struct MHD_WebSocketStream*) malloc ( + sizeof (struct MHD_WebSocketStream)); + if (NULL == ws_) + return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; + + /* initialize stream */ + memset (ws_, 0, sizeof (struct MHD_WebSocketStream)); + ws_->flags = flags; + ws_->max_payload_size = max_payload_size; + ws_->malloc = callback_malloc; + ws_->realloc = callback_realloc; + ws_->free = callback_free; + ws_->validity = MHD_WEBSOCKET_VALIDITY_VALID; + + /* return stream */ + *ws = ws_; + + return MHD_WEBSOCKET_STATUS_OK; +} + + +/** + * Frees a previously allocated websocket stream + */ +_MHD_EXTERN int +MHD_websocket_stream_free (struct MHD_WebSocketStream*ws) +{ + /* validate parameters */ + if (NULL == ws) + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + + /* free allocated payload data */ + if (ws->data_payload) + ws->free (ws->data_payload); + if (ws->control_payload) + ws->free (ws->control_payload); + + /* free the stream */ + free (ws); + + return MHD_WEBSOCKET_STATUS_OK; +} + + +/** + * Invalidates a websocket stream (no more decoding possible) + */ +_MHD_EXTERN int +MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws) +{ + /* validate parameters */ + if (NULL == ws) + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + + /* invalidate stream */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + + return MHD_WEBSOCKET_STATUS_OK; +} + + +/** + * Returns whether a websocket stream is valid + */ +_MHD_EXTERN int +MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws) +{ + /* validate parameters */ + if (NULL == ws) + return MHD_WEBSOCKET_VALIDITY_INVALID; + + return ws->validity; +} + + +/** + * Decodes incoming data to a websocket frame + */ +_MHD_EXTERN int +MHD_websocket_decode (struct MHD_WebSocketStream*ws, + const char*streambuf, + size_t streambuf_len, + size_t*streambuf_read_len, + char**payload, + size_t*payload_len) +{ + /* initialize output variables for errors cases */ + if (NULL != streambuf_read_len) + *streambuf_read_len = 0; + if (NULL != payload) + *payload = NULL; + if (NULL != payload_len) + *payload_len = 0; + + /* validate parameters */ + if ((NULL == ws) || + (NULL == streambuf) && (0 != streambuf_len) || + (NULL == streambuf_read_len) || + (NULL == payload) || + (NULL == payload_len) ) + { + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + } + + /* validate stream validity */ + if (MHD_WEBSOCKET_VALIDITY_INVALID == ws->validity) + return MHD_WEBSOCKET_STATUS_STREAM_BROKEN; + + /* decode loop */ + size_t current = 0; + while (current < streambuf_len) + { + switch (ws->decode_step) + { + /* start of frame */ + case MHD_WebSocket_DecodeStep_Start: + { + /* The first byte contains the opcode, the fin flag and three reserved bits */ + if (MHD_WEBSOCKET_VALIDITY_INVALID != ws->validity) + { + char opcode = streambuf [current]; + if (0 != (opcode & 0x70)) + { + /* RFC 6455 5.2 RSV1-3: If a reserved flag is set */ + /* (while it isn't specified by an extension) the communcation must fail. */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + switch (opcode & 0x0F) + { + case MHD_WebSocket_Opcode_Continuation: + if (0 == ws->data_type) + { + /* RFC 6455 5.4: Continuation frame without previous data frame */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + if (MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES == + ws->validity) + { + /* RFC 6455 5.5.1: After a close frame has been sent, */ + /* no data frames may be sent (so we don't accept data frames */ + /* for decoding anymore) */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + break; + + case MHD_WebSocket_Opcode_Text: + case MHD_WebSocket_Opcode_Binary: + if (0 != ws->data_type) + { + /* RFC 6455 5.4: Continuation expected, but new data frame */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + if (MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES == + ws->validity) + { + /* RFC 6455 5.5.1: After a close frame has been sent, */ + /* no data frames may be sent (so we don't accept data frames */ + /* for decoding anymore) */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + break; + + case MHD_WebSocket_Opcode_Close: + case MHD_WebSocket_Opcode_Ping: + case MHD_WebSocket_Opcode_Pong: + if ((opcode & 0x80) == 0) + { + /* RFC 6455 5.4: Control frames may not be fragmented */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + if (MHD_WebSocket_Opcode_Close == (opcode & 0x0F)) + { + /* RFC 6455 5.5.1: After a close frame has been sent, */ + /* no data frames may be sent (so we don't accept data frames */ + /* for decoding anymore) */ + ws->validity = + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES; + } + break; + + default: + /* RFC 6455 5.2 OPCODE: Only six opcodes are specified. */ + /* All other are invalid in version 13 of the protocol. */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + } + ws->frame_header [ws->frame_header_size++] = streambuf [current++]; + ws->decode_step = MHD_WebSocket_DecodeStep_Length1ofX; + } + break; + + case MHD_WebSocket_DecodeStep_Length1ofX: + { + /* The second byte specifies whether the data is masked and the size */ + /* (the client MUST mask the payload, the server MUST NOT mask the payload) */ + char frame_len = streambuf [current]; + char is_masked = (frame_len & 0x80); + frame_len &= 0x7f; + if (MHD_WEBSOCKET_VALIDITY_INVALID != ws->validity) + { + if (0 != is_masked) + { + if (MHD_WEBSOCKET_FLAG_CLIENT == (ws->flags + & MHD_WEBSOCKET_FLAG_CLIENT)) + { + /* RFC 6455 5.1: All frames from the server must be unmasked */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + } + else + { + if (MHD_WEBSOCKET_FLAG_SERVER == (ws->flags + & MHD_WEBSOCKET_FLAG_CLIENT)) + { + /* RFC 6455 5.1: All frames from the client must be masked */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + } + if (126 <= frame_len) + { + if (0 != (ws->frame_header [0] & 0x08)) + { + /* RFC 6455 5.5: Control frames may not have more payload than 125 bytes */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + } + if (1 == frame_len) + { + if (MHD_WebSocket_Opcode_Close == (ws->frame_header [0] & 0x0F)) + { + /* RFC 6455 5.5.1: The close frame must have at least */ + /* two bytes of payload if payload is used */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + } + } + ws->frame_header [ws->frame_header_size++] = streambuf [current++]; + + if (126 == frame_len) + { + ws->decode_step = MHD_WebSocket_DecodeStep_Length1of2; + } + else if (127 == frame_len) + { + ws->decode_step = MHD_WebSocket_DecodeStep_Length1of8; + } + else + { + size_t size = (size_t) frame_len; + if ((SIZE_MAX < size) || ws->max_payload_size && + (ws->max_payload_size < size) ) + { + /* RFC 6455 7.4.1 1009: If the message is too big to process, we may close the connection */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; + } + ws->payload_size = size; + if (0 != is_masked) + { + /* with mask */ + ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4; + } + else + { + /* without mask */ + *((unsigned long *) ws->mask_key) = 0; + ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted; + } + } + } + break; + + /* Payload size first byte of 2 bytes */ + case MHD_WebSocket_DecodeStep_Length1of2: + /* Payload size first 7 bytes of 8 bytes */ + case MHD_WebSocket_DecodeStep_Length1of8: + case MHD_WebSocket_DecodeStep_Length2of8: + case MHD_WebSocket_DecodeStep_Length3of8: + case MHD_WebSocket_DecodeStep_Length4of8: + case MHD_WebSocket_DecodeStep_Length5of8: + case MHD_WebSocket_DecodeStep_Length6of8: + case MHD_WebSocket_DecodeStep_Length7of8: + /* Mask first 3 bytes of 4 bytes */ + case MHD_WebSocket_DecodeStep_Mask1Of4: + case MHD_WebSocket_DecodeStep_Mask2Of4: + case MHD_WebSocket_DecodeStep_Mask3Of4: + ws->frame_header [ws->frame_header_size++] = streambuf [current++]; + ++ws->decode_step; + break; + + /* 2 byte length finished */ + case MHD_WebSocket_DecodeStep_Length2of2: + { + ws->frame_header [ws->frame_header_size++] = streambuf [current++]; + size_t size = (size_t) htons (*((unsigned + short*) &ws->frame_header [2])); + if (125 >= size) + { + /* RFC 6455 5.2 Payload length: The minimal number of bytes */ + /* must be used for the length */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size < + size) ) + { + /* RFC 6455 7.4.1 1009: If the message is too big to process, */ + /* we may close the connection */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; + } + ws->payload_size = size; + if (0 != (ws->frame_header [1] & 0x80)) + { + /* with mask */ + ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4; + } + else + { + /* without mask */ + *((unsigned long *) ws->mask_key) = 0; + ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted; + } + } + break; + + /* 8 byte length finished */ + case MHD_WebSocket_DecodeStep_Length8of8: + { + ws->frame_header [ws->frame_header_size++] = streambuf [current++]; + uint64_t size = htonll (*((uint64_t*) &ws->frame_header [2])); + if (0x7fffffffffffffff < size) + { + /* RFC 6455 5.2 frame-payload-length-63: The length may */ + /* not exceed 0x7fffffffffffffff */ + ws->decode_step = MHD_WebSocket_DecodeStep_BrokenStream; + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + if (65535 >= size) + { + /* RFC 6455 5.2 Payload length: The minimal number of bytes */ + /* must be used for the length */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + } + if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size < + size) ) + { + /* RFC 6455 7.4.1 1009: If the message is too big to process, */ + /* we may close the connection */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; + } + ws->payload_size = (size_t) size; + + if (0 != (ws->frame_header [1] & 0x80)) + { + /* with mask */ + ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4; + } + else + { + /* without mask */ + *((unsigned long *) ws->mask_key) = 0; + ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted; + } + } + break; + + /* mask finished */ + case MHD_WebSocket_DecodeStep_Mask4Of4: + ws->frame_header [ws->frame_header_size++] = streambuf [current++]; + *((unsigned long *) ws->mask_key) = *((unsigned + long *) &ws->frame_header [ws-> + frame_header_size + - 4]); + ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted; + break; + + /* header finished */ + case MHD_WebSocket_DecodeStep_HeaderCompleted: + /* return or assign either to data or control */ + { + int ret = MHD_websocket_decode_header_complete (ws, + payload, + payload_len); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + *streambuf_read_len = current; + return ret; + } + } + break; + + /* payload data */ + case MHD_WebSocket_DecodeStep_PayloadOfDataFrame: + case MHD_WebSocket_DecodeStep_PayloadOfControlFrame: + { + size_t bytes_needed = ws->payload_size - ws->payload_index; + size_t bytes_remaining = streambuf_len - current; + size_t bytes_to_take = bytes_needed < bytes_remaining ? bytes_needed : + bytes_remaining; + if (0 != bytes_to_take) + { + size_t utf8_start = ws->payload_index; + char *decode_payload = ws->decode_step == + MHD_WebSocket_DecodeStep_PayloadOfDataFrame ? + ws->data_payload_start : + ws->control_payload; + + /* copy the new payload data (with unmasking if necessary */ + MHD_websocket_copy_payload (decode_payload + ws->payload_index, + &streambuf [current], + bytes_to_take, + *((unsigned long *) ws->mask_key), + (unsigned long) (ws->payload_index + & 0x03)); + current += bytes_to_take; + ws->payload_index += bytes_to_take; + if ((MHD_WebSocket_DecodeStep_PayloadOfDataFrame == + ws->decode_step) && + (MHD_WebSocket_Opcode_Text == ws->data_type) || + (MHD_WebSocket_DecodeStep_PayloadOfControlFrame == + ws->decode_step) && + (MHD_WebSocket_Opcode_Close == (ws->frame_header [0] & 0x0f)) && + (2 < ws->payload_index) ) + { + /* RFC 6455 8.1: We need to check the UTF-8 validity */ + int utf8_step; + char*decode_payload_utf8; + size_t bytes_to_check; + size_t utf8_error_offset = 0; + if (MHD_WebSocket_DecodeStep_PayloadOfDataFrame == ws->decode_step) + { + utf8_step = ws->data_utf8_step; + decode_payload_utf8 = decode_payload + utf8_start; + bytes_to_check = bytes_to_take; + } + else + { + utf8_step = ws->control_utf8_step; + if ((MHD_WebSocket_Opcode_Close == (ws->frame_header [0] + & 0x0f)) && + (2 > utf8_start) ) + { + /* The first two bytes of the close frame are binary content and */ + /* must be skipped in the UTF-8 check */ + utf8_start = 2; + utf8_error_offset = 2; + } + decode_payload_utf8 = decode_payload + utf8_start; + bytes_to_check = bytes_to_take - utf8_start; + } + size_t utf8_check_offset = 0; + int utf8_result = MHD_websocket_check_utf8 (decode_payload_utf8, + bytes_to_check, + &utf8_step, + &utf8_check_offset); + if (MHD_WebSocket_UTF8Result_Invalid != utf8_result) + { + /* memorize current validity check step to continue later */ + ws->data_utf8_step = utf8_step; + } + else + { + /* RFC 6455 8.1: We must fail on broken UTF-8 sequence */ + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_MALFORMED_UTF8, + 0, + 0, + payload, + payload_len); + } + *streambuf_read_len = current - bytes_to_take + + utf8_check_offset + utf8_error_offset; + return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR; + } + } + } + } + + if (ws->payload_size == ws->payload_index) + { + /* all payload data of the current frame has been received */ + int ret = MHD_websocket_decode_payload_complete (ws, + payload, + payload_len); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + *streambuf_read_len = current; + return ret; + } + } + break; + + case MHD_WebSocket_DecodeStep_BrokenStream: + *streambuf_read_len = current; + return MHD_WEBSOCKET_STATUS_STREAM_BROKEN; + } + } + + /* Special treatment for zero payload length messages */ + if (MHD_WebSocket_DecodeStep_HeaderCompleted == ws->decode_step) + { + int ret = MHD_websocket_decode_header_complete (ws, + payload, + payload_len); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + *streambuf_read_len = current; + return ret; + } + } + switch (ws->decode_step) + { + case MHD_WebSocket_DecodeStep_PayloadOfDataFrame: + case MHD_WebSocket_DecodeStep_PayloadOfControlFrame: + if (ws->payload_size == ws->payload_index) + { + /* all payload data of the current frame has been received */ + int ret = MHD_websocket_decode_payload_complete (ws, + payload, + payload_len); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + *streambuf_read_len = current; + return ret; + } + } + break; + } + *streambuf_read_len = current; + + /* more data needed */ + return MHD_WEBSOCKET_STATUS_OK; +} + + +static int +MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws, + char**payload, + size_t*payload_len) +{ + /* assign either to data or control */ + char opcode = ws->frame_header [0] & 0x0f; + switch (opcode) + { + case MHD_WebSocket_Opcode_Continuation: + { + /* validate payload size */ + size_t new_size_total = ws->payload_size + ws->data_payload_size; + if ((0 != ws->max_payload_size) && (ws->max_payload_size < + new_size_total) ) + { + /* RFC 6455 7.4.1 1009: If the message is too big to process, */ + /* we may close the connection */ + ws->decode_step = MHD_WebSocket_DecodeStep_BrokenStream; + ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; + if (0 != (ws->flags + & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) + { + MHD_websocket_encode_close (ws, + MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED, + 0, + 0, + payload, + payload_len); + } + return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; + } + /* allocate buffer for continued data frame */ + char*new_buf = NULL; + if (0 != new_size_total) + { + new_buf = ws->realloc (ws->data_payload, new_size_total + 1); + if (NULL == new_buf) + { + return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; + } + new_buf [new_size_total] = 0; + ws->data_payload_start = &new_buf[ws->data_payload_size]; + } + else + { + ws->data_payload_start = new_buf; + } + ws->data_payload = new_buf; + ws->data_payload_size = new_size_total; + } + ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfDataFrame; + break; + + case MHD_WebSocket_Opcode_Text: + case MHD_WebSocket_Opcode_Binary: + /* allocate buffer for data frame */ + { + size_t new_size_total = ws->payload_size; + char*new_buf = NULL; + if (0 != new_size_total) + { + new_buf = ws->malloc (new_size_total + 1); + if (NULL == new_buf) + { + return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; + } + new_buf [new_size_total] = 0; + } + ws->data_payload = new_buf; + ws->data_payload_start = new_buf; + ws->data_payload_size = new_size_total; + ws->data_type = opcode; + } + ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfDataFrame; + break; + + case MHD_WebSocket_Opcode_Close: + case MHD_WebSocket_Opcode_Ping: + case MHD_WebSocket_Opcode_Pong: + /* allocate buffer for control frame */ + { + size_t new_size_total = ws->payload_size; + char*new_buf = NULL; + if (0 != new_size_total) + { + new_buf = ws->malloc (new_size_total + 1); + if (NULL == new_buf) + { + return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; + } + new_buf[new_size_total] = 0; + } + ws->control_payload = new_buf; + } + ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfControlFrame; + break; + } + + return MHD_WEBSOCKET_STATUS_OK; +} + + +static int +MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws, + char**payload, + size_t*payload_len) +{ + /* all payload data of the current frame has been received */ + char is_fin = ws->frame_header [0] & 0x80; + if (0 != is_fin) + { + /* the frame is complete */ + if (MHD_WebSocket_DecodeStep_PayloadOfDataFrame == ws->decode_step) + { + /* data frame */ + char data_type = ws->data_type; + if ((0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS)) && + (MHD_WebSocket_Opcode_Continuation == (ws->frame_header [0] & 0x0F))) + { + data_type |= 0x20; /* mark as last fragment */ + } + *payload = ws->data_payload; + *payload_len = ws->data_payload_size; + ws->data_payload = 0; + ws->data_payload_start = 0; + ws->data_payload_size = 0; + ws->decode_step = MHD_WebSocket_DecodeStep_Start; + ws->payload_index = 0; + ws->data_type = 0; + ws->frame_header_size = 0; + return data_type; + } + else + { + /* control frame */ + *payload = ws->control_payload; + *payload_len = ws->payload_size; + ws->control_payload = 0; + ws->decode_step = MHD_WebSocket_DecodeStep_Start; + ws->payload_index = 0; + ws->frame_header_size = 0; + return (ws->frame_header [0] & 0x0f); + } + } + else if (0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS)) + { + /* RFC 6455 5.4: To allow streaming, the user can choose */ + /* to return fragments */ + if ((MHD_WebSocket_Opcode_Text == ws->data_type) && + (MHD_WEBSOCKET_UTF8STEP_NORMAL != ws->data_utf8_step) ) + { + /* the last UTF-8 sequence is incomplete, so we keep the start of + that and only return the part before */ + size_t given_utf8; + switch (ws->data_utf8_step) + { + /* one byte given */ + case MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1: + case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2: + case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2: + case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2: + case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3: + case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3: + case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3: + given_utf8 = 1; + break; + /* two bytes given */ + case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2: + case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3: + given_utf8 = 2; + break; + /* three bytes given */ + case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3: + given_utf8 = 3; + break; + } + size_t new_len = ws->data_payload_size - given_utf8; + if (0 != new_len) + { + char *next_payload = ws->malloc (given_utf8 + 1); + if (NULL == next_payload) + { + return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; + } + memcpy (next_payload, + ws->data_payload_start + ws->payload_index - given_utf8, + given_utf8); + next_payload[given_utf8] = 0; + + ws->data_payload[new_len] = 0; + *payload = ws->data_payload; + *payload_len = new_len; + ws->data_payload = next_payload; + ws->data_payload_size = given_utf8; + } + else + { + *payload = NULL; + *payload_len = 0; + } + ws->decode_step = MHD_WebSocket_DecodeStep_Start; + ws->payload_index = 0; + ws->frame_header_size = 0; + return ws->data_type | 0x10; /* mark as fragment */ + } + else + { + /* we simply pass the entire data frame */ + *payload = ws->data_payload; + *payload_len = ws->data_payload_size; + ws->data_payload = 0; + ws->data_payload_start = 0; + ws->data_payload_size = 0; + ws->decode_step = MHD_WebSocket_DecodeStep_Start; + ws->payload_index = 0; + ws->frame_header_size = 0; + return ws->data_type | 0x10; /* mark as fragment */ + } + } + else + { + /* RFC 6455 5.4: We must await a continuation frame to get */ + /* the remainder of this data frame */ + ws->decode_step = MHD_WebSocket_DecodeStep_Start; + ws->frame_header_size = 0; + ws->payload_index = 0; + return MHD_WEBSOCKET_STATUS_OK; + } +} + + +/** + * Splits the received close reason + */ +_MHD_EXTERN int +MHD_websocket_split_close_reason (const char*payload, + size_t payload_len, + unsigned short*reason_code, + const char**reason_utf8, + size_t*reason_utf8_len) +{ + /* initialize output variables for errors cases */ + if (NULL != reason_code) + *reason_code = MHD_WEBSOCKET_CLOSEREASON_NO_REASON; + if (NULL != reason_utf8) + *reason_utf8 = NULL; + if (NULL != reason_utf8_len) + *reason_utf8_len = 0; + + /* validate parameters */ + if ((NULL == payload) && (0 != payload_len)) + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + if (1 == payload_len) + return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; + if (125 < payload_len) + return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; + + /* decode reason code */ + if (2 > payload_len) + { + if (NULL != reason_code) + *reason_code = MHD_WEBSOCKET_CLOSEREASON_NO_REASON; + } + else + { + if (NULL != reason_code) + *reason_code = htons (*((unsigned short*) payload)); + } + + /* decode reason text */ + if (2 >= payload_len) + { + if (NULL != reason_utf8) + *reason_utf8 = NULL; + if (NULL != reason_utf8_len) + *reason_utf8_len = 0; + } + else + { + if (NULL != reason_utf8) + *reason_utf8 = payload + 2; + if (NULL != reason_utf8_len) + *reason_utf8_len = payload_len - 2; + } + + return MHD_WEBSOCKET_STATUS_OK; +} + + +/** + * Encodes a text into a websocket text frame + */ +_MHD_EXTERN int +MHD_websocket_encode_text (struct MHD_WebSocketStream*ws, + const char*payload_utf8, + size_t payload_utf8_len, + int fragmentation, + char**frame, + size_t*frame_len, + int*utf8_step) +{ + /* initialize output variables for errors cases */ + if (NULL != frame) + *frame = NULL; + if (NULL != frame_len) + *frame_len = 0; + if ((NULL != utf8_step) && + ((MHD_WEBSOCKET_FRAGMENTATION_FIRST == fragmentation) || + (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation) )) + { + /* the old UTF-8 step will be ignored for new fragments */ + *utf8_step = MHD_WEBSOCKET_UTF8STEP_NORMAL; + } + + /* validate parameters */ + if ((NULL == ws) || + (0 != payload_utf8_len) && (NULL == payload_utf8) || + (NULL == frame) || + (NULL == frame_len) || + (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) || + (MHD_WEBSOCKET_FRAGMENTATION_LAST < fragmentation) || + (MHD_WEBSOCKET_FRAGMENTATION_NONE != fragmentation) && (NULL == + utf8_step) ) + { + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + } + + /* check max length */ + if ((uint64_t) 0x7FFFFFFFFFFFFFFF < (uint64_t) payload_utf8_len) + { + return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; + } + + /* check UTF-8 */ + int utf8_result = MHD_websocket_check_utf8 (payload_utf8, + payload_utf8_len, + utf8_step, + NULL); + if ((MHD_WebSocket_UTF8Result_Invalid == utf8_result) || + (MHD_WebSocket_UTF8Result_Incomplete == utf8_result) && + (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation) ) + { + return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR; + } + + /* encode data */ + return MHD_websocket_encode_data (ws, + payload_utf8, + payload_utf8_len, + fragmentation, + frame, + frame_len, + MHD_WebSocket_Opcode_Text); +} + + +/** + * Encodes binary data into a websocket binary frame + */ +_MHD_EXTERN int +MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws, + const char*payload, + size_t payload_len, + int fragmentation, + char**frame, + size_t*frame_len) +{ + /* initialize output variables for errors cases */ + if (NULL != frame) + *frame = NULL; + if (NULL != frame_len) + *frame_len = 0; + + /* validate parameters */ + if ((NULL == ws) || + (0 != payload_len) && (NULL == payload) || + (NULL == frame) || + (NULL == frame_len) || + (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) || + (MHD_WEBSOCKET_FRAGMENTATION_LAST < fragmentation) ) + { + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + } + + /* check max length */ + if ((uint64_t) 0x7FFFFFFFFFFFFFFF < (uint64_t) payload_len) + { + return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; + } + + return MHD_websocket_encode_data (ws, + payload, + payload_len, + fragmentation, + frame, + frame_len, + MHD_WebSocket_Opcode_Binary); +} + + +/** + * Internal function for encoding text/binary data into a websocket frame + */ +static int +MHD_websocket_encode_data (struct MHD_WebSocketStream*ws, + const char*payload, + size_t payload_len, + int fragmentation, + char**frame, + size_t*frame_len, + char opcode) +{ + /* calculate length and masking */ + char is_masked = MHD_websocket_encode_is_masked (ws); + size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len); + size_t total_len = overhead_len + payload_len; + unsigned long mask = 0 != is_masked ? MHD_websocket_generate_mask () : 0; + + /* allocate memory */ + char*result = ws->malloc (total_len + 1); + if (NULL == result) + return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; + result [total_len] = 0; + *frame = result; + *frame_len = total_len; + + /* add the opcode */ + switch (fragmentation) + { + case MHD_WEBSOCKET_FRAGMENTATION_NONE: + *(result++) = 0x80 | opcode; + break; + case MHD_WEBSOCKET_FRAGMENTATION_FIRST: + *(result++) = opcode; + break; + case MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING: + *(result++) = MHD_WebSocket_Opcode_Continuation; + break; + case MHD_WEBSOCKET_FRAGMENTATION_LAST: + *(result++) = 0x80 | MHD_WebSocket_Opcode_Continuation; + break; + } + + /* add the length */ + if (126 > payload_len) + { + *(result++) = is_masked | (char) payload_len; + } + else if (65536 > payload_len) + { + *(result++) = is_masked | 126; + *((unsigned short *) result) = htons ((unsigned short) payload_len); + result += 2; + } + else + { + *(result++) = is_masked | 127; + *((uint64_t *) result) = htonll ((uint64_t) payload_len); + result += 8; + + } + + /* add the mask */ + if (0 != is_masked) + { + *(result++) = ((char *) &mask)[0]; + *(result++) = ((char *) &mask)[1]; + *(result++) = ((char *) &mask)[2]; + *(result++) = ((char *) &mask)[3]; + } + + /* add the payload */ + if (0 != payload_len) + { + MHD_websocket_copy_payload (result, + payload, + payload_len, + mask, + 0); + } + + return MHD_WEBSOCKET_STATUS_OK; +} + + +/** + * Encodes a websocket ping frame + */ +_MHD_EXTERN int +MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws, + const char*payload, + size_t payload_len, + char**frame, + size_t*frame_len) +{ + /* encode the ping frame */ + return MHD_websocket_encode_ping_pong (ws, + payload, + payload_len, + frame, + frame_len, + MHD_WebSocket_Opcode_Ping); +} + + +/** + * Encodes a websocket pong frame + */ +_MHD_EXTERN int +MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws, + const char*payload, + size_t payload_len, + char**frame, + size_t*frame_len) +{ + /* encode the pong frame */ + return MHD_websocket_encode_ping_pong (ws, + payload, + payload_len, + frame, + frame_len, + MHD_WebSocket_Opcode_Pong); +} + + +/** + * Internal function for encoding ping/pong frames + */ +static int +MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws, + const char*payload, + size_t payload_len, + char**frame, + size_t*frame_len, + char opcode) +{ + /* initialize output variables for errors cases */ + if (NULL != frame) + *frame = NULL; + if (NULL != frame_len) + *frame_len = 0; + + /* validate the parameters */ + if ((NULL == ws) || + (0 != payload_len) && (NULL == payload) || + (NULL == frame) || + (NULL == frame_len) ) + { + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + } + + /* RFC 6455 5.5: Control frames may only have up to 125 bytes of payload data */ + if (125 < payload_len) + return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; + + /* calculate length and masking */ + char is_masked = MHD_websocket_encode_is_masked (ws); + size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len); + size_t total_len = overhead_len + payload_len; + unsigned long mask = is_masked != 0 ? MHD_websocket_generate_mask () : 0; + + /* allocate memory */ + char*result = ws->malloc (total_len + 1); + if (NULL == result) + return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; + result [total_len] = 0; + *frame = result; + *frame_len = total_len; + + /* add the opcode */ + *(result++) = 0x80 | opcode; + + /* add the length */ + *(result++) = is_masked | (char) payload_len; + + /* add the mask */ + if (0 != is_masked) + { + *(result++) = ((char *) &mask)[0]; + *(result++) = ((char *) &mask)[1]; + *(result++) = ((char *) &mask)[2]; + *(result++) = ((char *) &mask)[3]; + } + + /* add the payload */ + if (0 != payload_len) + { + MHD_websocket_copy_payload (result, + payload, + payload_len, + mask, + 0); + } + + return MHD_WEBSOCKET_STATUS_OK; +} + + +/** + * Encodes a websocket close frame + */ +_MHD_EXTERN int +MHD_websocket_encode_close (struct MHD_WebSocketStream*ws, + unsigned short reason_code, + const char*reason_utf8, + size_t reason_utf8_len, + char**frame, + size_t*frame_len) +{ + /* initialize output variables for errors cases */ + if (NULL != frame) + *frame = NULL; + if (NULL != frame_len) + *frame_len = 0; + + /* validate the parameters */ + if ((NULL == ws) || + (0 != reason_utf8_len) && (NULL == reason_utf8) || + (NULL == frame) || + (NULL == frame_len) || + (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) && (1000 > + reason_code) || + (0 != reason_utf8_len) && (MHD_WEBSOCKET_CLOSEREASON_NO_REASON == + reason_code) ) + { + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + } + + /* RFC 6455 5.5: Control frames may only have up to 125 bytes of payload data, */ + /* but in this case only 123 bytes, because 2 bytes are reserved */ + /* for the close reason code. */ + if (123 < reason_utf8_len) + return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; + + /* RFC 6455 5.5.1: If close payload data is given, it must be valid UTF-8 */ + if (0 != reason_utf8_len) + { + int utf8_result = MHD_websocket_check_utf8 (reason_utf8, + reason_utf8_len, + NULL, + NULL); + if (MHD_WebSocket_UTF8Result_Valid != utf8_result) + return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR; + } + + /* calculate length and masking */ + char is_masked = MHD_websocket_encode_is_masked (ws); + size_t payload_len = (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code ? + 2 + reason_utf8_len : 0); + size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len); + size_t total_len = overhead_len + payload_len; + unsigned long mask = is_masked != 0 ? MHD_websocket_generate_mask () : 0; + + /* allocate memory */ + char*result = ws->malloc (total_len + 1); + if (NULL == result) + return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; + result [total_len] = 0; + *frame = result; + *frame_len = total_len; + + /* add the opcode */ + *(result++) = 0x88; + + /* add the length */ + *(result++) = is_masked | (char) payload_len; + + /* add the mask */ + if (0 != is_masked) + { + *(result++) = ((char *) &mask)[0]; + *(result++) = ((char *) &mask)[1]; + *(result++) = ((char *) &mask)[2]; + *(result++) = ((char *) &mask)[3]; + } + + /* add the payload */ + if (0 != reason_code) + { + /* close reason code */ + unsigned short reason_code_nb = htons (reason_code); + MHD_websocket_copy_payload (result, + (const char*) &reason_code_nb, + 2, + mask, + 0); + result += 2; + + /* custom reason payload */ + if (0 != reason_utf8_len) + { + MHD_websocket_copy_payload (result, + reason_utf8, + reason_utf8_len, + mask, + 2); + } + } + + return MHD_WEBSOCKET_STATUS_OK; +} + + +/** + * Returns the 0x80 prefix for masked data, 0x00 otherwise + */ +static char +MHD_websocket_encode_is_masked (struct MHD_WebSocketStream*ws) +{ + return (ws->flags & MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT) == + MHD_WEBSOCKET_FLAG_CLIENT ? 0x80 : 0x00; +} + + +/** + * Calculates the size of the overhead in bytes + */ +static char +MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream*ws, + size_t payload_len) +{ + return 2 + (MHD_websocket_encode_is_masked (ws) != 0 ? 4 : 0) + (125 < + payload_len ? + (65535 < + payload_len + ? 8 : 2) : 0); +} + + +/** + * Copies the payload to the destination (using mask) + */ +static void +MHD_websocket_copy_payload (char*dst, + const char*src, + size_t len, + unsigned long mask, + unsigned long mask_offset) +{ + if (0 != len) + { + if (0 == mask) + { + /* when the mask is zero, we can just copy the data */ + memcpy (dst, src, len); + } + else + { + /* mask is used */ + char mask_[4]; + *((unsigned long *) mask_) = mask; + for (size_t i = 0; i < len; ++i) + { + dst[i] = src[i] ^ mask_[(i + mask_offset) & 3]; + } + } + } +} + + +/** + * Checks a UTF-8 sequence + */ +static int +MHD_websocket_check_utf8 (const char*buf, + size_t buf_len, + int*utf8_step, + size_t*buf_offset) +{ + int utf8_step_ = (NULL != utf8_step) ? *utf8_step : + MHD_WEBSOCKET_UTF8STEP_NORMAL; + + for (size_t i = 0; i < buf_len; ++i) + { + unsigned char character = (unsigned char) buf[i]; + switch (utf8_step_) + { + case MHD_WEBSOCKET_UTF8STEP_NORMAL: + if ((0x00 <= character) && (0x7F >= character)) + { + /* RFC 3629 4: single byte UTF-8 sequence */ + /* (nothing to do here) */ + } + else if ((0xC2 <= character) && (0xDF >= character)) + { + /* RFC 3629 4: two byte UTF-8 sequence */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1; + } + else if (0xE0 == character) + { + /* RFC 3629 4: three byte UTF-8 sequence, but the second byte must be 0xA0-0xBF */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2; + } + else if (0xED == character) + { + /* RFC 3629 4: three byte UTF-8 sequence, but the second byte must be 0x80-0x9F */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2; + } + else if ((0xE1 <= character) && (0xEC >= character) || + (0xEE <= character) && (0xEF >= character) ) + { + /* RFC 3629 4: three byte UTF-8 sequence, both tail bytes must be 0x80-0xBF */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2; + } + else if (0xF0 == character) + { + /* RFC 3629 4: four byte UTF-8 sequence, but the second byte must be 0x90-0xBF */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3; + } + else if (0xF4 == character) + { + /* RFC 3629 4: four byte UTF-8 sequence, but the second byte must be 0x80-0x8F */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3; + } + else if ((0xF1 <= character) && (0xF3 >= character)) + { + /* RFC 3629 4: four byte UTF-8 sequence, all three tail bytes must be 0x80-0xBF */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3; + } + else + { + /* RFC 3629 4: Invalid UTF-8 byte */ + if (NULL != buf_offset) + *buf_offset = i; + return MHD_WebSocket_UTF8Result_Invalid; + } + break; + + case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2: + if ((0xA0 <= character) && (0xBF >= character)) + { + /* RFC 3629 4: Second byte of three byte UTF-8 sequence */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2; + } + else + { + /* RFC 3629 4: Invalid UTF-8 byte */ + if (NULL != buf_offset) + *buf_offset = i; + return MHD_WebSocket_UTF8Result_Invalid; + } + break; + + case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2: + if ((0x80 <= character) && (0x9F >= character)) + { + /* RFC 3629 4: Second byte of three byte UTF-8 sequence */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2; + } + else + { + /* RFC 3629 4: Invalid UTF-8 byte */ + if (NULL != buf_offset) + *buf_offset = i; + return MHD_WebSocket_UTF8Result_Invalid; + } + break; + + case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2: + if ((0x80 <= character) && (0xBF >= character)) + { + /* RFC 3629 4: Second byte of three byte UTF-8 sequence */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2; + } + else + { + /* RFC 3629 4: Invalid UTF-8 byte */ + if (NULL != buf_offset) + *buf_offset = i; + return MHD_WebSocket_UTF8Result_Invalid; + } + break; + + case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3: + if ((0x90 <= character) && (0xBF >= character)) + { + /* RFC 3629 4: Second byte of four byte UTF-8 sequence */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3; + } + else + { + /* RFC 3629 4: Invalid UTF-8 byte */ + if (NULL != buf_offset) + *buf_offset = i; + return MHD_WebSocket_UTF8Result_Invalid; + } + break; + + case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3: + if ((0x80 <= character) && (0x8F >= character)) + { + /* RFC 3629 4: Second byte of four byte UTF-8 sequence */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3; + } + else + { + /* RFC 3629 4: Invalid UTF-8 byte */ + if (NULL != buf_offset) + *buf_offset = i; + return MHD_WebSocket_UTF8Result_Invalid; + } + break; + + case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3: + if ((0x80 <= character) && (0xBF >= character)) + { + /* RFC 3629 4: Second byte of four byte UTF-8 sequence */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3; + } + else + { + /* RFC 3629 4: Invalid UTF-8 byte */ + if (NULL != buf_offset) + *buf_offset = i; + return MHD_WebSocket_UTF8Result_Invalid; + } + break; + + case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3: + if ((0x80 <= character) && (0xBF >= character)) + { + /* RFC 3629 4: Third byte of four byte UTF-8 sequence */ + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3; + } + else + { + /* RFC 3629 4: Invalid UTF-8 byte */ + if (NULL != buf_offset) + *buf_offset = i; + return MHD_WebSocket_UTF8Result_Invalid; + } + break; + + /* RFC 3629 4: Second byte of two byte UTF-8 sequence */ + case MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1: + /* RFC 3629 4: Third byte of three byte UTF-8 sequence */ + case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2: + /* RFC 3629 4: Fourth byte of four byte UTF-8 sequence */ + case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3: + if ((0x80 <= character) && (0xBF >= character)) + { + utf8_step_ = MHD_WEBSOCKET_UTF8STEP_NORMAL; + } + else + { + /* RFC 3629 4: Invalid UTF-8 byte */ + if (NULL != buf_offset) + *buf_offset = i; + return MHD_WebSocket_UTF8Result_Invalid; + } + break; + + default: + /* Invalid last step...? */ + if (NULL != buf_offset) + *buf_offset = i; + return MHD_WebSocket_UTF8Result_Invalid; + } + } + + /* return values */ + if (NULL != utf8_step) + *utf8_step = utf8_step_; + if (NULL != buf_offset) + *buf_offset = buf_len; + if (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step_) + { + return MHD_WebSocket_UTF8Result_Incomplete; + } + return MHD_WebSocket_UTF8Result_Valid; +} + + +/** + * Calls srand in the scope of MHD to set the seed + * for the random number generator used for masking. + */ +_MHD_EXTERN int +MHD_websocket_srand (unsigned long seed) +{ + srand (seed); + + return MHD_WEBSOCKET_STATUS_OK; +} + + +/** + * Generates a mask for masking by calling + * a random number generator. + */ +static unsigned long +MHD_websocket_generate_mask () +{ + unsigned char mask_[4]; + mask_ [0] = (unsigned char) (rand () & 0xFF); + mask_ [1] = (unsigned char) (rand () & 0xFF); + mask_ [2] = (unsigned char) (rand () & 0xFF); + mask_ [3] = (unsigned char) (rand () & 0xFF); + + return *((unsigned long *) mask_); +} + + +/** + * Calls the malloc function associated with the websocket steam + */ +_MHD_EXTERN void* +MHD_websocket_malloc (struct MHD_WebSocketStream*ws, + size_t len) +{ + if (NULL == ws) + { + return NULL; + } + + return ws->malloc (len); +} + + +/** + * Calls the realloc function associated with the websocket steam + */ +_MHD_EXTERN void* +MHD_websocket_realloc (struct MHD_WebSocketStream*ws, + void*cls, + size_t len) +{ + if (NULL == ws) + { + return NULL; + } + + return ws->realloc (cls, len); +} + + +/** + * Calls the free function associated with the websocket steam + */ +_MHD_EXTERN int +MHD_websocket_free (struct MHD_WebSocketStream*ws, + void*cls) +{ + if (NULL == ws) + { + return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; + } + + ws->free (cls); + + return MHD_WEBSOCKET_STATUS_OK; +} diff --git a/src/microhttpd_ws/sha1.c b/src/microhttpd_ws/sha1.c @@ -0,0 +1,420 @@ +/* sha1.c - Functions to compute SHA1 message digest of files or + memory blocks according to the NIST specification FIPS-180-1. + + Copyright (C) 2000-2021 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Scott G. Miller + Credits: + Robert Klep <robert@ilse.nl> -- Expansion function fix +*/ + +/*#include <config.h>*/ + +#include "sha1.h" + +#include <stddef.h> +#include <string.h> + +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) (n) +#else +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#endif + +#define BLOCKSIZE 4096 +#if BLOCKSIZE % 64 != 0 +# error "invalid BLOCKSIZE" +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Take a pointer to a 160 bit block of data (five 32 bit ints) and + initialize it to the start constants of the SHA1 algorithm. This + must be called before using hash in the call to sha1_hash. */ +void +sha1_init_ctx (struct sha1_ctx *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + ctx->E = 0xc3d2e1f0; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + + +/* Put result from CTX in first 20 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32-bit value. */ +void * +sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf) +{ + ((sha1_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((sha1_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((sha1_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((sha1_uint32 *) resbuf)[3] = SWAP (ctx->D); + ((sha1_uint32 *) resbuf)[4] = SWAP (ctx->E); + + return resbuf; +} + + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32-bit value. */ +void * +sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf) +{ + /* Take yet unprocessed bytes into account. */ + sha1_uint32 bytes = ctx->buflen; + size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + ctx->buffer[size - 2] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); + ctx->buffer[size - 1] = SWAP (ctx->total[0] << 3); + + memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + + /* Process last bytes. */ + sha1_process_block (ctx->buffer, size * 4, ctx); + + return sha1_read_ctx (ctx, resbuf); +} + + +/* Compute SHA1 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +sha1_stream (FILE *stream, void *resblock) +{ + struct sha1_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + sha1_init_ctx (&ctx); + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + while (1) + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + + if (sum == BLOCKSIZE) + break; + + if (n == 0) + { + /* Check for the error flag IFF N == 0, so that we don't + exit the loop after a partial read due to e.g., EAGAIN + or EWOULDBLOCK. */ + if (ferror (stream)) + return 1; + goto process_partial_block; + } + + /* We've read at least one byte, so ignore errors. But always + check for EOF, since feof may be true even though N > 0. + Otherwise, we could end up calling fread after EOF. */ + if (feof (stream)) + goto process_partial_block; + } + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + sha1_process_block (buffer, BLOCKSIZE, &ctx); + } + +process_partial_block:; + + /* Process any remaining bytes. */ + if (sum > 0) + sha1_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + sha1_finish_ctx (&ctx, resblock); + return 0; +} + + +/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +sha1_buffer (const char *buffer, size_t len, void *resblock) +{ + struct sha1_ctx ctx; + + /* Initialize the computation context. */ + sha1_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + sha1_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return sha1_finish_ctx (&ctx, resblock); +} + + +void +sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&((char *) ctx->buffer)[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + sha1_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, + &((char *) ctx->buffer)[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +#if ! _STRING_ARCH_unaligned +# define alignof(type) offsetof (struct { char c; type x; }, x) +# define UNALIGNED_P(p) (((size_t) p) % alignof (sha1_uint32) != 0) + if (UNALIGNED_P (buffer)) + while (len > 64) + { + sha1_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else +#endif + { + sha1_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&((char *) ctx->buffer)[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + sha1_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memmove (ctx->buffer, &ctx->buffer[16], left_over); + } + ctx->buflen = left_over; + } +} + + +/* --- Code below is the primary difference between md5.c and sha1.c --- */ + +/* SHA1 round constants */ +#define K1 0x5a827999 +#define K2 0x6ed9eba1 +#define K3 0x8f1bbcdc +#define K4 0xca62c1d6 + +/* Round functions. Note that F2 is the same as F4. */ +#define F1(B,C,D) ( D ^ ( B & ( C ^ D ) ) ) +#define F2(B,C,D) (B ^ C ^ D) +#define F3(B,C,D) ( ( B & C ) | ( D & ( B | C ) ) ) +#define F4(B,C,D) (B ^ C ^ D) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. + Most of this code comes from GnuPG's cipher/sha1.c. */ + +void +sha1_process_block (const void *buffer, size_t len, struct sha1_ctx *ctx) +{ + const sha1_uint32 *words = (const sha1_uint32*) buffer; + size_t nwords = len / sizeof (sha1_uint32); + const sha1_uint32 *endp = words + nwords; + sha1_uint32 x[16]; + sha1_uint32 a = ctx->A; + sha1_uint32 b = ctx->B; + sha1_uint32 c = ctx->C; + sha1_uint32 d = ctx->D; + sha1_uint32 e = ctx->E; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + ctx->total[1] += ((len >> 31) >> 1) + (ctx->total[0] < len); + +#define rol(x, n) (((x) << (n)) | ((sha1_uint32) (x) >> (32 - (n)))) + +#define M(I) ( tm = x[I&0x0f] ^ x[(I-14)&0x0f] \ + ^ x[(I-8)&0x0f] ^ x[(I-3)&0x0f] \ + , (x[I&0x0f] = rol(tm, 1)) ) + +#define R(A,B,C,D,E,F,K,M) do { E += rol( A, 5 ) \ + + F( B, C, D ) \ + + K \ + + M; \ + B = rol( B, 30 ); \ + } while(0) + + while (words < endp) + { + sha1_uint32 tm; + int t; + for (t = 0; t < 16; t++) + { + x[t] = SWAP (*words); + words++; + } + + R (a, b, c, d, e, F1, K1, x[ 0]); + R (e, a, b, c, d, F1, K1, x[ 1]); + R (d, e, a, b, c, F1, K1, x[ 2]); + R (c, d, e, a, b, F1, K1, x[ 3]); + R (b, c, d, e, a, F1, K1, x[ 4]); + R (a, b, c, d, e, F1, K1, x[ 5]); + R (e, a, b, c, d, F1, K1, x[ 6]); + R (d, e, a, b, c, F1, K1, x[ 7]); + R (c, d, e, a, b, F1, K1, x[ 8]); + R (b, c, d, e, a, F1, K1, x[ 9]); + R (a, b, c, d, e, F1, K1, x[10]); + R (e, a, b, c, d, F1, K1, x[11]); + R (d, e, a, b, c, F1, K1, x[12]); + R (c, d, e, a, b, F1, K1, x[13]); + R (b, c, d, e, a, F1, K1, x[14]); + R (a, b, c, d, e, F1, K1, x[15]); + R (e, a, b, c, d, F1, K1, M (16) ); + R (d, e, a, b, c, F1, K1, M (17) ); + R (c, d, e, a, b, F1, K1, M (18) ); + R (b, c, d, e, a, F1, K1, M (19) ); + R (a, b, c, d, e, F2, K2, M (20) ); + R (e, a, b, c, d, F2, K2, M (21) ); + R (d, e, a, b, c, F2, K2, M (22) ); + R (c, d, e, a, b, F2, K2, M (23) ); + R (b, c, d, e, a, F2, K2, M (24) ); + R (a, b, c, d, e, F2, K2, M (25) ); + R (e, a, b, c, d, F2, K2, M (26) ); + R (d, e, a, b, c, F2, K2, M (27) ); + R (c, d, e, a, b, F2, K2, M (28) ); + R (b, c, d, e, a, F2, K2, M (29) ); + R (a, b, c, d, e, F2, K2, M (30) ); + R (e, a, b, c, d, F2, K2, M (31) ); + R (d, e, a, b, c, F2, K2, M (32) ); + R (c, d, e, a, b, F2, K2, M (33) ); + R (b, c, d, e, a, F2, K2, M (34) ); + R (a, b, c, d, e, F2, K2, M (35) ); + R (e, a, b, c, d, F2, K2, M (36) ); + R (d, e, a, b, c, F2, K2, M (37) ); + R (c, d, e, a, b, F2, K2, M (38) ); + R (b, c, d, e, a, F2, K2, M (39) ); + R (a, b, c, d, e, F3, K3, M (40) ); + R (e, a, b, c, d, F3, K3, M (41) ); + R (d, e, a, b, c, F3, K3, M (42) ); + R (c, d, e, a, b, F3, K3, M (43) ); + R (b, c, d, e, a, F3, K3, M (44) ); + R (a, b, c, d, e, F3, K3, M (45) ); + R (e, a, b, c, d, F3, K3, M (46) ); + R (d, e, a, b, c, F3, K3, M (47) ); + R (c, d, e, a, b, F3, K3, M (48) ); + R (b, c, d, e, a, F3, K3, M (49) ); + R (a, b, c, d, e, F3, K3, M (50) ); + R (e, a, b, c, d, F3, K3, M (51) ); + R (d, e, a, b, c, F3, K3, M (52) ); + R (c, d, e, a, b, F3, K3, M (53) ); + R (b, c, d, e, a, F3, K3, M (54) ); + R (a, b, c, d, e, F3, K3, M (55) ); + R (e, a, b, c, d, F3, K3, M (56) ); + R (d, e, a, b, c, F3, K3, M (57) ); + R (c, d, e, a, b, F3, K3, M (58) ); + R (b, c, d, e, a, F3, K3, M (59) ); + R (a, b, c, d, e, F4, K4, M (60) ); + R (e, a, b, c, d, F4, K4, M (61) ); + R (d, e, a, b, c, F4, K4, M (62) ); + R (c, d, e, a, b, F4, K4, M (63) ); + R (b, c, d, e, a, F4, K4, M (64) ); + R (a, b, c, d, e, F4, K4, M (65) ); + R (e, a, b, c, d, F4, K4, M (66) ); + R (d, e, a, b, c, F4, K4, M (67) ); + R (c, d, e, a, b, F4, K4, M (68) ); + R (b, c, d, e, a, F4, K4, M (69) ); + R (a, b, c, d, e, F4, K4, M (70) ); + R (e, a, b, c, d, F4, K4, M (71) ); + R (d, e, a, b, c, F4, K4, M (72) ); + R (c, d, e, a, b, F4, K4, M (73) ); + R (b, c, d, e, a, F4, K4, M (74) ); + R (a, b, c, d, e, F4, K4, M (75) ); + R (e, a, b, c, d, F4, K4, M (76) ); + R (d, e, a, b, c, F4, K4, M (77) ); + R (c, d, e, a, b, F4, K4, M (78) ); + R (b, c, d, e, a, F4, K4, M (79) ); + + a = ctx->A += a; + b = ctx->B += b; + c = ctx->C += c; + d = ctx->D += d; + e = ctx->E += e; + } +} diff --git a/src/microhttpd_ws/sha1.h b/src/microhttpd_ws/sha1.h @@ -0,0 +1,145 @@ +/* Declarations of functions and data types used for SHA1 sum + library functions. + Copyright (C) 2000-2021 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef SHA1_H +# define SHA1_H 1 + +#include <stdio.h> + +#if defined HAVE_LIMITS_H || _LIBC +# include <limits.h> +#endif + +/*#include "ansidecl.h"*/ + +/* The following contortions are an attempt to use the C preprocessor + to determine an unsigned integral type that is 32 bits wide. An + alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but + doing that would require that the configure script compile and *run* + the resulting executable. Locally running cross-compiled executables + is usually not possible. */ + +#ifdef _LIBC +# include <sys/types.h> +typedef u_int32_t sha1_uint32; +typedef uintptr_t sha1_uintptr; +#elif defined (HAVE_SYS_TYPES_H) && defined (HAVE_STDINT_H) +#include <stdint.h> +#include <sys/types.h> +typedef uint32_t sha1_uint32; +typedef uintptr_t sha1_uintptr; +#else +# define INT_MAX_32_BITS 2147483647 + +/* If UINT_MAX isn't defined, assume it's a 32-bit type. + This should be valid for all systems GNU cares about because + that doesn't include 16-bit systems, and only modern systems + (that certainly have <limits.h>) have 64+-bit integral types. */ + +# ifndef INT_MAX +# define INT_MAX INT_MAX_32_BITS +# endif + +# if INT_MAX == INT_MAX_32_BITS +typedef unsigned int sha1_uint32; +# else +# if SHRT_MAX == INT_MAX_32_BITS +typedef unsigned short sha1_uint32; +# else +# if LONG_MAX == INT_MAX_32_BITS +typedef unsigned long sha1_uint32; +# else +/* The following line is intended to evoke an error. + Using #error is not portable enough. */ +"Cannot determine unsigned 32-bit data type." +# endif +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure to save state of computation between the single steps. */ +struct sha1_ctx +{ + sha1_uint32 A; + sha1_uint32 B; + sha1_uint32 C; + sha1_uint32 D; + sha1_uint32 E; + + sha1_uint32 total[2]; + sha1_uint32 buflen; + sha1_uint32 buffer[32]; +}; + + +/* Initialize structure containing state of computation. */ +extern void sha1_init_ctx (struct sha1_ctx *ctx); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void sha1_process_block (const void *buffer, size_t len, + struct sha1_ctx *ctx); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void sha1_process_bytes (const void *buffer, size_t len, + struct sha1_ctx *ctx); + +/* Process the remaining bytes in the buffer and put result from CTX + in first 20 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF be correctly + aligned for a 32 bits value. */ +extern void *sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf); + + +/* Put result from CTX in first 20 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf); + + +/* Compute SHA1 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 20 bytes + beginning at RESBLOCK. */ +extern int sha1_stream (FILE *stream, void *resblock); + +/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *sha1_buffer (const char *buffer, size_t len, void *resblock); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/microhttpd_ws/test_websocket.c b/src/microhttpd_ws/test_websocket.c @@ -0,0 +1,8983 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2021 David Gausmann + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/** + * @file test_websocket.c + * @brief Testcase for WebSocket decoding/encoding + * @author David Gausmann + */ +#include "microhttpd.h" +#include "microhttpd_ws.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <time.h> + +int disable_alloc = 0; +size_t open_allocs = 0; + +/** + * Custom `malloc()` function used for memory tests + */ +static void* +test_malloc (size_t buf_len) +{ + if (0 != disable_alloc) + return NULL; + void*result = malloc (buf_len); + if (NULL != result) + ++open_allocs; + return result; +} + + +/** + * Custom `realloc()` function used for memory tests + */ +static void* +test_realloc (void*buf, size_t buf_len) +{ + if (0 != disable_alloc) + return NULL; + void*result = realloc (buf, buf_len); + if ((NULL != result) && (NULL == buf)) + ++open_allocs; + return result; +} + + +/** + * Custom `free()` function used for memory tests + */ +static void +test_free (void*buf) +{ + if (NULL != buf) + --open_allocs; + free (buf); +} + + +/** + * Helper function which allocates a big amount of data + */ +static void +allocate_length_test_data (char**buf1, + char**buf2, + size_t buf_len, + const char*buf1_prefix, + size_t buf1_prefix_len) +{ + if (NULL != *buf1) + free (*buf1); + if (NULL != *buf2) + free (*buf2); + *buf1 = (char*) malloc (buf_len + buf1_prefix_len); + *buf2 = (char*) malloc (buf_len); + if ((NULL == buf1) || (NULL == buf2)) + return; + memcpy (*buf1, + buf1_prefix, + buf1_prefix_len); + for (size_t i = 0; i < buf_len; i += 64) + { + size_t bytes_to_copy = buf_len - i; + if (64 < bytes_to_copy) + bytes_to_copy = 64; + memcpy (*buf1 + i + buf1_prefix_len, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-", + bytes_to_copy); + memcpy (*buf2 + i, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-", + bytes_to_copy); + } +} + + +/** + * Helper function which performs a single decoder test + */ +static int +test_decode_single (unsigned int test_line, + int flags, size_t max_payload_size, size_t decode_count, + size_t buf_step, + const char*buf, size_t buf_len, + const char*expected_payload, size_t expected_payload_len, + int expected_return, int expected_valid, size_t + expected_streambuf_read_len) +{ + struct MHD_WebSocketStream *ws = NULL; + int ret = MHD_WEBSOCKET_STATUS_OK; + + /* initialize stream */ + ret = MHD_websocket_stream_init (&ws, flags, max_payload_size); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + fprintf (stderr, + "Allocation failed for decode test in line %u.\n", + (unsigned int) test_line); + return 1; + } + + /* perform decoding in a loop */ + size_t streambuf_read_len = 0; + size_t payload_len = 0; + char*payload = NULL; + for (size_t i = 0; i < decode_count; ++i) + { + size_t streambuf_read_len_ = 0; + size_t bytes_to_take = buf_len - streambuf_read_len; + if ((0 != buf_step) && (buf_step < bytes_to_take)) + bytes_to_take = buf_step; + ret = MHD_websocket_decode (ws, buf + streambuf_read_len, bytes_to_take, + &streambuf_read_len_, &payload, &payload_len); + streambuf_read_len += streambuf_read_len_; + if (i + 1 < decode_count) + { + if (payload) + { + MHD_websocket_free (ws, payload); + payload = NULL; + payload_len = 0; + } + } + } + + /* check the (last) result */ + if (ret != expected_return) + { + fprintf (stderr, + "Decode test failed in line %u: The return value should be %d, but is %d\n", + (unsigned int) test_line, + (int) expected_return, + (int) ret); + MHD_websocket_free (ws, payload); + MHD_websocket_stream_free (ws); + return 1; + } + if (payload_len != expected_payload_len) + { + fprintf (stderr, + "Decode test failed in line %u: The payload_len should be %u, but is %u\n", + (unsigned int) test_line, + (unsigned int) expected_payload_len, + (unsigned int) payload_len); + MHD_websocket_free (ws, payload); + MHD_websocket_stream_free (ws); + return 1; + } + if (0 != payload_len) + { + if (NULL == payload) + { + fprintf (stderr, + "Decode test failed in line %u: The payload is NULL\n", + (unsigned int) test_line); + MHD_websocket_free (ws, payload); + MHD_websocket_stream_free (ws); + return 1; + } + else if (NULL == expected_payload) + { + fprintf (stderr, + "Decode test failed in line %u: The expected_payload is NULL (wrong test declaration)\n", + (unsigned int) test_line); + MHD_websocket_free (ws, payload); + MHD_websocket_stream_free (ws); + return 1; + } + else if (0 != memcmp (payload, expected_payload, payload_len)) + { + fprintf (stderr, + "Decode test failed in line %u: The payload differs from the expected_payload\n", + (unsigned int) test_line); + MHD_websocket_free (ws, payload); + MHD_websocket_stream_free (ws); + return 1; + } + } + else + { + if (NULL != payload) + { + fprintf (stderr, + "Decode test failed in line %u: The payload is not NULL, but payload_len is 0\n", + (unsigned int) test_line); + MHD_websocket_free (ws, payload); + MHD_websocket_stream_free (ws); + return 1; + } + else if (NULL != expected_payload) + { + fprintf (stderr, + "Decode test failed in line %u: The expected_payload is not NULL, but expected_payload_len is 0 (wrong test declaration)\n", + (unsigned int) test_line); + MHD_websocket_free (ws, payload); + MHD_websocket_stream_free (ws); + return 1; + } + } + if (streambuf_read_len != expected_streambuf_read_len) + { + fprintf (stderr, + "Decode test failed in line %u: The streambuf_read_len should be %u, but is %u\n", + (unsigned int) test_line, + (unsigned int) expected_streambuf_read_len, + (unsigned int) streambuf_read_len); + MHD_websocket_free (ws, payload); + MHD_websocket_stream_free (ws); + return 1; + } + ret = MHD_websocket_stream_is_valid (ws); + if (ret != expected_valid) + { + fprintf (stderr, + "Decode test failed in line %u: The stream validity should be %u, but is %u\n", + (unsigned int) test_line, + (int) expected_valid, + (int) ret); + MHD_websocket_free (ws, payload); + MHD_websocket_stream_free (ws); + return 1; + } + + /* cleanup */ + MHD_websocket_free (ws, payload); + MHD_websocket_stream_free (ws); + + return 0; +} + + +/** + * Test procedure for `MHD_websocket_stream_init()` and + * `MHD_websocket_stream_init()2` + */ +int +test_inits () +{ + int failed = 0; + struct MHD_WebSocketStream*ws; + int ret; + + /* + ------------------------------------------------------------------------------ + All valid flags + ------------------------------------------------------------------------------ + */ + /* Regular test: all valid flags for init */ + for (int i = 0; i < 7; ++i) + { + ws = NULL; + ret = MHD_websocket_stream_init (&ws, + i, + 0); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL == ws) ) + { + fprintf (stderr, + "Init test failed in line %u for flags %d.\n", + (unsigned int) __LINE__, + (int) i); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + } + /* Regular test: all valid flags for init2 */ + for (int i = 0; i < 7; ++i) + { + ws = NULL; + ret = MHD_websocket_stream_init2 (&ws, + i, + 0, + test_malloc, + test_realloc, + test_free); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL == ws) ) + { + fprintf (stderr, + "Init test failed in line %u for flags %d.\n", + (unsigned int) __LINE__, + (int) i); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + } + /* Fail test: Invalid flags for init */ + for (int i = 4; i < 32; ++i) + { + int flags = 1 << i; + ws = NULL; + ret = MHD_websocket_stream_init (&ws, + flags, + 0); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != ws) ) + { + fprintf (stderr, + "Init test failed in line %u for invalid flags %d.\n", + (unsigned int) __LINE__, + (int) flags); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + } + /* Fail test: Invalid flag for init2 */ + for (int i = 4; i < 32; ++i) + { + int flags = 1 << i; + ws = NULL; + ret = MHD_websocket_stream_init2 (&ws, + flags, + 0, + test_malloc, + test_realloc, + test_free); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != ws) ) + { + fprintf (stderr, + "Init test failed in line %u for invalid flags %d.\n", + (unsigned int) __LINE__, + (int) flags); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + } + + /* + ------------------------------------------------------------------------------ + max_payload_size + ------------------------------------------------------------------------------ + */ + /* Regular test: max_payload_size = 0 for init */ + ws = NULL; + ret = MHD_websocket_stream_init (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL == ws) ) + { + fprintf (stderr, + "Init test failed in line %u for max_payload_size 0.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Regular test: max_payload_size = 0 for init2 */ + ws = NULL; + ret = MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + test_realloc, + test_free); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL == ws) ) + { + fprintf (stderr, + "Init test failed in line %u for max_payload_size 0.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Edge test (success): max_payload_size = 1 for init */ + ws = NULL; + ret = MHD_websocket_stream_init (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 1); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL == ws) ) + { + fprintf (stderr, + "Init test failed in line %u for max_payload_size 1.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Edge test (success): max_payload_size = 1 for init2 */ + ws = NULL; + ret = MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 1, + test_malloc, + test_realloc, + test_free); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL == ws) ) + { + fprintf (stderr, + "Init test failed in line %u for max_payload_size 1.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Regular test: max_payload_size = 1000 for init */ + ws = NULL; + ret = MHD_websocket_stream_init (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 1000); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL == ws) ) + { + fprintf (stderr, + "Init test failed in line %u for max_payload_size 1000.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Regular test: max_payload_size = 1000 for init2 */ + ws = NULL; + ret = MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 1000, + test_malloc, + test_realloc, + test_free); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL == ws) ) + { + fprintf (stderr, + "Init test failed in line %u for max_payload_size 1000.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Edge test (success): max_payload_size = 0x7FFFFFFFFFFFFFFF for init */ + ws = NULL; + ret = MHD_websocket_stream_init (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + (uint64_t) 0x7FFFFFFFFFFFFFFF); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL == ws) ) + { + fprintf (stderr, + "Init test failed in line %u for max_payload_size 0x7FFFFFFFFFFFFFFF.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Edge test (success): max_payload_size = 0x7FFFFFFFFFFFFFFF for init2 */ + ws = NULL; + ret = MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + (uint64_t) 0x7FFFFFFFFFFFFFFF, + test_malloc, + test_realloc, + test_free); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL == ws) ) + { + fprintf (stderr, + "Init test failed in line %u for max_payload_size 0x7FFFFFFFFFFFFFFF.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Edge test (fail): max_payload_size = 0x8000000000000000 for init */ + ws = NULL; + ret = MHD_websocket_stream_init (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + (uint64_t) 0x8000000000000000); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != ws) ) + { + fprintf (stderr, + "Init test failed in line %u for max_payload_size 0x8000000000000000.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Edge test (fail): max_payload_size = 0x8000000000000000 for init2 */ + ws = NULL; + ret = MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + (uint64_t) 0x8000000000000000, + test_malloc, + test_realloc, + test_free); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != ws) ) + { + fprintf (stderr, + "Init test failed in line %u for max_payload_size 0x8000000000000000.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + + /* + ------------------------------------------------------------------------------ + Missing parameters + ------------------------------------------------------------------------------ + */ + /* Fail test: websocket stream variable missing for init */ + ws = NULL; + ret = MHD_websocket_stream_init (NULL, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != ws) ) + { + fprintf (stderr, + "Init test failed in line %u.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Fail test: websocket stream variable missing for init2 */ + ws = NULL; + ret = MHD_websocket_stream_init2 (NULL, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + test_realloc, + test_free); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != ws) ) + { + fprintf (stderr, + "Init test failed in line %u.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Fail test: malloc missing for init2 */ + ws = NULL; + ret = MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + NULL, + test_realloc, + test_free); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != ws) ) + { + fprintf (stderr, + "Init test failed in line %u.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Fail test: realloc missing for init2 */ + ws = NULL; + ret = MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + NULL, + test_free); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != ws) ) + { + fprintf (stderr, + "Init test failed in line %u.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + /* Fail test: free missing for init2 */ + ws = NULL; + ret = MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + test_realloc, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != ws) ) + { + fprintf (stderr, + "Init test failed in line %u.\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != ws) + { + MHD_websocket_stream_free (ws); + ws = NULL; + } + + return failed != 0 ? 0x01 : 0x00; +} + + +/** + * Test procedure for `MHD_websocket_create_accept()` + */ +int +test_accept () +{ + int failed = 0; + char accept_key[29]; + int ret; + + /* + ------------------------------------------------------------------------------ + accepting + ------------------------------------------------------------------------------ + */ + /* Regular test: Test case from RFC6455 4.2.2 */ + memset (accept_key, 0, 29); + ret = MHD_websocket_create_accept ("dGhlIHNhbXBsZSBub25jZQ==", + accept_key); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (0 != memcmp (accept_key, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", 29))) + { + fprintf (stderr, + "Accept test failed in line %u.\n", + (unsigned int) __LINE__); + ++failed; + } + + /* + ------------------------------------------------------------------------------ + Missing parameters + ------------------------------------------------------------------------------ + */ + /* Fail test: missing sec-key value */ + memset (accept_key, 0, 29); + ret = MHD_websocket_create_accept (NULL, + accept_key); + if (MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) + { + fprintf (stderr, + "Accept test failed in line %u.\n", + (unsigned int) __LINE__); + ++failed; + } + /* Fail test: missing accept variable */ + memset (accept_key, 0, 29); + ret = MHD_websocket_create_accept ("dGhlIHNhbXBsZSBub25jZQ==", + NULL); + if (MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) + { + fprintf (stderr, + "Accept test failed in line %u.\n", + (unsigned int) __LINE__); + ++failed; + } + + return failed != 0 ? 0x02 : 0x00; +} + + +/** + * Test procedure for `MHD_websocket_decode()` + */ +int +test_decodes () +{ + int failed = 0; + char *buf1 = NULL, *buf2 = NULL; + + /* + ------------------------------------------------------------------------------ + text frame + ------------------------------------------------------------------------------ + */ + /* Regular test: Masked text frame from RFC 6455, must succeed for server */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58", + 11, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Regular test: Unmasked text frame from RFC 6455, must succeed for client */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x05\x48\x65\x6c\x6c\x6f", + 7, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 7); + /* Fail test: Unmasked text frame from RFC 6455, must fail for server */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x05\x48\x65\x6c\x6c\x6f", + 7, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Fail test: Masked text frame from RFC 6455, must fail for client */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Regular test: Text frame with UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x90\x00\x00\x00\x00" "This is my n" + "\xC3\xB6" "te", + 22, + "This is my n" "\xC3\xB6" "te", + 16, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 22); + /* Fail test: Text frame with with invalid UTF-8 */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8F\x00\x00\x00\x00" "This is my n" "\xFF" + "te", + 21, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 18); + /* Fail test: Text frame with broken UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8F\x00\x00\x00\x00" "This is my n" "\xC3" + "te", + 21, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 19); + /* Regular test: Text frame without payload and mask (caller = server) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x80\x01\x02\x03\x04", + 6, + NULL, + 0, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 6); + /* Fail test: Text frame without payload and no mask (caller = server) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x00", + 2, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Regular test: Text frame without payload and mask (caller = client) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x00", + 2, + NULL, + 0, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 2); + /* Fail test: Text frame without payload and no mask (caller = client) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x80\x01\x02\x03\x04", + 6, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + + /* + ------------------------------------------------------------------------------ + binary frame + ------------------------------------------------------------------------------ + */ + /* Regular test: Masked binary frame (decoder = server) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x82\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58", + 11, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Regular test: Unmasked binary frame (decoder = client) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x82\x05\x48\x65\x6c\x6c\x6f", + 7, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 7); + /* Fail test: Unmasked binary frame (decoder = server) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x82\x05\x48\x65\x6c\x6c\x6f", + 7, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Fail test: Masked binary frame (decoder = client) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x82\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Regular test: Binary frame without payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x82\x80\x00\x00\x00\x00", + 6, + NULL, + 0, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 6); + /* Regular test: Fragmented binary frame without payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x02\x80\x00\x00\x00\x00\x80\x80\x00\x00\x00\x00", + 12, + NULL, + 0, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 12); + /* Regular test: Fragmented binary frame without payload, fragments to the caller, 1st call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 1, + 0, + "\x02\x80\x00\x00\x00\x00\x80\x80\x00\x00\x00\x00", + 12, + NULL, + 0, + MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 6); + /* Regular test: Fragmented binary frame without payload, fragments to the caller, 2nd call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 2, + 0, + "\x02\x80\x00\x00\x00\x00\x80\x80\x00\x00\x00\x00", + 12, + NULL, + 0, + MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 12); + /* Regular test: Fragmented binary frame wit payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x02\x83\x00\x00\x00\x00\x01\x02\x03\x80\x83\x00\x00\x00\x00\x04\x05\x06", + 18, + "\x01\x02\x03\x04\x05\x06", + 6, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 18); + /* Regular test: Fragmented binary frame with payload, fragments to the caller, 1st call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 1, + 0, + "\x02\x83\x00\x00\x00\x00\x01\x02\x03\x80\x83\x00\x00\x00\x00\x04\x05\x06", + 18, + "\x01\x02\x03", + 3, + MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 9); + /* Regular test: Fragmented binary frame without payload, fragments to the caller, 2nd call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 2, + 0, + "\x02\x83\x00\x00\x00\x00\x01\x02\x03\x80\x83\x00\x00\x00\x00\x04\x05\x06", + 18, + "\x04\x05\x06", + 3, + MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 18); + /* Regular test: Binary frame with bytes which look like invalid UTF-8 character */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x82\x85\x00\x00\x00\x00" "Hell\xf6", + 11, + "Hell\xf6", + 5, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Regular test: Binary frame with bytes which look like broken UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x82\x85\x00\x00\x00\x00" "H\xC3llo", + 11, + "H\xC3llo", + 5, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Regular test: Binary frame with bytes which look like valid UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x82\x85\x00\x00\x00\x00" "H\xC3\xA4lo", + 11, + "H\xC3\xA4lo", + 5, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Regular test: Fragmented binary frame with bytes which look like valid UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x02\x82\x00\x00\x00\x00" "H\xC3" + "\x80\x83\x00\x00\x00\x00" "\xA4lo", + 17, + "H\xC3\xA4lo", + 5, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + /* Regular test: Fragmented binary frame with bytes which look like valid UTF-8 sequence, + fragments to the caller, 1st call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 1, + 0, + "\x02\x82\x00\x00\x00\x00" "H\xC3" + "\x80\x83\x00\x00\x00\x00" "\xA4lo", + 17, + "H\xC3", + 2, + MHD_WEBSOCKET_STATUS_BINARY_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 8); + /* Regular test: Fragmented binary frame with bytes which look like valid UTF-8 sequence, + fragments to the caller, 2nd call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 2, + 0, + "\x02\x82\x00\x00\x00\x00" "H\xC3" + "\x80\x83\x00\x00\x00\x00" "\xA4lo", + 17, + "\xA4lo", + 3, + MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + + /* + ------------------------------------------------------------------------------ + close frame + ------------------------------------------------------------------------------ + */ + /* Regular test: Close frame with no payload but with mask (decoder = server) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x80\x00\x00\x00\x00", + 6, + NULL, + 0, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 6); + /* Regular test: Close frame with no payload (decoder = client) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x00", + 2, + NULL, + 0, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 2); + /* Fail test: Close frame with no payload and no mask (decoder = server) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x00", + 2, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Fail test: Close frame with no payload but with mask (decoder = client) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x80\x00\x00\x00\x00", + 6, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Regular test: Close frame with 2 byte payload for close reason */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x82\x00\x00\x00\x00\x03\xEB", + 8, + "\x03\xEB", + 2, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 8); + /* Fail test: Close frame with 1 byte payload (no valid close reason) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x81\x00\x00\x00\x00\x03", + 7, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Regular test: Close frame with close reason and UTF-8 description */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x95\x00\x00\x00\x00\x03\xEB" + "Something was wrong", + 27, + "\x03\xEB" "Something was wrong", + 21, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 27); + /* Regular test: Close frame with close reason and UTF-8 description (with UTF-8 sequence) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x96\x00\x00\x00\x00\x03\xEB" + "Something was wr" "\xC3\xB6" "ng", + 28, + "\x03\xEB" "Something was wr" "\xC3\xB6" "ng", + 22, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 28); + /* Fail test: Close frame with close reason and invalid UTF-8 in description */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x95\x00\x00\x00\x00\x03\xEB" + "Something was wr" "\xFF" "ng", + 27, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 24); + /* Fail test: Close frame with close reason and broken UTF-8 sequence in description */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x95\x00\x00\x00\x00\x03\xEB" + "Something was wr" "\xC3" "ng", + 27, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 25); + /* Edge test (success): Close frame with 125 bytes of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\xFD\x00\x00\x00\x00\x03\xEB" + "Something was wrong, so I decided to close this websocket. I hope you are not angry. But this is also the 123 cap test. :-)", + 131, + "\x03\xEB" + "Something was wrong, so I decided to close this websocket. I hope you are not angry. But this is also the 123 cap test. :-)", + 125, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 131); + /* Edge test (failure): Close frame with 126 bytes of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\xFE\x00\x7e\x00\x00\x00\x00\x03\xEB" + "Something was wrong, so I decided to close this websocket. I hope you are not angry. But this is also the 123 cap test. >:-)", + 134, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Fail test: Close frame with 500 bytes of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\xFE\x01\xf4\x00\x00\x00\x00\x03\xEB" + "The payload of this test isn't parsed.", + 49, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Edge test (failure): Close frame with 65535 bytes of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\xFE\xff\xff\x00\x00\x00\x00\x03\xEB" + "The payload of this test isn't parsed.", + 49, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Edge test (failure): Close frame with 65536 bytes of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\xFF\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xEB" + "The payload of this test isn't parsed.", + 54, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Fail test: Close frame with 1000000 bytes of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\xFF\x00\x00\x00\x00\x00\x0F\x42\x40\x00\x00\x00\x00\x03\xEB" + "The payload of this test isn't parsed.", + 54, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + + /* + ------------------------------------------------------------------------------ + ping frame + ------------------------------------------------------------------------------ + */ + /* Regular test: Ping frame with no payload but with mask (decoder = server) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\x80\x00\x00\x00\x00", + 6, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 6); + /* Regular test: Ping frame with no payload (decoder = client) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\x00", + 2, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 2); + /* Fail test: Ping frame with no payload and no mask (decoder = server) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\x00", + 2, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Fail test: Ping frame with no payload but with mask (decoder = client) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\x80\x00\x00\x00\x00", + 6, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Regular test: Ping frame with some (masked) payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\x88\x01\x20\x03\x40\xFF\xFF\xFF\xFF\x00\x00\x00\x00", + 14, + "\xFE\xDF\xFC\xBF\x01\x20\x03\x40", + 8, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 14); + /* Edge test (success): Ping frame with one byte of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\x81\x00\x00\x00\x00" "a", + 7, + "a", + 1, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 7); + /* Edge test (success): Ping frame with 125 bytes of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\xFD\x00\x00\x00\x00" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 131, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 125, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 131); + /* Edge test (fail): Ping frame with 126 bytes of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\xFE\x00\x7E\x00\x00\x00\x00" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + 134, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Regular test: Ping frame with UTF-8 data */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\x90\x00\x00\x00\x00" "Ping is bin" + "\xC3\xA4" "ry.", + 22, + "Ping is bin" "\xC3\xA4" "ry.", + 16, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 22); + /* Regular test: Ping frame with invalid UTF-8 data */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\x8F\x00\x00\x00\x00" "Ping is bin" "\xFF" + "ry.", + 21, + "Ping is bin" "\xFF" "ry.", + 15, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 21); + /* Regular test: Ping frame with broken UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\x8F\x00\x00\x00\x00" "Ping is bin" "\xC3" + "ry.", + 21, + "Ping is bin" "\xC3" "ry.", + 15, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 21); + + /* + ------------------------------------------------------------------------------ + pong frame + ------------------------------------------------------------------------------ + */ + /* Regular test: Pong frame with no payload but with mask (decoder = server) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\x80\x00\x00\x00\x00", + 6, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 6); + /* Regular test: Pong frame with no payload (decoder = client) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\x00", + 2, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 2); + /* Fail test: Pong frame with no payload and no mask (decoder = server) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\x00", + 2, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Fail test: Pong frame with no payload but with mask (decoder = client) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_CLIENT + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\x80\x00\x00\x00\x00", + 6, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Regular test: Pong frame with some (masked) payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\x88\x01\x20\x03\x40\xFF\xFF\xFF\xFF\x00\x00\x00\x00", + 14, + "\xFE\xDF\xFC\xBF\x01\x20\x03\x40", + 8, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 14); + /* Edge test (success): Pong frame with one byte of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\x81\x00\x00\x00\x00" "a", + 7, + "a", + 1, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 7); + /* Edge test (success): Pong frame with 125 bytes of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\xFD\x00\x00\x00\x00" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 131, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 125, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 131); + /* Edge test (fail): Pong frame with 126 bytes of payload */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\xFE\x00\x7E\x00\x00\x00\x00" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + 134, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 1); + /* Regular test: Pong frame with UTF-8 data */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\x90\x00\x00\x00\x00" "Pong is bin" + "\xC3\xA4" "ry.", + 22, + "Pong is bin" "\xC3\xA4" "ry.", + 16, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 22); + /* Regular test: Pong frame with invalid UTF-8 data */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\x8F\x00\x00\x00\x00" "Pong is bin" "\xFF" + "ry.", + 21, + "Pong is bin" "\xFF" "ry.", + 15, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 21); + /* Regular test: Pong frame with broken UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8A\x8F\x00\x00\x00\x00" "Pong is bin" "\xC3" + "ry.", + 21, + "Pong is bin" "\xC3" "ry.", + 15, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 21); + + /* + ------------------------------------------------------------------------------ + fragmentation + ------------------------------------------------------------------------------ + */ + /* Regular test: Fragmented, masked text frame, we are the server and don't want fragments as caller */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x80\x82\x3d\x37\xfa\x21\x51\x58", + 17, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + /* Regular test: Fragmented, masked text frame, we are the server and don't want fragments as caller, but call decode two times */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x80\x82\x3d\x37\xfa\x21\x51\x58", + 17, + NULL, + 0, + MHD_WEBSOCKET_STATUS_OK, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + /* Regular test: Fragmented, masked text frame, we are the server and want fragments, one call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 1, + 0, + "\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x80\x82\x3d\x37\xfa\x21\x51\x58", + 17, + "Hel", + 3, + MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 9); + /* Regular test: Fragmented, masked text frame, we are the server and want fragments, second call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 2, + 0, + "\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x80\x82\x3d\x37\xfa\x21\x51\x58", + 17, + "lo", + 2, + MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + /* Regular test: Fragmented, masked text frame, we are the server and want fragments, third call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 3, + 0, + "\x01\x83\x37\xfa\x21\x3d\x7f\x9f\x4d\x80\x82\x3d\x37\xfa\x21\x51\x58", + 17, + NULL, + 0, + MHD_WEBSOCKET_STATUS_OK, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + + + /* + ------------------------------------------------------------------------------ + invalid flags + ------------------------------------------------------------------------------ + */ + /* Regular test: Template with valid data for the next tests (this one must succeed) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x85\x00\x00\x00\x00Hello", + 11, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Fail test: RSV1 flag set */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x91\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: RSV2 flag set */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\xA1\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: RSV3 flag set */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\xC1\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + + /* + ------------------------------------------------------------------------------ + invalid opcodes + ------------------------------------------------------------------------------ + */ + /* Fail test: Invalid opcode 0 (0 is usually valid, but only if there was a data frame before) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x80\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Invalid opcode 3 */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x83\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Invalid opcode 4 */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x84\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Invalid opcode 5 */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x85\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Invalid opcode 6 */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x86\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Invalid opcode 7 */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x87\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Invalid opcode 0x0B */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8B\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Invalid opcode 0x0C */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8c\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Invalid opcode 0x0D */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8d\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Invalid opcode 0x0E */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8e\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Invalid opcode 0x0F */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x8f\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + + + /* + ------------------------------------------------------------------------------ + control frames without FIN flag + ------------------------------------------------------------------------------ + */ + /* Fail test: Close frame without FIN flag */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x08\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Ping frame without FIN flag */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x09\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Fail test: Pong frame without FIN flag */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x0a\x85\x00\x00\x00\x00Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + + /* + ------------------------------------------------------------------------------ + length checks (without max_payload_len) + ------------------------------------------------------------------------------ + */ + /* Edge test (success): 0 bytes of payload (requires 1 byte length) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x80\x00\x00\x00\x00", + 6, + NULL, + 0, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 6); + /* Edge test (success): 1 byte of payload (requires 1 byte length) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x81\x00\x00\x00\x00" "a", + 7, + "a", + 1, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 7); + /* Edge test (success): 125 bytes of payload (requires 1 byte length) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\xfd\x00\x00\x00\x00" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 131, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 125, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 131); + /* Edge test (success): 126 bytes of payload (requires 2 byte length) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\xfe\x00\x7e\x00\x00\x00\x00" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + 134, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + 126, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 134); + /* Edge test (success): 65535 bytes of payload (requires 2 byte length) */ + allocate_length_test_data (&buf1, + &buf2, + 65535, + "\x81\xfe\xff\xff\x00\x00\x00\x00", + 8); + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + buf1, + 65535 + 8, + buf2, + 65535, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 65535 + 8); + /* Edge test (success): 65536 bytes of payload (requires 8 byte length) */ + allocate_length_test_data (&buf1, + &buf2, + 65536, + "\x81\xff\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00", + 14); + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + buf1, + 65536 + 14, + buf2, + 65536, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 65536 + 14); + /* Regular test: 1 MB of payload */ + allocate_length_test_data (&buf1, + &buf2, + 1048576, + "\x81\xff\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00", + 14); + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + buf1, + 1048576 + 14, + buf2, + 1048576, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 1048576 + 14); + /* Regular test: 100 MB of payload */ + allocate_length_test_data (&buf1, + &buf2, + 104857600, + "\x81\xff\x00\x00\x00\x00\x06\x40\x00\x00\x00\x00\x00\x00", + 14); + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + buf1, + 104857600 + 14, + buf2, + 104857600, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 104857600 + 14); + if (NULL != buf1) + { + free (buf1); + buf1 = NULL; + } + if (NULL != buf2) + { + free (buf2); + buf2 = NULL; + } + /* Edge test (success): Maximum allowed length (here is only the header checked) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\xff\x7f\xff\xff\xff\xff\xff\xff\xff", + 10, + NULL, + 0, + MHD_WEBSOCKET_STATUS_OK, + MHD_WEBSOCKET_VALIDITY_VALID, + 10); + /* Edge test (fail): Too big payload length */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\xff\x80\x00\x00\x00\x00\x00\x00\x00", + 10, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 10); + /* Edge test (fail): Too big payload length */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\xff\xff\xff\xff\xff\xff\xff\xff\xff", + 10, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 10); + /* Fail test: Not the smallest payload length syntax used (2 byte instead of 1 byte) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\xfe\x00\x05\x00\x00\x00\x00" "abcde", + 13, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 4); + /* Fail test: Not the smallest payload length syntax used (8 byte instead of 1 byte) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\xff\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00" + "abcde", + 13, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 10); + /* Fail test: Not the smallest payload length syntax used (8 byte instead of 2 byte) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\xff\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00" + "abcde", + 13, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 10); + + /* + ------------------------------------------------------------------------------ + length checks (with max_payload_len) + ------------------------------------------------------------------------------ + */ + /* Regular test: Frame with less payload than specified as limit */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 100, + 1, + 0, + "\x81\x85\x00\x00\x00\x00" "Hello", + 11, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Edge test (success): Frame with the same payload as the specified limit */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 5, + 1, + 0, + "\x81\x85\x00\x00\x00\x00" "Hello", + 11, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Edge test (fail): Frame with more payload than specified as limit */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 4, + 1, + 0, + "\x81\x85\x00\x00\x00\x00" "Hello", + 11, + NULL, + 0, + MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED, + MHD_WEBSOCKET_VALIDITY_INVALID, + 2); + /* Regular test: Fragmented frames with the sum of payload less than specified as limit */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 100, + 1, + 0, + "\x01\x83\x00\x00\x00\x00" + "Hel\x80\x82\x00\x00\x00\x00" "lo", + 17, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + /* Edge test (success): Fragmented frames with the sum of payload equal to the specified limit */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 5, + 1, + 0, + "\x01\x83\x00\x00\x00\x00" + "Hel\x80\x82\x00\x00\x00\x00" "lo", + 17, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + /* Edge test (fail): Fragmented frames with the sum of payload more than specified as limit */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 4, + 1, + 0, + "\x01\x83\x00\x00\x00\x00" + "Hel\x80\x82\x00\x00\x00\x00" "lo", + 17, + NULL, + 0, + MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED, + MHD_WEBSOCKET_VALIDITY_INVALID, + 15); + /* Edge test (success): Fragmented frames with the sum of payload greater than + the specified limit, but we take fragments (one call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 5, + 1, + 0, + "\x01\x83\x00\x00\x00\x00" + "Hel\x80\x82\x00\x00\x00\x00" "lo", + 17, + "Hel", + 3, + MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 9); + /* Edge test (success): Fragmented frames with the sum of payload greater than + the specified limit, but we take fragments (two calls) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 5, + 2, + 0, + "\x01\x83\x00\x00\x00\x00" + "Hel\x80\x82\x00\x00\x00\x00" "lo", + 17, + "lo", + 2, + MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + + /* + ------------------------------------------------------------------------------ + UTF-8 sequences + ------------------------------------------------------------------------------ + */ + /* Regular test: No UTF-8 characters */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 a ", + 16, + " a ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Fail test: A UTF-8 tail character without sequence start character */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xA4 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 7); + /* Regular test: A two byte UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xC3\xA4 ", + 16, + " \xC3\xA4 ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Fail test: A broken two byte UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xC3 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Fail test: A two byte UTF-8 sequence with one UTF-8 tail too much */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xC3\xA4\xA4 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 9); + /* Regular test: A three byte UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEF\x8F\x8F ", + 16, + " \xEF\x8F\x8F ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Fail test: A broken byte UTF-8 sequence (two of three bytes) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEF\x8F ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 9); + /* Fail test: A broken byte UTF-8 sequence (one of three bytes) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEF ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Fail test: A three byte UTF-8 sequence followed by one UTF-8 tail byte */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEF\x8F\x8F\x8F ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 10); + /* Regular test: A four byte UTF-8 sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF2\x8F\x8F\x8F ", + 16, + " \xF2\x8F\x8F\x8F ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Fail test: A broken four byte UTF-8 sequence (three of four bytes) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF2\x8F\x8F ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 10); + /* Fail test: A broken four byte UTF-8 sequence (two of four bytes) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF2\x8F ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 9); + /* Fail test: A broken four byte UTF-8 sequence (one of four bytes) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF2 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Fail test: A four byte UTF-8 sequence followed by UTF-8 tail */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF2\x8F\x8F\x8F\x8F ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 11); + /* Fail test: A five byte UTF-8 sequence (only up to four bytes allowed) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xFB\x8F\x8F\x8F\x8F ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 7); + /* Fail test: A six byte UTF-8 sequence (only up to four bytes allowed) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xFD\x8F\x8F\x8F\x8F\x8F ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 7); + /* Fail test: A seven byte UTF-8 sequence (only up to four bytes allowed) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xFE\x8F\x8F\x8F\x8F\x8F\x8F ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 7); + /* Fail test: A eight byte UTF-8 sequence (only up to four bytes allowed) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xFF\x8F\x8F\x8F\x8F\x8F\x8F\x8F ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 7); + /* Edge test (success): The maxium allowed UTF-8 character */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF4\x8F\xBF\xBF ", + 16, + " \xF4\x8F\xBF\xBF ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (fail): The maxium allowed UTF-8 character + 1 */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF4\x90\x80\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (success): The last valid UTF8-1 character */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \x7F ", + 16, + " \x7F ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (fail): The value after the last valid UTF8-1 character */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 7); + /* Edge test (fail): The value before the first valid UTF8-2 character */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xC1\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 7); + /* Edge test (success): The first valid UTF8-2 character */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xC2\x80 ", + 16, + " \xC2\x80 ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (success): The last valid UTF8-2 character */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xDF\xBF ", + 16, + " \xDF\xBF ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (fail): The value after the lst valid UTF8-2 character */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xE0\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (fail): The value before the first valid UTF8-3 character (tail 1) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xE0\x9F\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (success): The first valid UTF8-3 character (tail 1) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xE0\xA0\x80 ", + 16, + " \xE0\xA0\x80 ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (success): The last valid UTF8-3 character (tail 1) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xE0\xBF\xBF ", + 16, + " \xE0\xBF\xBF ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (fail): The value after the first valid UTF8-3 character (tail 1) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xE0\xC0\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (success): The first valid UTF8-3 character (tail 2) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xE1\x80\x80 ", + 16, + " \xE1\x80\x80 ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (success): The last valid UTF8-3 character (tail 2) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEC\xBF\xBF ", + 16, + " \xEC\xBF\xBF ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (fail): The value after the last valid UTF8-3 character (tail 2) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEC\xC0\xBF ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (fail): The value before the first valid UTF8-3 character (tail 3) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xED\x7F\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (success): The first valid UTF8-3 character (tail 3) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xED\x80\x80 ", + 16, + " \xED\x80\x80 ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (success): The last valid UTF8-3 character (tail 3) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xED\x9F\xBF ", + 16, + " \xED\x9F\xBF ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (fail): The value after the last valid UTF8-3 character (tail 3) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xED\xA0\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (fail): The value before the first valid UTF8-3 character (tail 4) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEE\x7F\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (success): The first valid UTF8-3 character (tail 4) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEE\x80\x80 ", + 16, + " \xEE\x80\x80 ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (success): The last valid UTF8-3 character (tail 4) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEF\xBF\xBF ", + 16, + " \xEF\xBF\xBF ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (fail): The value after the last valid UTF8-3 character (tail 4) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEF\xBF\xC0 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 9); + /* Edge test (fail): The value after the last valid UTF8-3 character (tail 4) #2 */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xEF\xC0\xBF ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (fail): The value before the first valid UTF8-4 character (tail 1) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF0\x8F\x80\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (success): The first valid UTF8-4 character (tail 1) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF0\x90\x80\x80 ", + 16, + " \xF0\x90\x80\x80 ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (success): The last valid UTF8-4 character (tail 1) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF0\xBF\xBF\xBF ", + 16, + " \xF0\xBF\xBF\xBF ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (success): The first valid UTF8-4 character (tail 2) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF1\x80\x80\x80 ", + 16, + " \xF1\x80\x80\x80 ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (success): The last valid UTF8-4 character (tail 2) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF3\xBF\xBF\xBF ", + 16, + " \xF3\xBF\xBF\xBF ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (fail): A value before the last valid UTF8-4 character in the second byte (tail 2) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF3\x7F\x80\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (fail): A value after the last valid UTF8-4 character in the second byte (tail 2) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF3\xC0\x80\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (success): The first valid UTF8-4 character (tail 3) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF4\x80\x80\x80 ", + 16, + " \xF4\x80\x80\x80 ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (success): The last valid UTF8-4 character (tail 3) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF4\x8F\xBF\xBF ", + 16, + " \xF4\x8F\xBF\xBF ", + 10, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 16); + /* Edge test (fail): The value after the last valid UTF8-4 character (tail 3) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF4\x90\x80\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 8); + /* Edge test (fail): The first byte value the last valid UTF8-4 character */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x8A\x00\x00\x00\x00 \xF5\x90\x80\x80 ", + 16, + NULL, + 0, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 7); + + /* + ------------------------------------------------------------------------------ + Unfinished UTF-8 sequence between fragmented text frame + ------------------------------------------------------------------------------ + */ + /* Regular test: UTF-8 sequence between fragments, no fragmentation for the caller */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x01\x8D\x00\x00\x00\x00" "This is my n" + "\xC3\x80\x83\x00\x00\x00\x00\xB6" "te", + 28, + "This is my n" "\xC3\xB6" "te", + 16, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 28); + /* Regular test: UTF-8 sequence between fragments, fragmentation for the caller, 1st call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 1, + 0, + "\x01\x8D\x00\x00\x00\x00" "This is my n" + "\xC3\x80\x83\x00\x00\x00\x00\xB6" "te", + 28, + "This is my n", + 12, + MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 19); + /* Regular test: UTF-8 sequence between fragments, fragmentation for the caller, 2nd call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 2, + 0, + "\x01\x8D\x00\x00\x00\x00" "This is my n" + "\xC3\x80\x83\x00\x00\x00\x00\xB6" "te", + 28, + "\xC3\xB6" "te", + 4, + MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 28); + /* Edge test (success): UTF-8 sequence between fragments, but nothing before, fragmentation for the caller, 1st call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 1, + 0, + "\x01\x81\x00\x00\x00\x00\xC3\x80\x81\x00\x00\x00\x00\xB6", + 14, + NULL, + 0, + MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 7); + /* Edge test (success): UTF-8 sequence between fragments, but nothing before, fragmentation for the caller, 2nd call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 2, + 0, + "\x01\x81\x00\x00\x00\x00\xC3\x80\x81\x00\x00\x00\x00\xB6", + 14, + "\xC3\xB6", + 2, + MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 14); + + /* + ------------------------------------------------------------------------------ + Decoding with broken stream + ------------------------------------------------------------------------------ + */ + /* Failure test: Invalid sequence */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\xFF\x81\x85\x00\x00\x00\x00" "Hello", + 12, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Failure test: Call after invalidated stream */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\xFF\x81\x85\x00\x00\x00\x00" "Hello", + 12, + NULL, + 0, + MHD_WEBSOCKET_STATUS_STREAM_BROKEN, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Failure test: Call after invalidated stream (but with different buffer) */ + { + struct MHD_WebSocketStream*ws; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0)) + { + size_t streambuf_read_len = 0; + char*payload = NULL; + size_t payload_len = 0; + int ret = 0; + ret = MHD_websocket_decode (ws, + "\xFF", + 1, + &streambuf_read_len, + &payload, + &payload_len); + if (MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR != ret) + { + fprintf (stderr, + "Test failed in line %u: The return value should be -1, but is %d\n", + (unsigned int) __LINE__, + (int) ret); + ++failed; + } + else + { + ret = MHD_websocket_decode (ws, + "\x81\x85\x00\x00\x00\x00" "Hello", + 11, + &streambuf_read_len, + &payload, + &payload_len); + if (MHD_WEBSOCKET_STATUS_STREAM_BROKEN != ret) + { + fprintf (stderr, + "Test failed in line %u: The return value should be -2, but is %d\n", + (unsigned int) __LINE__, + (int) ret); + ++failed; + } + } + MHD_websocket_stream_free (ws); + } + else + { + fprintf (stderr, + "Individual test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + + /* + ------------------------------------------------------------------------------ + frame after close frame + ------------------------------------------------------------------------------ + */ + /* Regular test: Close frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x88\x80\x00\x00\x00\x00\x81\x85\x00\x00\x00\x00" + "Hello", + 17, + NULL, + 0, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 6); + /* Failure test: Text frame after close frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x88\x80\x00\x00\x00\x00\x81\x85\x00\x00\x00\x00" + "Hello", + 17, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 6); + /* Failure test: Binary frame after close frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x88\x80\x00\x00\x00\x00\x82\x85\x00\x00\x00\x00" + "Hello", + 17, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 6); + /* Failure test: Continue frame after close frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x88\x80\x00\x00\x00\x00\x80\x85\x00\x00\x00\x00" + "Hello", + 17, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 6); + /* Regular test: Ping frame after close frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x88\x80\x00\x00\x00\x00\x89\x85\x00\x00\x00\x00" + "Hello", + 17, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 17); + /* Regular test: Pong frame after close frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x88\x80\x00\x00\x00\x00\x8A\x85\x00\x00\x00\x00" + "Hello", + 17, + "Hello", + 5, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 17); + /* Regular test: Close frame after close frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x88\x80\x00\x00\x00\x00\x88\x80\x00\x00\x00\x00", + 12, + NULL, + 0, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 12); + + /* + ------------------------------------------------------------------------------ + decoding byte-by-byte + ------------------------------------------------------------------------------ + */ + /* Regular test: Text frame, 2 bytes per loop, 1st call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 2, + "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/", + 23, + NULL, + 0, + MHD_WEBSOCKET_STATUS_OK, + MHD_WEBSOCKET_VALIDITY_VALID, + 2); + /* Regular test: Text frame, 2 bytes per loop, 11th call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 11, + 2, + "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/", + 23, + NULL, + 0, + MHD_WEBSOCKET_STATUS_OK, + MHD_WEBSOCKET_VALIDITY_VALID, + 22); + /* Regular test: Text frame, 2 bytes per loop, 12th call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 12, + 2, + "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/", + 23, + "This is the test.", + 17, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 23); + /* Regular test: Text frame, 1 byte per loop, 1st call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 1, + "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/", + 23, + NULL, + 0, + MHD_WEBSOCKET_STATUS_OK, + MHD_WEBSOCKET_VALIDITY_VALID, + 1); + /* Regular test: Text frame, 1 byte per loop, 22nd call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 22, + 1, + "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/", + 23, + NULL, + 0, + MHD_WEBSOCKET_STATUS_OK, + MHD_WEBSOCKET_VALIDITY_VALID, + 22); + /* Regular test: Text frame, 1 byte per loop, 23rd call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 23, + 1, + "\x81\x91\x01\x02\x04\x08" "Ujm{!kw(uja(ugw|/", + 23, + "This is the test.", + 17, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 23); + + /* + ------------------------------------------------------------------------------ + mix of fragmented data frames and control frames + ------------------------------------------------------------------------------ + */ + /* Regular test: Fragmented text frame mixed with one ping frame (1st call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x89\x80\x00\x00\x00\x00" + "\x80\x8C\x00\x00\x00\x00" "is the test.", + 35, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + /* Regular test: Fragmented text frame mixed with one ping frame (2nd call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x89\x80\x00\x00\x00\x00" + "\x80\x8C\x00\x00\x00\x00" "is the test.", + 35, + "This is the test.", + 17, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 35); + /* Regular test: Fragmented text frame mixed with one close frame (1st call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x88\x80\x00\x00\x00\x00" + "\x80\x8C\x00\x00\x00\x00" "is the test.", + 35, + NULL, + 0, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 17); + /* Fail test: Fragmented text frame mixed with one ping frame (2nd call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x88\x80\x00\x00\x00\x00" + "\x80\x8C\x00\x00\x00\x00" "is the test.", + 35, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 17); + /* Regular test: Fragmented text frame mixed with one ping frame, the caller wants fragments (1st call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 1, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x89\x80\x00\x00\x00\x00" + "\x80\x8C\x00\x00\x00\x00" "is the test.", + 35, + "This ", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Regular test: Fragmented text frame mixed with one ping frame, the caller wants fragments (2nd call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 2, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x89\x80\x00\x00\x00\x00" + "\x80\x8C\x00\x00\x00\x00" "is the test.", + 35, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 17); + /* Regular test: Fragmented text frame mixed with one ping frame, the caller wants fragments (3rd call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 3, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x89\x80\x00\x00\x00\x00" + "\x80\x8C\x00\x00\x00\x00" "is the test.", + 35, + "is the test.", + 12, + MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 35); + + /* + ------------------------------------------------------------------------------ + mix of fragmented data frames and data frames + ------------------------------------------------------------------------------ + */ + /* Fail test: Fragmented text frame mixed with one non-fragmented binary frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x82\x81\x00\x00\x00\x00" + "a\x80\x8C\x00\x00\x00\x00" "is the test.", + 36, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 11); + /* Regular test: Fragmented text frame mixed with one non-fragmented binary frame; the caller wants fragments; 1st call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 1, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x82\x81\x00\x00\x00\x00" + "a\x80\x8C\x00\x00\x00\x00" "is the test.", + 36, + "This ", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Fail test: Fragmented text frame mixed with one non-fragmented binary frame; the caller wants fragments; 2nd call */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0, + 2, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x82\x81\x00\x00\x00\x00" + "a\x80\x8C\x00\x00\x00\x00" "is the test.", + 36, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 11); + /* Fail test: Fragmented text frame mixed with one fragmented binary frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x02\x81\x00\x00\x00\x00" + "a\x80\x8C\x00\x00\x00\x00" "is the test.", + 36, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 11); + /* Fail test: Fragmented text frame, continue frame, non-fragmented binary frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x00\x8C\x00\x00\x00\x00" + "is the test.\x82\x81\x00\x00\x00\x00" "a", + 36, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 29); + /* Fail test: Fragmented text frame, continue frame, fragmented binary frame */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x01\x85\x00\x00\x00\x00" + "This \x00\x8C\x00\x00\x00\x00" + "is the test.\x02\x81\x00\x00\x00\x00" "a", + 36, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 29); + + /* + ------------------------------------------------------------------------------ + multiple data frames + ------------------------------------------------------------------------------ + */ + /* Regular test: Text frame, binary frame, text frame (1st call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x81\x85\x00\x00\x00\x00" + "This \x82\x87\x00\x00\x00\x00" + "is the \x81\x85\x00\x00\x00\x00" "test.", + 35, + "This ", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Regular test: Text frame, binary frame, text frame (2nd call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x81\x85\x00\x00\x00\x00" + "This \x82\x87\x00\x00\x00\x00" + "is the \x81\x85\x00\x00\x00\x00" "test.", + 35, + "is the ", + 7, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 24); + /* Regular test: Text frame, binary frame, text frame (3rd call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 3, + 0, + "\x81\x85\x00\x00\x00\x00" + "This \x82\x87\x00\x00\x00\x00" + "is the \x81\x85\x00\x00\x00\x00" "test.", + 35, + "test.", + 5, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 35); + /* + ------------------------------------------------------------------------------ + multiple control frames + ------------------------------------------------------------------------------ + */ + /* Regular test: Ping frame, pong frame, close frame (1st call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + "\x89\x85\x00\x00\x00\x00" + "This \x8A\x87\x00\x00\x00\x00" + "is the \x88\x85\x00\x00\x00\x00" "test.", + 35, + "This ", + 5, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 11); + /* Regular test: Ping frame, pong frame, close frame (2nd call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 2, + 0, + "\x89\x85\x00\x00\x00\x00" + "This \x8A\x87\x00\x00\x00\x00" + "is the \x88\x85\x00\x00\x00\x00" "test.", + 35, + "is the ", + 7, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + 24); + /* Regular test: Ping frame, pong frame, close frame (3rd call) */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 3, + 0, + "\x89\x85\x00\x00\x00\x00" + "This \x8A\x87\x00\x00\x00\x00" + "is the \x88\x85\x00\x00\x00\x00" "test.", + 35, + "test.", + 5, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + 35); + + /* + ------------------------------------------------------------------------------ + generated close frames for errors + ------------------------------------------------------------------------------ + */ + /* Regular test: Close frame generated for protocol error */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS + | + MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR, + 0, + 1, + 0, + "\xFF", + 1, + "\x88\x02\x03\xEA", + 4, + MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 0); + /* Regular test: Close frame generated for UTF-8 sequence error */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS + | + MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR, + 0, + 1, + 0, + "\x81\x85\x00\x00\x00\x00T\xFFst.", + 11, + "\x88\x02\x03\xEF", + 4, + MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR, + MHD_WEBSOCKET_VALIDITY_INVALID, + 7); + /* Regular test: Close frame generated for message size exceeded */ + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS + | + MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR, + 3, + 1, + 0, + "\x81\x85\x00\x00\x00\x00T\xFFst.", + 11, + "\x88\x02\x03\xF1", + 4, + MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED, + MHD_WEBSOCKET_VALIDITY_INVALID, + 2); + + /* + ------------------------------------------------------------------------------ + terminating NUL character + ------------------------------------------------------------------------------ + */ + { + struct MHD_WebSocketStream*ws; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0)) + { + size_t streambuf_read_len = 0; + char*payload = NULL; + size_t payload_len = 0; + int ret = 0; + + /* Regular test: text frame */ + ret = MHD_websocket_decode (ws, + "\x81\x85\x00\x00\x00\x00" "Hello", + 11, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_TEXT_FRAME != ret) || + (5 != payload_len) || + (NULL == payload) || + (0 != memcmp ("Hello", payload, 5 + 1))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + payload = NULL; + } + + /* Regular test: text frame fragment */ + ret = MHD_websocket_decode (ws, + "\x01\x83\x00\x00\x00\x00" + "Hel\x80\x82\x00\x00\x00\x00" "lo", + 17, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_TEXT_FRAME != ret) || + (5 != payload_len) || + (NULL == payload) || + (0 != memcmp ("Hello", payload, 5 + 1))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + payload = NULL; + } + + /* Regular test: binary frame */ + ret = MHD_websocket_decode (ws, + "\x82\x85\x00\x00\x00\x00" "Hello", + 11, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_BINARY_FRAME != ret) || + (5 != payload_len) || + (NULL == payload) || + (0 != memcmp ("Hello", payload, 5 + 1))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + payload = NULL; + } + + /* Regular test: binary frame fragment */ + ret = MHD_websocket_decode (ws, + "\x02\x83\x00\x00\x00\x00" + "Hel\x80\x82\x00\x00\x00\x00" "lo", + 17, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_BINARY_FRAME != ret) || + (5 != payload_len) || + (NULL == payload) || + (0 != memcmp ("Hello", payload, 5 + 1))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + payload = NULL; + } + + MHD_websocket_stream_free (ws); + } + else + { + fprintf (stderr, + "Individual decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + { + struct MHD_WebSocketStream*ws; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS, + 0)) + { + size_t streambuf_read_len = 0; + char*payload = NULL; + size_t payload_len = 0; + int ret = 0; + + /* Regular test: text frame fragment (caller wants fragment, 1st call) */ + ret = MHD_websocket_decode (ws, + "\x01\x83\x00\x00\x00\x00" + "Hel\x80\x82\x00\x00\x00\x00" "lo", + 17, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT != ret) || + (3 != payload_len) || + (NULL == payload) || + (0 != memcmp ("Hel", payload, 3 + 1))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + payload = NULL; + } + + /* Regular test: text frame fragment (caller wants fragment, 2nd call) */ + ret = MHD_websocket_decode (ws, + "\x01\x83\x00\x00\x00\x00" + "Hel\x80\x82\x00\x00\x00\x00" "lo" + + streambuf_read_len, + 17 - streambuf_read_len, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT != ret) || + (2 != payload_len) || + (NULL == payload) || + (0 != memcmp ("lo", payload, 2 + 1))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + payload = NULL; + } + + /* Regular test: text frame fragment with broken UTF-8 sequence (caller wants fragment, 1st call) */ + ret = MHD_websocket_decode (ws, + "\x01\x83\x00\x00\x00\x00" + "He\xC3\x80\x82\x00\x00\x00\x00" "\xB6o", + 17, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_TEXT_FRAGMENT != ret) || + (2 != payload_len) || + (NULL == payload) || + (0 != memcmp ("He", payload, 2 + 1))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + payload = NULL; + } + + /* Regular test: text frame fragment with broken UTF-8 sequence (caller wants fragment, 2nd call) */ + ret = MHD_websocket_decode (ws, + "\x01\x83\x00\x00\x00\x00" + "He\xC3\x80\x82\x00\x00\x00\x00" "\xB6o" + + streambuf_read_len, + 17 - streambuf_read_len, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT != ret) || + (3 != payload_len) || + (NULL == payload) || + (0 != memcmp ("\xC3\xB6o", payload, 3 + 1))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + payload = NULL; + } + } + else + { + fprintf (stderr, + "Individual decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + + + /* + ------------------------------------------------------------------------------ + invalid parameters + ------------------------------------------------------------------------------ + */ + { + struct MHD_WebSocketStream*ws; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0)) + { + size_t streambuf_read_len = 0; + char*payload = NULL; + size_t payload_len = 0; + int ret = 0; + + /* Failure test: `ws` is NULL */ + payload = (char *) (uintptr_t) 0xBAADF00D; + payload_len = 0x87654321; + streambuf_read_len = 1000; + ret = MHD_websocket_decode (NULL, + "\x81\x85\x00\x00\x00\x00Hello", + 11, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != payload) || + (0 != payload_len) || + (0 != streambuf_read_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char *) (uintptr_t) 0xBAADF00D) == payload) + { + payload = NULL; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + } + /* Failure test: `buf` is NULL, while `buf_len` != 0 */ + payload = (char *) (uintptr_t) 0xBAADF00D; + payload_len = 0x87654321; + streambuf_read_len = 1000; + ret = MHD_websocket_decode (ws, + NULL, + 11, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != payload) || + (0 != payload_len) || + (0 != streambuf_read_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char *) (uintptr_t) 0xBAADF00D) == payload) + { + payload = NULL; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + } + /* Failure test: `streambuf_read_len` is NULL */ + payload = (char *) (uintptr_t) 0xBAADF00D; + payload_len = 0x87654321; + ret = MHD_websocket_decode (ws, + "\x81\x85\x00\x00\x00\x00Hello", + 11, + NULL, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != payload) || + (0 != payload_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char *) (uintptr_t) 0xBAADF00D) == payload) + { + payload = NULL; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + } + /* Failure test: `payload` is NULL */ + payload_len = 0x87654321; + streambuf_read_len = 1000; + ret = MHD_websocket_decode (ws, + "\x81\x85\x00\x00\x00\x00Hello", + 11, + &streambuf_read_len, + NULL, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != payload_len) || + (0 != streambuf_read_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Failure test: `payload_len` is NULL */ + payload = (char *) (uintptr_t) 0xBAADF00D; + streambuf_read_len = 1000; + ret = MHD_websocket_decode (ws, + "\x81\x85\x00\x00\x00\x00Hello", + 11, + &streambuf_read_len, + &payload, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != payload) || + (0 != streambuf_read_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char *) (uintptr_t) 0xBAADF00D) == payload) + { + payload = NULL; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + } + /* Regular test: `buf` is NULL and `buf_len` is 0 */ + payload = (char *) (uintptr_t) 0xBAADF00D; + payload_len = 0x87654321; + streambuf_read_len = 1000; + ret = MHD_websocket_decode (ws, + NULL, + 0, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL != payload) || + (0 != payload_len) || + (0 != streambuf_read_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char *) (uintptr_t) 0xBAADF00D) == payload) + { + payload = NULL; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + } + /* Regular test: `buf` is not NULL and `buf_len` is 0 */ + payload = (char *) (uintptr_t) 0xBAADF00D; + payload_len = 0x87654321; + streambuf_read_len = 1000; + ret = MHD_websocket_decode (ws, + "\x81\x85\x00\x00\x00\x00Hello", + 0, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL != payload) || + (0 != payload_len) || + (0 != streambuf_read_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char *) (uintptr_t) 0xBAADF00D) == payload) + { + payload = NULL; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + } + + MHD_websocket_stream_free (ws); + } + else + { + fprintf (stderr, + "Parameter decode tests failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + + /* + ------------------------------------------------------------------------------ + validity after temporary out-of-memory + ------------------------------------------------------------------------------ + */ + { + struct MHD_WebSocketStream*ws; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + test_realloc, + test_free)) + { + size_t streambuf_read_len = 0; + char*payload = NULL; + size_t payload_len = 0; + int ret = 0; + + /* Failure test: No memory allocation at the start */ + disable_alloc = 1; + payload = (char *) (uintptr_t) 0xBAADF00D; + payload_len = 0x87654321; + streambuf_read_len = 1000; + ret = MHD_websocket_decode (ws, + "\x81\x85\x00\x00\x00\x00Hello", + 11, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) || + (NULL != payload) || + (0 != payload_len) || + (1000 == streambuf_read_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid (ws))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char *) (uintptr_t) 0xBAADF00D) == payload) + { + payload = NULL; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + } + MHD_websocket_stream_free (ws); + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + test_realloc, + test_free)) + { + /* Failure test: No memory allocation after fragmented frame */ + disable_alloc = 0; + payload = (char *) (uintptr_t) 0xBAADF00D; + payload_len = 0x87654321; + streambuf_read_len = 1000; + ret = MHD_websocket_decode (ws, + "\x01\x83\x00\x00\x00\x00" "Hel", + 9, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (NULL != payload) || + (0 != payload_len) || + (9 != streambuf_read_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid ( + ws))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char *) (uintptr_t) 0xBAADF00D) == payload) + { + payload = NULL; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + } + disable_alloc = 1; + payload = (char *) (uintptr_t) 0xBAADF00D; + payload_len = 0x87654321; + streambuf_read_len = 1000; + ret = MHD_websocket_decode (ws, + "\x80\x82\x00\x00\x00\x00" "lo", + 8, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) || + (NULL != payload) || + (0 != payload_len) || + (1000 == streambuf_read_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid ( + ws))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char *) (uintptr_t) 0xBAADF00D) == payload) + { + payload = NULL; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + } + /* Regular test: Success after memory allocation ok again */ + /* (streambuf_read_len may not be overwritten for this test) */ + disable_alloc = 0; + payload = (char *) (uintptr_t) 0xBAADF00D; + payload_len = 0x87654321; + size_t old_streambuf_read_len = streambuf_read_len; + ret = MHD_websocket_decode (ws, + "\x80\x82\x00\x00\x00\x00lo" + + old_streambuf_read_len, + 8 - old_streambuf_read_len, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_TEXT_FRAME != ret) || + (NULL == payload) || + (5 != payload_len) || + (8 != streambuf_read_len + old_streambuf_read_len) || + (MHD_WEBSOCKET_VALIDITY_VALID != MHD_websocket_stream_is_valid ( + ws)) || + (0 != memcmp ("Hello", payload, 5))) + { + fprintf (stderr, + "Decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char *) (uintptr_t) 0xBAADF00D) == payload) + { + payload = NULL; + } + if (NULL != payload) + { + MHD_websocket_free (ws, payload); + } + + MHD_websocket_stream_free (ws); + } + else + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + else + { + fprintf (stderr, + "Memory decode tests failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + + /* + ------------------------------------------------------------------------------ + memory leak test, when freeing while decoding + ------------------------------------------------------------------------------ + */ + { + disable_alloc = 0; + struct MHD_WebSocketStream*ws; + size_t streambuf_read_len = 0; + char*payload = NULL; + size_t payload_len = 0; + int ret = 0; + + /* Regular test: Free while decoding of data frame */ + open_allocs = 0; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + test_realloc, + test_free)) + { + ret = MHD_websocket_decode (ws, + "\x81\x85\x00\x00\x00\x00Hel", + 9, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (0 != payload_len) || + (NULL != payload) || + (9 != streambuf_read_len) ) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + ret = MHD_websocket_stream_free (ws); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (0 != open_allocs) + { + fprintf (stderr, + "Memory decode test failed in line %u (memory leak detected)\n", + (unsigned int) __LINE__); + ++failed; + } + } + else + { + fprintf (stderr, + "Memory test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + + /* Regular test: Free while decoding of control frame */ + open_allocs = 0; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + test_realloc, + test_free)) + { + ret = MHD_websocket_decode (ws, + "\x88\x85\x00\x00\x00\x00Hel", + 9, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (0 != payload_len) || + (NULL != payload) || + (9 != streambuf_read_len) ) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + ret = MHD_websocket_stream_free (ws); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (0 != open_allocs) + { + fprintf (stderr, + "Memory decode test failed in line %u (memory leak detected)\n", + (unsigned int) __LINE__); + ++failed; + } + } + else + { + fprintf (stderr, + "Memory test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + + /* Regular test: Free while decoding of fragmented data frame */ + open_allocs = 0; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + test_realloc, + test_free)) + { + ret = MHD_websocket_decode (ws, + "\x01\x85\x00\x00\x00\x00Hello", + 11, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (0 != payload_len) || + (NULL != payload) || + (11 != streambuf_read_len) ) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + ret = MHD_websocket_stream_free (ws); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (0 != open_allocs) + { + fprintf (stderr, + "Memory decode test failed in line %u (memory leak detected)\n", + (unsigned int) __LINE__); + ++failed; + } + } + else + { + fprintf (stderr, + "Memory test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Regular test: Free while decoding of continued fragmented data frame */ + open_allocs = 0; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + test_realloc, + test_free)) + { + ret = MHD_websocket_decode (ws, + "\x01\x85\x00\x00\x00\x00Hello", + 11, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (0 != payload_len) || + (NULL != payload) || + (11 != streambuf_read_len) ) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + ret = MHD_websocket_decode (ws, + "\x80\x85\x00\x00\x00\x00Hel", + 9, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (0 != payload_len) || + (NULL != payload) || + (9 != streambuf_read_len) ) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + ret = MHD_websocket_stream_free (ws); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (0 != open_allocs) + { + fprintf (stderr, + "Memory decode test failed in line %u (memory leak detected)\n", + (unsigned int) __LINE__); + ++failed; + } + } + else + { + fprintf (stderr, + "Memory test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Regular test: Free while decoding of control frame during fragmented data frame */ + open_allocs = 0; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&ws, + MHD_WEBSOCKET_FLAG_SERVER + | + MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + test_malloc, + test_realloc, + test_free)) + { + ret = MHD_websocket_decode (ws, + "\x01\x85\x00\x00\x00\x00Hello", + 11, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (0 != payload_len) || + (NULL != payload) || + (11 != streambuf_read_len) ) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + ret = MHD_websocket_decode (ws, + "\x88\x85\x00\x00\x00\x00Hel", + 9, + &streambuf_read_len, + &payload, + &payload_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (0 != payload_len) || + (NULL != payload) || + (9 != streambuf_read_len) ) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + ret = MHD_websocket_stream_free (ws); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + fprintf (stderr, + "Memory decode test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (0 != open_allocs) + { + fprintf (stderr, + "Memory decode test failed in line %u (memory leak detected)\n", + (unsigned int) __LINE__); + ++failed; + } + } + else + { + fprintf (stderr, + "Memory test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + + if (NULL != buf1) + { + free (buf1); + buf1 = NULL; + } + if (NULL != buf2) + { + free (buf2); + buf2 = NULL; + } + return failed != 0 ? 0x04 : 0x00; +} + + +/** + * Test procedure for `MHD_websocket_encode_text()` + */ +int +test_encodes_text () +{ + int failed = 0; + struct MHD_WebSocketStream*wss; + struct MHD_WebSocketStream*wsc; + int ret; + char *buf1 = NULL, *buf2 = NULL; + char*frame = NULL; + size_t frame_len = 0; + int utf8_step = 0; + + if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc, + MHD_WEBSOCKET_FLAG_CLIENT, + 0)) + { + fprintf (stderr, + "No encode text tests possible due to failed stream init in line %u\n", + (unsigned int) __LINE__); + return 0x08; + } + if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss, + MHD_WEBSOCKET_FLAG_SERVER, + 0)) + { + fprintf (stderr, + "No encode text tests possible due to failed stream init in line %u\n", + (unsigned int) __LINE__); + if (NULL != wsc) + MHD_websocket_stream_free (wsc); + return 0x08; + } + + /* + ------------------------------------------------------------------------------ + Encoding + ------------------------------------------------------------------------------ + */ + /* Regular test: Some data without UTF-8, we are server */ + ret = MHD_websocket_encode_text (wss, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x81\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Some data without UTF-8, we are client */ + ret = MHD_websocket_encode_text (wsc, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (15 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + "blablabla", + 9, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Regular test: Some data with UTF-8, we are server */ + ret = MHD_websocket_encode_text (wss, + "bla" "\xC3\xA4" "blabla", + 11, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (13 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x81\x0B" "bla" "\xC3\xA4" "blabla", 13))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Some data with UTF-8, we are client */ + ret = MHD_websocket_encode_text (wsc, + "bla" "\xC3\xA4" "blabla", + 11, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (17 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + "bla" "\xC3\xA4" "blabla", + 11, + MHD_WEBSOCKET_STATUS_TEXT_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Edge test (success): Some data with NUL characters, we are server */ + ret = MHD_websocket_encode_text (wss, + "bla" "\0\0\0" "bla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x81\x09" "bla" "\0\0\0" "bla", 11))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: Some data with broken UTF-8, we are server */ + ret = MHD_websocket_encode_text (wss, + "bla" "\xC3" "blabla", + 10, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Fragmentation + ------------------------------------------------------------------------------ + */ + /* Regular test: Some data without UTF-8 */ + ret = MHD_websocket_encode_text (wss, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x81\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: First fragment without UTF-8 */ + ret = MHD_websocket_encode_text (wss, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_FIRST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x01\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Middle fragment without UTF-8 */ + ret = MHD_websocket_encode_text (wss, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x00\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment without UTF-8 */ + ret = MHD_websocket_encode_text (wss, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): First fragment with UTF-8 on the edge */ + ret = MHD_websocket_encode_text (wss, + "blablabl\xC3", + 9, + MHD_WEBSOCKET_FRAGMENTATION_FIRST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1 != utf8_step) || + (0 != memcmp (frame, "\x01\x09" "blablabl\xC3", 11))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Last fragment with UTF-8 on the edge */ + ret = MHD_websocket_encode_text (wss, + "\xA4" "blablabla", + 10, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (12 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0A" "\xA4" "blablabla", 12))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: Last fragment with UTF-8 on the edge (here with wrong old utf8_step) */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_NORMAL; + ret = MHD_websocket_encode_text (wss, + "\xA4" "blablabla", + 10, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment with UTF-8 on the edge for UTF2TAIL_1OF1 */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1; + ret = MHD_websocket_encode_text (wss, + "\xA4" "blablabla", + 10, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (12 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0A" "\xA4" "blablabla", 12))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment with UTF-8 on the edge for UTF3TAIL1_1OF2 */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2; + ret = MHD_websocket_encode_text (wss, + "\xA0\x80" "blablabla", + 11, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (13 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0B" "\xA0\x80" "blablabla", 13))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment with UTF-8 on the edge for UTF3TAIL2_1OF2 */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2; + ret = MHD_websocket_encode_text (wss, + "\x80\x80" "blablabla", + 11, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (13 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0B" "\x80\x80" "blablabla", 13))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment with UTF-8 on the edge for UTF3TAIL_1OF2 */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2; + ret = MHD_websocket_encode_text (wss, + "\x80\x80" "blablabla", + 11, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (13 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0B" "\x80\x80" "blablabla", 13))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment with UTF-8 on the edge for UTF3TAIL_2OF2 */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2; + ret = MHD_websocket_encode_text (wss, + "\x80" " blablabla", + 11, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (13 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0B" "\x80" " blablabla", 13))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment with UTF-8 on the edge for UTF4TAIL1_1OF3 */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3; + ret = MHD_websocket_encode_text (wss, + "\x90\x80\x80" "blablabla", + 12, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (14 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0C" "\x90\x80\x80" "blablabla", 14))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment with UTF-8 on the edge for UTF4TAIL2_1OF3 */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3; + ret = MHD_websocket_encode_text (wss, + "\x80\x80\x80" "blablabla", + 12, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (14 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0C" "\x80\x80\x80" "blablabla", 14))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment with UTF-8 on the edge for UTF4TAIL_1OF3 */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3; + ret = MHD_websocket_encode_text (wss, + "\x80\x80\x80" "blablabla", + 12, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (14 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0C" "\x80\x80\x80" "blablabla", 14))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment with UTF-8 on the edge for UTF4TAIL_2OF3 */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3; + ret = MHD_websocket_encode_text (wss, + "\x80\x80" " blablabla", + 12, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (14 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0C" "\x80\x80" " blablabla", 14))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment with UTF-8 on the edge for UTF4TAIL_3OF3 */ + utf8_step = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3; + ret = MHD_websocket_encode_text (wss, + "\x80" " blablabla", + 12, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (14 != frame_len) || + (NULL == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x0C" "\x80" " blablabla", 14))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Length checks + ------------------------------------------------------------------------------ + */ + /* Edge test (success): Text frame without data */ + ret = MHD_websocket_encode_text (wss, + NULL, + 0, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x81\x00", 2))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Text frame with 1 byte of data */ + ret = MHD_websocket_encode_text (wss, + "a", + 1, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (3 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x81\x01" "a", 3))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Text frame with 125 bytes of data */ + ret = MHD_websocket_encode_text (wss, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 125, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (127 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x81\x7D" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 127))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Text frame with 126 bytes of data */ + ret = MHD_websocket_encode_text (wss, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + 126, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (130 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x81\x7E\x00\x7E" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + 130))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Text frame with 65535 bytes of data */ + allocate_length_test_data (&buf1, + &buf2, + 65535, + "\x81\x7E\xFF\xFF", + 4); + ret = MHD_websocket_encode_text (wss, + buf2, + 65535, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (65535 + 4 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, buf1, 65535 + 4))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Text frame with 65536 bytes of data */ + allocate_length_test_data (&buf1, + &buf2, + 65536, + "\x81\x7F\x00\x00\x00\x00\x00\x01\x00\x00", + 10); + ret = MHD_websocket_encode_text (wss, + buf2, + 65536, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (65536 + 10 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, buf1, 65536 + 10))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Text frame with 100 MB of data */ + allocate_length_test_data (&buf1, + &buf2, + 104857600, + "\x81\x7F\x00\x00\x00\x00\x06\x40\x00\x00", + 10); + ret = MHD_websocket_encode_text (wss, + buf2, + 104857600, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (104857600 + 10 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, buf1, 104857600 + 10))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + if (NULL != buf1) + { + free (buf1); + buf1 = NULL; + } + if (NULL != buf2) + { + free (buf2); + buf2 = NULL; + } + /* Fail test: frame_len is greater than 0x7FFFFFFFFFFFFFFF + (this is the maximum allowed payload size) */ + frame_len = 0; + ret = MHD_websocket_encode_text (wss, + "abc", + (uint64_t) 0x8000000000000000, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Wrong parameters + ------------------------------------------------------------------------------ + */ + /* Fail test: `ws` not passed */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_text (NULL, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `payload_utf8` not passed, but `payload_utf8_len` != 0 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_text (wss, + NULL, + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: `payload_utf8` passed, but `payload_utf8_len` == 0 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_text (wss, + "abc", + 0, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (((char*) (uintptr_t) 0xBAADF00D) == frame) || + (0 != memcmp (frame, "\x81\x00", 2))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `frame` not passed */ + frame_len = 0x87654321; + ret = MHD_websocket_encode_text (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + NULL, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Fail test: `frame_len` not passed */ + frame = (char*) (uintptr_t) 0xBAADF00D; + ret = MHD_websocket_encode_text (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + NULL, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: `utf8_step` passed for non-fragmentation + (is allowed and `utf8_step` will be filled then) */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + utf8_step = -99; + ret = MHD_websocket_encode_text (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (5 != frame_len) || + (NULL == frame) || + (((char*) (uintptr_t) 0xBAADF00D) == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x81\x03" "abc", 5))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `utf8_step` passed for non-fragmentation with invalid UTF-8 + (is allowed and `utf8_step` will be filled then) */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + utf8_step = -99; + ret = MHD_websocket_encode_text (wss, + "ab\xC3", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) || + (MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1 != utf8_step) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `utf8_step` not passed for fragmentation #1 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_text (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_FIRST, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `utf8_step` not passed for fragmentation #2 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_text (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `utf8_step` not passed for fragmentation #3 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_text (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: `utf8_step` passed for fragmentation #1 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + utf8_step = -99; + ret = MHD_websocket_encode_text (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_FIRST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (5 != frame_len) || + (NULL == frame) || + (((char*) (uintptr_t) 0xBAADF00D) == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x01\x03" "abc", 5))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: `utf8_step` passed for fragmentation #2 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + utf8_step = MHD_WEBSOCKET_UTF8STEP_NORMAL; + ret = MHD_websocket_encode_text (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (5 != frame_len) || + (NULL == frame) || + (((char*) (uintptr_t) 0xBAADF00D) == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x00\x03" "abc", 5))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: `utf8_step` passed for fragmentation #3 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + utf8_step = MHD_WEBSOCKET_UTF8STEP_NORMAL; + ret = MHD_websocket_encode_text (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (5 != frame_len) || + (NULL == frame) || + (((char*) (uintptr_t) 0xBAADF00D) == frame) || + (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step) || + (0 != memcmp (frame, "\x80\x03" "abc", 5))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `fragmentation` has an invalid value */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + utf8_step = -99; + ret = MHD_websocket_encode_text (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_LAST + 1, + &frame, + &frame_len, + &utf8_step); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) || + (-99 != utf8_step) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + validity after temporary out-of-memory + ------------------------------------------------------------------------------ + */ + { + struct MHD_WebSocketStream*wsx; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&wsx, + MHD_WEBSOCKET_FLAG_SERVER, + 0, + test_malloc, + test_realloc, + test_free)) + { + /* Fail test: allocation while no memory available */ + disable_alloc = 1; + ret = MHD_websocket_encode_text (wsx, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wsx, frame); + frame = NULL; + } + /* Regular test: allocation while memory is available again */ + disable_alloc = 0; + ret = MHD_websocket_encode_text (wsx, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (5 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x81\x03" "abc", 5))) + { + fprintf (stderr, + "Encode text test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wsx, frame); + frame = NULL; + } + + MHD_websocket_stream_free (wsx); + } + else + { + fprintf (stderr, + "Couldn't perform memory test for text encoding in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + + if (NULL != buf1) + free (buf1); + if (NULL != buf2) + free (buf2); + if (NULL != wsc) + MHD_websocket_stream_free (wsc); + if (NULL != wss) + MHD_websocket_stream_free (wss); + + return failed != 0 ? 0x08 : 0x00; +} + + +/** + * Test procedure for `MHD_websocket_encode_binary()` + */ +int +test_encodes_binary () +{ + int failed = 0; + struct MHD_WebSocketStream*wss; + struct MHD_WebSocketStream*wsc; + int ret; + char *buf1 = NULL, *buf2 = NULL; + char*frame = NULL; + size_t frame_len = 0; + + if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc, + MHD_WEBSOCKET_FLAG_CLIENT, + 0)) + { + fprintf (stderr, + "No encode binary tests possible due to failed stream init in line %u\n", + (unsigned int) __LINE__); + return 0x10; + } + if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss, + MHD_WEBSOCKET_FLAG_SERVER, + 0)) + { + fprintf (stderr, + "No encode binary tests possible due to failed stream init in line %u\n", + (unsigned int) __LINE__); + if (NULL != wsc) + MHD_websocket_stream_free (wsc); + return 0x10; + } + + /* + ------------------------------------------------------------------------------ + Encoding + ------------------------------------------------------------------------------ + */ + /* Regular test: Some data, we are server */ + ret = MHD_websocket_encode_binary (wss, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x82\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Some data, we are client */ + ret = MHD_websocket_encode_binary (wsc, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (15 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + "blablabla", + 9, + MHD_WEBSOCKET_STATUS_BINARY_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Edge test (success): Some data with NUL characters, we are server */ + ret = MHD_websocket_encode_binary (wss, + "bla" "\0\0\0" "bla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x82\x09" "bla" "\0\0\0" "bla", 11))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Some data which looks like broken UTF-8, we are server */ + ret = MHD_websocket_encode_binary (wss, + "bla" "\xC3" "blabla", + 10, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (12 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x82\x0A" "bla" "\xC3" "blabla", 12))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Fragmentation + ------------------------------------------------------------------------------ + */ + /* Regular test: Some data */ + ret = MHD_websocket_encode_binary (wss, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x82\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: First fragment */ + ret = MHD_websocket_encode_binary (wss, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_FIRST, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x02\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Middle fragment */ + ret = MHD_websocket_encode_binary (wss, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x00\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Last fragment */ + ret = MHD_websocket_encode_binary (wss, + "blablabla", + 9, + MHD_WEBSOCKET_FRAGMENTATION_LAST, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x80\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Length checks + ------------------------------------------------------------------------------ + */ + /* Edge test (success): Binary frame without data */ + ret = MHD_websocket_encode_binary (wss, + NULL, + 0, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x82\x00", 2))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Binary frame with 1 byte of data */ + ret = MHD_websocket_encode_binary (wss, + "a", + 1, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (3 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x82\x01" "a", 3))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Binary frame with 125 bytes of data */ + ret = MHD_websocket_encode_binary (wss, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 125, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (127 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x82\x7D" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 127))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Binary frame with 126 bytes of data */ + ret = MHD_websocket_encode_binary (wss, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + 126, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (130 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x82\x7E\x00\x7E" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + 130))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Binary frame with 65535 bytes of data */ + allocate_length_test_data (&buf1, + &buf2, + 65535, + "\x82\x7E\xFF\xFF", + 4); + ret = MHD_websocket_encode_binary (wss, + buf2, + 65535, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (65535 + 4 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, buf1, 65535 + 4))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Binary frame with 65536 bytes of data */ + allocate_length_test_data (&buf1, + &buf2, + 65536, + "\x82\x7F\x00\x00\x00\x00\x00\x01\x00\x00", + 10); + ret = MHD_websocket_encode_binary (wss, + buf2, + 65536, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (65536 + 10 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, buf1, 65536 + 10))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Binary frame with 100 MB of data */ + allocate_length_test_data (&buf1, + &buf2, + 104857600, + "\x82\x7F\x00\x00\x00\x00\x06\x40\x00\x00", + 10); + ret = MHD_websocket_encode_binary (wss, + buf2, + 104857600, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (104857600 + 10 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, buf1, 104857600 + 10))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + if (NULL != buf1) + { + free (buf1); + buf1 = NULL; + } + if (NULL != buf2) + { + free (buf2); + buf2 = NULL; + } + /* Fail test: `frame_len` is greater than 0x7FFFFFFFFFFFFFFF + (this is the maximum allowed payload size) */ + frame_len = 0; + ret = MHD_websocket_encode_binary (wss, + "abc", + (uint64_t) 0x8000000000000000, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Wrong parameters + ------------------------------------------------------------------------------ + */ + /* Fail test: `ws` not passed */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_binary (NULL, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `payload` not passed, but `payload_len` != 0 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_binary (wss, + NULL, + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: `payload` passed, but `payload_len` == 0 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_binary (wss, + "abc", + 0, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (((char*) (uintptr_t) 0xBAADF00D) == frame) || + (0 != memcmp (frame, "\x82\x00", 2))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `frame` not passed */ + frame_len = 0x87654321; + ret = MHD_websocket_encode_binary (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + NULL, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Fail test: `frame_len` not passed */ + frame = (char*) (uintptr_t) 0xBAADF00D; + ret = MHD_websocket_encode_binary (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `fragmentation` has an invalid value */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_binary (wss, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_LAST + 1, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + validity after temporary out-of-memory + ------------------------------------------------------------------------------ + */ + { + struct MHD_WebSocketStream*wsx; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&wsx, + MHD_WEBSOCKET_FLAG_SERVER, + 0, + test_malloc, + test_realloc, + test_free)) + { + /* Fail test: allocation while no memory available */ + disable_alloc = 1; + ret = MHD_websocket_encode_binary (wsx, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wsx, frame); + frame = NULL; + } + /* Regular test: allocation while memory is available again */ + disable_alloc = 0; + ret = MHD_websocket_encode_binary (wsx, + "abc", + 3, + MHD_WEBSOCKET_FRAGMENTATION_NONE, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (5 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x82\x03" "abc", 5))) + { + fprintf (stderr, + "Encode binary test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wsx, frame); + frame = NULL; + } + + MHD_websocket_stream_free (wsx); + } + else + { + fprintf (stderr, + "Couldn't perform memory test for binary encoding in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + + if (NULL != buf1) + free (buf1); + if (NULL != buf2) + free (buf2); + if (NULL != wsc) + MHD_websocket_stream_free (wsc); + if (NULL != wss) + MHD_websocket_stream_free (wss); + + return failed != 0 ? 0x10 : 0x00; +} + + +/** + * Test procedure for `MHD_websocket_encode_close()` + */ +int +test_encodes_close () +{ + int failed = 0; + struct MHD_WebSocketStream*wss; + struct MHD_WebSocketStream*wsc; + int ret; + char *buf1 = NULL, *buf2 = NULL; + char*frame = NULL; + size_t frame_len = 0; + + if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc, + MHD_WEBSOCKET_FLAG_CLIENT, + 0)) + { + fprintf (stderr, + "No encode close tests possible due to failed stream init in line %u\n", + (unsigned int) __LINE__); + return 0x10; + } + if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss, + MHD_WEBSOCKET_FLAG_SERVER, + 0)) + { + fprintf (stderr, + "No encode close tests possible due to failed stream init in line %u\n", + (unsigned int) __LINE__); + if (NULL != wsc) + MHD_websocket_stream_free (wsc); + return 0x10; + } + + /* + ------------------------------------------------------------------------------ + Encoding + ------------------------------------------------------------------------------ + */ + /* Regular test: Some data, we are server */ + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "blablabla", + 9, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (13 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x88\x0B\x03\xE8" "blablabla", 13))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Some data, we are client */ + ret = MHD_websocket_encode_close (wsc, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "blablabla", + 9, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (17 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + "\x03\xE8" "blablabla", + 11, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Regular test: Close reason without text, we are server */ + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (4 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x88\x02\x03\xE8", 4))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Close reason without text, we are client */ + ret = MHD_websocket_encode_close (wsc, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (8 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + "\x03\xE8", + 2, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Regular test: Close without reason, we are server */ + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_NO_REASON, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x88\x00", 2))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Close without reason, we are client */ + ret = MHD_websocket_encode_close (wsc, + MHD_WEBSOCKET_CLOSEREASON_NO_REASON, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (6 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + NULL, + 0, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Regular test: Close with UTF-8 sequence in reason, we are client */ + ret = MHD_websocket_encode_close (wsc, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "bla" "\xC3\xA4" "blabla", + 11, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (19 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + "\x03\xE8" "bla" "\xC3\xA4" "blabla", + 13, + MHD_WEBSOCKET_STATUS_CLOSE_FRAME, + MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Edge test (success): Close reason with NUL characters, we are server */ + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY, + "bla" "\0\0\0" "bla", + 9, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (13 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x88\x0B\x03\xE9" "bla" "\0\0\0" "bla", 13))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: Some data with broken UTF-8, we are server */ + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "bla" "\xC3" "blabla", + 10, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Length checks + ------------------------------------------------------------------------------ + */ + /* Edge test (success): Close frame without payload */ + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_NO_REASON, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x88\x00", 2))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Close frame only reason code */ + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (4 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x88\x02\x03\xE8", 4))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Close frame with 1 bytes of reason text */ + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "a", + 1, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (5 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x88\x03\x03\xE8" "a", 5))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Close frame with 123 bytes of reason text */ + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456", + 123, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (127 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x88\x7D\x03\xE8" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456", + 127))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (fail): Close frame with 124 bytes of reason text*/ + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567", + 124, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Wrong parameters + ------------------------------------------------------------------------------ + */ + /* Fail test: `ws` not passed */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_close (NULL, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `payload` not passed, but `payload_len` != 0 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + NULL, + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: `payload` passed, but `payload_len` == 0 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "abc", + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (4 != frame_len) || + (NULL == frame) || + (((char*) (uintptr_t) 0xBAADF00D) == frame) || + (0 != memcmp (frame, "\x88\x02\x03\xE8", 4))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `frame` not passed */ + frame_len = 0x87654321; + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "abc", + 3, + NULL, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Fail test: `frame_len` not passed */ + frame = (char*) (uintptr_t) 0xBAADF00D; + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "abc", + 3, + &frame, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: no reason code passed, but reason text */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_close (wss, + MHD_WEBSOCKET_CLOSEREASON_NO_REASON, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (fail): Invalid reason code */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_close (wss, + 1, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (fail): Invalid reason code */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_close (wss, + 999, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Custom reason code */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_close (wss, + 2000, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (7 != frame_len) || + (NULL == frame) || + (((char*) (uintptr_t) 0xBAADF00D) == frame) || + (0 != memcmp (frame, "\x88\x05\x07\xD0" "abc", 7))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + validity after temporary out-of-memory + ------------------------------------------------------------------------------ + */ + { + struct MHD_WebSocketStream*wsx; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&wsx, + MHD_WEBSOCKET_FLAG_SERVER, + 0, + test_malloc, + test_realloc, + test_free)) + { + /* Fail test: allocation while no memory available */ + disable_alloc = 1; + ret = MHD_websocket_encode_close (wsx, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wsx, frame); + frame = NULL; + } + /* Regular test: allocation while memory is available again */ + disable_alloc = 0; + ret = MHD_websocket_encode_close (wsx, + MHD_WEBSOCKET_CLOSEREASON_REGULAR, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (7 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x88\x05\x03\xE8" "abc", 7))) + { + fprintf (stderr, + "Encode close test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wsx, frame); + frame = NULL; + } + + MHD_websocket_stream_free (wsx); + } + else + { + fprintf (stderr, + "Couldn't perform memory test for close encoding in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + + if (NULL != buf1) + free (buf1); + if (NULL != buf2) + free (buf2); + if (NULL != wsc) + MHD_websocket_stream_free (wsc); + if (NULL != wss) + MHD_websocket_stream_free (wss); + + return failed != 0 ? 0x20 : 0x00; +} + + +/** + * Test procedure for `MHD_websocket_encode_ping()` + */ +int +test_encodes_ping () +{ + int failed = 0; + struct MHD_WebSocketStream*wss; + struct MHD_WebSocketStream*wsc; + int ret; + char *buf1 = NULL, *buf2 = NULL; + char*frame = NULL; + size_t frame_len = 0; + + if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc, + MHD_WEBSOCKET_FLAG_CLIENT, + 0)) + { + fprintf (stderr, + "No encode ping tests possible due to failed stream init in line %u\n", + (unsigned int) __LINE__); + return 0x10; + } + if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss, + MHD_WEBSOCKET_FLAG_SERVER, + 0)) + { + fprintf (stderr, + "No encode ping tests possible due to failed stream init in line %u\n", + (unsigned int) __LINE__); + if (NULL != wsc) + MHD_websocket_stream_free (wsc); + return 0x10; + } + + /* + ------------------------------------------------------------------------------ + Encoding + ------------------------------------------------------------------------------ + */ + /* Regular test: Some data, we are server */ + ret = MHD_websocket_encode_ping (wss, + "blablabla", + 9, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x89\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Some data, we are client */ + ret = MHD_websocket_encode_ping (wsc, + "blablabla", + 9, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (15 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + "blablabla", + 9, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Regular test: Ping without payload, we are server */ + ret = MHD_websocket_encode_ping (wss, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x89\x00", 2))) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Ping without payload, we are client */ + ret = MHD_websocket_encode_ping (wsc, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (6 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Regular test: Ping with something like UTF-8 sequence in payload, we are client */ + ret = MHD_websocket_encode_ping (wsc, + "bla" "\xC3\xA4" "blabla", + 11, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (17 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + "bla" "\xC3\xA4" "blabla", + 11, + MHD_WEBSOCKET_STATUS_PING_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Edge test (success): Ping payload with NUL characters, we are server */ + ret = MHD_websocket_encode_ping (wss, + "bla" "\0\0\0" "bla", + 9, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x89\x09" "bla" "\0\0\0" "bla", 11))) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Ping payload with with something which looks like broken UTF-8, we are server */ + ret = MHD_websocket_encode_ping (wss, + "bla" "\xC3" "blabla", + 10, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (12 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x89\x0A" "bla" "\xC3" "blabla", 12))) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Length checks + ------------------------------------------------------------------------------ + */ + /* Edge test (success): Ping frame without payload */ + ret = MHD_websocket_encode_ping (wss, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x89\x00", 2))) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Ping frame with one byte of payload */ + ret = MHD_websocket_encode_ping (wss, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x89\x00", 2))) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Ping frame with 125 bytes of payload */ + ret = MHD_websocket_encode_ping (wss, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 125, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (127 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x89\x7D" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 127))) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (fail): Ping frame with 126 bytes of payload */ + ret = MHD_websocket_encode_ping (wss, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + 126, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Wrong parameters + ------------------------------------------------------------------------------ + */ + /* Fail test: `ws` not passed */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_ping (NULL, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `payload` not passed, but `payload_len` != 0 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_ping (wss, + NULL, + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: `payload` passed, but `payload_len` == 0 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_ping (wss, + "abc", + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (((char*) (uintptr_t) 0xBAADF00D) == frame) || + (0 != memcmp (frame, "\x89\x00", 2))) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `frame` not passed */ + frame_len = 0x87654321; + ret = MHD_websocket_encode_ping (wss, + "abc", + 3, + NULL, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) ) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Fail test: `frame_len` not passed */ + frame = (char*) (uintptr_t) 0xBAADF00D; + ret = MHD_websocket_encode_ping (wss, + "abc", + 3, + &frame, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + validity after temporary out-of-memory + ------------------------------------------------------------------------------ + */ + { + struct MHD_WebSocketStream*wsx; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&wsx, + MHD_WEBSOCKET_FLAG_SERVER, + 0, + test_malloc, + test_realloc, + test_free)) + { + /* Fail test: allocation while no memory available */ + disable_alloc = 1; + ret = MHD_websocket_encode_ping (wsx, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wsx, frame); + frame = NULL; + } + /* Regular test: allocation while memory is available again */ + disable_alloc = 0; + ret = MHD_websocket_encode_ping (wsx, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (5 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x89\x03" "abc", 5))) + { + fprintf (stderr, + "Encode ping test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wsx, frame); + frame = NULL; + } + + MHD_websocket_stream_free (wsx); + } + else + { + fprintf (stderr, + "Couldn't perform memory test for ping encoding in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + + if (NULL != buf1) + free (buf1); + if (NULL != buf2) + free (buf2); + if (NULL != wsc) + MHD_websocket_stream_free (wsc); + if (NULL != wss) + MHD_websocket_stream_free (wss); + + return failed != 0 ? 0x40 : 0x00; +} + + +/** + * Test procedure for `MHD_websocket_encode_pong()` + */ +int +test_encodes_pong () +{ + int failed = 0; + struct MHD_WebSocketStream*wss; + struct MHD_WebSocketStream*wsc; + int ret; + char *buf1 = NULL, *buf2 = NULL; + char*frame = NULL; + size_t frame_len = 0; + + if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wsc, + MHD_WEBSOCKET_FLAG_CLIENT, + 0)) + { + fprintf (stderr, + "No encode pong tests possible due to failed stream init in line %u\n", + (unsigned int) __LINE__); + return 0x10; + } + if (MHD_WEBSOCKET_STATUS_OK != MHD_websocket_stream_init (&wss, + MHD_WEBSOCKET_FLAG_SERVER, + 0)) + { + fprintf (stderr, + "No encode pong tests possible due to failed stream init in line %u\n", + (unsigned int) __LINE__); + if (NULL != wsc) + MHD_websocket_stream_free (wsc); + return 0x10; + } + + /* + ------------------------------------------------------------------------------ + Encoding + ------------------------------------------------------------------------------ + */ + /* Regular test: Some data, we are server */ + ret = MHD_websocket_encode_pong (wss, + "blablabla", + 9, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x8A\x09" "blablabla", 11))) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Some data, we are client */ + ret = MHD_websocket_encode_pong (wsc, + "blablabla", + 9, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (15 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + "blablabla", + 9, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Regular test: Pong without payload, we are server */ + ret = MHD_websocket_encode_pong (wss, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x8A\x00", 2))) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Pong without payload, we are client */ + ret = MHD_websocket_encode_pong (wsc, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (6 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + NULL, + 0, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Regular test: Pong with something like UTF-8 sequence in payload, we are client */ + ret = MHD_websocket_encode_pong (wsc, + "bla" "\xC3\xA4" "blabla", + 11, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (17 != frame_len) || + (NULL == frame) ) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + else + { + failed += test_decode_single (__LINE__, + MHD_WEBSOCKET_FLAG_SERVER + | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS, + 0, + 1, + 0, + frame, + frame_len, + "bla" "\xC3\xA4" "blabla", + 11, + MHD_WEBSOCKET_STATUS_PONG_FRAME, + MHD_WEBSOCKET_VALIDITY_VALID, + frame_len); + } + if (NULL != frame) + { + MHD_websocket_free (wsc, frame); + frame = NULL; + } + /* Edge test (success): Pong payload with NUL characters, we are server */ + ret = MHD_websocket_encode_pong (wss, + "bla" "\0\0\0" "bla", + 9, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (11 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x8A\x09" "bla" "\0\0\0" "bla", 11))) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: Pong payload with with something which looks like broken UTF-8, we are server */ + ret = MHD_websocket_encode_pong (wss, + "bla" "\xC3" "blabla", + 10, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (12 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x8A\x0A" "bla" "\xC3" "blabla", 12))) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Length checks + ------------------------------------------------------------------------------ + */ + /* Edge test (success): Pong frame without payload */ + ret = MHD_websocket_encode_pong (wss, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x8A\x00", 2))) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Pong frame with one byte of payload */ + ret = MHD_websocket_encode_pong (wss, + NULL, + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x8A\x00", 2))) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (success): Pong frame with 125 bytes of payload */ + ret = MHD_websocket_encode_pong (wss, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 125, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (127 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x8A\x7D" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", + 127))) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Edge test (fail): Pong frame with 126 bytes of payload */ + ret = MHD_websocket_encode_pong (wss, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + 126, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + Wrong parameters + ------------------------------------------------------------------------------ + */ + /* Fail test: `ws` not passed */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_pong (NULL, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `payload` not passed, but `payload_len` != 0 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_pong (wss, + NULL, + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Regular test: `payload` passed, but `payload_len` == 0 */ + frame = (char*) (uintptr_t) 0xBAADF00D; + frame_len = 0x87654321; + ret = MHD_websocket_encode_pong (wss, + "abc", + 0, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (2 != frame_len) || + (NULL == frame) || + (((char*) (uintptr_t) 0xBAADF00D) == frame) || + (0 != memcmp (frame, "\x8A\x00", 2))) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + /* Fail test: `frame` not passed */ + frame_len = 0x87654321; + ret = MHD_websocket_encode_pong (wss, + "abc", + 3, + NULL, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (0 != frame_len) ) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Fail test: `frame_len` not passed */ + frame = (char*) (uintptr_t) 0xBAADF00D; + ret = MHD_websocket_encode_pong (wss, + "abc", + 3, + &frame, + NULL); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (((char*) (uintptr_t) 0xBAADF00D) == frame) + { + frame = NULL; + } + if (NULL != frame) + { + MHD_websocket_free (wss, frame); + frame = NULL; + } + + /* + ------------------------------------------------------------------------------ + validity after temporary out-of-memory + ------------------------------------------------------------------------------ + */ + { + struct MHD_WebSocketStream*wsx; + if (MHD_WEBSOCKET_STATUS_OK == MHD_websocket_stream_init2 (&wsx, + MHD_WEBSOCKET_FLAG_SERVER, + 0, + test_malloc, + test_realloc, + test_free)) + { + /* Fail test: allocation while no memory available */ + disable_alloc = 1; + ret = MHD_websocket_encode_pong (wsx, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_MEMORY_ERROR != ret) || + (0 != frame_len) || + (NULL != frame) ) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wsx, frame); + frame = NULL; + } + /* Regular test: allocation while memory is available again */ + disable_alloc = 0; + ret = MHD_websocket_encode_pong (wsx, + "abc", + 3, + &frame, + &frame_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (5 != frame_len) || + (NULL == frame) || + (0 != memcmp (frame, "\x8A\x03" "abc", 5))) + { + fprintf (stderr, + "Encode pong test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + if (NULL != frame) + { + MHD_websocket_free (wsx, frame); + frame = NULL; + } + + MHD_websocket_stream_free (wsx); + } + else + { + fprintf (stderr, + "Couldn't perform memory test for pong encoding in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + } + + if (NULL != buf1) + free (buf1); + if (NULL != buf2) + free (buf2); + if (NULL != wsc) + MHD_websocket_stream_free (wsc); + if (NULL != wss) + MHD_websocket_stream_free (wss); + + return failed != 0 ? 0x80 : 0x00; +} + + +/** + * Test procedure for `MHD_websocket_split_close_reason()` + */ +int +test_split_close_reason () +{ + int failed = 0; + const char*payload; + unsigned short reason_code; + const char*reason_utf8; + size_t reason_utf8_len; + int ret; + + /* + ------------------------------------------------------------------------------ + Normal splits + ------------------------------------------------------------------------------ + */ + /* Regular test: Reason code + Reason text */ + reason_code = 9999; + reason_utf8 = (const char*) (intptr_t) 0xBAADF00D; + reason_utf8_len = 12345; + payload = "\x03\xE8" "abc"; + ret = MHD_websocket_split_close_reason (payload, + 5, + &reason_code, + &reason_utf8, + &reason_utf8_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (MHD_WEBSOCKET_CLOSEREASON_REGULAR != reason_code) || + (3 != reason_utf8_len) || + (payload + 2 != reason_utf8) ) + { + fprintf (stderr, + "split close reason test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Regular test: Reason code */ + reason_code = 9999; + reason_utf8 = (const char*) (intptr_t) 0xBAADF00D; + reason_utf8_len = 12345; + payload = "\x03\xE8"; + ret = MHD_websocket_split_close_reason (payload, + 2, + &reason_code, + &reason_utf8, + &reason_utf8_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (MHD_WEBSOCKET_CLOSEREASON_REGULAR != reason_code) || + (0 != reason_utf8_len) || + (NULL != reason_utf8) ) + { + fprintf (stderr, + "split close reason test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Regular test: No payload */ + reason_code = 9999; + reason_utf8 = (const char*) (intptr_t) 0xBAADF00D; + reason_utf8_len = 12345; + payload = NULL; + ret = MHD_websocket_split_close_reason (payload, + 0, + &reason_code, + &reason_utf8, + &reason_utf8_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) || + (0 != reason_utf8_len) || + (NULL != reason_utf8) ) + { + fprintf (stderr, + "split close reason test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Regular test: `payload` is not NULL given, but `payload_len` == 0 */ + reason_code = 9999; + reason_utf8 = (const char*) (intptr_t) 0xBAADF00D; + reason_utf8_len = 12345; + payload = "abc"; + ret = MHD_websocket_split_close_reason (payload, + 0, + &reason_code, + &reason_utf8, + &reason_utf8_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) || + (0 != reason_utf8_len) || + (NULL != reason_utf8) ) + { + fprintf (stderr, + "split close reason test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + + /* + ------------------------------------------------------------------------------ + Wrong parameters + ------------------------------------------------------------------------------ + */ + /* Fail test: `payload` not passed, but `payload_len` != 0 */ + reason_code = 9999; + reason_utf8 = (const char*) (intptr_t) 0xBAADF00D; + reason_utf8_len = 12345; + payload = NULL; + ret = MHD_websocket_split_close_reason (payload, + 3, + &reason_code, + &reason_utf8, + &reason_utf8_len); + if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) || + (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) || + (0 != reason_utf8_len) || + (NULL != reason_utf8) ) + { + fprintf (stderr, + "split close reason test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Regular test: `reason_code` not passed */ + reason_utf8 = (const char*) (intptr_t) 0xBAADF00D; + reason_utf8_len = 12345; + payload = "\x03\xE8" "abc"; + ret = MHD_websocket_split_close_reason (payload, + 5, + NULL, + &reason_utf8, + &reason_utf8_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (3 != reason_utf8_len) || + (payload + 2 != reason_utf8) ) + { + fprintf (stderr, + "split close reason test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Regular test: `reason_utf8` not passed */ + reason_code = 9999; + reason_utf8_len = 12345; + payload = "\x03\xE8" "abc"; + ret = MHD_websocket_split_close_reason (payload, + 5, + &reason_code, + NULL, + &reason_utf8_len); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (MHD_WEBSOCKET_CLOSEREASON_REGULAR != reason_code) || + (3 != reason_utf8_len) ) + { + fprintf (stderr, + "split close reason test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Regular test: `reason_utf8_len` not passed */ + reason_code = 9999; + reason_utf8 = (const char*) (intptr_t) 0xBAADF00D; + payload = "\x03\xE8" "abc"; + ret = MHD_websocket_split_close_reason (payload, + 5, + &reason_code, + &reason_utf8, + NULL); + if ((MHD_WEBSOCKET_STATUS_OK != ret) || + (MHD_WEBSOCKET_CLOSEREASON_REGULAR != reason_code) || + (payload + 2 != reason_utf8) ) + { + fprintf (stderr, + "split close reason test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + /* Regular test: `reason_code`, `reason_utf8` and `reason_utf8_len` not passed */ + /* (this is not prohibited, although it doesn't really make sense) */ + payload = "\x03\xE8" "abc"; + ret = MHD_websocket_split_close_reason (payload, + 5, + NULL, + NULL, + NULL); + if (MHD_WEBSOCKET_STATUS_OK != ret) + { + fprintf (stderr, + "split close reason test failed in line %u\n", + (unsigned int) __LINE__); + ++failed; + } + + return failed != 0 ? 0x100 : 0x00; +} + + +int +main (int argc, char *const *argv) +{ + unsigned int errorCount = 0; + (void) argc; (void) argv; /* Unused. Silent compiler warning. */ + + /* seed random number generator */ + MHD_websocket_srand ((unsigned long) time (NULL)); + + /* perform tests */ + errorCount += test_inits (); + errorCount += test_accept (); + errorCount += test_decodes (); + errorCount += test_encodes_text (); + errorCount += test_encodes_binary (); + errorCount += test_encodes_close (); + errorCount += test_encodes_ping (); + errorCount += test_encodes_pong (); + errorCount += test_split_close_reason (); + + /* output result */ + if (errorCount != 0) + fprintf (stderr, "Error (code: %u)\n", errorCount); + + return errorCount != 0; /* 0 == pass */ +}