[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd.spec
|
|
[-]
[+]
|
Changed |
_service
^
|
@@ -1,7 +1,7 @@
<services>
<service name="tar_git">
<param name="url">https://github.com/sailfishos-chum/libmicrohttpd</param>
- <param name="revision">0.9.73</param>
+ <param name="revision">0.9.75</param>
<param name="debian">N</param>
<param name="dumb">N</param>
</service>
|
[-]
[+]
|
Deleted |
_service:tar_git:libmicrohttpd-0.9.73.tar.gz/libmicrohttpd/src/testcurl/curl_version_check.c
^
|
@@ -1,184 +0,0 @@
-/*
- This file is part of libmicrohttpd
- Copyright (C) 2007 Christian Grothoff
-
- 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 2, 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 curl_version_check.c
- * @brief verify required cURL version is available to run tests
- * @author Sagie Amir
- */
-
-#include "MHD_config.h"
-#include "platform.h"
-#include <curl/curl.h>
-
-#ifndef WINDOWS
-#include <unistd.h>
-#endif
-
-static int
-parse_version_number (const char **s)
-{
- int i = 0;
- char num[17];
-
- while (i < 16 && ((**s >= '0') & (**s <= '9')))
- {
- num[i] = **s;
- (*s)++;
- i++;
- }
-
- num[i] = '\0';
-
- return atoi (num);
-}
-
-
-const char *
-parse_version_string (const char *s, int *major, int *minor, int *micro)
-{
- if (! s)
- return NULL;
- *major = parse_version_number (&s);
- if (*s != '.')
- return NULL;
- s++;
- *minor = parse_version_number (&s);
- if (*s != '.')
- return NULL;
- s++;
- *micro = parse_version_number (&s);
- return s;
-}
-
-
-#ifdef HTTPS_SUPPORT
-int
-curl_uses_nss_ssl ()
-{
- return (strstr (curl_version (), " NSS/") != NULL) ? 0 : -1;
-}
-
-
-#endif /* HTTPS_SUPPORT */
-
-/*
- * check local libcurl version matches required version
- */
-int
-curl_check_version (const char *req_version)
-{
- const char *ver;
- const char *curl_ver;
-#ifdef HTTPS_SUPPORT
- const char *ssl_ver;
- const char *req_ssl_ver;
-#endif /* HTTPS_SUPPORT */
-
- int loc_major, loc_minor, loc_micro;
- int rq_major, rq_minor, rq_micro;
-
- ver = curl_version ();
-#ifdef HAVE_MESSAGES
- fprintf (stderr, "curl version: %s\n", ver);
-#endif
- /*
- * this call relies on the cURL string to be of the exact following format :
- * 'libcurl/7.16.4 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/0.6.5' OR
- * 'libcurl/7.18.2 GnuTLS/2.4.0 zlib/1.2.3.3 libidn/0.6.5'
- */
- curl_ver = strchr (ver, '/');
- if (curl_ver == NULL)
- return -1;
- curl_ver++;
- /* Parse version numbers */
- if ( (NULL == parse_version_string (req_version, &rq_major, &rq_minor,
- &rq_micro)) ||
- (NULL == parse_version_string (curl_ver, &loc_major, &loc_minor,
- &loc_micro)) )
- return -1;
-
- /* Compare version numbers. */
- if (((loc_major > rq_major)
- || ((loc_major == rq_major) && (loc_minor > rq_minor))
- || ((loc_major == rq_major) && (loc_minor == rq_minor)
- && (loc_micro > rq_micro)) || ((loc_major == rq_major)
- && (loc_minor == rq_minor)
- && (loc_micro == rq_micro) )) == 0)
- {
- fprintf (stderr,
- "Error: running curl test depends on local libcurl version > %s\n",
- req_version);
- return -1;
- }
-
- /*
- * enforce required gnutls/openssl version.
- * TODO use curl version string to assert use of gnutls
- */
-#ifdef HTTPS_SUPPORT
- ssl_ver = strchr (curl_ver, ' ');
- if (ssl_ver == NULL)
- return -1;
- ssl_ver++;
- if (strncmp ("GnuTLS", ssl_ver, strlen ("GNUtls")) == 0)
- {
- ssl_ver = strchr (ssl_ver, '/');
- req_ssl_ver = MHD_REQ_CURL_GNUTLS_VERSION;
- }
- else if (strncmp ("OpenSSL", ssl_ver, strlen ("OpenSSL")) == 0)
- {
- ssl_ver = strchr (ssl_ver, '/');
- req_ssl_ver = MHD_REQ_CURL_OPENSSL_VERSION;
- }
- else if (strncmp ("NSS", ssl_ver, strlen ("NSS")) == 0)
- {
- ssl_ver = strchr (ssl_ver, '/');
- req_ssl_ver = MHD_REQ_CURL_NSS_VERSION;
- }
- else
- {
- fprintf (stderr, "Error: unrecognized curl ssl library\n");
- return -1;
- }
- if (ssl_ver == NULL)
- return -1;
- ssl_ver++;
- if ( (NULL == parse_version_string (req_ssl_ver, &rq_major, &rq_minor,
- &rq_micro)) ||
- (NULL == parse_version_string (ssl_ver, &loc_major, &loc_minor,
- &loc_micro)) )
- return -1;
-
- if (((loc_major > rq_major)
- || ((loc_major == rq_major) && (loc_minor > rq_minor))
- || ((loc_major == rq_major) && (loc_minor == rq_minor)
- && (loc_micro > rq_micro)) || ((loc_major == rq_major)
- && (loc_minor == rq_minor)
- && (loc_micro == rq_micro) )) == 0)
- {
- fprintf (stderr,
- "Error: running curl test depends on local libcurl SSL version > %s\n",
- req_ssl_ver);
- return -1;
- }
-#endif /* HTTPS_SUPPORT */
- return 0;
-}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/.gitmodules
^
|
@@ -1,3 +1,3 @@
[submodule "libmicrohttpd"]
path = libmicrohttpd
- url = https://gnunet.org/git/libmicrohttpd.git
+ url = https://git.gnunet.org/libmicrohttpd.git
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/.gitignore
^
|
@@ -62,3 +62,5 @@
uncrustify.cfg
**.dvi
**.t2d
+src/microhttpd_ws/test_websocket
+.texlipse
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/.gitlab-ci.yml
^
|
@@ -11,14 +11,14 @@
before_script:
# CCache Config
- mkdir -p cache
- - export CCACHE_BASEDIR=${PWD}
- - export CCACHE_DIR=${PWD}/cache
+ - export CCACHE_BASEDIR="${PWD}"
+ - export CCACHE_DIR="${PWD}/cache"
- export CC="ccache gcc"
after_script:
# somehow after_script looses environment
- - export CCACHE_BASEDIR=${PWD}
- - export CCACHE_DIR=${PWD}/cache
+ - export CCACHE_BASEDIR="${PWD}"
+ - export CCACHE_DIR="${PWD}/cache"
- ccache -s
variables:
@@ -35,11 +35,10 @@
gcc/Stretch:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$DEBIAN_BUILD
script:
- - export CFLAGS=$CFLAGS_DEFAULT
+ - export CFLAGS="$CFLAGS_DEFAULT"
- ./bootstrap
- ./configure $CONFIGURE_BASE_FLAGS
- - make -j$(nproc)
- - make check
+ - make -j$(nproc) && make -k check
tags:
- shared
- linux
@@ -58,13 +57,12 @@
Sanitizers/Stretch:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$DEBIAN_BUILD
script:
- - export CFLAGS="$CFLAGS_DEFAULT -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=address"
+ - export CFLAGS="$CFLAGS_DEFAULT"
- ./bootstrap
- export CC="ccache clang"
- - export UBSAN_OPTIONS=print_stacktrace=1
- export ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.8/bin/llvm-symbolizer
- - ./configure $CONFIGURE_BASE_FLAGS --disable-doc
- - make check
+ - ./configure $CONFIGURE_BASE_FLAGS --disable-doc --enable-sanitizers
+ - make -j$(nproc) && make -k check
tags:
- shared
- linux
@@ -79,11 +77,11 @@
Scan-Build/Debian:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$DEBIAN_BUILD
script:
- - export CFLAGS=$CFLAGS_DEFAULT
+ - export CFLAGS="$CFLAGS_DEFAULT"
- ./bootstrap
- scan-build ./configure $CONFIGURE_BASE_FLAGS
- scan-build -v -enable-checker security,nullability --status-bugs -o scan-build make -j$(nproc)
- - scan-build -v -enable-checker security,nullability --status-bugs -o scan-build make check
+ - scan-build -v -enable-checker security,nullability --status-bugs -o scan-build make -k check
tags:
- shared
- linux
@@ -98,6 +96,7 @@
MinGW/Debian:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$MINGW_BUILD
script:
+ - export CFLAGS="$CFLAGS_DEFAULT"
- export CC="ccache $PREFIX-gcc"
- ./bootstrap
- ./configure $CONFIGURE_BASE_FLAGS --build=x86_64-pc-linux-gnu --host=$PREFIX
@@ -109,7 +108,7 @@
dist/Stretch:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$DEBIAN_BUILD
script:
- - export CFLAGS=$CFLAGS_DEFAULT
+ - export CFLAGS="$CFLAGS_DEFAULT"
- ./bootstrap
- ./configure $CONFIGURE_BASE_FLAGS
- make -j$(nproc) dist
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/AUTHORS
^
|
@@ -63,6 +63,7 @@
Jose Bollo <jobol@nonadev.net>
Jonathan McDougall <jonathanmcdougall@gmail.com>
Tim Ruhsen <tim.ruehsen@gmx.de>
+Lawrence Sebald <lawrence.sebald@nasa.gov> (iovec-based responses)
Documentation contributions also came from:
Marco Maggi <marco.maggi-ipsu@poste.it>
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/ChangeLog
^
|
@@ -1,3 +1,337 @@
+Sun 26 Dec 2021 20:30:00 MSK
+ Releasing GNU libmicrohttpd 0.9.75 -EG
+
+December 2021
+ Fixed Makefile warning on MinGW.
+ Fixed compiler warning on MinGW.
+ Fixed "configure" portability (for NetBSD).
+ MSVC project cosmetics.
+ MSVC fixed project to fix linker warning.
+ Fixed compiler warning on some platforms.
+ Further improved test_client_put_stop to get stable results on all
+ platforms.
+ Added workaround for platforms (like OpenBSD) where system monotonic clocks
+ may jump forward and back.
+ Added more checks in test_large_put, increased timeout (was too small for
+ this test). -EG
+
+Sun 19 Dec 2021 18:30:00 MSK
+ Releasing GNU libmicrohttpd 0.9.74 -EG
+
+December 2021
+ Fixed doxy for MHD_suspend_connection().
+ Some code improvements for new test test_client_put_stop.
+ Added special log message if thread creation failed due to system limits.
+ Fully restructured new_connection_process_() to correctly handle errors,
+ fixed missing decrement of number of daemon connections if any error
+ encountered, fixed app notification of connection termination when app has
+ not been notified about connection start, fixed (highly unlikely) reset of
+ the list of connections if reached daemon's connections limit.
+ configure: fixed some compiler warnings reported in config.log.
+ Fixed tests on FreeBSD to support system-limited rate of RST packets and
+ 'blackhole' system setting. -EG
+ Fixed tests for libmagic to really use libmagic in examples. -CG
+ Used tricks in code formatting to workaround uncrustify bugs.
+ configure: improved compatibility with various shells.
+ configure: added selective enable of sanitizers.
+ Fixed compatibility with old GnuTLS versions.
+ Fixed tests compatibility with old libcurl versions.
+ Fixed busy-waiting in test_timeout (fixed CPU load spikes in the test).
+ test_https_time_out: check rewritten, previously it is was no-op.
+ test_upgrade{,_large}: fixed passing of socket value to GnuTLS on W32.
+ Simplified Makefile for HTTPS tests.
+ Added detection of old broken GnuTLS builds (on RHEL6 and clones) and
+ disabled some tests broken with these builds.
+ Muted compiler warnings with old libcurl versions.
+ Reworked dlltool support: added support for weakened oversimplified
+ half-broken llvm-dlltool
+ Silenced MS lib tool warning and MS lib tool invocation.
+ Added Makefiles rules for automatic regeneration of all required files if
+ anything is missing.
+ Added Makefile silent rules support for W32 RC and W32 static libs.
+ Added local patches for autotools (mainly for libtool) to build MHD
+ correctly on modern MinGW64/Clang.
+ Updated HTTP headers macros from registry. -EG
+
+November 2021
+ Clarified comments and doxy for MHD_str* and related tests.
+ MHD_uint32_to_strx(): rewritten for readability and minor optimization,
+ used indexes instead of pointers.
+ Documented in doxy how to use MHD_AccessHandlerCallback.
+ mhd_sockets: added more network error codes.
+ W32 socket pair: set TCP_NODELAY to avoid unwanted buffering and delays.
+ Additional doxy fixes in microhttpd.h.
+ Fixed blocking sockets setting in tests and examples for W32.
+ Added checks for fcntl() results in tests and examples.
+ Added series of tests based on simple HTTP client implementation developed
+ for testing of MHD.
+ Renamed 'early_response' connection flag to 'discard_request' and reworked
+ handling of connection's flags.
+ Clarified request termination reasons doxy, fixed reporting of
+ MHD_REQUEST_TERMINATED_READ_ERROR (previously this code was not really used
+ in reporting).
+ Enforce all libcurl tests exit code to be zero or one.
+ Rewritten client upload processing: removed redundant checks, fixed
+ skipping of chunk closure when not data is not received yet, fixed skipping
+ of the last LF in termination chunk, handle correctly chunk sizes with more
+ than 16 digits (leading zeros are valid according to HTTP RFC), fixed
+ handling of CRCR, LFCR, LFLF, and bare CR as single line delimiters, report
+ error when invalid chunk format is received without waiting to receive
+ (possibly missing) end of the line, reply to the client with special error
+ if chunk size is too large to be handled by MHD (>16 EiB).
+ Added error reply if client used too large request payload (>16 EiB).
+ Fixed return value for MHD_FEATURE_AUTOSUPPRESS_SIGPIPE on W32, now it
+ returns MHD_YES as W32 does not need sigpipe suppression.
+ configure: reordered and improved headers detection. Some headers require
+ other headers to be included before, now configure supports it.
+ Added missing ifdef guard for <stdbool.h>.
+ mhd_sockets: reordered includes for better compatibility.
+ Some code readability and formatting improvements. -EG
+
+October 2021
+ Added test family test_toolarge to check correct handling of the buffers
+ when the size of data is larger than free space.
+ Fixed missing updated of read and write buffers sizes.
+ Added detection and use of supported "noreturn" keyword for function
+ declaration. It should help compiler and static analyser.
+ Added support for leak sanitizer.
+ Fixed analyser errors on W32.
+ Partially reworked memory allocation from the pool, more robust
+ implementation, always track read and write buffers.
+ Added custom memory poisoning in memory pool with address sanitizer.
+ Added missing update of the read buffer size.
+ Addition for doxy for new behaviour of MHD_del_response_header().
+ Added two tests with non-standard symbols in requests.
+ Removed double close of connection with error in headers processing.
+ Respond to the client with error if chunked request has broken chunked
+ encoding as required by HTTP RFC instead of just closing the connection.
+ Fixed request headers processing. Do not recognize bare CR as end of line.
+ Fixed processing of CRCR, bare CR, LFCR, and LFLF as end of the line for
+ request chunked encoding. Now only CRLF or bare LF are recognized as end
+ of line.
+ Added Lawrence Sebald to the AUTHORS file (iovec-based responses).
+ Check for PAGESIZE and PAGE_SIZE macros and check whether they can be used
+ for static variable initialization.
+ Include "MHD_config.h" before all other includes to set macros required to
+ be set before standard includes.
+ Chunked response: abort with error if application returns more data than
+ requested.
+ Monotonic clock: use only native clock on W32 as all other clocks are just
+ wrappers.
+ W32: fixed builds with MSVC, added projects for VS2022, added MSVC
+ universal project that use latest available toolset, use C17 if supported.
+ Chunked response: fixed calculation of number of bytes left to send.
+ microhttpd.h: doxy clarifications for sockets polling.
+ Updated HTTP statuses, methods, and headers names from the registries.
+ Further improved doxy for MHD_add_response_header().
+ A few comments improvements and clarifications.
+ Added internal connection's flag indicating discard of the request. -EG
+ Websockets update by David Gausmann. -DG
+ Fixed reported value for MHD_CONNECTION_INFO_CONNECTION_TIMEOUT.
+ Minor code readability improvements in MHD_set_connection_option().
+ Improved doxy for MHD_get_timeout().
+ Memorypool: minor code improvements. -EG
+
+September 2021
+ Improved system includes headers detection and usage. Removed unused
+ headers detection.
+ Added indirect calculation of maximum values at compile time by
+ using types size detection. These values are used only to mute
+ compiler warnings.
+ Fixed pre-compiler errors if various *_MAX macros defined with
+ non-digits symbols not readable for pre-compiler.
+ Limit number of used CPU cores in tests to 6, unless heavy tests are
+ enabled.
+ Disabled parallel tests with libcurl if heavy tests are enabled.
+ configure: removed '--enable-sanitizer' and added '--enable-sanitizers'
+ parameters. Added testing for supported sanitizers and enabling only
+ supported sanitizers.
+ Added support for run-time sanitizers settings for tests when
+ sanitizers are enabled.
+ Added support for undefined behavior sanitizer without run-time library.
+ Fixed various undefined behavior sanitizer detected errors, improved
+ portability.
+ Fixed how bitwise NOT is used with enum, fixed portability.
+ microhttpd.h: changed macros MHD_CONTENT_READER_* to use ssize_t.
+ test_postprocessor: added more check, improved error reporting, added
+ new test data.
+ postprocessor: fixed undefined behavior (memcpy(), memmove() with zero
+ size and NULL pointer).
+ Updated copyright year in W32 DLLs.
+ postprocessor: fixed empty key processing.
+ test_postprocessor: added tests with hex-encoded values.
+ postprocessor: fixed incomplete processing of the last part of hex-encoded
+ value if data was broken into certain sized pieces.
+ Used type specifiers for printf() from inttypes.h to improved compatibility
+ with various run-time libs. Fallback to standard values if type specifiers
+ are not defined.
+ Added detection of used run-time library (MSVCRT/UCRT) on W32.
+ testcurl: fixed incorrect case-insensitive match for method name. Method
+ name must be checked by using case-sensitive match.
+ microhttpd.h: clarified some doxy descriptions.
+ Prevented potential double sending of error responses.
+ Fixed application notification with MHD_REQUEST_TERMINATED_COMPLETED_OK
+ when error response has been sent (MHD_REQUEST_TERMINATED_WITH_ERROR is
+ used).
+ Avoid trying to send error response if response is already being sent.
+ Improved log error message when error response is processing. -EG
+
+August 2021
+ Silently drop "keep-alive" token from response "connection" header,
+ "keep-alive" cannot be enforced and always enabled if possible.
+ Further improved doxy for MHD_add_response_header().
+ Added detection of the "Date:" header in the response headers set by
+ app at response forming time.
+ Disallow space in response header name, allow tab in response header
+ value.
+ Added internal MHD_uint8_to_str_pad() function.
+ Used internal MHD_uint8_to_str_pad() in datestamp generation function.
+ Added detection and reporting of incorrect "Upgrade" responses. -EG
+ Fixed short busy waiting (up to one second) when connection is going
+ to be closed. -AI
+ Minor improvement for test_callback, test_get_chunked
+ Fixed chunked responses with known size.
+ Added two more tests for chunked response.
+ Fixed chunked responses with predefined data (without data callback).
+ Fixed calculation of the buffer size for the next response chunk.
+ Completely rewritten reply header build function. The old version
+ had several levels of hacks, was unmaintainable, did not follow
+ HTTP specification in details; fixed used caseless header matching
+ where case-sensitive matching must be used; removed two passes of
+ header building. New version use clear logic and can be extended
+ when needed.
+ Changed behaviour: "Connection: keep-alive" is not being sent
+ for HTTP/1.1 connection (as per HTTP RFC).
+ test_get_chunked: fixed error reporting.
+ HTTPS tests: fixed memory leaks if function failed.
+ libcurl tests: improved handling of curl multi_*.
+ Added two tests for correct choice of "Keep-Alive" or "Close".
+ Simplified Makefile for testcurl.
+ Fixed select() error handling in tests.
+ microhttpd.h: minor macro formatting
+ Changed behaviour: if response size is unknown and chunked encoding is
+ allowed, chunked encoding is used even for non-keep-alive connection as
+ required by HTTP RFC.
+ Added two more tests for chunked replies.
+ Simplified keepalive_possible(); added new value for MHD_ConnKeepAlive,
+ added third state "Upgrade".
+ Changed behaviour: used HTTP/1.1 replies for HTTP/1.0 requests as
+ required by HTTP RFC. HTTP/1.0 reply still can be enforced by response
+ flag.
+ Added more doxy for MHD_ResponseFlags, added new names with the same
+ values as old names: MHD_RF_HTTP_1_0_COMPATIBLE_STRICT and
+ MHD_RF_HTTP_1_0_SERVER.
+ Added new value MHD_RF_SEND_KEEP_ALIVE_HEADER to enforce sending of
+ "Connection: keep-alive" even for HTTP/1.1 clients when keep-alive is
+ used.
+ test_get_close_keep_alive: added more combinations of parameters to
+ check.
+ Added separate flag for chunked response in connection instead of
+ reusing the same flag as for chunked request.
+ Added new connection's flag "stop_with_error".
+ Fixed empty first line processing: the request could be not processed
+ unless something else kicks next processing the same connection again.
+ Added new connection states: MHD_CONNECTION_REQ_LINE_RECEIVING,
+ MHD_CONNECTION_FULL_REQ_RECEIVED, MHD_CONNECTION_START_REPLY to
+ simplify states logic.
+ Changed write buffer allocation logic: as connection buffer size is
+ known and fixed, use initially use full buffer for writing and reduce
+ size of part used for writing if another allocation from the same
+ buffer needs to be done. Implemented helper function to automatically
+ reduce the size of read or write part to allocate buffer for other
+ needs.
+ Added define of NDEBUG if neither _DEBUG nor NDEBUG are defined.
+ As accepted sockets inherit non-blocking flag from listening socket
+ on all platform except Linux, track this state to use less number
+ of syscalls.
+ Fixed compiler and static analyser warnings.
+ Moved HTTPS tests helper file to the HTTPS tests directory.
+ Minor Makefiles cleanup.
+ Added support for new monotonic clock ids.
+ Added new internal monotonic clock function with milliseconds accuracy.
+ Fixed support of custom connection timeout in thread-per-connection mode.
+ Added more error checking to test_timeout.
+ microhttpd.h: removed duplicated macro.
+ Refined timeouts handling. Switched from seconds resolution to milliseconds
+ resolution, added automatic detection and support of low-resolution system
+ clock to avoid busy-waiting at connection expiration. Added log message
+ for too large timeout period (> 146 million years) with trim to supported
+ values. -EG
+
+Wed 04 Aug 2021 06:56:52 PM CEST
+ Introduce new MHD_CONNECTION_INFO_HTTP_STATUS. -CG
+
+July 2021
+ Added automatic response flags with detection when response
+ is being formed.
+ Added special processing for response "Connection" headers, combined
+ multiple "Connection" headers into single header.
+ Restructured MSVC project files.
+ Changed MSVC project defaults to Vista+ (WinXP is still supported).
+ Fixed copy-paste error in mhd_aligh.h, added support for MSVC.
+ Added internal function for printing hex and decimals numbers.
+ Reply chunked body handling fixes, used new internal functions
+ instead of snprintf().
+ Added automatic response flag when app sets chunked encoding header.
+ New internal function for chunked reply footer forming. Unification with
+ reply header forming function just over-complicated things and made
+ function hardly maintainable.
+ Added new function MHD_get_reason_phrase_len_for(), related tests and
+ updated scripts for response phrases.
+ Added more tests for chunked replies.
+ Added function to reset connection state after finishing processing of
+ request-reply to prepare for the next request.
+ Added even more tests for chunked replies.
+ Added internal function for printing uint64_t decimal numbers. -EG
+
+June 2021
+ Tests: implemented checking of response footer.
+ Fixed loss of incoming data if more than half of buffer is
+ used for the next request data.
+ Fixed completely broken calculation of request header size.
+ Chunked response: do not ask app callback for more data then
+ it is possible to process (more than 16 MBytes).
+ Check and report if app used wrong response code (>999 or <100)
+ Refuse to add second "Transfer-Encoding" header.
+ HTTPS tests: check whether all libcurl function succeeded.
+ HTTPS tests: implemented new detection of TLS backend.
+ HTTPS tests: fixed tests with new TLS defaults (SSL forbidden).
+ Implemented detection of basic HTTP methods, fixed wrong
+ caseless matching for HTTP method names.
+ MHD_create_response_*() functions: improved doxy.
+ MHD_add_response_header: added detailed comment about automatic
+ headers.
+ Do not allow responses with 1xx codes for HTTP/1.0 requests.
+ Fixed used order of headers: now user response headers are used in
+ the same order as was added by application.
+ Added new internal function MHD_get_response_element_n_().
+ Added detection of more compiler built-ins for bits rotations.
+ Minor optimisation of caseless strings matching.
+ Added MHD_str_remove_token_caseless_() function and tests.
+ Added MHD_str_remove_tokens_caseless_() function and tests. -EG
+
+May 2021
+ Doxy description clarifications for MHD_get_timeout() and related
+ functions.
+ Added MHD_create_response_from_buffer_with_free_callback_cls().
+ Added SHA-1 calculation (required for WebSockets).
+ Added new internal header mhd_aligh.h for checking alignment of
+ variables.
+ Fixed SHA-256 and MD5 calculation with unaligned data.
+ Added tests for hashes with unaligned data.
+ Used compiler built-ins for bits rotations.
+ Added detection of HTTP version at early stage.
+ Added early response of unsupported HTTP version.
+ Fixed wrong caseless matches for HTTP version strings.
+ Added calculation of error responses at compile time (avoided
+ repeated strlen() for known data). -EG
+
+April 2021
+ New test for reply chunked encoding. -EG
+
+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
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/Makefile.am
^
|
@@ -32,10 +32,19 @@
w32/VS2019/hellobrowser.vcxproj w32/VS2019/hellobrowser.vcxproj.filters \
w32/VS2019/simplepost.vcxproj w32/VS2019/largepost.vcxproj \
w32/VS2019/libmicrohttpd.sln
+W32VS2022 = w32/VS2022/libmicrohttpd.vcxproj w32/VS2022/libmicrohttpd.vcxproj.filters \
+ w32/VS2022/hellobrowser.vcxproj w32/VS2022/hellobrowser.vcxproj.filters \
+ w32/VS2022/simplepost.vcxproj w32/VS2022/largepost.vcxproj \
+ w32/VS2022/libmicrohttpd.sln
+W32VSAV = w32/VS-Any-Version/libmicrohttpd.vcxproj w32/VS-Any-Version/libmicrohttpd.vcxproj.filters \
+ w32/VS-Any-Version/hellobrowser.vcxproj w32/VS-Any-Version/hellobrowser.vcxproj.filters \
+ w32/VS-Any-Version/simplepost.vcxproj w32/VS-Any-Version/largepost.vcxproj \
+ w32/VS-Any-Version/libmicrohttpd.sln
+
EXTRA_DIST = \
acinclude.m4 \
libmicrohttpd.pc.in \
- $(W32COMMON) $(W32VS2013) $(W32VS2015) $(W32VS2017) $(W32VS2019)
+ $(W32COMMON) $(W32VS2013) $(W32VS2015) $(W32VS2017) $(W32VS2019) $(W32VS2022) $(W32VSAV)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libmicrohttpd.pc
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/NEWS
^
|
@@ -1,3 +1,172 @@
+Sun 26 Dec 2021 20:30:00 MSK
+Released GNU libmicrohttpd 0.9.75 -EG
+
+ This is a correction release.
+ The main improvement is the implementation of workaround for some
+ OSes (like OpenBSD 7) where "monotonic" clock may jump back. Now
+ MHD is able to automatically detect such situation and recover if
+ the jump is small. This workaround is needed with increased
+ accuracy of connection timeout introduced in previous version, as
+ with lower accuracy (v0.9.73 and before) these jumpbacks were
+ unnoticeable.
+ Other changes: fixed some compiler, Makefile, and configure
+ warnings on specific platforms; one test further improved.
+
+ -- Evgeny Grin (Karlson2k)
+
+
+Sun 19 Dec 2021 18:30:00 MSK
+Released GNU libmicrohttpd 0.9.74
+
+ This release brings a lot of fixes and improvements, and
+ important new features.
+ The most significant addition is the new experimental
+ implementation of WebSockets contributed by David Gausmann. This
+ implementation is not fully tested yet so currently it is disabled
+ by default.
+ Other changes include a lot of improvements and clarifications
+ in doxy comments in microhttpd.h header file, improved compliance
+ with the RFC HTTP specifications, the new implementation of reply
+ header forming, the new implementation of request chunked encoding
+ parsing, new automatic error replies, internal optimisations, and
+ many important fixes, including fixes for long-standing bugs.
+
+ More detailed list of notable changes:
+
+ API changes:
+ + Added new function MHD_get_reason_phrase_len_for().
+ + Added MHD_CONNECTION_INFO_HTTP_STATUS type of information
+ queried by MHD_get_connection_info().
+ + Added new response flag MHD_RF_SEND_KEEP_ALIVE_HEADER to force
+ sending of "keep-alive" header even if not required by RFC.
+ + Added new response creation function
+ MHD_create_response_from_buffer_with_free_callback_cls() with
+ custom cleanup callback.
+ + Added new response flag MHD_RF_HTTP_1_0_COMPATIBLE_STRICT with
+ the same functionality as existing MHD_RF_HTTP_VERSION_1_0_ONLY
+ flag. The old flag will be deprecated.
+ + Added new response flag MHD_RF_HTTP_1_0_SERVER with the same
+ functionality as existing MHD_RF_HTTP_VERSION_1_0_RESPONSE flag.
+ The old flag will be deprecated.
+
+ New features:
+ + Added experimental WebSockets extension with separate header.
+ Disabled by default as it is not fully tested yet.
+ + Added '--enable-sanitizers[=address,undefined,leak,user-poison]'
+ configure parameter (instead of '--enable-sanitizer'),
+ implemented custom memory poisoning for memory pools.
+
+ Improvements and enhancements:
+ * Doxy function descriptions was corrected, clarified, extended,
+ and improved. Now it should be much easier to learn MHD just by
+ reading the headers.
+ * Completely rewritten reply header forming. New implementation is
+ more robust, simpler maintainable and expandable, and better
+ follows RFC HTTP specifications.
+ * Performance improvements: now HTTP version and request method are
+ decoded one time only (previously MHD used string comparison many
+ times during processing the data).
+ * Rewritten request chunked payload decoding. The new
+ implementation better conforms to the HTTP RFC, detects format
+ problems earlier, replies to the clients with description of
+ detected problems, handles untypical (but syntactically correct)
+ values properly.
+ * Added special replies for wrong/unsupported HTTP versions in
+ requests, broken HTTP chunked encoding in requests,
+ * As required by HTTP RFC, added automatic error replies if client
+ used broken chunked encoding, too large chunk size, too large
+ payload size, or broken Content-Length header.
+ * Optimized connection's memory pool handling.
+ * Changed timeout precision from one second to one millisecond.
+ * Added some checks for incorrect user data, reporting problems in
+ MHD log.
+ * Improved performance of hash calculations functions by using
+ compiler built-ins (if available).
+ * Implemented SHA-1 calculations (required for WebSockets).
+ * Added universal MSVC project that works with any (sufficiently
+ new) version of MSVC.
+ * Developed simple HTTP client to test MHD under very special
+ conditions.
+ * Implemented 45 new tests.
+ * Improved existing tests to test more aspects of MHD.
+ * Added check for correct results of system and libcurl functions.
+ * Response headers are checked during forming of responses.
+ * HTTPS tests were improved.
+ * Added rebuild on W32 of all required files if files are missing.
+ * Many internal optimisations and improvements.
+
+ Functionality changes:
+ * Keep-alive header is omitted by default for HTTP/1.1 connections.
+ Use of header can be enforced by response flag.
+ * Chunked encoding is used for HTTP/1.1 non-keep-alive connections
+ for responses with unknown size. Previously MHD used "indication
+ of the end of the response by closing connection" in such cases,
+ however it is not correct for HTTP/1.1 connections as per HTTP
+ RFC.
+ * As required by HTTP RFC, use HTTP/1.1 version instead of HTTP/1.0
+ in reply headers when client is HTTP/1.0 . HTTP/1.0 version can
+ be enforced by response flag.
+ * User response headers are used in replies in the same order as
+ was added by application.
+ * Allowed tab characters in response header values.
+ * All custom "Connection:" response headers are automatically
+ combined into single "Connection:" header.
+ * "keep-alive" token silently dropped from custom "Connection:"
+ response header. "Keep-alive" cannot be enforced and used
+ automatically if possible.
+ * Allow tab character in custom response header value.
+ * Disallow space character in custom response header value.
+ * Do not allow responses with 1xx codes for HTTP/1.0 requests.
+ * Detected and reported incorrect "Upgrade" responses.
+ * W32 targets are changed to Vista+ by default. XP is supported
+ still.
+
+ Fixes:
+ # Fixed short busy-waiting (up to one second) when connection is
+ going to be expired and closed.
+ # Fixed handling of errors during start of new connection, fixed
+ inability to accept new connections in thread-per-connection mode
+ due to the missing decrement of number of daemon's connections if
+ start of new thread is failed.
+ # Fixed incorrect parsing of LFLF, LFCR, CRCR, and bare CR as
+ single linefeed in request header and request chunked payload.
+ Now only CRLF or bare LF are recognized as linefeed.
+ # Fixed response chunked encoding handling. Now it works properly
+ with non-keep-alive connection, with fixed size replies (if
+ chunked was enforced by header), and in other situations.
+ # Other fixes for chunked replies.
+ # Fixed handling of custom connection timeout in thread-per-
+ connection mode.
+ # Fixed wrongly used MHD_REQUEST_TERMINATED_COMPLETED_OK code for
+ application notification when MHD_REQUEST_TERMINATED_WITH_ERROR
+ code must be used.
+ # Fixed code MHD_REQUEST_TERMINATED_READ_ERROR not reported (code
+ MHD_REQUEST_TERMINATED_WITH_ERROR was incorrectly used instead).
+ # Fixed handling of request chunked encoding with untypical
+ formatting.
+ # Fixed processing of last part of hex-encoded values under
+ certain conditions.
+ # Fixed value returned for MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE.
+ # Fixed returned value for MHD_FEATURE_AUTOSUPPRESS_SIGPIPE on W32,
+ now it is MHD_YES as W32 does not need SIGPIPE suppression.
+ # Fixed portability of bitwise NOT for enums values.
+ # Fixed SHA-256 and MD5 calculations with unaligned data.
+ # Fixed incorrect caseless matching for HTTP version.
+ # Fixed incorrect caseless matching for request method.
+ # Fixed compatibility with old GnuTLS versions.
+ # Fixed compiler warnings on 32-bits platforms.
+ # Fixed blocking sockets setting in tests and examples for W32.
+ # Fixed examples to really use libmagic if present.
+ # HTTPS tests were fixed.
+ # Fixed libcurl test with case-insensitive match for HTTP methods,
+ method names must use case-sensitive match.
+ # Fixed tests compatibility with old libcurl versions.
+ # Fixed build on W32 with llvm-dlltool (this tool is too
+ oversimplified)
+
+ -- Evgeny Grin (Karlson2k)
+
+
Sun 25 Apr 2021 14:00:00 MSK
Released GNU libmicrohttpd 0.9.73
@@ -6,7 +175,7 @@
responses, based on the patch contributed by NASA engineers.
Other changes include compatibility with autoconf 2.70+, improved
testsuite compatibility with CI systems, fixed and improved MSVC
- builds, and implemention of ALPN support.
+ builds, and implementation of ALPN support.
More detailed list of notable changes:
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/README
^
|
@@ -11,6 +11,17 @@
License (LGPLv2.1+) and the eCos License. See COPYING for details.
+Joining GNU
+===========
+
+This is a GNU program, developed by the GNU Project and part of the
+GNU Operating System. If you are the author of an awesome program and
+want to join us in writing Free Software, please consider making it an
+official GNU program and become a GNU maintainer. You can find
+instructions on how to do so at http://www.gnu.org/help/evaluation.
+We are looking forward to hacking with you!
+
+
Installation
============
@@ -63,8 +74,8 @@
This is a beta release for libmicrohttpd. Before declaring the
library stable, we should have testcases for the following features:
-- HTTP/1.1 pipelining (need to figure out how to ensure curl pipelines
- -- and it seems libcurl has issues with pipelining,
+- HTTP/1.1 pipelining (need to figure out how to ensure curl pipelines
+ -- and it seems libcurl has issues with pipelining,
see http://curl.haxx.se/mail/lib-2007-12/0248.html)
- resource limit enforcement
- client queuing early response, suppressing 100 CONTINUE
@@ -85,5 +96,3 @@
src/include/microhttpd2.h. The experimental code will need MUCH
more testing and development, you are strongly advised to stick
to microhttpd.h unless you are a MHD developer!
-
-
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/build-aux/config.rpath
^
|
@@ -2,7 +2,7 @@
# Output a system dependent set of variables, describing how to set the
# run time search path of shared libraries in an executable.
#
-# Copyright 1996-2016 Free Software Foundation, Inc.
+# Copyright 1996-2020 Free Software Foundation, Inc.
# Taken from GNU libtool, 2001
# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
#
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/configure.ac
^
|
@@ -1,5 +1,6 @@
# This file is part of libmicrohttpd.
-# (C) 2006-2020 Christian Grothoff (and other contributing authors)
+# (C) 2006-2021 Christian Grothoff (and other contributing authors)
+# (C) 2014-2021 Evgeny Grin (Karlson2k)
#
# libmicrohttpd is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
@@ -22,15 +23,15 @@
#
AC_PREREQ([2.64])
LT_PREREQ([2.4.0])
-AC_INIT([GNU Libmicrohttpd],[0.9.73],[libmicrohttpd@gnu.org])
+AC_INIT([GNU Libmicrohttpd],[0.9.75],[libmicrohttpd@gnu.org])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([silent-rules] [subdir-objects])
AC_CONFIG_HEADERS([MHD_config.h])
AC_CONFIG_MACRO_DIR([m4])
-LIB_VERSION_CURRENT=70
+LIB_VERSION_CURRENT=72
LIB_VERSION_REVISION=0
-LIB_VERSION_AGE=58
+LIB_VERSION_AGE=60
AC_SUBST(LIB_VERSION_CURRENT)
AC_SUBST(LIB_VERSION_REVISION)
AC_SUBST(LIB_VERSION_AGE)
@@ -56,6 +57,7 @@
# Checks for programs.
AC_PROG_AWK
AC_PROG_GREP
+AC_PROG_FGREP
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
@@ -78,6 +80,86 @@
LT_INIT([win32-dll])
LT_LANG([Windows Resource])
+# Check for headers that are ALWAYS required
+AC_CHECK_HEADERS_ONCE([stdio.h string.h stdint.h errno.h limits.h fcntl.h], [],
+ [AC_MSG_ERROR([Compiling libmicrohttpd requires standard POSIX headers files])], [AC_INCLUDES_DEFAULT])
+
+# Check for basic optional headers
+AC_CHECK_HEADERS([stddef.h stdlib.h inttypes.h sys/types.h sys/stat.h unistd.h], [], [], [AC_INCLUDES_DEFAULT])
+
+# Check for clock-specific optional headers
+AC_CHECK_HEADERS([sys/time.h time.h], [], [], [AC_INCLUDES_DEFAULT])
+
+# Check for system information and parameters optional headers
+AC_CHECK_HEADERS([endian.h machine/endian.h sys/endian.h sys/byteorder.h \
+ sys/machine.h machine/param.h sys/param.h sys/isa_defs.h \
+ sys/ioctl.h], [], [], [AC_INCLUDES_DEFAULT])
+
+# Check for network and sockets optional headers
+AC_CHECK_HEADERS([sys/socket.h sys/select.h netinet/in.h arpa/inet.h \
+ netinet/ip.h netinet/tcp.h net/if.h \
+ netdb.h sockLib.h inetLib.h], [], [],
+ [AC_INCLUDES_DEFAULT
+ [
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif /* HAVE_NETINET_IP_H */
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif /* HAVE_NETINET_TCP_H */
+ ]]
+)
+
+# Check for other optional headers
+AC_CHECK_HEADERS([sys/msg.h sys/mman.h signal.h], [], [], [AC_INCLUDES_DEFAULT])
+
+AC_CHECK_HEADER([[search.h]],
+ [
+ gl_FUNC_TSEARCH
+ AS_IF([[test "x$HAVE_TSEARCH" = "x1" && test "x$REPLACE_TSEARCH" != "x1"]],
+ [AC_DEFINE([[HAVE_SEARCH_H]], [[1]],
+ [Define to 1 if you have the <search.h> header file and your system have properly functioning tsearch(), tfind() and tdelete() functions])])
+ ],
+ [], [AC_INCLUDES_DEFAULT])
+
+AM_CONDITIONAL([MHD_HAVE_TSEARCH], [[test "x$ac_cv_header_search_h" = xyes && test "x$HAVE_TSEARCH" = "x1" && test "x$REPLACE_TSEARCH" != "x1"]])
+
+# Optional headers used for tests
+AC_CHECK_HEADERS([sys/sysctl.h netinet/ip_icmp.h netinet/icmp_var.h], [], [],
+ [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif /* HAVE_SYS_SYSCTL_H */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif /* HAVE_NETINET_IP_H */
+#ifdef HAVE_NETINET_IP_ICMP_H
+#include <netinet/ip_icmp.h>
+#endif /* HAVE_NETINET_IP_ICMP_H */
+ ]]
+)
+
# Checks for gettext.
m4_ifdef([AM_GNU_GETTEXT], [
AS_VAR_SET_IF([enable_nls], [], [[enable_nls=no]])
@@ -115,14 +197,6 @@
[LDFLAGS="$LDFLAGS -z relro -z now"])])
-AC_ARG_ENABLE([sanitizer],
- [AS_HELP_STRING([--enable-sanitizer], [enable Address Sanitizer and Undefined Behavior Sanitizer])],
-[AS_IF([test x$enableval = xyes],[
- CFLAGS="$CFLAGS -fsanitize=address,undefined -fno-omit-frame-pointer"
- ])])
-
-
-
# Workaround for libgcrypt
AS_IF([[test "x$lt_sysroot" != "x" && test "x$SYSROOT" = "x"]], [[SYSROOT="$lt_sysroot"]])
@@ -262,7 +336,8 @@
CFLAGS="$CFLAGS $errattr_CFLAGS"
inln_prfx="none"
# Prefer always inline functions
-for inln_prfx_chk in InlineWithAttr __forceinline inline __inline__ __inline _inline _Inline; do
+for inln_prfx_chk in InlineWithAttr __forceinline inline __inline__ __inline _inline _Inline
+do
# Try to link to avoid "symbol undefined" problems at build time
AS_IF([[test "x$inln_prfx_chk" = "xInlineWithAttr"]],
[
@@ -292,6 +367,8 @@
c = sumfn(a, b);
else
c = 0 - sumfn(a, b);
+ if (c)
+ return 0;
]])
],
[[ inln_prfx="$inln_prfx_chk" ]])
@@ -307,6 +384,49 @@
AC_MSG_RESULT([[$inln_prfx]])
CFLAGS="$save_CFLAGS"
+AC_CHECK_HEADERS([stdalign.h], [], [], [AC_INCLUDES_DEFAULT])
+AC_CACHE_CHECK([[for C11 'alignof()' support]], [[mhd_cv_c_alignof]],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+#ifdef HAVE_STDALIGN_H
+#include <stdalign.h>
+#endif
+ ]], [[
+ int var1[(alignof(int) >= 2) ? 1 : -1];
+ int var2[alignof(unsigned int) - 1];
+ int var3[(alignof(char) > 0) ? 1 : -1];
+ int var4[(alignof(long) >= 4) ? 1 : -1];
+
+ /* Mute compiler warnings */
+ var1[0] = var2[0] = var3[0] = 0;
+ var4[0] = 1;
+ if (var1[0] + var2[0] + var3[0] == var4[0])
+ return 1;
+ ]])
+ ], [
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+#ifdef HAVE_STDALIGN_H
+#include <stdalign.h>
+#endif
+ ]], [[
+ /* Should fail if 'alignof()' works */
+ int var1[alignof(nonexisting_type) - 1];
+
+ /* Mute compiler warnings */
+ var1[0] = 1;
+ if (var1[0] + 1 == 1)
+ return 1;
+ ]])
+ ], [[mhd_cv_c_alignof='no']], [[mhd_cv_c_alignof='yes']])
+ ], [[mhd_cv_c_alignof='no']])
+ ])
+AS_VAR_IF([mhd_cv_c_alignof], ["yes"],
+ [AC_DEFINE([[HAVE_C_ALIGNOF]], [1], [Define to 1 if your compiler supports 'alignof()'])])
+
+
# Check system type
shutdown_trig_select='no'
AC_MSG_CHECKING([[for target host OS]])
@@ -349,13 +469,14 @@
AC_MSG_RESULT([[$mhd_host_os]])],
[*cygwin*],
[AC_DEFINE_UNQUOTED(CYGWIN,1,[This is a Cygwin system])
- mhd_host_os='Windows (Cygwin)'
+ mhd_host_os='Windows/Cygwin'
AC_MSG_RESULT([[$mhd_host_os]])
os_is_windows=yes],
[*mingw*],
- [AC_DEFINE_UNQUOTED(MINGW,1,[This is a MinGW system])
- AC_DEFINE_UNQUOTED(WINDOWS,1,[This is a Windows system])
- mhd_host_os='Windows (MinGW)'
+ [
+ AC_DEFINE([MINGW],[1],[This is a MinGW system])
+ AC_DEFINE([WINDOWS],[1],[This is a Windows system])
+ mhd_host_os='Windows/MinGW'
AC_MSG_RESULT([[$mhd_host_os]])
LIBS="$LIBS -lws2_32"
AC_CHECK_HEADERS([winsock2.h ws2tcpip.h], [], [AC_MSG_ERROR([[Winsock2 headers are required for W32]])], [AC_INCLUDES_DEFAULT])
@@ -381,8 +502,8 @@
AC_MSG_RESULT([[$mhd_host_os]])
],
[
- mhd_host_os='unrecognised OS'
- AC_MSG_RESULT([[$mhd_host_os]])
+ AC_MSG_RESULT([unrecognised OS])
+ mhd_host_os="${host_os}"
AC_MSG_WARN([Unrecognised OS $host_os])
AC_DEFINE_UNQUOTED(OTHEROS,1,[Some strange OS])
])
@@ -392,7 +513,7 @@
AS_VAR_IF([os_is_windows], ["yes"],
[
- AC_MSG_CHECKING([[whether target W32 version is specified by precomiler defines]])
+ AC_MSG_CHECKING([[whether target W32 version is specified by precompiler defines]])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
/* Note: check logic is reversed for easy log reading */
#ifdef WINVER
@@ -556,6 +677,49 @@
]
)
+AS_IF([test "x${os_is_windows}" = "xyes" && test "x${os_is_native_w32}" = "xyes"],
+ [
+ AC_CACHE_CHECK([W32 run-time library type], [mhd_cv_wctr_type],
+ [
+ AC_EGREP_CPP([MHDMARKER: UCRT run-time library in use!], [
+#include <stdio.h>
+#if defined(_UCRT)
+#define CRT_STR "MHDMARKER: UCRT run-time library in use!"
+#endif
+#if defined(__MSVCRT_VERSION__)
+#if (__MSVCRT_VERSION__ >= 0xE00) && (__MSVCRT_VERSION__ < 0x1000)
+#define CRT_STR "MHDMARKER: UCRT run-time library in use!"
+#endif
+#if (__MSVCRT_VERSION__ > 0x1400)
+#define CRT_STR "MHDMARKER: UCRT run-time library in use!"
+#endif
+#endif
+
+#ifndef CRT_STR
+#define CRT_STR "MHDMARKER: MSVCRT run-time library in use!"
+#endif
+
+int main(void)
+{
+ printf ("%\n", CRT_STR);
+ return 0;
+}
+ ],
+ [mhd_cv_wctr_type="ucrt"], [mhd_cv_wctr_type="msvcrt"])
+ ]
+ )
+ mhd_host_os="${mhd_host_os}-${mhd_cv_wctr_type}"
+ AS_VAR_IF([mhd_cv_wctr_type], ["msvcrt"],
+ [
+ AX_APPEND_COMPILE_FLAGS([-U__USE_MINGW_ANSI_STDIO -D__USE_MINGW_ANSI_STDIO=0], [CPPFLAGS])
+ AC_SUBST([W32CRT], [MSVCRT])
+ ], [AC_SUBST([W32CRT], [UCRT])]
+ )
+ ]
+)
+
+
+
AC_ARG_WITH([threads],
[AS_HELP_STRING([--with-threads=LIB],[choose threading library (posix, w32, auto, none) [auto]])],
[], [with_threads='auto'])
@@ -651,6 +815,7 @@
[AC_DEFINE([MHD_USE_W32_THREADS],[1],[define to use W32 threads])])])
AM_CONDITIONAL([USE_POSIX_THREADS], [test "x$USE_THREADS" = "xposix"])
AM_CONDITIONAL([USE_W32_THREADS], [test "x$USE_THREADS" = "xw32"])
+AM_CONDITIONAL([USE_THREADS], [test "x$USE_THREADS" != "xnone"])
AM_CONDITIONAL([DISABLE_THREADS], [test "x$USE_THREADS" = "xnone"])
AC_MSG_RESULT([$USE_THREADS])
@@ -705,7 +870,7 @@
])
AS_IF([[test "x$HAVE_THREAD_NAME_FUNC" != "xyes" && test "x$ac_cv_have_decl_pthread_attr_setname_np" = "xyes"]],
- [AC_MSG_CHECKING([[for pthread_attr_setname_np(3) in IBM i form]])
+ [AC_MSG_CHECKING([[for pthread_attr_setname_np(3) in IBM i or Solaris form]])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([[
#include <pthread.h>
@@ -718,7 +883,7 @@
pthread_attr_setname_np(&thr_attr, "name");
pthread_attr_destroy(&thr_attr);
]])],
- [AC_DEFINE([[HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI]], [[1]], [Define if you have IBM i form of pthread_attr_setname_np(3) function.])
+ [AC_DEFINE([[HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI]], [[1]], [Define if you have IBM i form (or Solaris form) of pthread_attr_setname_np(3) function.])
HAVE_THREAD_NAME_FUNC="yes"
AC_MSG_RESULT([[yes]])],
[AC_MSG_RESULT([[no]])]
@@ -746,7 +911,7 @@
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>
#endif
-]], [[int res = pthread_setname_np(pthread_self(), "name", 0);]])],
+]], [[int res = pthread_setname_np(pthread_self(), "name", 0); if (res) return res;]])],
[AC_DEFINE([[HAVE_PTHREAD_SETNAME_NP_NETBSD]], [[1]], [Define if you have NetBSD form (or OSF1 form) of pthread_setname_np(3) function.])
HAVE_THREAD_NAME_FUNC="yes"
AC_MSG_RESULT([[yes]])],
@@ -762,7 +927,7 @@
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>
#endif
-]], [[int res = pthread_setname_np(pthread_self(), "name");]])],
+]], [[int res = pthread_setname_np(pthread_self(), "name"); if (res) return res;]])],
[AC_DEFINE([[HAVE_PTHREAD_SETNAME_NP_GNU]], [[1]], [Define if you have GNU/Linux form of pthread_setname_np(3) function.])
HAVE_THREAD_NAME_FUNC="yes"
AC_MSG_RESULT([[yes]])],
@@ -778,7 +943,7 @@
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>
#endif
-]], [[int res = pthread_setname_np("name");]])],
+]], [[int res = pthread_setname_np("name"); if (res) return res;]])],
[AC_DEFINE([[HAVE_PTHREAD_SETNAME_NP_DARWIN]], [[1]], [Define if you have Darwin form of pthread_setname_np(3) function.])
HAVE_THREAD_NAME_FUNC="yes"
AC_MSG_RESULT([[yes]])],
@@ -830,9 +995,9 @@
#if defined(MHD_USE_POSIX_THREADS) && (defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) || \
defined(HAVE_PTHREAD_SETNAME_NP_GNU) || defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) || defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) || \
defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) )
-int a = 1;
+(void) 0; /* no-op */
#elif defined(MHD_USE_W32_THREADS) && defined(_MSC_FULL_VER)
-int b = 2;
+(void) 0; /* no-op */
#else
#error No thread name function is available.
choke me
@@ -856,16 +1021,38 @@
AM_CONDITIONAL(HAVE_W32, [test "x$os_is_native_w32" = "xyes"])
w32_shared_lib_exp=no
-if test "x$enable_shared" = "xyes" && test "x$os_is_native_w32" = "xyes"; then
- if test "x$ac_cv_use_ms_lib_tool" = "xyes" || test -n "$DLLTOOL"; then
- w32_shared_lib_exp=yes
- else
- AC_MSG_WARN([[GNU dlltool or MS lib.exe is required for creating shared library export on W32]])
- AC_MSG_WARN([[Export library libmicrohttpd.lib will not be created]])
- fi
-fi
-AM_CONDITIONAL(W32_SHARED_LIB_EXP, [test "x$w32_shared_lib_exp" = "xyes"])
-AM_CONDITIONAL(USE_MS_LIB_TOOL, [test "x$ac_cv_use_ms_lib_tool" = "xyes"])
+AS_IF([test "x$enable_shared" = "xyes" && test "x$os_is_native_w32" = "xyes"],
+ [
+ AS_IF([test "x$ac_cv_use_ms_lib_tool" = "xyes" || test -n "$DLLTOOL"],
+ [
+ w32_shared_lib_exp=yes
+ use_expfile="no"
+ AS_VAR_IF([ac_cv_use_ms_lib_tool], ["yes"], [use_expfile="yes"],
+ [
+ AC_CACHE_CHECK([whether $DLLTOOL supports export file generation], [mhd_cv_dlltool_expfile],
+ [
+ AS_IF([AC_RUN_LOG([$DLLTOOL -e conftest.exp >&2 ])],
+ [
+ AS_IF([test -f conftest.exp], [mhd_cv_dlltool_expfile="yes"], [mhd_cv_dlltool_expfile="no"])
+ ], [mhd_cv_dlltool_expfile="no"]
+ )
+ rm -f conftest.exp
+ ]
+ )
+ use_expfile="${mhd_cv_dlltool_expfile}"
+ ]
+ )
+ ],
+ [
+ AC_MSG_WARN([[GNU dlltool or MS lib.exe is required for creating shared library export on W32]])
+ AC_MSG_WARN([[Export library libmicrohttpd.lib will not be created]])
+ ]
+ )
+ ]
+)
+AM_CONDITIONAL([W32_SHARED_LIB_EXP], [test "x$w32_shared_lib_exp" = "xyes"])
+AM_CONDITIONAL([USE_MS_LIB_TOOL], [test "x$ac_cv_use_ms_lib_tool" = "xyes"])
+AM_CONDITIONAL([USE_EXPORT_FILE], [test "x$use_expfile" = "xyes"])
MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER([AC_DEFINE([HAVE_LISTEN_SHUTDOWN],[1],[can use shutdown on listen sockets])])
AM_CONDITIONAL([HAVE_LISTEN_SHUTDOWN], [test "x$mhd_cv_host_shtdwn_trgr_select" = "xyes"])
@@ -935,11 +1122,20 @@
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])
-AS_VAR_IF([enable_heavy_tests], ["yes"], [], [enable_heavy_tests=no])
+AS_VAR_IF([enable_heavy_tests], ["yes"],
+ [
+ HEAVY_TESTS_NOTPARALLEL='.NOTPARALLEL:'
+ AC_DEFINE([_MHD_HEAVY_TESTS], [1], [Define to 1 to enable "heavy" test paths.])
+ ],
+ [
+ enable_heavy_tests=no
+ HEAVY_TESTS_NOTPARALLEL=" "
+ ]
+)
AM_CONDITIONAL([HEAVY_TESTS],[test "x$enable_heavy_tests" = "xyes"])
AC_ARG_ENABLE([[poll]],
@@ -965,7 +1161,7 @@
],[have_poll='no'])
AC_MSG_RESULT([$have_poll])])
AS_IF([test "$enable_poll" = "yes" && test "$have_poll" != "yes"],
- AC_MSG_ERROR([[Support for poll was explicitly requested but cannot be enabled on this platform.]]))
+ [AC_MSG_ERROR([[Support for poll was explicitly requested but cannot be enabled on this platform.]])])
enable_poll="$have_poll"])
AC_ARG_ENABLE([[epoll]],
@@ -975,13 +1171,22 @@
)
AS_IF([test "$enable_epoll" != "no"],
- [AX_HAVE_EPOLL
- AS_IF([test "${ax_cv_have_epoll}" = "yes"],
- [AC_DEFINE([[EPOLL_SUPPORT]],[[1]],[Define to 1 to enable epoll support])
- enable_epoll='yes'],
- [AS_IF([test "$enable_epoll" = "yes"],
- AC_MSG_ERROR([[Support for epoll was explicitly requested but cannot be enabled on this platform.]]))
- enable_epoll='no'])])
+ [
+ AX_HAVE_EPOLL
+ AS_IF([test "${ax_cv_have_epoll}" = "yes"],
+ [
+ AC_DEFINE([[EPOLL_SUPPORT]],[[1]],[Define to 1 to enable epoll support])
+ enable_epoll='yes'
+ ],
+ [
+ AS_IF([test "$enable_epoll" = "yes"],
+ [AC_MSG_ERROR([[Support for epoll was explicitly requested but cannot be enabled on this platform.]])]
+ )
+ enable_epoll='no'
+ ]
+ )
+ ]
+)
AM_CONDITIONAL([MHD_HAVE_EPOLL], [[test "x$enable_epoll" = xyes]])
@@ -999,27 +1204,94 @@
AS_IF([test "x$mhd_cv_have_epoll_create1" = "xyes"],[
AC_DEFINE([[HAVE_EPOLL_CREATE1]], [[1]], [Define if you have epoll_create1 function.])]))
-# Check for headers that are ALWAYS required
-AC_CHECK_HEADERS_ONCE([fcntl.h math.h errno.h limits.h stdio.h locale.h sys/stat.h sys/types.h], [], [AC_MSG_ERROR([Compiling libmicrohttpd requires standard UNIX headers files])], [AC_INCLUDES_DEFAULT])
+AC_CACHE_CHECK([for suported 'noreturn' keyword], [mhd_cv_decl_noreturn],
+ [
+ mhd_cv_decl_noreturn="none"
+ save_CFLAGS="${CFLAGS}"
+ CFLAGS="${CFLAGS} ${errattr_CFLAGS}"
+ for decl_noret in '_Noreturn' '__attribute__((__noreturn__))' '__declspec(noreturn)'
+ do
+ AC_LINK_IFELSE([AC_LANG_SOURCE(
+ [[
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
-# Check for optional headers
-AC_CHECK_HEADERS([sys/types.h sys/time.h sys/msg.h time.h sys/mman.h sys/ioctl.h \
- sys/socket.h sys/select.h netdb.h netinet/in.h netinet/ip.h netinet/tcp.h arpa/inet.h \
- endian.h machine/endian.h sys/endian.h sys/param.h sys/machine.h sys/byteorder.h machine/param.h sys/isa_defs.h \
- signal.h \
- inttypes.h stddef.h unistd.h \
- sockLib.h inetLib.h net/if.h], [], [], [AC_INCLUDES_DEFAULT])
+${decl_noret} void myexitfunc(int code)
+{
+#ifdef HAVE_STDLIB_H
+ exit (code);
+#else
+ (void)code;
+#endif
+}
-AC_CHECK_HEADER([[search.h]],
- [
- gl_FUNC_TSEARCH
- AS_IF([[test "x$HAVE_TSEARCH" = "x1" && test "x$REPLACE_TSEARCH" != "x1"]],
- [AC_DEFINE([[HAVE_SEARCH_H]], [[1]],
- [Define to 1 if you have the <search.h> header file and your system have properly functioning tsearch(), tfind() and tdelete() functions])])
- ],
- [], [AC_INCLUDES_DEFAULT])
+int main (int argc, char *const *argv)
+{
+ (void) argv;
+ if (argc > 2)
+ myexitfunc (2);
+ return 0;
+}
+ ]]
+ )], [mhd_cv_decl_noreturn="${decl_noret}"]
+ )
+ AS_IF([test "x${mhd_cv_decl_noreturn}" != "xnone"], [break])
+ done
+ CFLAGS="${save_CFLAGS}"
+ ]
+)
+AS_VAR_IF([mhd_cv_decl_noreturn], ["none"],
+ [AC_DEFINE([_MHD_NORETURN], [], [Define to supported 'noreturn' function declaration])],
+ [AC_DEFINE_UNQUOTED([_MHD_NORETURN], [${mhd_cv_decl_noreturn}], [Define to supported 'noreturn' function declaration])]
+)
-AM_CONDITIONAL([MHD_HAVE_TSEARCH], [[test "x$ac_cv_header_search_h" = xyes && test "x$HAVE_TSEARCH" = "x1" && test "x$REPLACE_TSEARCH" != "x1"]])
+# Check for types sizes
+# Types sizes are used as an indirect indication of maximum allowed values for types
+# which is used to exclude by preprocessor some compiler checks for values clips
+# Assuming no staffing or uniform staffing for integer types
+AC_CACHE_CHECK([size of tv_sec member of struct timeval], [mhd_cv_size_timeval_tv_sec],
+ [
+ AC_COMPUTE_INT([mhd_cv_size_timeval_tv_sec], [((long int)sizeof(test_var.tv_sec))],
+ [[
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif /* HAVE_TIME_H */
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+struct timeval test_var;
+ ]],
+ [
+ # The size is used only to exclude additional checks/comparison in code
+ # to avoid compiler warnings. With larger size MHD code will use
+ # additional checks which ensure that value will fit but it may produce
+ # a harmless compiler warning.
+ AC_MSG_WARN([The size cannot be determined, assuming 8.])
+ mhd_cv_size_timeval_tv_sec=8
+ ]
+ )
+ ]
+)
+AC_DEFINE_UNQUOTED([SIZEOF_STRUCT_TIMEVAL_TV_SEC], [$mhd_cv_size_timeval_tv_sec],
+ [The size of `tv_sec' member of `struct timeval', as computed by sizeof])
+AC_CHECK_SIZEOF([uint64_t], [], [[#include <stdint.h>]])
+AC_CHECK_SIZEOF([unsigned int], [], [[#include <stdint.h>]])
+AC_CHECK_SIZEOF([size_t], [],
+ [[
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif /* HAVE_STDDEF_H */
+#include <stdio.h>
+ ]]
+)
+AC_CHECK_SIZEOF([unsigned long long], [], [[#include <stdint.h>]])
AC_CHECK_HEADERS([dlfcn.h],[have_tlsplugin=yes],[have_tlsplugin=no], [AC_INCLUDES_DEFAULT])
AM_CONDITIONAL([MHD_HAVE_TLS_PLUGIN], [[test "x$have_tlsplugin" = xyes]])
@@ -1078,7 +1350,7 @@
(void)getsockname(socket(0,0,0),(struct sockaddr *)&ss,(void*)0);
],
[
- AC_CACHE_CHECK([[whether getsockname() is usable]], [[mhc_cv_getsockname_usable]],
+ AC_CACHE_CHECK([[whether getsockname() is usable]], [[mhd_cv_getsockname_usable]],
[
AC_RUN_IFELSE(
[
@@ -1167,17 +1439,156 @@
]]
)
],
- [[mhc_cv_getsockname_usable='yes']],
- [[mhc_cv_getsockname_usable='no']],
- [[mhc_cv_getsockname_usable='assuming yes']]
+ [[mhd_cv_getsockname_usable='yes']],
+ [[mhd_cv_getsockname_usable='no']],
+ [[mhd_cv_getsockname_usable='assuming yes']]
)
]
)
- AS_VAR_IF([[mhc_cv_getsockname_usable]], [["no"]], [:],
+ AS_VAR_IF([[mhd_cv_getsockname_usable]], [["no"]], [:],
[AC_DEFINE([[MHD_USE_GETSOCKNAME]], [[1]], [Define if you have usable `getsockname' function.])])
]
)
+AC_CACHE_CHECK([for usable PAGESIZE macro], [mhd_cv_macro_pagesize_usable],
+ [
+ AC_LINK_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifndef PAGESIZE
+#error No PAGESIZE macro defined
+choke me now
+#endif
+ ]],
+ [[
+ long pgsz = PAGESIZE + 0;
+ if (1 > pgsz) return 1;
+ ]]
+ )
+ ],
+ [[mhd_cv_macro_pagesize_usable="yes"]], [[mhd_cv_macro_pagesize_usable="no"]]
+ )
+ ]
+)
+AS_VAR_IF([[mhd_cv_macro_pagesize_usable]], [["yes"]],
+ [
+ AC_DEFINE([[MHD_USE_PAGESIZE_MACRO]],[[1]],[Define if you have usable PAGESIZE macro])
+ AC_CACHE_CHECK([whether PAGESIZE macro could be used for static init], [mhd_cv_macro_pagesize_usable_static],
+ [
+ AC_LINK_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifndef PAGESIZE
+#error No PAGESIZE macro defined
+choke me now
+#endif
+static long ac_pgsz = PAGESIZE + 0;
+ ]],
+ [[
+ if (1 > ac_pgsz) return 1;
+ ]]
+ )
+ ],
+ [[mhd_cv_macro_pagesize_usable_static="yes"]], [[mhd_cv_macro_pagesize_usable_static="no"]]
+ )
+ ]
+ )
+ AS_VAR_IF([[mhd_cv_macro_pagesize_usable_static]], [["yes"]],
+ [AC_DEFINE([[MHD_USE_PAGESIZE_MACRO_STATIC]],[[1]],[Define if you have PAGESIZE macro usable for static init])]
+ )
+ ],
+ [
+ AC_CACHE_CHECK([for usable PAGE_SIZE macro], [mhd_cv_macro_page_size_usable],
+ [
+ AC_LINK_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifndef PAGE_SIZE
+#error No PAGE_SIZE macro defined
+choke me now
+#endif
+ ]],
+ [[
+ long pgsz = PAGE_SIZE + 0;
+ if (1 > pgsz) return 1;
+ ]]
+ )
+ ],
+ [[mhd_cv_macro_page_size_usable="yes"]], [[mhd_cv_macro_page_size_usable="no"]]
+ )
+ ]
+ )
+ AS_VAR_IF([[mhd_cv_macro_page_size_usable]], [["yes"]],
+ [
+ AC_DEFINE([[MHD_USE_PAGE_SIZE_MACRO]],[[1]],[Define if you have usable PAGE_SIZE macro])
+ AC_CACHE_CHECK([whether PAGE_SIZE macro could be used for static init], [mhd_cv_macro_page_size_usable_static],
+ [
+ AC_LINK_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifndef PAGE_SIZE
+#error No PAGE_SIZE macro defined
+choke me now
+#endif
+static long ac_pgsz = PAGE_SIZE + 0;
+ ]],
+ [[
+ if (1 > ac_pgsz) return 1;
+ ]]
+ )
+ ],
+ [[mhd_cv_macro_page_size_usable_static="yes"]], [[mhd_cv_macro_page_size_usable_static="no"]]
+ )
+ ]
+ )
+ AS_VAR_IF([[mhd_cv_macro_page_size_usable_static]], [["yes"]],
+ [AC_DEFINE([[MHD_USE_PAGE_SIZE_MACRO_STATIC]],[[1]],[Define if you have PAGE_SIZE macro usable for static init])]
+ )
+ ]
+ )
+ ]
+)
+
# Check for inter-thread signaling type
AC_ARG_ENABLE([[itc]],
[AS_HELP_STRING([[--enable-itc=TYPE]], [use TYPE of inter-thread communication (pipe, socketpair, eventfd) [auto]])], [],
@@ -1204,7 +1615,9 @@
AC_LINK_IFELSE([
AC_LANG_PROGRAM([[
#include <sys/eventfd.h>
- ]], [[int ef = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)
+ ]], [[
+ int ef = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+ if (ef) return ef - 1;
]])
], [[mhd_cv_eventfd_usable='yes']], [[mhd_cv_eventfd_usable='no']])
])
@@ -1356,8 +1769,8 @@
#include <time.h>]])
-AC_CHECK_DECLS([SOCK_NONBLOCK], [AC_DEFINE([HAVE_SOCK_NONBLOCK], [1], [SOCK_NONBLOCK is defined in a socket header])], [],
- [
+AC_CHECK_DECL([SOCK_NONBLOCK], [AC_DEFINE([HAVE_SOCK_NONBLOCK], [1], [SOCK_NONBLOCK is defined in a socket header])], [],
+ [[
#if defined(HAVE_SYS_TYPES_H)
# include <sys/types.h>
#endif
@@ -1366,7 +1779,8 @@
#elif defined(HAVE_WINSOCK2_H)
# include <winsock2.h>
#endif
- ])
+ ]]
+)
AC_CHECK_DECL([[clock_gettime]],
@@ -1429,6 +1843,63 @@
[AC_MSG_RESULT([[no]])
])
+AS_VAR_IF([ac_cv_header_time_h], ["yes"],
+ [
+ AC_CACHE_CHECK([[for C11 timespec_get()]], [mhd_cv_func_timespec_get],
+ [
+ AC_LINK_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+#include <time.h>
+
+#ifndef TIME_UTC
+#error TIME_UTC must be defined to use timespec_get()
+choke me now
+#endif
+ ]],
+ [[
+ struct timespec ts;
+ if (TIME_UTC != timespec_get (&ts, TIME_UTC))
+ return 1;
+ ]]
+ )
+ ], [[mhd_cv_func_timespec_get="yes"]], [[mhd_cv_func_timespec_get="no"]]
+ )
+ ]
+ )
+ AS_VAR_IF([mhd_cv_func_timespec_get], ["yes"],
+ [AC_DEFINE([HAVE_TIMESPEC_GET], [1], [Define to 1 if you have C11 `mhd_cv_func_timespec_get' function and TIME_UTC macro.])]
+ )
+ ]
+)
+
+AS_VAR_SET_IF([ac_cv_func_gettimeofday], [mhd_cv_func_gettimeofday="${ac_cv_func_gettimeofday}"])
+AC_CACHE_CHECK([[for gettimeofday(2)]], [mhd_cv_func_gettimeofday], [
+ AC_LINK_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif /* HAVE_TIME_H */
+ ]],
+ [[
+ struct timeval tv;
+ if (0 != gettimeofday (&tv, (void*) 0))
+ return 1;
+ ]]
+ )
+ ], [[mhd_cv_func_gettimeofday="yes"]], [[mhd_cv_func_gettimeofday="no"]]
+ )
+])
+AS_VAR_IF([mhd_cv_func_gettimeofday], ["yes"],
+ [AC_DEFINE([HAVE_GETTIMEOFDAY], [1], [Define to 1 if you have `gettimeofday' function.])]
+)
+
# IPv6
AC_MSG_CHECKING(for IPv6)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@@ -1460,6 +1931,35 @@
MHD_CHECK_FUNC([[sysconf]], [[#include <unistd.h>]], [[long a = sysconf(0); if (a) return 1;]])
+MHD_CHECK_FUNC([[sysctl]], [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+ ]], [[int mib[2] = {CTL_KERN, KERN_MAXPROC}; if (sysctl(mib, 2, NULL, NULL, NULL, 0)) return 1;]]
+)
+
+MHD_CHECK_FUNC([[sysctlbyname]], [[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+ ]], [[sysctlbyname("test", NULL, NULL, NULL, 0);]]
+)
+
+MHD_CHECK_FUNC([[usleep]], [[#include <unistd.h>]], [[usleep(100000);]])
+MHD_CHECK_FUNC([[nanosleep]], [[#include <time.h>]], [[struct timespec ts2, ts1 = {0, 0}; nanosleep(&ts1, &ts2);]])
+
HIDDEN_VISIBILITY_CFLAGS=""
AS_CASE(["$host"],
[*-*-mingw*],[
@@ -1506,7 +2006,8 @@
AC_MSG_CHECKING([[for suitable libmagic]])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM(
- [[
+ [AC_INCLUDES_DEFAULT
+ [
#include <magic.h>
]],
[[
@@ -1521,7 +2022,7 @@
)
],
[
- AC_DEFINE([HAVE_LIBMAGIC], [1], [Define to 1 if you have suitable libmagic.])
+ AC_DEFINE([MHD_HAVE_LIBMAGIC], [1], [Define to 1 if you have suitable libmagic.])
mhd_have_libmagic="yes"
AC_MSG_RESULT([[yes]])
],
@@ -1529,7 +2030,7 @@
]
)
LIBS="$SAVE_LIBS"
-AM_CONDITIONAL([HAVE_LIBMAGIC], [[test "x$mhd_have_libmagic" = "xyes"]])
+AM_CONDITIONAL([MHD_HAVE_LIBMAGIC], [[test "x$mhd_have_libmagic" = "xyes"]])
# large file support (> 4 GB)
AC_SYS_LARGEFILE
@@ -2119,6 +2620,44 @@
AC_SUBST([GNUTLS_LDFLAGS])
AC_SUBST([GNUTLS_LIBS])
+AS_VAR_IF([have_gnutls], ["yes"],
+ [
+ AC_CACHE_CHECK([for GnuTLS quirks], [mhd_cv_gnutls_mthread_broken],
+ [
+ mhd_cv_gnutls_mthread_broken="no"
+ AS_IF([test -r /etc/redhat-release],
+ [
+ AS_IF([$FGREP ' release 6.' /etc/redhat-release >/dev/null || $FGREP '(Santiago)' /etc/redhat-release >/dev/null],
+ [mhd_cv_gnutls_mthread_broken="found"],
+ )
+ ]
+ )
+ AS_VAR_IF([mhd_cv_gnutls_mthread_broken], ["no"],
+ [
+ AS_IF([command -v rpm >/dev/null],
+ [
+ AS_IF([test r`rpm -E '%{rhel} 2>/dev/null'` = "r6"],
+ [mhd_cv_gnutls_mthread_broken="found"],
+ )
+ ]
+ )
+ ]
+ )
+ ]
+ )
+ AC_CACHE_CHECK([for gnutls-cli binary], [mhd_cv_gnutls_cli],
+ [
+ mhd_cv_gnutls_cli="no"
+ AS_IF([command -v gnutls-cli >/dev/null 2>&1],
+ [AS_IF([AC_RUN_LOG([gnutls-cli --version >&2])], [mhd_cv_gnutls_cli="yes"])]
+ )
+ ]
+ )
+ ]
+)
+AM_CONDITIONAL([HAVE_GNUTLS_MTHREAD_BROKEN], [[test "x${mhd_cv_gnutls_mthread_broken}" = "xfound"]])
+AM_CONDITIONAL([USE_UPGRADE_TLS_TESTS], [[test "x${mhd_cv_gnutls_mthread_broken}" = "xno" || test "x${mhd_cv_gnutls_cli}" = "xyes"]])
+
# optional: HTTP Basic Auth support. Enabled by default
AC_MSG_CHECKING([[whether to support HTTP basic authentication]])
AC_ARG_ENABLE([bauth],
@@ -2162,7 +2701,7 @@
[
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#include <stdlib.h>
- ]],[[void * ptr = calloc(1, 2)]])
+ ]],[[void * ptr = calloc(1, 2); if (ptr) return 1;]])
],
[[mhd_cv_have_func_calloc='yes']],
[[mhd_cv_have_func_calloc='no']]
@@ -2323,8 +2862,20 @@
AM_CONDITIONAL([USE_COVERAGE], [test "x$use_gcov" = "xyes"])
AX_COUNT_CPUS
-AS_IF([[test "$CPU_COUNT" -gt "32"]], [[CPU_COUNT="32"]])dnl Limit resource usage
-AC_SUBST([CPU_COUNT])
+AC_MSG_CHECKING([for number of CPU cores to use in tests])
+AS_VAR_IF([enable_heavy_tests], ["yes"],
+ [
+ # Enable usage of many core if heavy tests are enabled
+ AS_IF([[test "$CPU_COUNT" -gt "32"]], [[CPU_COUNT="32"]])dnl Limit resource usage
+ ],
+ [
+ # Limit usage to just a few cores if heavy tests are not enabled
+ AS_IF([[test "$CPU_COUNT" -gt "6"]], [[CPU_COUNT="6"]])
+ AS_IF([[test "$CPU_COUNT" -lt "2"]], [[CPU_COUNT="2"]])
+ ]
+)
+AC_MSG_RESULT([$CPU_COUNT])
+
AC_MSG_CHECKING([[whether to enable debug asserts]])
AC_ARG_ENABLE([[asserts]],
@@ -2360,8 +2911,468 @@
[AC_DEFINE([[NDEBUG]], [[1]], [Define to disable usage of debug asserts.])]
)
+AS_UNSET([enabled_sanitizers])
+AM_TESTS_ENVIRONMENT=""
+AM_ASAN_OPTIONS=""
+AM_UBSAN_OPTIONS=""
+AM_LSAN_OPTIONS=""
+AS_UNSET([ASAN_OPTIONS])
+AS_UNSET([UBSAN_OPTIONS])
+AS_UNSET([LSAN_OPTIONS])
+
+AC_MSG_CHECKING([whether to enable run-time sanitizers])
+AC_ARG_ENABLE([sanitizers],
+ [AS_HELP_STRING([[--enable-sanitizers[=address,undefined,leak,user-poison]]],
+ [enable run-time sanitizers, specify the list of types of sanitizers to enable or ]
+ [leave the list empty to enable all suppoted and availabe sanitizers])],
+ [], [enable_sanitizers=no])
+AS_IF([test "x${enable_sanitizers}" = "x"], [enable_sanitizers="auto"])
+AS_VAR_IF([enable_sanitizers], ["yes"], [enable_sanitizers="auto"])
+AS_IF([test "x${enable_sanitizers}" = "xno"],
+ [
+ enable_sanitizers="no"
+ enable_san_address="no"
+ enable_san_undef="no"
+ enable_san_leak="no"
+ enable_san_upoison="no"
+ ],
+ [test "x${enable_sanitizers}" = "xauto"],
+ [
+ enable_san_address="auto"
+ enable_san_undef="auto"
+ enable_san_leak="auto"
+ enable_san_upoison="auto"
+ ],
+ [
+ AS_UNSET([san])
+ enable_san_address="no"
+ enable_san_undef="no"
+ enable_san_leak="no"
+ enable_san_upoison="no"
+ for san in `AS_ECHO([${enable_sanitizers}]) | tr ',' ' '`
+ do
+ AS_CASE([$san],
+ [address], [enable_san_address="yes"],
+ [undefined], [enable_san_undef="yes"],
+ [leak], [enable_san_leak="yes"],
+ [user-poison|user_poison], [enable_san_upoison="yes"],
+ [no|yes|auto], [AC_MSG_ERROR(["$san" cannot be used with other options for --enable-sanitizers=])],
+ [AC_MSG_ERROR([Unknown parameter "$san" for --enable-sanitizers=])]
+ )
+ done
+ AS_IF([test "x${enable_san_upoison}" = "xyes" && test "x${enable_san_address}" = "xno"],
+ [AC_MSG_ERROR([User memory poisoning cannot be used without address sanitizer])]
+ )
+ enable_sanitizers="selected"
+ ]
+)
+AS_CASE([${enable_sanitizers}],
+ [selected], [AC_MSG_RESULT([selected])],
+ [auto], [AC_MSG_RESULT([yes, detect and use supported sanitizers])],
+ [AC_MSG_RESULT([no])]
+)
+AS_VAR_IF([enable_sanitizers], ["no"], [:],
+ [
+ AS_UNSET([san_FLAGS]) # the sanitizer flags to be added to both CFLAGS and LDFLAGS
+ AS_UNSET([san_CFLAGS]) # the sanitizer flags to be added to CFLAGS
+ saved_CFLAGS="$CFLAGS"
+ AC_CACHE_CHECK([whether '-fsanitize=' works for $CC],
+ [mhd_cv_cc_sanitizer_works],
+ [
+ CFLAGS="${saved_CFLAGS} -fsanitize=wrongFeatureName"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],
+ [mhd_cv_cc_sanitizer_works=no], [mhd_cv_cc_sanitizer_works=yes])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_sanitizer_works], ["yes"],
+ [
+ AS_VAR_IF([enable_san_address], ["no"], [:],
+ [
+ AC_CACHE_CHECK([for address sanitizer], [mhd_cv_cc_sanitizer_address],
+ [
+ CFLAGS="${saved_CFLAGS} ${san_CFLAGS} ${san_FLAGS} -fsanitize=address"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [mhd_cv_cc_sanitizer_address=yes], [mhd_cv_cc_sanitizer_address=no])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_sanitizer_address],["yes"],
+ [
+ AC_DEFINE([MHD_ASAN_ACTIVE], [1], [Define to '1' if you have address sanitizer enabled])
+ AX_APPEND_FLAG([-fsanitize=address], [san_FLAGS])
+ enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }address"
+ AS_VAR_IF([enable_san_leak], ["no"], [:],
+ [
+ AC_CACHE_CHECK([whether leak detect is not rejected by address sanitizer], [mhd_cv_cc_sanitizer_address_leak],
+ [
+ CFLAGS="${saved_CFLAGS} ${san_CFLAGS} ${san_FLAGS}"
+ ASAN_OPTIONS="exitcode=88:detect_leaks=1:halt_on_error=1"
+ export ASAN_OPTIONS
+ AC_RUN_IFELSE([AC_LANG_PROGRAM([],[])],
+ [mhd_cv_cc_sanitizer_address_leak=yes], [mhd_cv_cc_sanitizer_address_leak=no],
+ [
+ # Cross-compiling with sanitizers?
+ mhd_cv_cc_sanitizer_address_leak='assuming no'
+ ]
+ )
+ AS_UNSET([ASAN_OPTIONS])
+ ]
+ )
+ ]
+ )
+ AC_CACHE_CHECK([for pointer compare sanitizer], [mhd_cv_cc_sanitizer_pointer_compare],
+ [
+ CFLAGS="${saved_CFLAGS} ${san_CFLAGS} ${san_FLAGS} -fsanitize=pointer-compare"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [mhd_cv_cc_sanitizer_pointer_compare=yes], [mhd_cv_cc_sanitizer_pointer_compare=no])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_sanitizer_pointer_compare],["yes"],
+ [
+ AX_APPEND_FLAG([-fsanitize=pointer-compare], [san_FLAGS])
+ enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }pointer compare"
+ ]
+ )
+ AC_CACHE_CHECK([for pointer subtract sanitizer], [mhd_cv_cc_sanitizer_pointer_subtract],
+ [
+ CFLAGS="${saved_CFLAGS} ${san_CFLAGS} ${san_FLAGS} -fsanitize=pointer-subtract"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [mhd_cv_cc_sanitizer_pointer_subtract=yes], [mhd_cv_cc_sanitizer_pointer_subtract=no])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_sanitizer_pointer_subtract],["yes"],
+ [
+ AX_APPEND_FLAG([-fsanitize=pointer-subtract], [san_FLAGS])
+ enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }pointer subtract"
+ ]
+ )
+ AS_VAR_IF([enable_san_upoison], ["no"], [:],
+ [
+ AC_CHECK_HEADERS([sanitizer/asan_interface.h], [], [], [AC_INCLUDES_DEFAULT])
+ AS_IF([test "x${mhd_cv_cc_sanitizer_pointer_compare}" = "xyes" && test "x${ac_cv_header_sanitizer_asan_interface_h}" = "xyes"],
+ [
+ AC_CACHE_CHECK([whether '__attribute__((no_sanitize("pointer-compare","pointer-subtract")))' works], [mhd_cv_func_attribute_nosanitize_ptr],
+ [
+ ASAN_OPTIONS="exitcode=88:detect_invalid_pointer_pairs=3:halt_on_error=1"
+ export ASAN_OPTIONS
+ CFLAGS="${saved_CFLAGS} ${san_CFLAGS} ${san_FLAGS} ${errattr_CFLAGS}"
+ AC_RUN_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+#include <stdlib.h>
+
+__attribute__((no_sanitize("pointer-compare","pointer-subtract")))
+int ptr_process(void *ptr1, void *ptr2)
+{
+ if ((char*)ptr1 <= (char*)ptr2)
+ return (int) ((char*)ptr2 - (char*)ptr1);
+ return (int) ((char*)ptr1 - (char*)ptr2);
+}
+ ]],
+ [[
+ int *a = (int*) malloc (sizeof(int)*4);
+ int *b = (int*) malloc (sizeof(long)*6);
+ int c = ptr_process(a, b);
+ if (c)
+ {
+ free (b);
+ free (a);
+ return 0;
+ }
+ free (a);
+ free (b);
+ ]]
+ )
+ ],
+ [mhd_cv_func_attribute_nosanitize_ptr=yes], [mhd_cv_func_attribute_nosanitize_ptr=no],
+ [
+ # Cross-compiling with sanitizers??
+ mhd_cv_func_attribute_nosanitize_ptr='assuming no'
+ ]
+ )
+ AS_UNSET([ASAN_OPTIONS])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_func_attribute_nosanitize_ptr], ["yes"],
+ [AC_DEFINE([FUNC_ATTR_PTRCOMPARE_WOKRS],[1],[Define to '1' if '__attribute__((no_sanitize("pointer-compare","pointer-subtract")))' works])],
+ [
+ AC_CACHE_CHECK([whether '__attribute__((no_sanitize("address")))' works for pointers compare], [mhd_cv_func_attribute_nosanitize_addr],
+ [
+ ASAN_OPTIONS="exitcode=88:detect_invalid_pointer_pairs=3:halt_on_error=1"
+ export ASAN_OPTIONS
+ CFLAGS="${saved_CFLAGS} ${san_CFLAGS} ${san_FLAGS} ${errattr_CFLAGS}"
+ AC_RUN_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+#include <stdlib.h>
+
+__attribute__((no_sanitize("address")))
+int ptr_process(void *ptr1, void *ptr2)
+{
+ if ((char*)ptr1 <= (char*)ptr2)
+ return (int) ((char*)ptr2 - (char*)ptr1);
+ return (int) ((char*)ptr1 - (char*)ptr2);
+}
+ ]],
+ [[
+ int *a = (int*) malloc (sizeof(int)*4);
+ int *b = (int*) malloc (sizeof(long)*6);
+ int c = ptr_process(a, b);
+ if (c)
+ {
+ free (b);
+ free (a);
+ return 0;
+ }
+ free (a);
+ free (b);
+ ]]
+ )
+ ],
+ [mhd_cv_func_attribute_nosanitize_addr=yes], [mhd_cv_func_attribute_nosanitize_addr=no],
+ [
+ # Cross-compiling with sanitizers??
+ mhd_cv_func_attribute_nosanitize_addr='assuming no'
+ ]
+ )
+ AS_UNSET([ASAN_OPTIONS])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_func_attribute_nosanitize_addr], ["yes"],
+ [AC_DEFINE([FUNC_ATTR_NOSANITIZE_WORKS],[1],[Define to '1' if '__attribute__((no_sanitize("address")))' works for pointers compare])]
+ )
+ ]
+ )
+ ]
+ )
+ ]
+ )
+ ]
+ )
+ AS_IF([test "x${enable_san_address}" = "xyes" && test "x${mhd_cv_cc_sanitizer_address}" != "xyes"],
+ [AC_MSG_ERROR([Address sanitizer cannot be enabled])]
+ )
+ enable_san_address="${mhd_cv_cc_sanitizer_address}"
+ ]
+ )
+ AS_VAR_IF([enable_san_undef], ["no"], [:],
+ [
+ dnl Ensure that '#' will be processed correctly
+ [
+ test_undf_prog='
+#include <stdio.h>
+
+void func_out_b(char *arr)
+{
+ arr[0] = 0;
+ arr[16] = 2;
+}
+
+unsigned int int_deref(void *ptr)
+{
+ return (*((int*)ptr)) + 2;
+}
+
+int func1(void)
+{
+ char chr[16];
+ func_out_b (chr);
+ return int_deref(chr + 1) + int_deref(chr + 2);
+}
+
+int main(void)
+{
+ unsigned long ulvar;
+ signed char ch1;
+ ulvar = -1 * func1();
+ ch1 = ulvar * 6UL;
+ printf("%lu\n", ulvar + ch1);
+ return 0;
+}
+ '
+ ]
+ AC_CACHE_CHECK([for undefined behavior sanitizer], [mhd_cv_cc_sanitizer_undefined],
+ [
+ CFLAGS="${saved_CFLAGS} ${san_FLAGS} ${san_CFLAGS} -fsanitize=undefined"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([${test_undf_prog}])],
+ [mhd_cv_cc_sanitizer_undefined=yes], [mhd_cv_cc_sanitizer_undefined=no])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_sanitizer_undefined],["yes"],
+ [
+ AX_APPEND_FLAG([-fsanitize=undefined], [san_FLAGS])
+ enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }undefined"
+ ],
+ [
+ AC_CACHE_CHECK([for undefined behavior sanitizer with '-fsanitize-undefined-trap-on-error'], [mhd_cv_cc_sanitizer_undefined_trap],
+ [
+ CFLAGS="${saved_CFLAGS} ${san_FLAGS} ${san_CFLAGS} -fsanitize=undefined -fsanitize-undefined-trap-on-error"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([${test_undf_prog}])],
+ [mhd_cv_cc_sanitizer_undefined_trap=yes], [mhd_cv_cc_sanitizer_undefined_trap=no])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_sanitizer_undefined_trap], ["yes"],
+ [
+ AX_APPEND_FLAG([-fsanitize=undefined], [san_FLAGS])
+ AX_APPEND_FLAG([-fsanitize-undefined-trap-on-error], [san_FLAGS])
+ enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }undefined"
+ AC_MSG_WARN([Enabled sanitizer without run-time library, error reporting will be limited])
+ ],
+ [
+ AS_IF([test -z "${enabled_sanitizers}"],
+ [
+ # Last resort
+ AC_CACHE_CHECK([for undefined behavior sanitizer with '-fsanitize-trap=all'], [mhd_cv_cc_sanitizer_undefined_trap_all],
+ [
+ CFLAGS="${saved_CFLAGS} ${san_FLAGS} ${san_CFLAGS} -fsanitize=undefined -fsanitize-trap=all"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([${test_undf_prog}])],
+ [mhd_cv_cc_sanitizer_undefined_trap_all=yes], [mhd_cv_cc_sanitizer_undefined_trap_all=no])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_sanitizer_undefined_trap_all],["yes"],
+ [
+ AX_APPEND_FLAG([-fsanitize=undefined], [san_FLAGS])
+ AX_APPEND_FLAG([-fsanitize-trap=all], [san_FLAGS])
+ CFLAGS="${saved_CFLAGS} ${san_FLAGS} ${san_CFLAGS} -fsanitize=undefined -fsanitize-trap=all"
+ enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }undefined"
+ AC_MSG_WARN([Enabled sanitizer without run-time library, error reporting will be limited])
+ ]
+ )
+ ]
+ )
+ ]
+ )
+ ]
+ )
+ AS_CASE(["$enabled_sanitizers"], [*undefined],
+ [
+ AS_VAR_IF([mhd_cv_cc_sanitizer_undefined], ["yes"],[],
+ [
+ # A workaround for broken clang which is trying to use UBSan lib
+ # even when instructed to not use it
+ CFLAGS="${saved_CFLAGS} ${san_FLAGS} ${san_CFLAGS}"
+ AX_APPEND_LINK_FLAGS([-fsanitize-trap=implicit-conversion],
+ [san_FLAGS], [], [AC_LANG_SOURCE([${test_undf_prog}])])
+ ]
+ )
+ CFLAGS="${saved_CFLAGS} ${san_FLAGS} ${san_CFLAGS}"
+ AX_APPEND_LINK_FLAGS([-fsanitize=bounds-strict -fsanitize=local-bounds -fsanitize=implicit-conversion -fsanitize=nullability-arg],
+ [san_CFLAGS], [], [AC_LANG_SOURCE([${test_undf_prog}])])
+ ]
+ )
+ AS_UNSET([test_undf_prog])
+ AS_CASE(["$enabled_sanitizers"],
+ [*undefined], [enable_san_undef="yes"],
+ [
+ AS_VAR_IF([enable_san_undef], [yes], [AC_MSG_ERROR([Undefined behavior sanitizer cannot be enabled])])
+ enable_san_undef="no"
+ ]
+ )
+ ]
+ )
+ AS_VAR_IF([enable_san_leak], ["no"], [:],
+ [
+ AC_CACHE_CHECK([for leak sanitizer], [mhd_cv_cc_sanitizer_leak],
+ [
+ CFLAGS="${saved_CFLAGS} ${san_FLAGS} ${san_CFLAGS} -fsanitize=leak"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [mhd_cv_cc_sanitizer_leak=yes], [mhd_cv_cc_sanitizer_leak=no])
+ ]
+ )
+ AS_VAR_IF([mhd_cv_cc_sanitizer_leak],["yes"],
+ [
+ AX_APPEND_FLAG([-fsanitize=leak], [san_FLAGS])
+ enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }leak"
+ ]
+ )
+ AS_IF([test "x${enable_san_leak}" = "xyes" && test "x${mhd_cv_cc_sanitizer_leak}" != "xyes"],
+ [AC_MSG_ERROR([User poison cannot be enabled])]
+ )
+ enable_san_leak="${mhd_cv_cc_sanitizer_leak}"
+ ]
+ )
+ AS_IF([test -z "${enabled_sanitizers}"],
+ [AC_MSG_ERROR([cannot find any sanitizer supported by $CC])])
+ AS_VAR_IF([enable_san_upoison], ["no"], [:],
+ [
+ AC_MSG_CHECKING([whether to enable user memory poisoning])
+ AS_IF([test "x${mhd_cv_cc_sanitizer_address}" = "xyes" && test "x${mhd_cv_cc_sanitizer_pointer_compare}" = "xyes" && \
+ test "x${ac_cv_header_sanitizer_asan_interface_h}" = "xyes" && \
+ (test "x${mhd_cv_func_attribute_nosanitize_ptr}" = "xyes" || test "x${mhd_cv_func_attribute_nosanitize_addr}" = "xyes")],
+ [
+ AC_DEFINE([MHD_ASAN_POISON_ACTIVE], [1], [Define to '1' if user memory poison is used])
+ enabled_sanitizers="${enabled_sanitizers}${enabled_sanitizers:+, }user-poison"
+ enable_san_upoison="yes"
+ AC_MSG_RESULT([yes])
+ ],
+ [
+ AC_MSG_RESULT([no])
+ AS_VAR_IF([enable_san_upoison], ["yes"],
+ [AC_MSG_ERROR([User memory poisoning cannot be enabled])])
+ enable_san_upoison="no"
+ ]
+ )
+ ]
+ )
+ AS_VAR_IF([enable_san_address], ["yes"],
+ [
+ AS_VAR_IF([mhd_cv_cc_sanitizer_address],["yes"],
+ [
+ AX_APPEND_FLAG([-D_FORTIFY_SOURCE=0], [san_CFLAGS])
+ CFLAGS="${saved_CFLAGS} ${san_FLAGS} ${san_CFLAGS}"
+ AX_APPEND_COMPILE_FLAGS([-Wp,-U_FORTIFY_SOURCE], [san_CFLAGS])
+ ],
+ [AC_MSG_WARN([$CC does not support address sanitizer])]
+ )
+ ]
+ )
+ CFLAGS="${saved_CFLAGS} ${san_FLAGS} ${san_CFLAGS}"
+ # Always stop on sanitizer error
+ AX_APPEND_COMPILE_FLAGS([-fno-sanitize-recover=all], [san_CFLAGS])
+ # Get a better output for sanitizers error reporting
+ AX_APPEND_COMPILE_FLAGS([-fno-omit-frame-pointer -fno-optimize-sibling-calls],
+ [san_CFLAGS])
+ AS_VAR_IF([enable_san_address], ["yes"],
+ [
+ AM_ASAN_OPTIONS="exitcode=88:strict_string_checks=1:detect_stack_use_after_return=1"
+ AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:check_initialization_order=1:strict_init_order=1:redzone=64"
+ AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:max_free_fill_size=1024:detect_invalid_pointer_pairs=3"
+ AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:handle_ioctl=1:halt_on_error=1"
+ AS_VAR_IF([enable_san_upoison], ["yes"], [AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:allow_user_poisoning=1"])
+ AS_VAR_IF([enable_san_leak], ["yes"],
+ [AS_VAR_IF([mhd_cv_cc_sanitizer_address_leak], ["yes"],
+ [AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:detect_leaks=1"])
+ ], [AM_ASAN_OPTIONS="${AM_ASAN_OPTIONS}:detect_leaks=0"]
+ )
+ ]
+ )
+ AS_VAR_IF([enable_san_undef], [yes],
+ [AM_UBSAN_OPTIONS="exitcode=87:print_stacktrace=1:halt_on_error=1"])
+ AS_VAR_IF([enable_san_leak], ["yes"],
+ [AM_LSAN_OPTIONS="use_unaligned=1"]
+ )
+ AM_TESTS_ENVIRONMENT='\
+ASAN_OPTIONS="$(AM_ASAN_OPTIONS)" ; export ASAN_OPTIONS ; \
+UBSAN_OPTIONS="$(AM_UBSAN_OPTIONS)" ; export UBSAN_OPTIONS ; \
+LSAN_OPTIONS="$(AM_LSAN_OPTIONS)" ; export LSAN_OPTIONS ;'
+ ]
+ )
+ CFLAGS="${saved_CFLAGS} ${san_FLAGS} ${san_CFLAGS}"
+ AS_UNSET([saved_CFLAGS])
+ ]
+)
+AM_CONDITIONAL([USE_SANITIZERS],
+ [test -n "$enabled_sanitizers" && test "x$mhd_cv_cc_sanitizer_works" = "xyes"])
+AC_SUBST([AM_ASAN_OPTIONS])
+AC_SUBST([AM_UBSAN_OPTIONS])
+AC_SUBST([AM_LSAN_OPTIONS])
+AC_SUBST([AM_TESTS_ENVIRONMENT])
+
MHD_LIB_LDFLAGS="$MHD_LIB_LDFLAGS -export-dynamic -no-undefined"
+AC_SUBST([CPU_COUNT])
+AC_SUBST([HEAVY_TESTS_NOTPARALLEL])
AC_SUBST(MHD_LIB_CPPFLAGS)
AC_SUBST(MHD_LIB_CFLAGS)
AC_SUBST(MHD_LIB_LDFLAGS)
@@ -2413,6 +3424,7 @@
src/include/Makefile
src/lib/Makefile
src/microhttpd/Makefile
+src/microhttpd_ws/Makefile
src/examples/Makefile
src/testcurl/Makefile
src/testcurl/https/Makefile
@@ -2432,7 +3444,7 @@
AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} Configuration Summary:
Target directory: ${prefix}
Cross-compiling: ${cross_compiling}
- Operating System: ${host_os}${os_ver_msg}
+ Operating System: ${mhd_host_os}${os_ver_msg}
Shutdown of listening socket triggers select: ${mhd_cv_host_shtdwn_trgr_select}
Inter-thread comm: ${use_itc}
poll support: ${enable_poll=no}
@@ -2442,6 +3454,7 @@
Threading lib: ${USE_THREADS}
Use thread names: ${enable_thread_names}
Use debug asserts: ${enable_asserts}
+ Use sanitizers: ${enabled_sanitizers:=no}
Messages: ${enable_messages}
Gettext: ${have_po}
Basic auth.: ${enable_bauth}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/autotools-patches/0003-Pass-various-flags-to-GCC.patch
^
|
@@ -0,0 +1,20 @@
+diff --git a/build-aux/ltmain.sh b/build-aux/ltmain.sh
+index 21e5e078..ddd6e36f 100755
+--- a/build-aux/ltmain.sh
++++ b/build-aux/ltmain.sh
+@@ -7369,11 +7369,14 @@ func_mode_link ()
+ # -fsanitize=* Clang/GCC memory and address sanitizer
+ # -fuse-ld=* Linker select flags for GCC
+ # -static-* direct GCC to link specific libraries statically
++ # -shared-* direct GCC to link shared version of specific libraries
+ # -fcilkplus Cilk Plus language extension features for C/C++
+ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
+ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \
+- -specs=*|-fsanitize=*|-fuse-ld=*|-static-*|-fcilkplus)
++ -specs=*|-fsanitize=*|-fuse-ld=*|-static-*|-shared-*|-fcilkplus| \
++ -ftree-parallelize-loops=*|-fgnu-tm|-ffast-math| \
++ -funsafe-math-optimizations|-fvtable-verify*)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ func_append compile_command " $arg"
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/autotools-patches/0003-Pass-various-runtime-library-flags-to-GCC.mingw-mod.patch
^
|
@@ -0,0 +1,30 @@
+[PATCH 3/6] Pass various runtime library flags to GCC.
+* build-aux/ltmain.in (func_mode_link): Pass the
+-shared-libgcc and -static-lib* flags along to GCC.
+---
+ build-aux/ltmain.sh | 5 ++++-
+ 1 files changed, 4 insertions(+), 1 deletions(-)
+
+diff --git a/build-aux/ltmain.sh b/build-aux/ltmain.sh
+index 1821779..eda7790 100644
+--- a/build-aux/ltmain.sh
++++ b/build-aux/ltmain.sh
+@@ -5084,9 +5084,14 @@ func_mode_link ()
+ # --sysroot=* for sysroot support
+ # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+ # -stdlib=* select c++ std lib with clang
++ # -{shared,static}-libgcc, -static-{libgfortran|libstdc++}
++ # link against specified runtime library
+ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
+- -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*)
++ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \
++ -ftree-parallelize-loops=*|-fcilkplus|-fgnu-tm|-ffast-math| \
++ -funsafe-math-optimizations|-fvtable-verify*| \
++ -shared-libgcc|-static-libgcc|-static-libgfortran|-static-libstdc++)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ func_append compile_command " $arg"
+--
+1.7.1
+
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/autotools-patches/0006-Fix-strict-ansi-vs-posix-mod.patch
^
|
@@ -0,0 +1,22 @@
+[PATCH 6/6] Fix STRICT_ANSI vs POSIX
+* build-aux/ltmain.in (func_mode_link): Also check for _POSIX
+as well as __STRICT_ANSI__ to avoid re-definitions.
+---
+ build-aux/ltmain.sh | 4 +++-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/build-aux/ltmain.sh b/build-aux/ltmain.sh
+index af46cb8..244bb5b 100644
+--- a/build-aux/ltmain.sh
++++ b/build-aux/ltmain.sh
+@@ -3382,7 +3382,7 @@
+
+ /* declarations of non-ANSI functions */
+ #if defined __MINGW32__
+-# ifdef __STRICT_ANSI__
++# if defined(__STRICT_ANSI__) && !defined(__MINGW64_VERSION_MAJOR) || defined(_POSIX_)
+ int _putenv (const char *);
+ # endif
+ #elif defined __CYGWIN__
+--
+1.7.0.2.msysgit.0
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/autotools-patches/0009-libtool-2.4.2.418-msysize-mod.patch
^
|
@@ -0,0 +1,443 @@
+diff --git a/build-aux/config.guess b/build-aux/config.guess
+index f50dcdb6..cd89e459 100755
+--- a/build-aux/config.guess
++++ b/build-aux/config.guess
+@@ -883,6 +883,9 @@ EOF
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
++ amd64:MSYS*:*:* | x86_64:MSYS*:*:*)
++ echo x86_64-unknown-msys
++ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+diff -Naur libtool-2.4.2.418-orig/build-aux/ltmain.sh libtool-2.4.2.418/build-aux/ltmain.sh
+--- libtool-2.4.2.418-orig/build-aux/ltmain.sh 2013-10-27 02:53:58.000000000 +0400
++++ libtool-2.4.2.418/build-aux/ltmain.sh 2014-09-02 10:29:08.840800000 +0400
+@@ -2315,7 +2315,7 @@
+ case $host in
+ # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452
+ # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788
+- *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*)
++ *cygwin* | *msys* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*)
+ # don't eliminate duplications in $postdeps and $predeps
+ opt_duplicate_compiler_generated_deps=:
+ ;;
+@@ -3328,7 +3328,7 @@
+
+ # On Cygwin there's no "real" PIC flag so we must build both object types
+ case $host_os in
+- cygwin* | mingw* | pw32* | os2* | cegcc*)
++ cygwin* | msys* | mingw* | pw32* | os2* | cegcc*)
+ pic_mode=default
+ ;;
+ esac
+@@ -4201,7 +4201,7 @@
+ 'exit $?'
+ tstripme=$stripme
+ case $host_os in
+- cygwin* | mingw* | pw32* | cegcc*)
++ cygwin* | msys* | mingw* | pw32* | cegcc*)
+ case $realname in
+ *.dll.a)
+ tstripme=
+@@ -4307,7 +4307,7 @@
+
+ # Do a test to see if this is really a libtool program.
+ case $host in
+- *cygwin* | *mingw*)
++ *cygwin* | *msys* | *mingw*)
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ wrapper=$func_ltwrapper_scriptname_result
+@@ -4382,7 +4382,7 @@
+ # remove .exe since cygwin /usr/bin/install will append another
+ # one anyway
+ case $install_prog,$host in
+- */usr/bin/install*,*cygwin*)
++ */usr/bin/install*,*cygwin* | */usr/bin/install*,*msys*)
+ case $file:$destfile in
+ *.exe:*.exe)
+ # this is ok
+@@ -4535,7 +4535,7 @@
+ $RM $export_symbols
+ eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+ case $host in
+- *cygwin* | *mingw* | *cegcc* )
++ *cygwin* | *msys* | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+ ;;
+@@ -4547,7 +4547,7 @@
+ eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ case $host in
+- *cygwin* | *mingw* | *cegcc* )
++ *cygwin* | *msys* | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+ ;;
+@@ -4561,7 +4561,7 @@
+ func_basename "$dlprefile"
+ name=$func_basename_result
+ case $host in
+- *cygwin* | *mingw* | *cegcc* )
++ *cygwin* | *msys* | *mingw* | *cegcc* )
+ # if an import library, we need to obtain dlname
+ if func_win32_import_lib_p "$dlprefile"; then
+ func_tr_sh "$dlprefile"
+@@ -4736,7 +4736,7 @@
+ # Transform the symbol file into the correct name.
+ symfileobj=$output_objdir/${my_outputname}S.$objext
+ case $host in
+- *cygwin* | *mingw* | *cegcc* )
++ *cygwin* | *msys* | *mingw* | *cegcc* )
+ if test -f "$output_objdir/$my_outputname.def"; then
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+@@ -5629,7 +5629,7 @@
+ {
+ EOF
+ case $host in
+- *mingw* | *cygwin* )
++ *mingw* | *cygwin* | *msys* )
+ # make stdout use "unix" line endings
+ echo " setmode(1,_O_BINARY);"
+ ;;
+@@ -6350,7 +6350,7 @@
+ $debug_cmd
+
+ case $host in
+- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
++ *-*-cygwin* | *-*-msys* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ # It is impossible to link a dll without this setting, and
+ # we shouldn't force the makefile maintainer to figure out
+ # what system we are compiling for in order to pass an extra
+@@ -6843,7 +6843,7 @@
+ ;;
+ esac
+ case $host in
+- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
++ *-*-cygwin* | *-*-msys* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$dir:"*) ;;
+@@ -6863,7 +6863,7 @@
+ -l*)
+ if test X-lc = "X$arg" || test X-lm = "X$arg"; then
+ case $host in
+- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
++ *-*-cygwin* | *-*-msys* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ # These systems don't actually have a C or math library (as such)
+ continue
+ ;;
+@@ -6946,7 +6946,7 @@
+
+ -no-install)
+ case $host in
+- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
++ *-*-cygwin* | *-*-msys* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+ # The PATH hackery in wrapper scripts is required on Windows
+ # and Darwin in order for the loader to find any dlls it needs.
+ func_warning "'-no-install' is ignored for $host"
+@@ -7812,7 +7812,7 @@
+ fi
+ case $host in
+ # special handling for platforms with PE-DLLs.
+- *cygwin* | *mingw* | *cegcc* )
++ *cygwin* | *msys* | *mingw* | *cegcc* )
+ # Linker will automatically link against shared library if both
+ # static and shared are present. Therefore, ensure we extract
+ # symbols from the import library if a shared library is present
+@@ -7956,7 +7956,7 @@
+ if test -n "$library_names" &&
+ { test no = "$use_static_libs" || test -z "$old_library"; }; then
+ case $host in
+- *cygwin* | *mingw* | *cegcc* | *os2*)
++ *cygwin* | *msys* | *mingw* | *cegcc* | *os2*)
+ # No point in relinking DLLs because paths are not encoded
+ func_append notinst_deplibs " $lib"
+ need_relink=no
+@@ -8026,7 +8026,7 @@
+ elif test -n "$soname_spec"; then
+ # bleh windows
+ case $host in
+- *cygwin* | mingw* | *cegcc* | *os2*)
++ *cygwin* | *msys* | mingw* | *cegcc* | *os2*)
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix=-$major
+@@ -8899,7 +8899,7 @@
+ if test yes = "$build_libtool_libs"; then
+ if test -n "$rpath"; then
+ case $host in
+- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
++ *-*-cygwin* | *-*-msys* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ # these systems don't actually have a c library (as such)!
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+@@ -9413,7 +9413,7 @@
+
+ orig_export_symbols=
+ case $host_os in
+- cygwin* | mingw* | cegcc*)
++ cygwin* | *msys* | mingw* | cegcc*)
+ if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+ # exporting using user supplied symfile
+ func_dll_def_p "$export_symbols" || {
+@@ -9970,7 +9970,7 @@
+
+ prog)
+ case $host in
+- *cygwin*) func_stripname '' '.exe' "$output"
++ *cygwin* | *msys*) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result.exe;;
+ esac
+ test -n "$vinfo" && \
+@@ -10081,7 +10081,7 @@
+ esac
+ fi
+ case $host in
+- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
++ *-*-cygwin* | *-*-msys* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$libdir:"*) ;;
+@@ -10159,7 +10159,7 @@
+ # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
+ wrappers_required=false
+ ;;
+- *cygwin* | *mingw* )
++ *cygwin* | *msys* | *mingw* )
+ test yes = "$build_libtool_libs" || wrappers_required=false
+ ;;
+ *)
+@@ -10305,14 +10305,14 @@
+ esac
+ # test for cygwin because mv fails w/o .exe extensions
+ case $host in
+- *cygwin*)
++ *cygwin* | *msys*)
+ exeext=.exe
+ func_stripname '' '.exe' "$outputname"
+ outputname=$func_stripname_result ;;
+ *) exeext= ;;
+ esac
+ case $host in
+- *cygwin* | *mingw* )
++ *cygwin* | *msys* | *mingw* )
+ func_dirname_and_basename "$output" "" "."
+ output_name=$func_basename_result
+ output_path=$func_dirname_result
+@@ -10644,7 +10644,7 @@
+ # tests/bindir.at for full details.
+ tdlname=$dlname
+ case $host,$output,$installed,$module,$dlname in
+- *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
++ *cygwin*,*lai,yes,no,*.dll | *msys*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
+ # If a -bindir argument was supplied, place the dll there.
+ if test -n "$bindir"; then
+ func_relative_path "$install_libdir" "$bindir"
+diff -Naur libtool-2.4.2.418-orig/m4/libtool.m4 libtool-2.4.2.418/m4/libtool.m4
+--- libtool-2.4.2.418-orig/m4/libtool.m4 2013-10-26 03:37:46.000000000 +0400
++++ libtool-2.4.2.418/m4/libtool.m4 2014-09-02 10:19:40.084800000 +0400
+@@ -1665,7 +1665,7 @@
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+- cygwin* | mingw* | cegcc*)
++ cygwin* | msys* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+@@ -1913,7 +1913,7 @@
+ lt_cv_dlopen_libs=
+ ;;
+
+- cygwin*)
++ cygwin* | msys*)
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ ;;
+@@ -2399,7 +2399,7 @@
+ # libtool to hard-code these into programs
+ ;;
+
+-cygwin* | mingw* | pw32* | cegcc*)
++cygwin* | msys* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+@@ -2431,6 +2431,12 @@
+ m4_if([$1], [],[
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"])
+ ;;
++ msys*)
++ # MSYS DLLs use 'msys-' prefix rather than 'lib'
++ soname_spec='`echo $libname | sed -e 's/^lib/msys-/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
++m4_if([$1], [],[
++ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"])
++ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+@@ -2465,7 +2471,7 @@
+ # Convert to MSYS style.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'`
+ ;;
+- cygwin*)
++ cygwin* | msys*)
+ # Convert to unix form, then to dos form, then back to unix form
+ # but this time dos style (no spaces!) so that the unix form looks
+ # like /cygdrive/c/PROGRA~1:/cygdr...
+@@ -3203,7 +3209,7 @@
+ esac
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ case $host_os in
+- cygwin* | mingw* | pw32* | cegcc*)
++ cygwin* | msys* | mingw* | pw32* | cegcc*)
+ if test yes != "$GCC"; then
+ reload_cmds=false
+ fi
+@@ -3259,7 +3265,7 @@
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+-cygwin*)
++cygwin* | msys*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+@@ -3564,7 +3570,7 @@
+ [lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+ case $host_os in
+-cygwin* | mingw* | pw32* | cegcc*)
++cygwin* | msys* | mingw* | pw32* | cegcc*)
+ # two different shell functions defined in ltmain.sh;
+ # decide which one to use based on capabilities of $DLLTOOL
+ case `$DLLTOOL --help 2>&1` in
+@@ -3634,7 +3640,7 @@
+ [AC_REQUIRE([AC_CANONICAL_HOST])dnl
+ LIBM=
+ case $host in
+-*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*)
++*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-msys* | *-*-haiku* | *-*-pw32* | *-*-darwin*)
+ # These system don't have libm, or don't need it
+ ;;
+ *-ncr-sysv4.3*)
+@@ -3709,7 +3715,7 @@
+ aix*)
+ symcode='[[BCDT]]'
+ ;;
+-cygwin* | mingw* | pw32* | cegcc*)
++cygwin* | msys* | mingw* | pw32* | cegcc*)
+ symcode='[[ABCDGISTW]]'
+ ;;
+ hpux*)
+@@ -4015,7 +4021,7 @@
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+- mingw* | cygwin* | os2* | pw32* | cegcc*)
++ mingw* | cygwin* | msys* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+@@ -4086,7 +4092,7 @@
+ ;;
+ esac
+ ;;
+- mingw* | cygwin* | os2* | pw32* | cegcc*)
++ mingw* | cygwin* | msys* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+@@ -4334,7 +4340,7 @@
+ # PIC is the default for these OSes.
+ ;;
+
+- mingw* | cygwin* | pw32* | os2* | cegcc*)
++ mingw* | cygwin* | msys* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+@@ -4433,7 +4439,7 @@
+ esac
+ ;;
+
+- mingw* | cygwin* | pw32* | os2* | cegcc*)
++ mingw* | cygwin* | msys* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+@@ -4699,7 +4705,7 @@
+ pw32*)
+ _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds
+ ;;
+- cygwin* | mingw* | cegcc*)
++ cygwin* | msys* | mingw* | cegcc*)
+ case $cc_basename in
+ cl*)
+ _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+@@ -4757,7 +4763,7 @@
+ extract_expsyms_cmds=
+
+ case $host_os in
+- cygwin* | mingw* | pw32* | cegcc*)
++ cygwin* | msys* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+@@ -4872,7 +4878,7 @@
+ fi
+ ;;
+
+- cygwin* | mingw* | pw32* | cegcc*)
++ cygwin* | msys* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+@@ -5247,7 +5253,7 @@
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+ ;;
+
+- cygwin* | mingw* | pw32* | cegcc*)
++ cygwin* | msys* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+@@ -6241,7 +6247,7 @@
+ esac
+ ;;
+
+- cygwin* | mingw* | pw32* | cegcc*)
++ cygwin* | msys* | mingw* | pw32* | cegcc*)
+ case $GXX,$cc_basename in
+ ,cl* | no,cl*)
+ # Native MSVC
+@@ -7937,7 +7943,7 @@
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+ ;;
+- *-*-cygwin* )
++ *-*-cygwin* | *-*-msys* )
+ lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+ ;;
+ * ) # otherwise, assume *nix
+@@ -7945,12 +7951,12 @@
+ ;;
+ esac
+ ;;
+- *-*-cygwin* )
++ *-*-cygwin* | *-*-msys* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+ ;;
+- *-*-cygwin* )
++ *-*-cygwin* | *-*-msys* )
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+ * ) # otherwise, assume *nix
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/autotools-patches/0010-libtool-2.4.2-include-process-h-mod.patch
^
|
@@ -0,0 +1,12 @@
+diff --git a/build-aux/ltmain.sh b/build-aux/ltmain.sh
+index 0418007..91276c2 100644
+--- a/build-aux/ltmain.sh
++++ b/build-aux/ltmain.sh
+@@ -4163,6 +4163,7 @@
+ # include <unistd.h>
+ # include <stdint.h>
+ # ifdef __CYGWIN__
++# include <process.h>
+ # include <io.h>
+ # endif
+ #endif
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/autotools-patches/0011-Pick-up-clang_rt-static-archives-compiler-internal-l.patch
^
|
@@ -0,0 +1,33 @@
+From a18473ed4e5574dab899db640b8efeff78939b54 Mon Sep 17 00:00:00 2001
+From: Manoj Gupta <manojgupta@chromium.org>
+Date: Wed, 10 Oct 2018 10:50:23 +0300
+Subject: [PATCH 1/2] Pick up clang_rt static archives compiler internal
+ libraries
+
+Libtool checks only for libraries linked as -l* when trying to
+find internal compiler libraries. Clang, however uses the absolute
+path to link its internal libraries e.g. compiler_rt. This patch
+handles clang's statically linked libraries when finding internal
+compiler libraries.
+https://crbug.com/749263
+https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27866
+---
+ m4/libtool.m4 | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/m4/libtool.m4 b/m4/libtool.m4
+index b55a6e5..d9322d0 100644
+--- a/m4/libtool.m4
++++ b/m4/libtool.m4
+@@ -7556,7 +7556,7 @@ if AC_TRY_EVAL(ac_compile); then
+ for p in `eval "$output_verbose_link_cmd"`; do
+ case $prev$p in
+
+- -L* | -R* | -l*)
++ -L* | -R* | -l* | */libclang_rt.*.a)
+ # Some compilers place space between "-{L,R}" and the path.
+ # Remove the space.
+ if test x-L = "$p" ||
+--
+2.7.4
+
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/autotools-patches/0012-Prefer-response-files-over-linker-scripts-for-mingw-mod.patch
^
|
@@ -0,0 +1,83 @@
+From ec15841963ca3aab3bc88fb0932c014337284bfc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin@martin.st>
+Date: Wed, 10 Oct 2018 10:47:21 +0300
+Subject: [PATCH 2/2] Prefer response files over linker scripts for mingw tools
+
+The GCC/binutils tools support response files just fine, while
+lld (impersonating GNU ld) only supports response files, not
+linker scripts. Using a linker script as input just to pass a
+list of files is overkill for cases when a response file is enough.
+---
+ build-aux/ltmain.in | 28 ++++++++++++++--------------
+ m4/libtool.m4 | 2 ++
+ 2 files changed, 16 insertions(+), 14 deletions(-)
+
+diff --git a/build-aux/ltmain.sh b/build-aux/ltmain.sh
+index e2fb263..db5d590 100644
+--- a/build-aux/ltmain.sh
++++ b/build-aux/ltmain.sh
+@@ -7932,20 +7932,7 @@ EOF
+ last_robj=
+ k=1
+
+- if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
+- output=$output_objdir/$output_la.lnkscript
+- func_verbose "creating GNU ld script: $output"
+- echo 'INPUT (' > $output
+- for obj in $save_libobjs
+- do
+- func_to_tool_file "$obj"
+- $ECHO "$func_to_tool_file_result" >> $output
+- done
+- echo ')' >> $output
+- func_append delfiles " $output"
+- func_to_tool_file "$output"
+- output=$func_to_tool_file_result
+- elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
++ if test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
+ output=$output_objdir/$output_la.lnk
+ func_verbose "creating linker input file list: $output"
+ : > $output
+@@ -7964,6 +7951,19 @@ EOF
+ func_append delfiles " $output"
+ func_to_tool_file "$output"
+ output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
++ elif test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
++ output=$output_objdir/$output_la.lnkscript
++ func_verbose "creating GNU ld script: $output"
++ echo 'INPUT (' > $output
++ for obj in $save_libobjs
++ do
++ func_to_tool_file "$obj"
++ $ECHO "$func_to_tool_file_result" >> $output
++ done
++ echo ')' >> $output
++ func_append delfiles " $output"
++ func_to_tool_file "$output"
++ output=$func_to_tool_file_result
+ else
+ if test -n "$save_libobjs"; then
+ func_verbose "creating reloadable object files..."
+diff --git a/m4/libtool.m4 b/m4/libtool.m4
+index d9322d0..9046a84 100644
+--- a/m4/libtool.m4
++++ b/m4/libtool.m4
+@@ -5130,6 +5130,7 @@ _LT_EOF
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
++ _LT_TAGVAR(file_list_spec, $1)='@'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+@@ -6706,6 +6707,7 @@ if test yes != "$_lt_caught_CXX_error"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
++ _LT_TAGVAR(file_list_spec, $1)='@'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+--
+2.7.4
+
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/autotools-patches/0013-Allow-statically-linking-compiler-support-libraries-mod.patch
^
|
@@ -0,0 +1,38 @@
+From b9f77cae8cfbe850e58cac686fcb4d246b5bfc51 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin@martin.st>
+Date: Mon, 19 Aug 2019 13:34:51 +0300
+Subject: [PATCH] Allow statically linking compiler support libraries when
+ linking a library
+
+For cases with deplibs_check_method="file_magic ..." (as it is for mingw),
+there were previously no way that a static library could be accepted
+here.
+---
+ build-aux/ltmain.in | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/build-aux/ltmain.sh b/build-aux/ltmain.sh
+index e2fb2633..db4d775c 100644
+--- a/build-aux/ltmain.sh
++++ b/build-aux/ltmain.sh
+@@ -5870,8 +5870,15 @@ func_mode_link ()
+ fi
+ case $linkmode in
+ lib)
+- # Linking convenience modules into shared libraries is allowed,
+- # but linking other static libraries is non-portable.
++ # Linking convenience modules and compiler provided static libraries
++ # into shared libraries is allowed, but linking other static
++ # libraries is non-portable.
++ case $deplib in
++ */libgcc*.$libext | */libclang_rt*.$libext)
++ deplibs="$deplib $deplibs"
++ continue
++ ;;
++ esac
+ case " $dlpreconveniencelibs " in
+ *" $deplib "*) ;;
+ *)
+--
+2.17.1
+
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/autotools-patches/0014-Support-llvm-objdump-f-output-mod.patch
^
|
@@ -0,0 +1,39 @@
+From 03dabb6a70847761e65572a2a7b770a3b1b9f123 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= <mati865@gmail.com>
+Date: Mon, 12 Apr 2021 23:44:10 +0200
+Subject: [PATCH] Support llvm-objdump -f output
+
+---
+ build-aux/ltmain.in | 2 +-
+ m4/libtool.m4 | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/build-aux/ltmain.sh b/build-aux/ltmain.sh
+index a9f070a..4a434cc 100644
+--- a/build-aux/ltmain.sh
++++ b/build-aux/ltmain.sh
+@@ -3019,7 +3019,7 @@ func_win32_libid ()
+ *ar\ archive*) # could be an import, or static
+ # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
+ if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+- $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
++ $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64|coff-arm|coff-arm64|coff-i386|coff-x86-64)' >/dev/null; then
+ case $nm_interface in
+ "MS dumpbin")
+ if func_cygming_ms_implib_p "$1" ||
+diff --git a/m4/libtool.m4 b/m4/libtool.m4
+index 21a7d60..594be9c 100644
+--- a/m4/libtool.m4
++++ b/m4/libtool.m4
+@@ -3473,7 +3473,7 @@ mingw* | pw32*)
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ # Keep this pattern in sync with the one in func_win32_libid.
+- lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
++ lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64|coff-arm|coff-arm64|coff-i386|coff-x86-64)'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+--
+2.31.1
+
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/autotools-patches/apply-all.sh
^
|
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+#
+# This file applies optional libtool patches mainly for better MSys2 compatibility,
+# especially for MSys2/Clang{64,32} toolchains.
+# It's a pity that these patches haven't been sent upstream.
+#
+# Based on Debian SID baseline files as of December 2021.
+#
+
+patchesdir="$(dirname "$BASH_SOURCE")" || exit 2
+test -n "$patchesdir" || exit 2
+
+patches=(
+ 0003-Pass-various-flags-to-GCC.patch
+ 0006-Fix-strict-ansi-vs-posix-mod.patch
+ 0009-libtool-2.4.2.418-msysize-mod.patch
+ 0010-libtool-2.4.2-include-process-h-mod.patch
+ 0011-Pick-up-clang_rt-static-archives-compiler-internal-l.patch
+ 0012-Prefer-response-files-over-linker-scripts-for-mingw-mod.patch
+ 0013-Allow-statically-linking-compiler-support-libraries-mod.patch
+ 0014-Support-llvm-objdump-f-output-mod.patch
+)
+
+failed=( )
+
+cd "${patchesdir}/../.." || exit 1
+
+for patch in ${patches[@]}; do
+ patch -N -p1 --no-backup-if-mismatch -r - -i "${patchesdir}/${patch}" || failed+=("$patch")
+done
+
+if [[ -n "${failed[@]}" ]]; then
+ printf 'Failed patch: %s\n' "${failed[@]}" >&2
+ exit 2
+fi
+
+exit 0
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/gen_http_methods_insert.sh
^
|
@@ -4,7 +4,7 @@
# Generate header insert for HTTP methods
#
-# Copyright (c) 2015-2019 Karlson2k (Evgeny Grin) <k2k@yandex.ru>
+# Copyright (c) 2015-2021 Karlson2k (Evgeny Grin) <k2k@yandex.ru>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
@@ -17,7 +17,7 @@
* @defgroup methods HTTP methods
* HTTP methods (as strings).
* See: http://www.iana.org/assignments/http-methods/http-methods.xml
- * Registry export date: '$(date -u +%Y-%m-%d)'
+ * Registry export date: '"$(date -u +%Y-%m-%d)"'
* @{
*/
@@ -26,7 +26,7 @@
FNR > 1 {
gsub(/^\[|^"\[|\]"$|\]$/, "", $4)
gsub(/\]\[/, "; ", $4)
- if (substr($4, 1, 7) == "RFC7231") {
+ if (substr($4, 1, 26) == "RFC-ietf-httpbis-semantics") {
if ($2 == "yes")
{ safe_m = "Safe. " }
else
@@ -36,7 +36,9 @@
else
{ indem_m = "Not idempotent." }
print "/* " safe_m " " indem_m " " $4 ". */"
- print "#define MHD_HTTP_METHOD_" toupper(gensub(/-/, "_", "g", $1)) " \""$1"\""
+ mthd = gensub(/\*/, "ASTERISK", "g", $1)
+ mthd = gensub(/[^a-zA-Z0-9_]/, "_", "g", mthd)
+ printf ("%-32s \"%s\"\n", "#define MHD_HTTP_METHOD_" toupper(mthd), $1)
}
}' methods.csv >> header_insert_methods.h && \
echo '
@@ -45,7 +47,7 @@
FNR > 1 {
gsub(/^\[|^"\[|\]"$|\]$/, "", $4)
gsub(/\]\[/, "; ", $4)
- if (substr($4, 1, 7) != "RFC7231") {
+ if (substr($4, 1, 26) != "RFC-ietf-httpbis-semantics") {
if ($2 == "yes")
{ safe_m = "Safe. " }
else
@@ -55,7 +57,9 @@
else
{ indem_m = "Not idempotent." }
print "/* " safe_m " " indem_m " " $4 ". */"
- print "#define MHD_HTTP_METHOD_" toupper(gensub(/-/, "_", "g", $1)) " \""$1"\""
+ mthd = gensub(/\*/, "ASTERISK", "g", $1)
+ mthd = gensub(/[^a-zA-Z0-9_]/, "_", "g", mthd)
+ printf ("%-38s \"%s\"\n", "#define MHD_HTTP_METHOD_" toupper(mthd), $1)
}
}' methods.csv >> header_insert_methods.h && \
echo OK && \
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/gen_http_statuses_inserts.sh
^
|
@@ -4,7 +4,7 @@
# Generate code and header inserts for HTTP statues
#
-# Copyright (c) 2019 Karlson2k (Evgeny Grin) <k2k@yandex.ru>
+# Copyright (c) 2019-2021 Karlson2k (Evgeny Grin) <k2k@yandex.ru>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
@@ -13,25 +13,25 @@
wget -nv https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv -O http-status-codes-1.csv || exit
echo Generating...
-echo "/**
+echo '/**
* @defgroup httpcode HTTP response codes.
* These are the status codes defined for HTTP responses.
* See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
- * Registry export date: $(date -u +%Y-%m-%d)
+ * Registry export date: '"$(date -u +%Y-%m-%d)"'
* @{
*/
-" > header_insert_statuses.h && \
+' > header_insert_statuses.h && \
gawk -e 'BEGIN {FPAT = "([^,]*)|(\"[^\"]+\")"}
FNR > 1 {
gsub(/^\[|^"\[|\]"$|\]$/, "", $3)
gsub(/\]\[/, "; ", $3)
- if ($1 == 306) {
- $2 = "Switch Proxy"
+ if ($1 == 306) {
+ $2 = "Switch Proxy"
$3 = "Not used! " $3
}
- if ($2 != "Unassigned") {
- print "/* " $1 sprintf("%-24s", " \"" $2 "\". ") $3 ". */"
- print "#define MHD_HTTP_" toupper(gensub(/[^A-Za-z0-0]/, "_", "g", $2)) " "$1""
+ if ($2 != "Unassigned" && $2 != "(Unused)") {
+ printf ("/* %s %-22s %s. */\n", $1, "\"" $2 "\".", $3)
+ printf ("#define MHD_HTTP_%-27s %s\n", toupper(gensub(/[^A-Za-z0-0]/, "_", "g", $2)), $1)
} else {
print ""
}
@@ -39,13 +39,13 @@
echo '
/* Not registered non-standard codes */
/* 449 "Reply With". MS IIS extension. */
-#define MHD_HTTP_RETRY_WITH 449
+#define MHD_HTTP_RETRY_WITH 449
/* 450 "Blocked by Windows Parental Controls". MS extension. */
#define MHD_HTTP_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS 450
/* 509 "Bandwidth Limit Exceeded". Apache extension. */
-#define MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED 509
+#define MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED 509
' >> header_insert_statuses.h && \
gawk -e 'BEGIN {
FPAT = "([^,]*)|(\"[^\"]+\")"
@@ -70,31 +70,32 @@
desc = $3
if (num % 100 == 0) {
if (num != 100) {
- printf (" /* %s */ %-24s /* %s */\n};\n\n", prev_num, "\""prev_reason"\"", prev_desc)
+ printf (" /* %s */ %-36s /* %s */\n};\n\n", prev_num, "_MHD_S_STR_W_LEN (\""prev_reason"\")", prev_desc)
}
prev_num = num;
- print "static const char *const " hundreds[$1/100] "_hundred[] = {"
+ print "static const struct _MHD_str_w_len " hundreds[$1/100] "_hundred[] = {"
}
if (num == 306) {
- reason = "Switch Proxy"
+ reason = "Switch Proxy"
desc = "Not used! " desc
}
- if (reason == "Unassigned") next
+ if (reason == "Unassigned" || reason == "(Unused)") next
if (prev_num != num)
- printf (" /* %s */ %-24s /* %s */\n", prev_num, "\""prev_reason"\",", prev_desc)
+ printf (" /* %s */ %-36s /* %s */\n", prev_num, "_MHD_S_STR_W_LEN (\""prev_reason"\"),", prev_desc)
while(++prev_num < num) {
if (prev_num == 449) {prev_reason="Reply With"; prev_desc="MS IIS extension";}
else if (prev_num == 450) {prev_reason="Blocked by Windows Parental Controls"; prev_desc="MS extension";}
else if (prev_num == 509) {prev_reason="Bandwidth Limit Exceeded"; prev_desc="Apache extension";}
else {prev_reason="Unknown"; prev_desc="Not used";}
- printf (" /* %s */ %-24s /* %s */\n", prev_num, "\""prev_reason"\",", prev_desc)
+ if (prev_reason=="Unknown") printf (" /* %s */ %-36s /* %s */\n", prev_num, "{\""prev_reason"\", 0},", prev_desc)
+ else printf (" /* %s */ %-36s /* %s */\n", prev_num, "_MHD_S_STR_W_LEN (\""prev_reason"\"),", prev_desc)
}
prev_num = num
prev_reason = reason
prev_desc = desc
}
END {
- printf (" /* %s */ %-24s /* %s */\n};\n", prev_num, "\""prev_reason"\"", prev_desc)
+ printf (" /* %s */ %-36s /* %s */\n};\n", prev_num, "_MHD_S_STR_W_LEN (\""prev_reason"\")", prev_desc)
}' http-status-codes-1.csv > code_insert_statuses.c && \
echo OK && \
rm http-status-codes-1.csv || exit
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/contrib/uncrustify.cfg
^
|
@@ -66,6 +66,7 @@
sp_after_assign = add
# we want "char *foo;"
+sp_before_ptr_star = add
sp_after_ptr_star = remove
sp_between_ptr_star = remove
@@ -75,6 +76,9 @@
sp_inside_fparen = remove
sp_inside_sparen = remove
+# Add or remove space around compare operator '<', '>', '==', etc
+sp_compare = add # ignore/add/remove/force
+
# add space before function call and decl: "foo (x)"
sp_func_call_paren = add
sp_func_proto_paren = add
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/doc/chapters/bibliography.inc
^
|
@@ -16,6 +16,9 @@
@emph{RFC 2617}: Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P.,
Luotonen, A., and L. Stewart, "HTTP Authentication: Basic and Digest Access Authentication", RFC 2617, June 1999.
+@item
+@emph{RFC 6455}: Fette, I., Melnikov, A., "The WebSocket Protocol", RFC 6455, December 2011.
+
@item
A well--structured @emph{HTML} reference can be found on
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/doc/chapters/websocket.inc
^
|
@@ -0,0 +1,886 @@
+Websockets are a genuine way to implement push notifications,
+where the server initiates the communication while the client can be idle.
+Usually a HTTP communication is half-duplex and always requested by the client,
+but websockets are full-duplex and only initialized by the client.
+In the further communication both sites can use the websocket at any time
+to send data to the other site.
+
+To initialize a websocket connection the client sends a special HTTP request
+to the server and initializes
+a handshake between client and server which switches from the HTTP protocol
+to the websocket protocol.
+Thus both the server as well as the client must support websockets.
+If proxys are used, they must support websockets too.
+In this chapter we take a look on server and client, but with a focus on
+the server with @emph{libmicrohttpd}.
+
+Since version 0.9.52 @emph{libmicrohttpd} supports upgrading requests,
+which is required for switching from the HTTP protocol.
+Since version 0.9.74 the library @emph{libmicrohttpd_ws} has been added
+to support the websocket protocol.
+
+@heading Upgrading connections with libmicrohttpd
+
+To support websockets we need to enable upgrading of HTTP connections first.
+This is done by passing the flag @code{MHD_ALLOW_UPGRADE} to
+@code{MHD_start_daemon()}.
+
+
+@verbatim
+daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
+ MHD_USE_THREAD_PER_CONNECTION |
+ MHD_ALLOW_UPGRADE |
+ MHD_USE_ERROR_LOG,
+ PORT, NULL, NULL,
+ &access_handler, NULL,
+ MHD_OPTION_END);
+@end verbatim
+@noindent
+
+
+The next step is to turn a specific request into an upgraded connection.
+This done in our @code{access_handler} by calling
+@code{MHD_create_response_for_upgrade()}.
+An @code{upgrade_handler} will be passed to perform the low-level actions
+on the socket.
+
+@emph{Please note that the socket here is just a regular socket as provided
+by the operating system.
+To use it as a websocket, some more steps from the following
+chapters are required.}
+
+
+@verbatim
+static enum MHD_Result
+access_handler (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **ptr)
+{
+ /* ... */
+ /* some code to decide whether to upgrade or not */
+ /* ... */
+
+ /* create the response for upgrade */
+ response = MHD_create_response_for_upgrade (&upgrade_handler,
+ NULL);
+
+ /* ... */
+ /* additional headers, etc. */
+ /* ... */
+
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_SWITCHING_PROTOCOLS,
+ response);
+ MHD_destroy_response (response);
+
+ return ret;
+}
+@end verbatim
+@noindent
+
+
+In the @code{upgrade_handler} we receive the low-level socket,
+which is used for the communication with the specific client.
+In addition to the low-level socket we get:
+@itemize @bullet
+@item
+Some data, which has been read too much while @emph{libmicrohttpd} was
+switching the protocols.
+This value is usually empty, because it would mean that the client
+has sent data before the handshake was complete.
+
+@item
+A @code{struct MHD_UpgradeResponseHandle} which is used to perform
+special actions like closing, corking or uncorking the socket.
+These commands are executed by passing the handle
+to @code{MHD_upgrade_action()}.
+
+
+@end itemize
+
+Depending of the flags specified while calling @code{MHD_start_deamon()}
+our @code{upgrade_handler} is either executed in the same thread
+as our deamon or in a thread specific for each connection.
+If it is executed in the same thread then @code{upgrade_handler} is
+a blocking call for our webserver and
+we should finish it as fast as possible (i. e. by creating a thread and
+passing the information there).
+If @code{MHD_USE_THREAD_PER_CONNECTION} was passed to
+@code{MHD_start_daemon()} then a separate thread is used and
+thus our @code{upgrade_handler} needs not to start a separate thread.
+
+An @code{upgrade_handler}, which is called with a separate thread
+per connection, could look like this:
+
+
+@verbatim
+static void
+upgrade_handler (void *cls,
+ struct MHD_Connection *connection,
+ void *con_cls,
+ const char *extra_in,
+ size_t extra_in_size,
+ MHD_socket fd,
+ struct MHD_UpgradeResponseHandle *urh)
+{
+ /* ... */
+ /* do something with the socket `fd` like `recv()` or `send()` */
+ /* ... */
+
+ /* close the socket when it is not needed anymore */
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+}
+@end verbatim
+@noindent
+
+
+This is all you need to know for upgrading connections
+with @emph{libmicrohttpd}.
+The next chapters focus on using the websocket protocol
+with @emph{libmicrohttpd_ws}.
+
+
+@heading Websocket handshake with libmicrohttpd_ws
+
+To request a websocket connection the client must send
+the following information with the HTTP request:
+
+@itemize @bullet
+@item
+A @code{GET} request must be sent.
+
+@item
+The version of the HTTP protocol must be 1.1 or higher.
+
+@item
+A @code{Host} header field must be sent
+
+@item
+A @code{Upgrade} header field containing the keyword "websocket"
+(case-insensitive).
+Please note that the client could pass multiple protocols separated by comma.
+
+@item
+A @code{Connection} header field that includes the token "Upgrade"
+(case-insensitive).
+Please note that the client could pass multiple tokens separated by comma.
+
+@item
+A @code{Sec-WebSocket-Key} header field with a base64-encoded value.
+The decoded the value is 16 bytes long
+and has been generated randomly by the client.
+
+@item
+A @code{Sec-WebSocket-Version} header field with the value "13".
+
+@end itemize
+
+
+Optionally the client can also send the following information:
+
+
+@itemize @bullet
+@item
+A @code{Origin} header field can be used to determine the source
+of the client (i. e. the website).
+
+@item
+A @code{Sec-WebSocket-Protocol} header field can contain a list
+of supported protocols by the client, which can be sent over the websocket.
+
+@item
+A @code{Sec-WebSocket-Extensions} header field which may contain extensions
+to the websocket protocol. The extensions must be registered by IANA.
+
+@end itemize
+
+
+A valid example request from the client could look like this:
+
+
+@verbatim
+GET /chat HTTP/1.1
+Host: server.example.com
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Sec-WebSocket-Version: 13
+@end verbatim
+@noindent
+
+
+To complete the handshake the server must respond with
+some specific response headers:
+
+@itemize @bullet
+@item
+The HTTP response code @code{101 Switching Protocols} must be answered.
+
+@item
+An @code{Upgrade} header field containing the value "websocket" must be sent.
+
+@item
+A @code{Connection} header field containing the value "Upgrade" must be sent.
+
+@item
+A @code{Sec-WebSocket-Accept} header field containing a value, which
+has been calculated from the @code{Sec-WebSocket-Key} request header field,
+must be sent.
+
+@end itemize
+
+
+Optionally the server may send following headers:
+
+
+@itemize @bullet
+@item
+A @code{Sec-WebSocket-Protocol} header field containing a protocol
+of the list specified in the corresponding request header field.
+
+@item
+A @code{Sec-WebSocket-Extension} header field containing all used extensions
+of the list specified in the corresponding request header field.
+
+@end itemize
+
+
+A valid websocket HTTP response could look like this:
+
+@verbatim
+HTTP/1.1 101 Switching Protocols
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+@end verbatim
+@noindent
+
+
+To upgrade a connection to a websocket the @emph{libmicrohttpd_ws} provides
+some helper functions for the @code{access_handler} callback function:
+
+@itemize @bullet
+@item
+@code{MHD_websocket_check_http_version()} checks whether the HTTP version
+is 1.1 or above.
+
+@item
+@code{MHD_websocket_check_connection_header()} checks whether the value
+of the @code{Connection} request header field contains
+an "Upgrade" token (case-insensitive).
+
+@item
+@code{MHD_websocket_check_upgrade_header()} checks whether the value
+of the @code{Upgrade} request header field contains
+the "websocket" keyword (case-insensitive).
+
+@item
+@code{MHD_websocket_check_version_header()} checks whether the value
+of the @code{Sec-WebSocket-Version} request header field is "13".
+
+@item
+@code{MHD_websocket_create_accept_header()} takes the value from
+the @code{Sec-WebSocket-Key} request header and calculates the value
+for the @code{Sec-WebSocket-Accept} response header field.
+
+@end itemize
+
+
+The @code{access_handler} example of the previous chapter can now be
+extended with these helper functions to perform the websocket handshake:
+
+@verbatim
+static enum MHD_Result
+access_handler (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **ptr)
+{
+ static int aptr;
+ struct MHD_Response *response;
+ int ret;
+
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) upload_data; /* Unused. Silent compiler warning. */
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO; /* unexpected method */
+ if (&aptr != *ptr)
+ {
+ /* do never respond on first call */
+ *ptr = &aptr;
+ return MHD_YES;
+ }
+ *ptr = NULL; /* reset when done */
+
+ if (0 == strcmp (url, "/"))
+ {
+ /* Default page for visiting the server */
+ struct MHD_Response *response = MHD_create_response_from_buffer (
+ strlen (PAGE),
+ PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ }
+ else if (0 == strcmp (url, "/chat"))
+ {
+ char is_valid = 1;
+ const char* value = NULL;
+ char sec_websocket_accept[29];
+
+ if (0 != MHD_websocket_check_http_version (version))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONNECTION);
+ if (0 != MHD_websocket_check_connection_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_UPGRADE);
+ if (0 != MHD_websocket_check_upgrade_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
+ if (0 != MHD_websocket_check_version_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
+ if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
+ {
+ is_valid = 0;
+ }
+
+ if (1 == is_valid)
+ {
+ /* upgrade the connection */
+ response = MHD_create_response_for_upgrade (&upgrade_handler,
+ NULL);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONNECTION,
+ "Upgrade");
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_UPGRADE,
+ "websocket");
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
+ sec_websocket_accept);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_SWITCHING_PROTOCOLS,
+ response);
+ MHD_destroy_response (response);
+ }
+ else
+ {
+ /* return error page */
+ struct MHD_Response*response = MHD_create_response_from_buffer (
+ strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
+ PAGE_INVALID_WEBSOCKET_REQUEST,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_BAD_REQUEST,
+ response);
+ MHD_destroy_response (response);
+ }
+ }
+ else
+ {
+ struct MHD_Response*response = MHD_create_response_from_buffer (
+ strlen (PAGE_NOT_FOUND),
+ PAGE_NOT_FOUND,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_FOUND,
+ response);
+ MHD_destroy_response (response);
+ }
+
+ return ret;
+}
+@end verbatim
+@noindent
+
+Please note that we skipped the check of the Host header field here,
+because we don't know the host for this example.
+
+@heading Decoding/encoding the websocket protocol with libmicrohttpd_ws
+
+Once the websocket connection is established you can receive/send frame data
+with the low-level socket functions @code{recv()} and @code{send()}.
+The frame data which goes over the low-level socket is encoded according
+to the websocket protocol.
+To use received payload data, you need to decode the frame data first.
+To send payload data, you need to encode it into frame data first.
+
+@emph{libmicrohttpd_ws} provides serveral functions for encoding of
+payload data and decoding of frame data:
+
+@itemize @bullet
+@item
+@code{MHD_websocket_decode()} decodes received frame data.
+The payload data may be of any kind, depending upon what the client has sent.
+So this decode function is used for all kind of frames and returns
+the frame type along with the payload data.
+
+@item
+@code{MHD_websocket_encode_text()} encodes text.
+The text must be encoded with UTF-8.
+
+@item
+@code{MHD_websocket_encode_binary()} encodes binary data.
+
+@item
+@code{MHD_websocket_encode_ping()} encodes a ping request to
+check whether the websocket is still valid and to test latency.
+
+@item
+@code{MHD_websocket_encode_ping()} encodes a pong response to
+answer a received ping request.
+
+@item
+@code{MHD_websocket_encode_close()} encodes a close request.
+
+@item
+@code{MHD_websocket_free()} frees data returned by the encode/decode functions.
+
+@end itemize
+
+Since you could receive or send fragmented data (i. e. due to a too
+small buffer passed to @code{recv}) all of these encode/decode
+functions require a pointer to a @code{struct MHD_WebSocketStream} passed
+as argument.
+In this structure @emph{libmicrohttpd_ws} stores information
+about encoding/decoding of the particular websocket.
+For each websocket you need a unique @code{struct MHD_WebSocketStream}
+to encode/decode with this library.
+
+To create or destroy @code{struct MHD_WebSocketStream}
+we have additional functions:
+
+@itemize @bullet
+@item
+@code{MHD_websocket_stream_init()} allocates and initializes
+a new @code{struct MHD_WebSocketStream}.
+You can specify some options here to alter the behavior of the websocket stream.
+
+@item
+@code{MHD_websocket_stream_free()} frees a previously allocated
+@code{struct MHD_WebSocketStream}.
+
+@end itemize
+
+With these encode/decode functions we can improve our @code{upgrade_handler}
+callback function from an earlier example to a working websocket:
+
+
+@verbatim
+static void
+upgrade_handler (void *cls,
+ struct MHD_Connection *connection,
+ void *con_cls,
+ const char *extra_in,
+ size_t extra_in_size,
+ MHD_socket fd,
+ struct MHD_UpgradeResponseHandle *urh)
+{
+ /* make the socket blocking (operating-system-dependent code) */
+ make_blocking (fd);
+
+ /* create a websocket stream for this connection */
+ struct MHD_WebSocketStream* ws;
+ int result = MHD_websocket_stream_init (&ws,
+ 0,
+ 0);
+ if (0 != result)
+ {
+ /* Couldn't create the websocket stream.
+ * So we close the socket and leave
+ */
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ return;
+ }
+
+ /* Let's wait for incoming data */
+ const size_t buf_len = 256;
+ char buf[buf_len];
+ ssize_t got;
+ while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
+ {
+ got = recv (fd,
+ buf,
+ buf_len,
+ 0);
+ if (0 >= got)
+ {
+ /* the TCP/IP socket has been closed */
+ break;
+ }
+
+ /* parse the entire received data */
+ size_t buf_offset = 0;
+ while (buf_offset < (size_t) got)
+ {
+ size_t new_offset = 0;
+ char *frame_data = NULL;
+ size_t frame_len = 0;
+ int status = MHD_websocket_decode (ws,
+ buf + buf_offset,
+ ((size_t) got) - buf_offset,
+ &new_offset,
+ &frame_data,
+ &frame_len);
+ if (0 > status)
+ {
+ /* an error occurred and the connection must be closed */
+ if (NULL != frame_data)
+ {
+ MHD_websocket_free (ws, frame_data);
+ }
+ break;
+ }
+ else
+ {
+ buf_offset += new_offset;
+ if (0 < status)
+ {
+ /* the frame is complete */
+ switch (status)
+ {
+ case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
+ /* The client has sent some text.
+ * We will display it and answer with a text frame.
+ */
+ if (NULL != frame_data)
+ {
+ printf ("Received message: %s\n", frame_data);
+ MHD_websocket_free (ws, frame_data);
+ frame_data = NULL;
+ }
+ result = MHD_websocket_encode_text (ws,
+ "Hello",
+ 5, /* length of "Hello" */
+ 0,
+ &frame_data,
+ &frame_len,
+ NULL);
+ if (0 == result)
+ {
+ send_all (fd,
+ frame_data,
+ frame_len);
+ }
+ break;
+
+ case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
+ /* if we receive a close frame, we will respond with one */
+ MHD_websocket_free (ws,
+ frame_data);
+ frame_data = NULL;
+
+ result = MHD_websocket_encode_close (ws,
+ 0,
+ NULL,
+ 0,
+ &frame_data,
+ &frame_len);
+ if (0 == result)
+ {
+ send_all (fd,
+ frame_data,
+ frame_len);
+ }
+ break;
+
+ case MHD_WEBSOCKET_STATUS_PING_FRAME:
+ /* if we receive a ping frame, we will respond */
+ /* with the corresponding pong frame */
+ {
+ char *pong = NULL;
+ size_t pong_len = 0;
+ result = MHD_websocket_encode_pong (ws,
+ frame_data,
+ frame_len,
+ &pong,
+ &pong_len);
+ if (0 == result)
+ {
+ send_all (fd,
+ pong,
+ pong_len);
+ }
+ MHD_websocket_free (ws,
+ pong);
+ }
+ break;
+
+ default:
+ /* Other frame types are ignored
+ * in this minimal example.
+ * This is valid, because they become
+ * automatically skipped if we receive them unexpectedly
+ */
+ break;
+ }
+ }
+ if (NULL != frame_data)
+ {
+ MHD_websocket_free (ws, frame_data);
+ }
+ }
+ }
+ }
+
+ /* free the websocket stream */
+ MHD_websocket_stream_free (ws);
+
+ /* close the socket when it is not needed anymore */
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+}
+
+/* This helper function is used for the case that
+ * we need to resend some data
+ */
+static void
+send_all (MHD_socket fd,
+ const char *buf,
+ size_t len)
+{
+ ssize_t ret;
+ size_t off;
+
+ for (off = 0; off < len; off += ret)
+ {
+ ret = send (fd,
+ &buf[off],
+ (int) (len - off),
+ 0);
+ if (0 > ret)
+ {
+ if (EAGAIN == errno)
+ {
+ ret = 0;
+ continue;
+ }
+ break;
+ }
+ if (0 == ret)
+ break;
+ }
+}
+
+/* This helper function contains operating-system-dependent code and
+ * is used to make a socket blocking.
+ */
+static void
+make_blocking (MHD_socket fd)
+{
+#if defined(MHD_POSIX_SOCKETS)
+ int flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (-1 == flags)
+ return;
+ if ((flags & ~O_NONBLOCK) != flags)
+ if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+ abort ();
+#elif defined(MHD_WINSOCK_SOCKETS)
+ unsigned long flags = 0;
+
+ ioctlsocket (fd, FIONBIO, &flags);
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+@end verbatim
+@noindent
+
+
+Please note that the websocket in this example is only half-duplex.
+It waits until the blocking @code{recv()} call returns and
+only does then something.
+In this example all frame types are decoded by @emph{libmicrohttpd_ws},
+but we only do something when a text, ping or close frame is received.
+Binary and pong frames are ignored in our code.
+This is legit, because the server is only required to implement at
+least support for ping frame or close frame (the other frame types
+could be skipped in theory, because they don't require an answer).
+The pong frame doesn't require an answer and whether text frames or
+binary frames get an answer simply belongs to your server application.
+So this is a valid minimal example.
+
+Until this point you've learned everything you need to basically
+use websockets with @emph{libmicrohttpd} and @emph{libmicrohttpd_ws}.
+These libraries offer much more functions for some specific cases.
+
+
+The further chapters of this tutorial focus on some specific problems
+and the client site programming.
+
+
+@heading Using full-duplex websockets
+
+To use full-duplex websockets you can simply create two threads
+per websocket connection.
+One of these threads is used for receiving data with
+a blocking @code{recv()} call and the other thread is triggered
+by the application internal codes and sends the data.
+
+A full-duplex websocket example is implemented in the example file
+@code{websocket_chatserver_example.c}.
+
+@heading Error handling
+
+The most functions of @emph{libmicrohttpd_ws} return a value
+of @code{enum MHD_WEBSOCKET_STATUS}.
+The values of this enumeration can be converted into an integer
+and have an easy interpretation:
+
+@itemize @bullet
+@item
+If the value is less than zero an error occurred and the call has failed.
+Check the enumeration values for more specific information.
+
+@item
+If the value is equal to zero, the call succeeded.
+
+@item
+If the value is greater than zero, the call succeeded and the value
+specifies the decoded frame type.
+Currently positive values are only returned by @code{MHD_websocket_decode()}
+(of the functions with this return enumeration type).
+
+@end itemize
+
+A websocket stream can also get broken when invalid frame data is received.
+Also the other site could send a close frame which puts the stream into
+a state where it may not be used for regular communication.
+Whether a stream has become broken, can be checked with
+@code{MHD_websocket_stream_is_valid()}.
+
+
+@heading Fragmentation
+
+In addition to the regular TCP/IP fragmentation the websocket protocol also
+supports fragmentation.
+Fragmentation could be used for continuous payload data such as video data
+from a webcam.
+Whether or not you want to receive fragmentation is specified upon
+initialization of the websocket stream.
+If you pass @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} in the flags parameter
+of @code{MHD_websocket_stream_init()} then you can receive fragments.
+If you don't pass this flag (in the most cases you just pass zero as flags)
+then you don't want to handle fragments on your own.
+@emph{libmicrohttpd_ws} removes then the fragmentation for you
+in the background.
+You only get the completely assembled frames.
+
+Upon encoding you specify whether or not you want to create a fragmented frame
+by passing a flag to the corresponding encode function.
+Only @code{MHD_websocket_encode_text()} and @code{MHD_websocket_encode_binary()}
+can be used for fragmentation, because the other frame types may
+not be fragmented.
+Encoding fragmented frames is independent of
+the @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} flag upon initialization.
+
+@heading Quick guide to websockets in JavaScript
+
+Websockets are supported in all modern web browsers.
+You initialize a websocket connection by creating an instance of
+the @code{WebSocket} class provided by the web browser.
+
+There are some simple rules for using websockets in the browser:
+
+@itemize @bullet
+@item
+When you initialize the instance of the websocket class you must pass an URL.
+The URL must either start with @code{ws://}
+(for not encrypted websocket protocol) or @code{wss://}
+(for TLS-encrypted websocket protocol).
+
+@strong{IMPORTANT:} If your website is accessed via @code{https://}
+then you are in a security context, which means that you are only allowed to
+access other secure protocols.
+So you can only use @code{wss://} for websocket connections then.
+If you try to @code{ws://} instead then your websocket connection will
+automatically fail.
+
+@item
+The WebSocket class uses events to handle the receiving of data.
+JavaScript is per definition a single-threaded language so
+the receiving events will never overlap.
+Sending is done directly by calling a method of the instance of
+the WebSocket class.
+
+@end itemize
+
+
+Here is a short example for receiving/sending data to the same host
+as the website is running on:
+
+@verbatim
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Websocket Demo</title>
+<script>
+
+let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' +
+ window.location.host + '/chat';
+let socket = null;
+
+window.onload = function(event) {
+ socket = new WebSocket(url);
+ socket.onopen = function(event) {
+ document.write('The websocket connection has been established.<br>');
+
+ // Send some text
+ socket.send('Hello from JavaScript!');
+ }
+
+ socket.onclose = function(event) {
+ document.write('The websocket connection has been closed.<br>');
+ }
+
+ socket.onerror = function(event) {
+ document.write('An error occurred during the websocket communication.<br>');
+ }
+
+ socket.onmessage = function(event) {
+ document.write('Websocket message received: ' + event.data + '<br>');
+ }
+}
+
+</script>
+</head>
+<body>
+</body>
+</html>
+
+@end verbatim
+@noindent
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/doc/examples/tlsauthentication.c
^
|
@@ -30,13 +30,14 @@
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
unsigned long l;
size_t i;
+ size_t j;
char *tmp;
size_t length = strlen (message);
tmp = malloc (length * 2 + 1);
if (NULL == tmp)
return NULL;
- tmp[0] = 0;
+ j = 0;
for (i = 0; i < length; i += 3)
{
l = (((unsigned long) message[i]) << 16)
@@ -44,17 +45,21 @@
| (((i + 2) < length) ? ((unsigned long) message[i + 2]) : 0);
- strncat (tmp, &lookup[(l >> 18) & 0x3F], 1);
- strncat (tmp, &lookup[(l >> 12) & 0x3F], 1);
+ tmp [j++] = lookup[(l >> 18) & 0x3F];
+ tmp [j++] = lookup[(l >> 12) & 0x3F];
if (i + 1 < length)
- strncat (tmp, &lookup[(l >> 6) & 0x3F], 1);
+ tmp [j++] = lookup[(l >> 6) & 0x3F];
if (i + 2 < length)
- strncat (tmp, &lookup[l & 0x3F], 1);
+ tmp [j++] = lookup[l & 0x3F];
}
- if (length % 3)
- strncat (tmp, "==", 3 - length % 3);
+ if (0 != length % 3)
+ tmp [j++] = '=';
+ if (1 == length % 3)
+ tmp [j++] = '=';
+
+ tmp [j] = 0;
return tmp;
}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/doc/examples/websocket.c
^
|
@@ -0,0 +1,452 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#else
+#include <winsock2.h>
+#endif
+#include <microhttpd.h>
+#include <microhttpd_ws.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define PORT 80
+
+#define PAGE \
+ "<!DOCTYPE html>\n" \
+ "<html>\n" \
+ "<head>\n" \
+ "<meta charset=\"UTF-8\">\n" \
+ "<title>Websocket Demo</title>\n" \
+ "<script>\n" \
+ "\n" \
+ "let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
+ " + '://' +\n" \
+ " window.location.host + '/chat';\n" \
+ "let socket = null;\n" \
+ "\n" \
+ "window.onload = function(event) {\n" \
+ " socket = new WebSocket(url);\n" \
+ " socket.onopen = function(event) {\n" \
+ " document.write('The websocket connection has been " \
+ "established.<br>');\n" \
+ "\n" \
+ " // Send some text\n" \
+ " socket.send('Hello from JavaScript!');\n" \
+ " }\n" \
+ "\n" \
+ " socket.onclose = function(event) {\n" \
+ " document.write('The websocket connection has been closed.<br>');\n" \
+ " }\n" \
+ "\n" \
+ " socket.onerror = function(event) {\n" \
+ " document.write('An error occurred during the websocket " \
+ "communication.<br>');\n" \
+ " }\n" \
+ "\n" \
+ " socket.onmessage = function(event) {\n" \
+ " document.write('Websocket message received: ' + " \
+ "event.data + '<br>');\n" \
+ " }\n" \
+ "}\n" \
+ "\n" \
+ "</script>\n" \
+ "</head>\n" \
+ "<body>\n" \
+ "</body>\n" \
+ "</html>"
+
+#define PAGE_NOT_FOUND \
+ "404 Not Found"
+
+#define PAGE_INVALID_WEBSOCKET_REQUEST \
+ "Invalid WebSocket request!"
+
+static void
+send_all (MHD_socket fd,
+ const char *buf,
+ size_t len);
+
+static void
+make_blocking (MHD_socket fd);
+
+static void
+upgrade_handler (void *cls,
+ struct MHD_Connection *connection,
+ void *con_cls,
+ const char *extra_in,
+ size_t extra_in_size,
+ MHD_socket fd,
+ struct MHD_UpgradeResponseHandle *urh)
+{
+ /* make the socket blocking (operating-system-dependent code) */
+ make_blocking (fd);
+
+ /* create a websocket stream for this connection */
+ struct MHD_WebSocketStream *ws;
+ int result = MHD_websocket_stream_init (&ws,
+ 0,
+ 0);
+ if (0 != result)
+ {
+ /* Couldn't create the websocket stream.
+ * So we close the socket and leave
+ */
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ return;
+ }
+
+ /* Let's wait for incoming data */
+ const size_t buf_len = 256;
+ char buf[buf_len];
+ ssize_t got;
+ while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
+ {
+ got = recv (fd,
+ buf,
+ buf_len,
+ 0);
+ if (0 >= got)
+ {
+ /* the TCP/IP socket has been closed */
+ break;
+ }
+
+ /* parse the entire received data */
+ size_t buf_offset = 0;
+ while (buf_offset < (size_t) got)
+ {
+ size_t new_offset = 0;
+ char *frame_data = NULL;
+ size_t frame_len = 0;
+ int status = MHD_websocket_decode (ws,
+ buf + buf_offset,
+ ((size_t) got) - buf_offset,
+ &new_offset,
+ &frame_data,
+ &frame_len);
+ if (0 > status)
+ {
+ /* an error occurred and the connection must be closed */
+ if (NULL != frame_data)
+ {
+ MHD_websocket_free (ws, frame_data);
+ }
+ break;
+ }
+ else
+ {
+ buf_offset += new_offset;
+ if (0 < status)
+ {
+ /* the frame is complete */
+ switch (status)
+ {
+ case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
+ /* The client has sent some text.
+ * We will display it and answer with a text frame.
+ */
+ if (NULL != frame_data)
+ {
+ printf ("Received message: %s\n", frame_data);
+ MHD_websocket_free (ws, frame_data);
+ frame_data = NULL;
+ }
+ result = MHD_websocket_encode_text (ws,
+ "Hello",
+ 5, /* length of "Hello" */
+ 0,
+ &frame_data,
+ &frame_len,
+ NULL);
+ if (0 == result)
+ {
+ send_all (fd,
+ frame_data,
+ frame_len);
+ }
+ break;
+
+ case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
+ /* if we receive a close frame, we will respond with one */
+ MHD_websocket_free (ws,
+ frame_data);
+ frame_data = NULL;
+
+ result = MHD_websocket_encode_close (ws,
+ 0,
+ NULL,
+ 0,
+ &frame_data,
+ &frame_len);
+ if (0 == result)
+ {
+ send_all (fd,
+ frame_data,
+ frame_len);
+ }
+ break;
+
+ case MHD_WEBSOCKET_STATUS_PING_FRAME:
+ /* if we receive a ping frame, we will respond */
+ /* with the corresponding pong frame */
+ {
+ char *pong = NULL;
+ size_t pong_len = 0;
+ result = MHD_websocket_encode_pong (ws,
+ frame_data,
+ frame_len,
+ &pong,
+ &pong_len);
+ if (0 == result)
+ {
+ send_all (fd,
+ pong,
+ pong_len);
+ }
+ MHD_websocket_free (ws,
+ pong);
+ }
+ break;
+
+ default:
+ /* Other frame types are ignored
+ * in this minimal example.
+ * This is valid, because they become
+ * automatically skipped if we receive them unexpectedly
+ */
+ break;
+ }
+ }
+ if (NULL != frame_data)
+ {
+ MHD_websocket_free (ws, frame_data);
+ }
+ }
+ }
+ }
+
+ /* free the websocket stream */
+ MHD_websocket_stream_free (ws);
+
+ /* close the socket when it is not needed anymore */
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+}
+
+
+/* This helper function is used for the case that
+ * we need to resend some data
+ */
+static void
+send_all (MHD_socket fd,
+ const char *buf,
+ size_t len)
+{
+ ssize_t ret;
+ size_t off;
+
+ for (off = 0; off < len; off += ret)
+ {
+ ret = send (fd,
+ &buf[off],
+ (int) (len - off),
+ 0);
+ if (0 > ret)
+ {
+ if (EAGAIN == errno)
+ {
+ ret = 0;
+ continue;
+ }
+ break;
+ }
+ if (0 == ret)
+ break;
+ }
+}
+
+
+/* This helper function contains operating-system-dependent code and
+ * is used to make a socket blocking.
+ */
+static void
+make_blocking (MHD_socket fd)
+{
+#ifndef _WIN32
+ int flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (-1 == flags)
+ abort ();
+ if ((flags & ~O_NONBLOCK) != flags)
+ if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+ abort ();
+#else /* _WIN32 */
+ unsigned long flags = 0;
+
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ abort ();
+#endif /* _WIN32 */
+}
+
+
+static enum MHD_Result
+access_handler (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **ptr)
+{
+ static int aptr;
+ struct MHD_Response *response;
+ int ret;
+
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) upload_data; /* Unused. Silent compiler warning. */
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO; /* unexpected method */
+ if (&aptr != *ptr)
+ {
+ /* do never respond on first call */
+ *ptr = &aptr;
+ return MHD_YES;
+ }
+ *ptr = NULL; /* reset when done */
+
+ if (0 == strcmp (url, "/"))
+ {
+ /* Default page for visiting the server */
+ struct MHD_Response *response = MHD_create_response_from_buffer (
+ strlen (PAGE),
+ PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ }
+ else if (0 == strcmp (url, "/chat"))
+ {
+ char is_valid = 1;
+ const char *value = NULL;
+ char sec_websocket_accept[29];
+
+ if (0 != MHD_websocket_check_http_version (version))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONNECTION);
+ if (0 != MHD_websocket_check_connection_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_UPGRADE);
+ if (0 != MHD_websocket_check_upgrade_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
+ if (0 != MHD_websocket_check_version_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
+ if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
+ {
+ is_valid = 0;
+ }
+
+ if (1 == is_valid)
+ {
+ /* upgrade the connection */
+ response = MHD_create_response_for_upgrade (&upgrade_handler,
+ NULL);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONNECTION,
+ "Upgrade");
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_UPGRADE,
+ "websocket");
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
+ sec_websocket_accept);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_SWITCHING_PROTOCOLS,
+ response);
+ MHD_destroy_response (response);
+ }
+ else
+ {
+ /* return error page */
+ struct MHD_Response *response = MHD_create_response_from_buffer (
+ strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
+ PAGE_INVALID_WEBSOCKET_REQUEST,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_BAD_REQUEST,
+ response);
+ MHD_destroy_response (response);
+ }
+ }
+ else
+ {
+ struct MHD_Response *response = MHD_create_response_from_buffer (
+ strlen (PAGE_NOT_FOUND),
+ PAGE_NOT_FOUND,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_FOUND,
+ response);
+ MHD_destroy_response (response);
+ }
+
+ return ret;
+}
+
+
+int
+main (int argc,
+ char *const *argv)
+{
+ (void) argc; /* Unused. Silent compiler warning. */
+ (void) argv; /* Unused. Silent compiler warning. */
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
+ | MHD_USE_THREAD_PER_CONNECTION
+ | MHD_ALLOW_UPGRADE
+ | MHD_USE_ERROR_LOG,
+ PORT, NULL, NULL,
+ &access_handler, NULL,
+ MHD_OPTION_END);
+
+ if (NULL == daemon)
+ return 1;
+ (void) getc (stdin);
+
+ MHD_stop_daemon (daemon);
+
+ return 0;
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/doc/libmicrohttpd-tutorial.texi
^
|
@@ -68,6 +68,7 @@
* Improved processing of POST data::
* Session management::
* Adding a layer of security::
+* Websockets::
* Bibliography::
* License text::
* Example programs::
@@ -109,6 +110,10 @@
@chapter Adding a layer of security
@include chapters/tlsauthentication.inc
+@node Websockets
+@chapter Websockets
+@include chapters/websocket.inc
+
@node Bibliography
@appendix Bibliography
@include chapters/bibliography.inc
@@ -128,6 +133,7 @@
* largepost.c::
* sessions.c::
* tlsauthentication.c::
+* websocket.c::
@end menu
@node hellobrowser.c
@@ -178,4 +184,10 @@
@verbatiminclude examples/tlsauthentication.c
@end smalldisplay
+@node websocket.c
+@section websocket.c
+@smalldisplay
+@verbatiminclude examples/websocket.c
+@end smalldisplay
+
@bye
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/doc/libmicrohttpd.texi
^
|
@@ -67,6 +67,7 @@
* microhttpd-post:: Adding a @code{POST} processor.
* microhttpd-info:: Obtaining and modifying status information.
* microhttpd-util:: Utilities.
+* microhttpd-websocket:: Websockets.
Appendices
@@ -1246,6 +1247,493 @@
@end deftp
+@deftp {Enumeration} MHD_WEBSOCKET_FLAG
+@cindex websocket
+Options for the MHD websocket stream.
+
+This is used for initialization of a websocket stream when calling
+@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2} and
+alters the behavior of the websocket stream.
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_FLAG_SERVER
+The websocket stream is initialized in server mode (default).
+Thus all outgoing payload will not be masked.
+All incoming payload must be masked.
+
+This flag cannot be used together with @code{MHD_WEBSOCKET_FLAG_CLIENT}.
+
+@item MHD_WEBSOCKET_FLAG_CLIENT
+The websocket stream is initialized in client mode.
+You will usually never use that mode in combination with @emph{libmicrohttpd},
+because @emph{libmicrohttpd} provides a server and not a client.
+In client mode all outgoing payload will be masked
+(XOR-ed with random values).
+All incoming payload must be unmasked.
+If you use this mode, you must always call @code{MHD_websocket_stream_init2}
+instead of @code{MHD_websocket_stream_init}, because you need
+to pass a random number generator callback function for masking.
+
+This flag cannot be used together with @code{MHD_WEBSOCKET_FLAG_SERVER}.
+
+@item MHD_WEBSOCKET_FLAG_NO_FRAGMENTS
+You don't want to get fragmented data while decoding (default).
+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 @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}.
+
+@item MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
+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 sequence (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 @code{MHD_WEBSOCKET_FLAG_NO_FRAGMENTS}.
+
+@item MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR
+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
+@code{payload} and @code{payload_len} of @code{MHD_websocket_decode} and
+the return value is negative (a value of @code{enum MHD_WEBSOCKET_STATUS}).
+
+The generated close frame must be freed by the caller
+with @code{MHD_websocket_free}.
+
+@end table
+@end deftp
+
+
+@deftp {Enumeration} MHD_WEBSOCKET_FRAGMENTATION
+@cindex websocket
+This enumeration is used to specify the fragmentation behavior
+when encoding of data (text/binary) for a websocket stream.
+This is used with @code{MHD_websocket_encode_text} or
+@code{MHD_websocket_encode_binary}.
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_FRAGMENTATION_NONE
+You don't want to use fragmentation.
+The encoded frame consists of only one frame.
+
+@item MHD_WEBSOCKET_FRAGMENTATION_FIRST
+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.
+
+@item MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING
+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.
+
+@item MHD_WEBSOCKET_FRAGMENTATION_LAST
+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 types of frames again.
+
+@end table
+@end deftp
+
+
+@deftp {Enumeration} MHD_WEBSOCKET_STATUS
+@cindex websocket
+This enumeration is used for the return value of almost
+every websocket stream function.
+Errors are negative and values equal to or above zero mean a success.
+Positive values are only used by @code{MHD_websocket_decode}.
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_STATUS_OK
+The call succeeded.
+Especially for @code{MHD_websocket_decode} this means that no error occurred,
+but also no frame has been completed yet.
+For other functions this means simply a success.
+
+@item MHD_WEBSOCKET_STATUS_TEXT_FRAME
+@code{MHD_websocket_decode} has decoded a text frame.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded text (if any).
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_BINARY_FRAME
+@code{MHD_websocket_decode} has decoded a binary frame.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded binary data (if any).
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_CLOSE_FRAME
+@code{MHD_websocket_decode} has decoded a close frame.
+This means you must close the socket using @code{MHD_upgrade_action}
+with @code{MHD_UPGRADE_ACTION_CLOSE}.
+You may respond with a close frame before closing.
+The parameters @code{payload} and @code{payload_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 @code{enum MHD_WEBSOCKET_CLOSEREASON}).
+After these two bytes a UTF-8 encoded close reason may follow.
+You can call @code{MHD_websocket_split_close_reason} to split that
+close reason.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_PING_FRAME
+@code{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 @code{payload} and @code{payload_len} are filled with
+the binary ping data (if any).
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_PONG_FRAME
+@code{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 @code{payload} and @code{payload_len} are filled with
+the binary pong data (if any).
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT
+@code{MHD_websocket_decode} has decoded a text frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded text (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FRAME}, but it can only
+appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} during
+the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT
+@code{MHD_websocket_decode} has decoded a binary frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded binary data (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FRAME}, but it can only
+appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} during
+the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_TEXT_NEXT_FRAGMENT
+@code{MHD_websocket_decode} has decoded the next text frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded text (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT}, but it appears
+only after the first and before the last fragment of a series of fragments.
+It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
+during the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT
+@code{MHD_websocket_decode} has decoded the next binary frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded binary data (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT}, but it appears
+only after the first and before the last fragment of a series of fragments.
+It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
+during the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT
+@code{MHD_websocket_decode} has decoded the last text frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded text (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT}, but it appears
+only for the last fragment of a series of fragments.
+It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
+during the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT
+@code{MHD_websocket_decode} has decoded the last binary frame fragment.
+The parameters @code{payload} and @code{payload_len} are filled with
+the decoded binary data (if any).
+This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT}, but it appears
+only for the last fragment of a series of fragments.
+It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
+during the call of @code{MHD_websocket_stream_init} or
+@code{MHD_websocket_stream_init2}.
+You must free the returned @code{payload} after use with
+@code{MHD_websocket_free}.
+
+@item MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR
+The call failed and the stream is invalid now for decoding.
+You must close the websocket now using @code{MHD_upgrade_action}
+with @code{MHD_UPGRADE_ACTION_CLOSE}.
+You may send a close frame before closing.
+This is only used by @code{MHD_websocket_decode} and happens
+if the stream contains errors (i. e. invalid byte data).
+
+@item MHD_WEBSOCKET_STATUS_STREAM_BROKEN
+You tried to decode something, but the stream has already
+been marked invalid.
+You must close the websocket now using @code{MHD_upgrade_action}
+with @code{MHD_UPGRADE_ACTION_CLOSE}.
+You may send a close frame before closing.
+This is only used by @code{MHD_websocket_decode} and happens
+if you call @code{MDM_websocket_decode} again after
+has been invalidated.
+You can call @code{MHD_websocket_stream_is_valid} at any time
+to check whether a stream is invalid or not.
+
+@item MHD_WEBSOCKET_STATUS_MEMORY_ERROR
+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
+@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2} to
+avoid this and close the websocket instead.
+
+@item MHD_WEBSOCKET_STATUS_PARAMETER_ERROR
+You passed invalid parameters during the function call
+(i. e. a NULL pointer for a required parameter).
+The stream remains valid.
+
+@item MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED
+The maximum payload size has been exceeded.
+If you got this return code from @code{MHD_websocket_decode} then
+the stream becomes invalid and the websocket must be closed
+using @code{MHD_upgrade_action} with @code{MHD_UPGRADE_ACTION_CLOSE}.
+You may send a close frame before closing.
+The maximum payload size is specified during the call of
+@code{MHD_websocket_stream_init} or @code{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 GiB on 32 bit systems).
+If you got this return code from @code{MHD_websocket_encode_close},
+@code{MHD_websocket_encode_ping} or @code{MHD_websocket_encode_pong} then
+you passed to much payload data. The stream remains valid then.
+
+@item MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR
+An UTF-8 sequence is invalid.
+If you got this return code from @code{MHD_websocket_decode} then
+the stream becomes invalid and you must close the websocket
+using @code{MHD_upgrade_action} with @code{MHD_UPGRADE_ACTION_CLOSE}.
+You may send a close frame before closing.
+If you got this from @code{MHD_websocket_encode_text} or
+@code{MHD_websocket_encode_close} then you passed invalid UTF-8 text.
+The stream remains valid then.
+
+@item MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER
+A check routine for the HTTP headers came to the conclusion that
+the header value isn't valid for a websocket handshake request.
+This value can only be returned from the following functions:
+@code{MHD_websocket_check_http_version},
+@code{MHD_websocket_check_connection_header},
+@code{MHD_websocket_check_upgrade_header},
+@code{MHD_websocket_check_version_header},
+@code{MHD_websocket_create_accept_header}
+
+@end table
+@end deftp
+
+
+@deftp {Enumeration} MHD_WEBSOCKET_CLOSEREASON
+@cindex websocket
+Enumeration of possible close reasons for websocket 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:
+@itemize @bullet
+@item 0-999 are never used (if you pass 0 in
+@code{MHD_websocket_encode_close} then no close reason is used).
+@item 1000-2999 are specified by RFC 6455.
+@item 3000-3999 are specified by libraries, etc. but must be registered by IANA.
+@item 4000-4999 are reserved for private use.
+@end itemize
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_CLOSEREASON_NO_REASON
+This value is used as placeholder for @code{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 be a result of decoding, because this value
+is not a valid close reason for the websocket protocol.
+
+@item MHD_WEBSOCKET_CLOSEREASON_REGULAR
+You close the websocket because it fulfilled its purpose and shall
+now be closed in a normal, planned way.
+
+@item MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY
+You close the websocket because you are shutting down the server or
+something similar.
+
+@item MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR
+You close the websocket because a protocol error occurred
+during decoding (i. e. invalid byte data).
+
+@item MHD_WEBSOCKET_CLOSEREASON_UNSUPPORTED_DATATYPE
+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.
+
+@item MHD_WEBSOCKET_CLOSEREASON_MALFORMED_UTF8
+You close the websocket because it contains malformed UTF-8.
+The UTF-8 validity is automatically checked by @code{MHD_websocket_decode},
+so you don't need to check it on your own.
+UTF-8 is specified in RFC 3629.
+
+@item MHD_WEBSOCKET_CLOSEREASON_POLICY_VIOLATED
+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
+@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2}.
+
+@item MHD_WEBSOCKET_CLOSEREASON_MISSING_EXTENSION
+This status code can be sent by the client if it
+expected a specific extension, but this extension hasn't been negotiated.
+
+@item MHD_WEBSOCKET_CLOSEREASON_UNEXPECTED_CONDITION
+The server closes the websocket because it encountered
+an unexpected condition that prevented it from fulfilling the request.
+
+@end table
+@end deftp
+
+
+@deftp {Enumeration} MHD_WEBSOCKET_UTF8STEP
+@cindex websocket
+Enumeration of possible UTF-8 check steps for websocket functions
+
+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.
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_UTF8STEP_NORMAL
+There is no open UTF-8 sequence.
+The next byte must be 0x00-0x7F or 0xC2-0xF4.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1
+The second byte of a two byte UTF-8 sequence.
+The first byte was 0xC2-0xDF.
+The next byte must be 0x80-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2
+The second byte of a three byte UTF-8 sequence.
+The first byte was 0xE0.
+The next byte must be 0xA0-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2
+The second byte of a three byte UTF-8 sequence.
+The first byte was 0xED.
+The next byte must by 0x80-0x9F.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2
+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.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2
+The third byte of a three byte UTF-8 sequence.
+The next byte must be 0x80-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3
+The second byte of a four byte UTF-8 sequence.
+The first byte was 0xF0.
+The next byte must be 0x90-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3
+The second byte of a four byte UTF-8 sequence.
+The first byte was 0xF4.
+The next byte must be 0x80-0x8F.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3
+The second byte of a four byte UTF-8 sequence.
+The first byte was 0xF1-0xF3.
+The next byte must be 0x80-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3
+The third byte of a four byte UTF-8 sequence.
+The next byte must be 0x80-0xBF.
+
+@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3
+The fourth byte of a four byte UTF-8 sequence.
+The next byte must be 0x80-0xBF.
+
+@end table
+@end deftp
+
+
+@deftp {Enumeration} MHD_WEBSOCKET_VALIDITY
+@cindex websocket
+Enumeration of validity values of a websocket stream
+
+These values are used for @code{MHD_websocket_stream_is_valid}
+and specify the validity status.
+
+Note that websocket streams are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@table @code
+@item MHD_WEBSOCKET_VALIDITY_INVALID
+The stream is invalid.
+It cannot be used for decoding anymore.
+
+@item MHD_WEBSOCKET_VALIDITY_VALID
+The stream is valid.
+Decoding works as expected.
+
+@item MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES
+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.
+
+@end table
+@end deftp
+
+
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@c ------------------------------------------------------------
@@ -1291,6 +1779,12 @@
@end deftp
+@deftp {C Struct} MHD_WebSocketStream
+@cindex websocket
+Information about a MHD websocket stream.
+@end deftp
+
+
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@c ------------------------------------------------------------
@@ -1549,6 +2043,95 @@
@end deftypefn
+@deftypefn {Function Pointer} void* {*MHD_WebSocketMallocCallback} (size_t buf_len)
+@cindex websocket
+This callback function is used internally by many websocket functions
+for allocating data.
+By default @code{malloc} is used.
+You can use your own allocation function with @code{MHD_websocket_stream_init2}
+if you wish to.
+This can be useful for operating systems like Windows
+where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
+You can call the associated @code{malloc} callback of
+a websocket stream with @code{MHD_websocket_malloc}.
+
+@table @var
+@item buf_len
+size of the buffer to allocate in bytes.
+@end table
+
+Return the pointer of the allocated buffer or @code{NULL} on failure.
+@end deftypefn
+
+
+@deftypefn {Function Pointer} void* {*MHD_WebSocketReallocCallback} (void *buf, size_t new_buf_len)
+@cindex websocket
+This callback function is used internally by many websocket
+functions for reallocating data.
+By default @code{realloc} is used.
+You can use your own reallocation function with
+@code{MHD_websocket_stream_init2} if you wish to.
+This can be useful for operating systems like Windows
+where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
+You can call the associated @code{realloc} callback of
+a websocket stream with @code{MHD_websocket_realloc}.
+
+@table @var
+@item buf
+current buffer, may be @code{NULL};
+
+@item new_buf_len
+new size of the buffer in bytes.
+@end table
+
+Return the pointer of the reallocated buffer or @code{NULL} on failure.
+On failure the old pointer must remain valid.
+@end deftypefn
+
+
+@deftypefn {Function Pointer} void {*MHD_WebSocketFreeCallback} (void *buf)
+@cindex websocket
+This callback function is used internally by many websocket
+functions for freeing data.
+By default @code{free} is used.
+You can use your own free function with
+@code{MHD_websocket_stream_init2} if you wish to.
+This can be useful for operating systems like Windows
+where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
+You can call the associated @code{free} callback of
+a websocket stream with @code{MHD_websocket_free}.
+
+@table @var
+@item cls
+current buffer to free, this may be @code{NULL} then nothing happens.
+@end table
+@end deftypefn
+
+
+@deftypefn {Function Pointer} size_t {*MHD_WebSocketRandomNumberGenerator} (void *cls, void* buf, size_t buf_len)
+@cindex websocket
+This callback function is used for generating random numbers
+for masking payload data in client mode.
+If you use websockets in server mode with @emph{libmicrohttpd} then
+you don't need a random number generator, because
+the server doesn't mask its outgoing messages.
+However if you wish to use a websocket stream in client mode,
+you must pass this callback function to @code{MHD_websocket_stream_init2}.
+
+@table @var
+@item cls
+closure specified in @code{MHD_websocket_stream_init2};
+@item buf
+buffer to fill with random values;
+@item buf_len
+size of buffer in bytes.
+@end table
+
+Return the number of generated random bytes.
+The return value should usually equal to buf_len.
+@end deftypefn
+
+
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@c ------------------------------------------------------------
@@ -3159,6 +3742,12 @@
Takes no extra arguments.
+@item MHD_CONNECTION_INFO_HTTP_STATUS
+Returns the HTTP status code of the response that was
+queued. Returns NULL if no response was queued yet.
+
+Takes no extra arguments.
+
@end table
@end deftp
@@ -3339,6 +3928,704 @@
+
+
+@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket
+@chapter Websocket functions.
+
+@noindent
+Websocket functions provide what you need to use an upgraded connection
+as a websocket.
+These functions are only available if you include the header file
+@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
+
+@menu
+* microhttpd-websocket handshake:: Websocket handshake functions
+* microhttpd-websocket stream:: Websocket stream functions
+* microhttpd-websocket decode:: Websocket decode functions
+* microhttpd-websocket encode:: Websocket encode functions
+* microhttpd-websocket memory:: Websocket memory functions
+@end menu
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket handshake
+@section Websocket handshake functions
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_http_version (const char* http_version)
+@cindex websocket
+Checks the HTTP version of the incoming request.
+Websocket requests are only allowed for HTTP/1.1 or above.
+
+@table @var
+@item http_version
+The value of the @code{version} parameter of your
+@code{access_handler} callback.
+If you pass @code{NULL} then this is handled like a not
+matching HTTP version.
+@end table
+
+Returns 0 when the HTTP version is
+valid for a websocket request and
+a value less than zero when the HTTP version isn't
+valid for a websocket request.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_connection_header (const char* connection_header)
+@cindex websocket
+Checks the value of the @code{Connection} HTTP request header.
+Websocket requests require the token @code{Upgrade} in
+the @code{Connection} HTTP request header.
+
+@table @var
+@item connection_header
+Value of the @code{Connection} request header.
+You can get this request header value by passing
+@code{MHD_HTTP_HEADER_CONNECTION} to
+@code{MHD_lookup_connection_value()}.
+If you pass @code{NULL} then this is handled like a not
+matching @code{Connection} header value.
+@end table
+
+Returns 0 when the @code{Connection} header is
+valid for a websocket request and
+a value less than zero when the @code{Connection} header isn't
+valid for a websocket request.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_upgrade_header (const char* upgrade_header)
+@cindex websocket
+Checks the value of the @code{Upgrade} HTTP request header.
+Websocket requests require the value @code{websocket} in
+the @code{Upgrade} HTTP request header.
+
+@table @var
+@item upgrade_header
+Value of the @code{Upgrade} request header.
+You can get this request header value by passing
+@code{MHD_HTTP_HEADER_UPGRADE} to
+@code{MHD_lookup_connection_value()}.
+If you pass @code{NULL} then this is handled like a not
+matching @code{Upgrade} header value.
+@end table
+
+Returns 0 when the @code{Upgrade} header is
+valid for a websocket request and
+a value less than zero when the @code{Upgrade} header isn't
+valid for a websocket request.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_version_header (const char* version_header)
+@cindex websocket
+Checks the value of the @code{Sec-WebSocket-Version} HTTP request header.
+Websocket requests require the value @code{13} in
+the @code{Sec-WebSocket-Version} HTTP request header.
+
+@table @var
+@item version_header
+Value of the @code{Sec-WebSocket-Version} request header.
+You can get this request header value by passing
+@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION} to
+@code{MHD_lookup_connection_value()}.
+If you pass @code{NULL} then this is handled like a not
+matching @code{Sec-WebSocket-Version} header value.
+@end table
+
+Returns 0 when the @code{Sec-WebSocket-Version} header is
+valid for a websocket request and
+a value less than zero when the @code{Sec-WebSocket-Version} header isn't
+valid for a websocket request.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_create_accept_header (const char* sec_websocket_key, char* sec_websocket_accept)
+@cindex websocket
+Checks the value of the @code{Sec-WebSocket-Key}
+HTTP request header and generates the value for
+the @code{Sec-WebSocket-Accept} HTTP response header.
+The generated value must be sent to the client.
+
+@table @var
+@item sec_websocket_key
+Value of the @code{Sec-WebSocket-Key} request header.
+You can get this request header value by passing
+@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY} to
+@code{MHD_lookup_connection_value()}.
+If you pass @code{NULL} then this is handled like a not
+matching @code{Sec-WebSocket-Key} header value.
+
+@item sec_websocket_accept
+Response buffer, which will receive
+the generated value for the @code{Sec-WebSocket-Accept}
+HTTP response header.
+This buffer must be at least 29 bytes long and
+will contain the response value plus a terminating @code{NUL}
+character on success.
+Must not be @code{NULL}.
+You can add this HTTP header to your response by passing
+@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT} to
+@code{MHD_add_response_header()}.
+@end table
+
+Returns 0 when the @code{Sec-WebSocket-Key} header was
+not empty and a result value for the @code{Sec-WebSocket-Accept}
+was calculated.
+A value less than zero is returned when the @code{Sec-WebSocket-Key}
+header isn't valid for a websocket request or when any
+error occurred.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket stream
+@section Websocket stream functions
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_init (struct MHD_WebSocketStream **ws, int flags, size_t max_payload_size)
+@cindex websocket
+Creates a new websocket stream, used for decoding/encoding.
+
+@table @var
+@item ws
+pointer a variable to fill with the newly created
+@code{struct MHD_WebSocketStream},
+receives @code{NULL} on error. May not be @code{NULL}.
+
+If not required anymore, free the created websocket stream with
+@code{MHD_websocket_stream_free()}.
+
+@item flags
+combination of @code{enum MHD_WEBSOCKET_FLAG} values to
+modify the behavior of the websocket stream.
+
+@item max_payload_size
+maximum size for incoming payload data in bytes. Use 0 to allow each size.
+@end table
+
+Returns 0 on success, negative values on error.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} 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, void* cls_rng, MHD_WebSocketRandomNumberGenerator callback_rng)
+@cindex websocket
+Creates a new websocket stream, used for decoding/encoding,
+but with custom memory functions for malloc, realloc and free.
+Also a random number generator can be specified for client mode.
+
+@table @var
+@item ws
+pointer a variable to fill with the newly created
+@code{struct MHD_WebSocketStream},
+receives @code{NULL} on error. Must not be @code{NULL}.
+
+If not required anymore, free the created websocket stream with
+@code{MHD_websocket_stream_free}.
+
+@item flags
+combination of @code{enum MHD_WEBSOCKET_FLAG} values to
+modify the behavior of the websocket stream.
+
+@item max_payload_size
+maximum size for incoming payload data in bytes. Use 0 to allow each size.
+
+@item callback_malloc
+callback function for allocating memory. Must not be @code{NULL}.
+The shorter @code{MHD_websocket_stream_init()} passes a reference to @code{malloc} here.
+
+@item callback_realloc
+callback function for reallocating memory. Must not be @code{NULL}.
+The shorter @code{MHD_websocket_stream_init()} passes a reference to @code{realloc} here.
+
+@item callback_free
+callback function for freeing memory. Must not be @code{NULL}.
+The shorter @code{MHD_websocket_stream_init()} passes a reference to @code{free} here.
+
+@item cls_rng
+closure for the random number generator.
+This is only required when
+@code{MHD_WEBSOCKET_FLAG_CLIENT} is passed in @code{flags}.
+The given value is passed to the random number generator callback.
+May be @code{NULL} if not needed.
+Should be @code{NULL} when you are not using @code{MHD_WEBSOCKET_FLAG_CLIENT}.
+The shorter @code{MHD_websocket_stream_init} passes @code{NULL} here.
+
+@item callback_rng
+callback function for a secure random number generator.
+This is only required when @code{MHD_WEBSOCKET_FLAG_CLIENT} is
+passed in @code{flags} and must not be @code{NULL} then.
+Should be @code{NULL} otherwise.
+The shorter @code{MHD_websocket_stream_init()} passes @code{NULL} here.
+@end table
+
+Returns 0 on success, negative values on error.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_free (struct MHD_WebSocketStream *ws)
+@cindex websocket
+Frees a previously allocated websocket stream
+
+@table @var
+@item ws
+websocket stream to free, this value may be @code{NULL}.
+@end table
+
+Returns 0 on success, negative values on error.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_invalidate (struct MHD_WebSocketStream *ws)
+@cindex websocket
+Invalidates a websocket stream.
+After invalidation a websocket stream cannot be used for decoding anymore.
+Encoding is still possible.
+
+@table @var
+@item ws
+websocket stream to invalidate.
+@end table
+
+Returns 0 on success, negative values on error.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_VALIDITY} MHD_websocket_stream_is_valid (struct MHD_WebSocketStream *ws)
+@cindex websocket
+Queries whether a websocket stream is valid.
+Invalidated websocket streams cannot be used for decoding anymore.
+Encoding is still possible.
+
+@table @var
+@item ws
+websocket stream to invalidate.
+@end table
+
+Returns 0 if invalid, 1 if valid for all types or
+2 if valid only for control frames.
+Can be compared with @code{enum MHD_WEBSOCKET_VALIDITY}.
+@end deftypefun
+
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket decode
+@section Websocket decode functions
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} 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)
+@cindex websocket
+Decodes a byte sequence for a websocket stream.
+Decoding is done until either a frame is complete or
+the end of the byte sequence is reached.
+
+@table @var
+@item ws
+websocket stream for decoding.
+
+@item streambuf
+byte sequence for decoding.
+This is what you typically received via @code{recv()}.
+
+@item streambuf_len
+length of the byte sequence in parameter @code{streambuf}.
+
+@item streambuf_read_len
+pointer to a variable, which receives the number of bytes,
+that has been processed by this call.
+This value may be less than the value of @code{streambuf_len} when
+a frame is decoded before the end of the buffer is reached.
+The remaining bytes of @code{buf} must be passed to
+the next call of this function.
+
+@item payload
+pointer to a variable, which receives the allocated buffer with the payload
+data of the decoded frame. Must not be @code{NULL}.
+If no decoded data is available or an error occurred @code{NULL} is returned.
+When the returned value is not @code{NULL} then the buffer contains always
+@code{payload_len} bytes plus one terminating @code{NUL} character
+(regardless of the frame type).
+
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+If you passed the flag @code{MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR}
+upon creation of the websocket stream and a decoding error occurred
+(function 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 @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
+upon creation of the 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.
+
+@item payload_len
+pointer to a variable, which receives length of the result
+@code{payload} buffer in bytes.
+Must not be @code{NULL}.
+This receives 0 when no data is available, when the decoded payload
+has a length of zero or when an error occurred.
+@end table
+
+Returns a value greater than zero when a frame is complete.
+Compare with @code{enum MHD_WEBSOCKET_STATUS} to distinguish the frame type.
+Returns 0 when the call succeeded, but no frame is available.
+Returns a value less than zero on errors.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} 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)
+@cindex websocket
+Splits the payload of a decoded close frame.
+
+@table @var
+@item payload
+payload of the close frame.
+This parameter may only be @code{NULL} if @code{payload_len} is 0.
+
+@item payload_len
+length of @code{payload}.
+
+@item reason_code
+pointer to a variable, which receives the numeric close reason.
+If there was no close reason, this is 0.
+This value can be compared with @code{enum MHD_WEBSOCKET_CLOSEREASON}.
+May be @code{NULL}.
+
+@item reason_utf8
+pointer to a variable, which receives the literal close reason.
+If there was no literal close reason, this will be @code{NULL}.
+May be @code{NULL}.
+
+Please note that no memory is allocated in this function.
+If not @code{NULL} the returned value of this parameter
+points to a position in the specified @code{payload}.
+
+@item reason_utf8_len
+pointer to a variable, which receives the length of the literal close reason.
+If there was no literal close reason, this is 0.
+May be @code{NULL}.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket encode
+@section Websocket encode functions
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} 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)
+@cindex websocket
+Encodes an UTF-8 encoded text into websocket text frame
+
+@table @var
+@item ws
+websocket stream;
+
+@item payload_utf8
+text to send. This must be UTF-8 encoded.
+If you don't want UTF-8 then send a binary frame
+with @code{MHD_websocket_encode_binary()} instead.
+May be be @code{NULL} if @code{payload_utf8_len} is 0,
+must not be @code{NULL} otherwise.
+
+@item payload_utf8_len
+length of @code{payload_utf8} in bytes.
+
+@item fragmentation
+A value of @code{enum MHD_WEBSOCKET_FRAGMENTATION}
+to specify the fragmentation behavior.
+Specify @code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or just 0
+if you don't want to use fragmentation (default).
+
+@item frame
+pointer to a variable, which receives a buffer with the encoded text frame.
+Must not be @code{NULL}.
+The buffer contains what you typically send via @code{send()} to the recipient.
+If no encoded data is available the variable receives @code{NULL}.
+
+If the variable is not @code{NULL} then the buffer contains always
+@code{frame_len} bytes plus one terminating @code{NUL} character.
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+@item frame_len
+pointer to a variable, which receives the length of the encoded frame in bytes.
+Must not be @code{NULL}.
+
+@item utf8_step
+If fragmentation is used (the parameter @code{fragmentation} is not 0)
+then is parameter is required and must not be @code{NULL}.
+If no fragmentation is used, this parameter is optional and
+should be @code{NULL}.
+
+This parameter is a pointer to a variable which contains the last check status
+of the UTF-8 sequence. It is required to continue a previous
+UTF-8 sequence check when fragmentation is used, because a UTF-8 sequence
+could be splitted upon fragments.
+
+@code{enum MHD_WEBSOCKET_UTF8STEP} is used for this value.
+If you start a new fragment using
+@code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or
+@code{MHD_WEBSOCKET_FRAGMENTATION_FIRST} the old value of this variable
+will be discarded and the value of this variable will be initialized
+to @code{MHD_WEBSOCKET_UTF8STEP_NORMAL}.
+On all other fragmentation modes the previous value of the pointed variable
+will be used to continue the UTF-8 sequence check.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_binary (struct MHD_WebSocketStream* ws, const char* payload, size_t payload_len, int fragmentation, char** frame, size_t* frame_len)
+@cindex websocket
+Encodes binary data into websocket binary frame
+
+@table @var
+@item ws
+websocket stream;
+
+@item payload
+binary data to send.
+May be be @code{NULL} if @code{payload_len} is 0,
+must not be @code{NULL} otherwise.
+
+@item payload_len
+length of @code{payload} in bytes.
+
+@item fragmentation
+A value of @code{enum MHD_WEBSOCKET_FRAGMENTATION}
+to specify the fragmentation behavior.
+Specify @code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or just 0
+if you don't want to use fragmentation (default).
+
+@item frame
+pointer to a variable, which receives a buffer with the encoded binary frame.
+Must not be @code{NULL}.
+The buffer contains what you typically send via @code{send()} to the recipient.
+If no encoded data is available the variable receives @code{NULL}.
+
+If the variable is not @code{NULL} then the buffer contains always
+@code{frame_len} bytes plus one terminating @code{NUL} character.
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+@item frame_len
+pointer to a variable, which receives the length of the encoded frame in bytes.
+Must not be @code{NULL}.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_ping (struct MHD_WebSocketStream* ws, const char* payload, size_t payload_len, char** frame, size_t* frame_len)
+@cindex websocket
+Encodes a websocket ping frame.
+Ping frames are used to check whether a recipient is still available
+and what latency the websocket connection has.
+
+@table @var
+@item ws
+websocket stream;
+
+@item payload
+binary ping data to send.
+May be @code{NULL} if @code{payload_len} is 0.
+
+@item payload_len
+length of @code{payload} in bytes.
+This may not exceed 125 bytes.
+
+@item frame
+pointer to a variable, which receives a buffer with the encoded ping frame.
+Must not be @code{NULL}.
+The buffer contains what you typically send via @code{send()} to the recipient.
+If no encoded data is available the variable receives @code{NULL}.
+
+If the variable is not @code{NULL} then the buffer contains always
+@code{frame_len} bytes plus one terminating @code{NUL} character.
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+@item frame_len
+pointer to a variable, which receives the length of the encoded frame in bytes.
+Must not be @code{NULL}.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_pong (struct MHD_WebSocketStream* ws, const char* payload, size_t payload_len, char** frame, size_t* frame_len)
+@cindex websocket
+Encodes a websocket pong frame.
+Pong frames are used to answer a previously received websocket ping frame.
+
+@table @var
+@item ws
+websocket stream;
+
+@item payload
+binary pong data to send, which should be
+the decoded payload from the received ping frame.
+May be @code{NULL} if @code{payload_len} is 0.
+
+@item payload_len
+length of @code{payload} in bytes.
+This may not exceed 125 bytes.
+
+@item frame
+pointer to a variable, which receives a buffer with the encoded pong frame.
+Must not be @code{NULL}.
+The buffer contains what you typically send via @code{send()} to the recipient.
+If no encoded data is available the variable receives @code{NULL}.
+
+If the variable is not @code{NULL} then the buffer contains always
+@code{frame_len} bytes plus one terminating @code{NUL} character.
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+@item frame_len
+pointer to a variable, which receives the length of the encoded frame in bytes.
+Must not be @code{NULL}.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@deftypefun {enum MHD_WEBSOCKET_STATUS} 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)
+@cindex websocket
+Encodes a websocket close frame.
+Close frames are used to close a websocket connection in a formal way.
+
+@table @var
+@item ws
+websocket stream;
+
+@item reason_code
+reason for close.
+You can use @code{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
+@code{MHD_WEBSOCKET_CLOSEREASON_NO_REASON} (or just 0) then
+no reason is encoded.
+
+@item reason_utf8
+An UTF-8 encoded text reason why the connection is closed.
+This may be @code{NULL} if @code{reason_utf8_len} is 0.
+This must be @code{NULL} if @code{reason_code} equals to zero
+(@code{MHD_WEBSOCKET_CLOSEREASON_NO_REASON}).
+
+@item reason_utf8_len
+length of the UTF-8 encoded text reason in bytes.
+This may not exceed 123 bytes.
+
+@item frame
+pointer to a variable, which receives a buffer with the encoded close frame.
+Must not be @code{NULL}.
+The buffer contains what you typically send via @code{send()} to the recipient.
+If no encoded data is available the variable receives @code{NULL}.
+
+If the variable is not @code{NULL} then the buffer contains always
+@code{frame_len} bytes plus one terminating @code{NUL} character.
+The caller must free this buffer using @code{MHD_websocket_free()}.
+
+@item frame_len
+pointer to a variable, which receives the length of the encoded frame in bytes.
+Must not be @code{NULL}.
+@end table
+
+Returns 0 on success or a value less than zero on errors.
+Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
+@end deftypefun
+
+
+@c ------------------------------------------------------------
+@node microhttpd-websocket memory
+@section Websocket memory functions
+
+
+@deftypefun {void*} MHD_websocket_malloc (struct MHD_WebSocketStream* ws, size_t buf_len)
+@cindex websocket
+Allocates memory with the associated @code{malloc()} function
+of the websocket stream.
+The memory allocation function could be different for a websocket stream if
+@code{MHD_websocket_stream_init2()} has been used for initialization.
+
+@table @var
+@item ws
+websocket stream;
+
+@item buf_len
+size of the buffer to allocate in bytes.
+@end table
+
+Returns the pointer of the allocated buffer or @code{NULL} on failure.
+@end deftypefun
+
+
+@deftypefun {void*} MHD_websocket_realloc (struct MHD_WebSocketStream* ws, void* buf, size_t new_buf_len)
+@cindex websocket
+Reallocates memory with the associated @code{realloc()} function
+of the websocket stream.
+The memory reallocation function could be different for a websocket stream if
+@code{MHD_websocket_stream_init2()} has been used for initialization.
+
+@table @var
+@item ws
+websocket stream;
+
+@item buf
+current buffer, may be @code{NULL};
+
+@item new_buf_len
+new size of the buffer in bytes.
+@end table
+
+Return the pointer of the reallocated buffer or @code{NULL} on failure.
+On failure the old pointer remains valid.
+@end deftypefun
+
+
+@deftypefun {void} MHD_websocket_free (struct MHD_WebSocketStream* ws, void* buf)
+@cindex websocket
+Frees memory with the associated @code{free()} function
+of the websocket stream.
+The memory free function could be different for a websocket stream if
+@code{MHD_websocket_stream_init2()} has been used for initialization.
+
+@table @var
+@item ws
+websocket stream;
+
+@item buf
+buffer to free, this may be @code{NULL} then nothing happens.
+@end table
+
+@end deftypefun
+
+
+
+
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/m4/ax_pthread.m4
^
|
@@ -14,20 +14,24 @@
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
-# Also sets PTHREAD_CC to any special C compiler that is needed for
-# multi-threaded programs (defaults to the value of CC otherwise). (This
-# is necessary on AIX to use the special cc_r compiler alias.)
+# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is
+# needed for multi-threaded programs (defaults to the value of CC
+# respectively CXX otherwise). (This is necessary on e.g. AIX to use the
+# special cc_r/CC_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also to link with them as well. For example, you might link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threaded programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
+# CXX="$PTHREAD_CXX"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
@@ -83,8 +87,7 @@
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
-# locally patched for autoconf 2.70 compatibility
-#serial 27
+#serial 31
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
@@ -106,6 +109,7 @@
ax_pthread_save_CFLAGS="$CFLAGS"
ax_pthread_save_LIBS="$LIBS"
AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
+ AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"])
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
@@ -483,18 +487,28 @@
[#handle absolute path differently from PATH based program lookup
AS_CASE(["x$CC"],
[x/*],
- [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
- [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+ [
+ AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])
+ AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])])
+ ],
+ [
+ AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])
+ AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])])
+ ]
+ )
+ ])
;;
esac
fi
fi
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX"
AC_SUBST([PTHREAD_LIBS])
AC_SUBST([PTHREAD_CFLAGS])
AC_SUBST([PTHREAD_CC])
+AC_SUBST([PTHREAD_CXX])
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test "x$ax_pthread_ok" = "xyes"; then
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/m4/mhd_shutdown_socket_trigger.m4
^
|
@@ -26,10 +26,12 @@
AC_REQUIRE([AC_PROG_CC])dnl
AC_REQUIRE([AX_PTHREAD])dnl
AC_CHECK_HEADERS([sys/time.h],[AC_CHECK_FUNCS([gettimeofday])],[], [AC_INCLUDES_DEFAULT])
- AC_CHECK_HEADERS([time.h],[AC_CHECK_FUNCS([nanosleep])],[], [AC_INCLUDES_DEFAULT])
- AC_CHECK_HEADERS([unistd.h],[AC_CHECK_FUNCS([usleep])],[], [AC_INCLUDES_DEFAULT])
+ dnl AC_CHECK_HEADERS([time.h],[AC_CHECK_FUNCS([nanosleep])],[], [AC_INCLUDES_DEFAULT])
+ MHD_CHECK_FUNC([[usleep]], [[#include <unistd.h>]], [[usleep(100000);]])
+ dnl AC_CHECK_HEADERS([unistd.h],[AC_CHECK_FUNCS([usleep])],[], [AC_INCLUDES_DEFAULT])
+ MHD_CHECK_FUNC([[nanosleep]], [[#include <time.h>]], [[struct timespec ts2, ts1 = {0, 0}; nanosleep(&ts1, &ts2);]])
AC_CHECK_HEADERS([string.h sys/types.h sys/socket.h netinet/in.h time.h sys/select.h netinet/tcp.h],[],[], [AC_INCLUDES_DEFAULT])
- AC_CACHE_CHECK([[whether shutdown of listen socket trigger select()]],
+ AC_CACHE_CHECK([[whether shutdown of listen socket triggers select()]],
[[mhd_cv_host_shtdwn_trgr_select]], [dnl
_MHD_OS_KNOWN_SOCKET_SHUTDOWN_TRIGGER([[mhd_cv_host_shtdwn_trgr_select]])
AS_VAR_IF([mhd_cv_host_shtdwn_trgr_select], [["maybe"]],
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/po/libmicrohttpd.pot
^
|
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: GNU libmicrohttpd 0.9.73\n"
+"Project-Id-Version: GNU libmicrohttpd 0.9.75\n"
"Report-Msgid-Bugs-To: libmicrohttpd@gnu.org\n"
-"POT-Creation-Date: 2021-04-24 21:59+0300\n"
+"POT-Creation-Date: 2021-12-26 20:30+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,23 +17,23 @@
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: src/microhttpd/connection_https.c:161
+#: src/microhttpd/connection_https.c:167
msgid "Error: received handshake message out of context.\n"
msgstr ""
-#: src/microhttpd/mhd_locks.h:125
+#: src/microhttpd/mhd_locks.h:127
msgid "Failed to destroy mutex.\n"
msgstr ""
-#: src/microhttpd/mhd_locks.h:158
+#: src/microhttpd/mhd_locks.h:160
msgid "Failed to lock mutex.\n"
msgstr ""
-#: src/microhttpd/mhd_locks.h:184
+#: src/microhttpd/mhd_locks.h:186
msgid "Failed to unlock mutex.\n"
msgstr ""
-#: src/microhttpd/internal.h:96
+#: src/microhttpd/internal.h:105
msgid "Failed to close FD.\n"
msgstr ""
@@ -71,241 +71,267 @@
msgid "Authentication failed, arguments do not match.\n"
msgstr ""
-#: src/microhttpd/digestauth.c:1289
+#: src/microhttpd/digestauth.c:1290
msgid "Digest size mismatch.\n"
msgstr ""
-#: src/microhttpd/digestauth.c:1382
+#: src/microhttpd/digestauth.c:1383
msgid "Could not register nonce (is the nonce array size zero?).\n"
msgstr ""
-#: src/microhttpd/digestauth.c:1407
+#: src/microhttpd/digestauth.c:1408
msgid "Failed to allocate memory for auth response header.\n"
msgstr ""
-#: src/microhttpd/digestauth.c:1449
+#: src/microhttpd/digestauth.c:1450
msgid "Failed to add Digest auth header.\n"
msgstr ""
-#: src/microhttpd/daemon.c:136
+#: src/microhttpd/daemon.c:137
#, c-format
msgid "Fatal error in GNU libmicrohttpd %s:%u: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:449
+#: src/microhttpd/daemon.c:450
msgid "Failed to add IP connection count node.\n"
msgstr ""
-#: src/microhttpd/daemon.c:507
+#: src/microhttpd/daemon.c:508
msgid "Failed to find previously-added IP address.\n"
msgstr ""
-#: src/microhttpd/daemon.c:513
+#: src/microhttpd/daemon.c:514
msgid "Previously-added IP address had counter of zero.\n"
msgstr ""
-#: src/microhttpd/daemon.c:565
+#: src/microhttpd/daemon.c:566
msgid "Too long trust certificate.\n"
msgstr ""
-#: src/microhttpd/daemon.c:577
+#: src/microhttpd/daemon.c:578
msgid "Bad trust certificate format.\n"
msgstr ""
-#: src/microhttpd/daemon.c:602
+#: src/microhttpd/daemon.c:603
msgid "Too long key or certificate.\n"
msgstr ""
-#: src/microhttpd/daemon.c:623
+#: src/microhttpd/daemon.c:624
msgid ""
"Failed to setup x509 certificate/key: pre 3.X.X version of GnuTLS does not "
"support setting key password.\n"
msgstr ""
-#: src/microhttpd/daemon.c:637
+#: src/microhttpd/daemon.c:638
#, c-format
msgid "GnuTLS failed to setup x509 certificate/key: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:652
+#: src/microhttpd/daemon.c:653
msgid "You need to specify a certificate and key location.\n"
msgstr ""
-#: src/microhttpd/daemon.c:682
+#: src/microhttpd/daemon.c:683
#, c-format
msgid "Error: invalid credentials type %d specified.\n"
msgstr ""
-#: src/microhttpd/daemon.c:1086
+#: src/microhttpd/daemon.c:1093
#, c-format
msgid "Maximum socket in select set: %d\n"
msgstr ""
-#: src/microhttpd/daemon.c:1147
+#: src/microhttpd/daemon.c:1156
msgid ""
"MHD_get_fdset2() called with except_fd_set set to NULL. Such behavior is "
"unsupported.\n"
msgstr ""
-#: src/microhttpd/daemon.c:1361 src/microhttpd/daemon.c:7387
+#: src/microhttpd/daemon.c:1373 src/microhttpd/daemon.c:7532
msgid ""
"Initiated daemon shutdown while \"upgraded\" connection was not closed.\n"
msgstr ""
-#: src/microhttpd/daemon.c:1375 src/microhttpd/daemon.c:1613
-msgid "Failed to forward to application "
+#: src/microhttpd/daemon.c:1387
+#, c-format
+msgid ""
+"Failed to forward to application %<PRIu64> bytes of data received from "
+"remote side: application shut down socket.\n"
msgstr ""
-#: src/microhttpd/daemon.c:1543 src/microhttpd/daemon.c:1669
-msgid "Failed to forward to remote client "
+#: src/microhttpd/daemon.c:1555
+#, c-format
+msgid ""
+"Failed to forward to remote client %<PRIu64> bytes of data received from "
+"application: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:1739
+#: src/microhttpd/daemon.c:1625
+#, c-format
+msgid ""
+"Failed to forward to application %<PRIu64> bytes of data received from "
+"remote side: %s\n"
+msgstr ""
+
+#: src/microhttpd/daemon.c:1681
+#, c-format
+msgid ""
+"Failed to forward to remote client %<PRIu64> bytes of data received from "
+"application: daemon shut down.\n"
+msgstr ""
+
+#: src/microhttpd/daemon.c:1751
msgid "Error preparing select.\n"
msgstr ""
-#: src/microhttpd/daemon.c:1774 src/microhttpd/daemon.c:1929
-#: src/microhttpd/daemon.c:2073
+#: src/microhttpd/daemon.c:1786 src/microhttpd/daemon.c:1988
+#: src/microhttpd/daemon.c:2126
#, c-format
msgid "Error during select (%d): `%s'\n"
msgstr ""
-#: src/microhttpd/daemon.c:1824 src/microhttpd/daemon.c:1950
-#: src/microhttpd/daemon.c:2142
+#: src/microhttpd/daemon.c:1836 src/microhttpd/daemon.c:2009
+#: src/microhttpd/daemon.c:2195
#, c-format
msgid "Error during poll: `%s'\n"
msgstr ""
-#: src/microhttpd/daemon.c:1913 src/microhttpd/daemon.c:2055
+#: src/microhttpd/daemon.c:1972 src/microhttpd/daemon.c:2108
msgid "Failed to add FD to fd_set.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2195
+#: src/microhttpd/daemon.c:2247
msgid "Processing thread terminating. Closing connection.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2225
+#: src/microhttpd/daemon.c:2277
msgid ""
"Failed to signal thread termination via inter-thread communication channel.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2305
+#: src/microhttpd/daemon.c:2359
msgid "Internal server error. This should be impossible.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2315 src/microhttpd/daemon.c:2353
+#: src/microhttpd/daemon.c:2369 src/microhttpd/daemon.c:2408
msgid "PSK not supported by this server.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2330
+#: src/microhttpd/daemon.c:2384
msgid "PSK authentication failed: gnutls_malloc failed to allocate memory.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2339
+#: src/microhttpd/daemon.c:2393
msgid "PSK authentication failed: PSK too long.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2401
+#: src/microhttpd/daemon.c:2456
#, c-format
msgid "Accepted connection on socket %d.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2414 src/microhttpd/daemon.c:2745
+#: src/microhttpd/daemon.c:2469 src/microhttpd/daemon.c:2790
msgid "Server reached connection limit. Closing inbound connection.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2432
+#: src/microhttpd/daemon.c:2487
msgid "Connection rejected by application. Closing connection.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2450 src/microhttpd/daemon.c:2478
-#: src/microhttpd/daemon.c:2718 src/microhttpd/daemon.c:4310
+#: src/microhttpd/daemon.c:2505 src/microhttpd/daemon.c:2532
+#: src/microhttpd/daemon.c:2774 src/microhttpd/daemon.c:4413
#, c-format
msgid "Error allocating memory: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:2546
+#: src/microhttpd/daemon.c:2602
msgid "Failed to initialise TLS session.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2572
+#: src/microhttpd/daemon.c:2628
msgid "Failed to set ALPN protocols.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2600
+#: src/microhttpd/daemon.c:2656
#, c-format
msgid "Failed to setup TLS credentials: unknown credential type %d.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2610
+#: src/microhttpd/daemon.c:2666
msgid "Unknown credential type.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2637
+#: src/microhttpd/daemon.c:2693
msgid "TLS connection on non-TLS daemon.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2783
+#: src/microhttpd/daemon.c:2831 src/microhttpd/daemon.c:7189
+msgid ""
+"Failed to create a new thread because it would have exceeded the system "
+"limit on the number of threads or no system resources available.\n"
+msgstr ""
+
+#: src/microhttpd/daemon.c:2837
#, c-format
msgid "Failed to create a thread: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:2809 src/microhttpd/daemon.c:4821
-#: src/microhttpd/daemon.c:4854 src/microhttpd/daemon.c:6208
-#: src/microhttpd/daemon.c:6227 src/microhttpd/connection.c:3870
-#: src/microhttpd/response.c:1236 src/microhttpd/response.c:1262
+#: src/microhttpd/daemon.c:2869 src/microhttpd/daemon.c:4924
+#: src/microhttpd/daemon.c:4957 src/microhttpd/daemon.c:6330
+#: src/microhttpd/daemon.c:6349 src/microhttpd/connection.c:4908
+#: src/microhttpd/response.c:1787 src/microhttpd/response.c:1813
#, c-format
msgid "Call to epoll_ctl failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:2920
+#: src/microhttpd/daemon.c:2989
#, c-format
msgid ""
"New connection socket descriptor (%d) is not less than FD_SETSIZE (%d).\n"
msgstr ""
-#: src/microhttpd/daemon.c:2937
+#: src/microhttpd/daemon.c:3006
msgid "Epoll mode supports only non-blocking sockets\n"
msgstr ""
-#: src/microhttpd/daemon.c:2974
+#: src/microhttpd/daemon.c:3043
msgid ""
"Failed to signal new connection via inter-thread communication channel.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3019
+#: src/microhttpd/daemon.c:3088
msgid "Failed to start serving new connection.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3092 src/microhttpd/daemon.c:3745
-#: src/microhttpd/daemon.c:7254 src/microhttpd/connection.c:759
-#: src/microhttpd/connection.c:778
+#: src/microhttpd/daemon.c:3162 src/microhttpd/daemon.c:3832
+#: src/microhttpd/daemon.c:7399 src/microhttpd/connection.c:899
+#: src/microhttpd/connection.c:918
msgid "Failed to remove FD from epoll set.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3148
+#: src/microhttpd/daemon.c:3220
msgid "Cannot suspend connections without enabling MHD_ALLOW_SUSPEND_RESUME!\n"
msgstr ""
-#: src/microhttpd/daemon.c:3155
+#: src/microhttpd/daemon.c:3227
msgid "Error: connection scheduled for \"upgrade\" cannot be suspended.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3179
+#: src/microhttpd/daemon.c:3251
msgid "Cannot resume connections without enabling MHD_ALLOW_SUSPEND_RESUME!\n"
msgstr ""
-#: src/microhttpd/daemon.c:3194
+#: src/microhttpd/daemon.c:3266
msgid "Failed to signal resume via inter-thread communication channel.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3334
+#: src/microhttpd/daemon.c:3406
msgid ""
"Failed to signal resume of connection via inter-thread communication "
"channel.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3388
+#: src/microhttpd/daemon.c:3460
msgid ""
"MHD_add_connection() has been called for daemon started without MHD_USE_ITC "
"flag.\n"
@@ -313,628 +339,700 @@
"already added sockets.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3399
+#: src/microhttpd/daemon.c:3471
#, c-format
msgid "Failed to set nonblocking mode on new client socket: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:3418
+#: src/microhttpd/daemon.c:3490
#, c-format
msgid "Failed to suppress SIGPIPE on new client socket: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:3444
+#: src/microhttpd/daemon.c:3516
msgid "Failed to set noninheritable mode on new client socket.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3567
+#: src/microhttpd/daemon.c:3646
#, c-format
msgid "Error accepting connection: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:3585
+#: src/microhttpd/daemon.c:3663
msgid ""
"Hit process or system resource limit at FIRST connection. This is really bad "
"as there is no sane way to proceed. Will try busy waiting for system "
"resources to become magically available.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3600
+#: src/microhttpd/daemon.c:3680
#, c-format
msgid ""
"Hit process or system resource limit at %u connections, temporarily "
"suspending accept(). Consider setting a lower MHD_OPTION_CONNECTION_LIMIT.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3613
+#: src/microhttpd/daemon.c:3694
#, c-format
msgid "Failed to set nonblocking mode on incoming connection socket: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:3626
+#: src/microhttpd/daemon.c:3708
msgid "Failed to set noninheritable mode on incoming connection socket.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3636
+#: src/microhttpd/daemon.c:3720
#, c-format
msgid "Failed to suppress SIGPIPE on incoming connection socket: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:3657
+#: src/microhttpd/daemon.c:3742
#, c-format
msgid "Accepted connection on socket %d\n"
msgstr ""
-#: src/microhttpd/daemon.c:3702 src/microhttpd/daemon.c:7428
-#: src/microhttpd/daemon.c:7460 src/microhttpd/daemon.c:7493
-#: src/microhttpd/daemon.c:7599
+#: src/microhttpd/daemon.c:3787 src/microhttpd/daemon.c:7573
+#: src/microhttpd/daemon.c:7605 src/microhttpd/daemon.c:7638
+#: src/microhttpd/daemon.c:7744
msgid "Failed to join a thread.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3811
+#: src/microhttpd/daemon.c:3911
msgid "Illegal call to MHD_get_timeout.\n"
msgstr ""
-#: src/microhttpd/daemon.c:4039
+#: src/microhttpd/daemon.c:4142
msgid ""
"MHD_run_from_select() called with except_fd_set set to NULL. Such behavior "
"is deprecated.\n"
msgstr ""
-#: src/microhttpd/daemon.c:4120
+#: src/microhttpd/daemon.c:4223
msgid "Could not obtain daemon fdsets.\n"
msgstr ""
-#: src/microhttpd/daemon.c:4137
+#: src/microhttpd/daemon.c:4240
msgid "Could not add listen socket to fdset.\n"
msgstr ""
-#: src/microhttpd/daemon.c:4166
+#: src/microhttpd/daemon.c:4269
msgid "Could not add control inter-thread communication channel FD to fdset.\n"
msgstr ""
-#: src/microhttpd/daemon.c:4246
+#: src/microhttpd/daemon.c:4349
#, c-format
msgid "select failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:4386 src/microhttpd/daemon.c:4540
+#: src/microhttpd/daemon.c:4489 src/microhttpd/daemon.c:4643
#, c-format
msgid "poll failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:4683 src/microhttpd/daemon.c:4908
+#: src/microhttpd/daemon.c:4786 src/microhttpd/daemon.c:5011
#, c-format
msgid "Call to epoll_wait failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:4873 src/microhttpd/daemon.c:5410
+#: src/microhttpd/daemon.c:4976 src/microhttpd/daemon.c:5531
msgid "Failed to remove listen FD from epoll set.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5255
+#: src/microhttpd/daemon.c:5376
#, c-format
msgid "Failed to block SIGPIPE on daemon thread: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:5391
+#: src/microhttpd/daemon.c:5512
msgid "Using MHD_quiesce_daemon in this mode requires MHD_USE_ITC.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5419
+#: src/microhttpd/daemon.c:5540
msgid "Failed to signal quiesce via inter-thread communication channel.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5442
+#: src/microhttpd/daemon.c:5563
msgid "failed to signal quiesce via inter-thread communication channel.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5559
-msgid "Warning: Too large timeout value, ignored.\n"
+#: src/microhttpd/daemon.c:5675 src/microhttpd/connection.c:5048
+#, c-format
+msgid ""
+"The specified connection timeout (%u) is too large. Maximum allowed value "
+"(%<PRIu64>) will be used instead.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5604
+#: src/microhttpd/daemon.c:5726
msgid ""
"Warning: Zero size, specified for thread pool size, is ignored. Thread pool "
"is not used.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5613
+#: src/microhttpd/daemon.c:5735
msgid ""
"Warning: \"1\", specified for thread pool size, is ignored. Thread pool is "
"not used.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5627
+#: src/microhttpd/daemon.c:5749
#, c-format
msgid "Specified thread pool size (%u) too big.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5639
+#: src/microhttpd/daemon.c:5761
msgid ""
"MHD_OPTION_THREAD_POOL_SIZE option is specified but "
"MHD_USE_INTERNAL_POLLING_THREAD flag is not specified.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5648
+#: src/microhttpd/daemon.c:5770
msgid ""
"Both MHD_OPTION_THREAD_POOL_SIZE option and MHD_USE_THREAD_PER_CONNECTION "
"flag are specified.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5666 src/microhttpd/daemon.c:5679
-#: src/microhttpd/daemon.c:5692 src/microhttpd/daemon.c:5705
-#: src/microhttpd/daemon.c:5757 src/microhttpd/daemon.c:5786
-#: src/microhttpd/daemon.c:5807 src/microhttpd/daemon.c:5829
-#: src/microhttpd/daemon.c:6097
+#: src/microhttpd/daemon.c:5788 src/microhttpd/daemon.c:5801
+#: src/microhttpd/daemon.c:5814 src/microhttpd/daemon.c:5827
+#: src/microhttpd/daemon.c:5879 src/microhttpd/daemon.c:5908
+#: src/microhttpd/daemon.c:5929 src/microhttpd/daemon.c:5951
+#: src/microhttpd/daemon.c:6219
#, c-format
msgid "MHD HTTPS option %d passed to MHD but MHD_USE_TLS not set.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5725
+#: src/microhttpd/daemon.c:5847
msgid "Error initializing DH parameters.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5735
+#: src/microhttpd/daemon.c:5857
msgid "Diffie-Hellman parameters string too long.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5746
+#: src/microhttpd/daemon.c:5868
msgid "Bad Diffie-Hellman parameters format.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5774
+#: src/microhttpd/daemon.c:5896
#, c-format
msgid "Setting priorities to `%s' failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:5795
+#: src/microhttpd/daemon.c:5917
msgid ""
"MHD_OPTION_HTTPS_CERT_CALLBACK requires building MHD with GnuTLS >= 3.0.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5817
+#: src/microhttpd/daemon.c:5939
msgid ""
"MHD_OPTION_HTTPS_CERT_CALLBACK2 requires building MHD with GnuTLS >= 3.6.3.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5852
+#: src/microhttpd/daemon.c:5974
msgid ""
"MHD_OPTION_LISTEN_SOCKET specified for daemon with MHD_USE_NO_LISTEN_SOCKET "
"flag set.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5890
+#: src/microhttpd/daemon.c:6012
msgid ""
"MHD_OPTION_EXTERNAL_LOGGER is not the first option specified for the daemon. "
"Some messages may be printed by the standard MHD logger.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5915
+#: src/microhttpd/daemon.c:6037
msgid "TCP fastopen is not supported on this platform.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5934
+#: src/microhttpd/daemon.c:6056
msgid ""
"Flag MHD_USE_PEDANTIC_CHECKS is ignored because another behavior is "
"specified by MHD_OPTION_STRICT_CLIENT.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6072
+#: src/microhttpd/daemon.c:6194
#, c-format
msgid "MHD HTTPS option %d passed to MHD compiled without GNUtls >= 3.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6111
+#: src/microhttpd/daemon.c:6233
#, c-format
msgid "MHD HTTPS option %d passed to MHD compiled without HTTPS support.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6118
+#: src/microhttpd/daemon.c:6240
#, c-format
msgid "Invalid option %d! (Did you terminate the list with MHD_OPTION_END?).\n"
msgstr ""
-#: src/microhttpd/daemon.c:6148
+#: src/microhttpd/daemon.c:6270
#, c-format
msgid "Call to epoll_create1 failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6158
+#: src/microhttpd/daemon.c:6280
msgid "Failed to set noninheritable mode on epoll FD.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6465
+#: src/microhttpd/daemon.c:6587
msgid ""
"Warning: MHD_USE_THREAD_PER_CONNECTION must be used only with "
"MHD_USE_INTERNAL_POLLING_THREAD. Flag MHD_USE_INTERNAL_POLLING_THREAD was "
"added. Consider setting MHD_USE_INTERNAL_POLLING_THREAD explicitly.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6478
+#: src/microhttpd/daemon.c:6600
msgid "Using debug build of libmicrohttpd.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6492
+#: src/microhttpd/daemon.c:6614
#, c-format
msgid "Failed to create inter-thread communication channel: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6509
+#: src/microhttpd/daemon.c:6631
msgid ""
"file descriptor for inter-thread communication channel exceeds maximum "
"value.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6529
+#: src/microhttpd/daemon.c:6651
msgid "Specified value for NC_SIZE too large.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6543
+#: src/microhttpd/daemon.c:6665
#, c-format
msgid "Failed to allocate memory for nonce-nc map: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6560
+#: src/microhttpd/daemon.c:6682
msgid "MHD failed to initialize nonce-nc mutex.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6581
+#: src/microhttpd/daemon.c:6703
msgid "MHD thread polling only works with MHD_USE_INTERNAL_POLLING_THREAD.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6605
+#: src/microhttpd/daemon.c:6727
#, c-format
msgid "Failed to create socket for listening: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6626 src/microhttpd/daemon.c:6645
-#: src/microhttpd/daemon.c:6668 src/microhttpd/daemon.c:6706
-#: src/microhttpd/daemon.c:6783 src/microhttpd/daemon.c:6814
+#: src/microhttpd/daemon.c:6748 src/microhttpd/daemon.c:6767
+#: src/microhttpd/daemon.c:6790 src/microhttpd/daemon.c:6828
+#: src/microhttpd/daemon.c:6905 src/microhttpd/daemon.c:6936
#, c-format
msgid "setsockopt failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6679
+#: src/microhttpd/daemon.c:6801
msgid "Cannot allow listening address reuse: SO_REUSEPORT not defined.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6715
+#: src/microhttpd/daemon.c:6837
msgid ""
"Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6794
+#: src/microhttpd/daemon.c:6916
#, c-format
msgid "Failed to bind to port %u: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6825
+#: src/microhttpd/daemon.c:6947
#, c-format
msgid "Failed to listen for connections: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6856
+#: src/microhttpd/daemon.c:6978
#, c-format
msgid "Failed to get listen port number: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6867
+#: src/microhttpd/daemon.c:6989
msgid ""
"Failed to get listen port number (`struct sockaddr_storage` too small!?).\n"
msgstr ""
-#: src/microhttpd/daemon.c:6908
+#: src/microhttpd/daemon.c:7030
msgid "Unknown address family!\n"
msgstr ""
-#: src/microhttpd/daemon.c:6921
+#: src/microhttpd/daemon.c:7045
#, c-format
msgid "Failed to set nonblocking mode on listening socket: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6944
+#: src/microhttpd/daemon.c:7070
#, c-format
msgid "Listen socket descriptor (%d) is not less than FD_SETSIZE (%d).\n"
msgstr ""
-#: src/microhttpd/daemon.c:6965
+#: src/microhttpd/daemon.c:7094
msgid ""
"Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL is not supported.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6979 src/microhttpd/daemon.c:6989
+#: src/microhttpd/daemon.c:7108 src/microhttpd/daemon.c:7118
msgid "MHD failed to initialize IP connection limit mutex.\n"
msgstr ""
-#: src/microhttpd/daemon.c:7007
+#: src/microhttpd/daemon.c:7136
msgid "Failed to initialize TLS support.\n"
msgstr ""
-#: src/microhttpd/daemon.c:7040 src/microhttpd/daemon.c:7105
-#: src/microhttpd/daemon.c:7204
+#: src/microhttpd/daemon.c:7169 src/microhttpd/daemon.c:7242
+#: src/microhttpd/daemon.c:7349
msgid "Failed to initialise mutex.\n"
msgstr ""
-#: src/microhttpd/daemon.c:7058
+#: src/microhttpd/daemon.c:7195
#, c-format
msgid "Failed to create listen thread: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:7116
+#: src/microhttpd/daemon.c:7253
#, c-format
msgid "Failed to create worker inter-thread communication channel: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:7129
+#: src/microhttpd/daemon.c:7266
msgid ""
"File descriptor for worker inter-thread communication channel exceeds "
"maximum value.\n"
msgstr ""
-#: src/microhttpd/daemon.c:7167
+#: src/microhttpd/daemon.c:7304
msgid "MHD failed to initialize cleanup connection mutex.\n"
msgstr ""
-#: src/microhttpd/daemon.c:7184
+#: src/microhttpd/daemon.c:7323
+msgid ""
+"Failed to create a new pool thread because it would have exceeded the system "
+"limit on the number of threads or no system resources available.\n"
+msgstr ""
+
+#: src/microhttpd/daemon.c:7329
#, c-format
msgid "Failed to create pool thread: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:7373 src/microhttpd/daemon.c:7406
+#: src/microhttpd/daemon.c:7518 src/microhttpd/daemon.c:7551
msgid "MHD_stop_daemon() called while we have suspended connections.\n"
msgstr ""
-#: src/microhttpd/daemon.c:7445 src/microhttpd/daemon.c:7543
-#: src/microhttpd/daemon.c:7581
+#: src/microhttpd/daemon.c:7590 src/microhttpd/daemon.c:7688
+#: src/microhttpd/daemon.c:7726
msgid "Failed to signal shutdown via inter-thread communication channel.\n"
msgstr ""
-#: src/microhttpd/daemon.c:7518
+#: src/microhttpd/daemon.c:7663
msgid "MHD_stop_daemon() was called twice."
msgstr ""
-#: src/microhttpd/daemon.c:8031
+#: src/microhttpd/daemon.c:8176
msgid "Failed to initialize winsock.\n"
msgstr ""
-#: src/microhttpd/daemon.c:8034
+#: src/microhttpd/daemon.c:8179
msgid "Winsock version 2.2 is not available.\n"
msgstr ""
-#: src/microhttpd/daemon.c:8042 src/microhttpd/daemon.c:8046
+#: src/microhttpd/daemon.c:8187 src/microhttpd/daemon.c:8191
msgid "Failed to initialise multithreading in libgcrypt.\n"
msgstr ""
-#: src/microhttpd/daemon.c:8052
+#: src/microhttpd/daemon.c:8197
msgid "libgcrypt is too old. MHD was compiled for libgcrypt 1.6.0 or newer.\n"
msgstr ""
-#: src/microhttpd/mhd_sockets.h:333
+#: src/microhttpd/mhd_sockets.h:345
msgid "Close socket failed.\n"
msgstr ""
-#: src/microhttpd/connection.c:140
+#: src/microhttpd/connection.c:206
msgid "The operation would block, retry later"
msgstr ""
-#: src/microhttpd/connection.c:142
+#: src/microhttpd/connection.c:208
msgid "The connection was forcibly closed by remote peer"
msgstr ""
-#: src/microhttpd/connection.c:144
+#: src/microhttpd/connection.c:210
msgid "The socket is not connected"
msgstr ""
-#: src/microhttpd/connection.c:146
+#: src/microhttpd/connection.c:212
msgid "Not enough system resources to serve the request"
msgstr ""
-#: src/microhttpd/connection.c:148
+#: src/microhttpd/connection.c:214
msgid "Bad FD value"
msgstr ""
-#: src/microhttpd/connection.c:150
+#: src/microhttpd/connection.c:216
msgid "Argument value is invalid"
msgstr ""
-#: src/microhttpd/connection.c:152
+#: src/microhttpd/connection.c:218
msgid "Argument value is not supported"
msgstr ""
-#: src/microhttpd/connection.c:154
+#: src/microhttpd/connection.c:220
msgid "The socket is no longer available for sending"
msgstr ""
-#: src/microhttpd/connection.c:156
+#: src/microhttpd/connection.c:222
msgid "TLS encryption or decryption error"
msgstr ""
-#: src/microhttpd/connection.c:161
+#: src/microhttpd/connection.c:227
msgid "Not an error code"
msgstr ""
-#: src/microhttpd/connection.c:164
+#: src/microhttpd/connection.c:230
msgid "Wrong error code value"
msgstr ""
-#: src/microhttpd/connection.c:868 src/microhttpd/connection.c:962
+#: src/microhttpd/connection.c:1047 src/microhttpd/connection.c:1157
msgid "Closing connection (out of memory)."
msgstr ""
-#: src/microhttpd/connection.c:913
+#: src/microhttpd/connection.c:1094
msgid "Closing connection (application reported error generating data)."
msgstr ""
-#: src/microhttpd/connection.c:1010
+#: src/microhttpd/connection.c:1212
+msgid "No callback for the chunked data."
+msgstr ""
+
+#: src/microhttpd/connection.c:1230
msgid "Closing connection (application error generating response)."
msgstr ""
-#: src/microhttpd/connection.c:1653
+#: src/microhttpd/connection.c:1254
+msgid "Closing connection (application returned more data than requested)."
+msgstr ""
+
+#: src/microhttpd/connection.c:2292
#, c-format
msgid ""
-"Error processing request (HTTP response code is %u (`%s')). Closing "
+"Error processing request (HTTP response code is %u ('%s')). Closing "
"connection.\n"
msgstr ""
-#: src/microhttpd/connection.c:1680 src/microhttpd/connection.c:2722
-msgid "Closing connection (failed to queue response)."
+#: src/microhttpd/connection.c:2301
+msgid "Too late to send an error response, response is being sent already.\n"
+msgstr ""
+
+#: src/microhttpd/connection.c:2307
+msgid "Too late for error response."
+msgstr ""
+
+#: src/microhttpd/connection.c:2335
+msgid "Failed to create error response.\n"
+msgstr ""
+
+#: src/microhttpd/connection.c:2351
+msgid "Closing connection (failed to queue error response)."
msgstr ""
-#: src/microhttpd/connection.c:1691 src/microhttpd/connection.c:3708
-msgid "Closing connection (failed to create response header)."
+#: src/microhttpd/connection.c:2383
+msgid "Closing connection (failed to create error response header)."
msgstr ""
-#: src/microhttpd/connection.c:1737 src/microhttpd/connection.c:2888
-#: src/microhttpd/connection.c:2956 src/microhttpd/connection.c:3372
+#: src/microhttpd/connection.c:2435 src/microhttpd/connection.c:3795
+#: src/microhttpd/connection.c:3872 src/microhttpd/connection.c:4437
#, c-format
msgid "In function %s handling connection at state: %s\n"
msgstr ""
-#: src/microhttpd/connection.c:1955
+#: src/microhttpd/connection.c:2678
msgid "Not enough memory in pool to allocate header record!\n"
msgstr ""
-#: src/microhttpd/connection.c:2002
+#: src/microhttpd/connection.c:2724
msgid "Not enough memory in pool to parse cookies!\n"
msgstr ""
-#: src/microhttpd/connection.c:2233 src/microhttpd/connection.c:2437
+#: src/microhttpd/connection.c:3083 src/microhttpd/connection.c:3322
msgid "Application reported internal error, closing connection."
msgstr ""
-#: src/microhttpd/connection.c:2302 src/microhttpd/connection.c:2381
-msgid ""
-"Received malformed HTTP request (bad chunked encoding). Closing connection."
-msgstr ""
-
-#: src/microhttpd/connection.c:2445
+#: src/microhttpd/connection.c:3331
msgid "libmicrohttpd API violation.\n"
msgstr ""
-#: src/microhttpd/connection.c:2461
+#: src/microhttpd/connection.c:3346
msgid ""
"WARNING: incomplete upload processing and connection not suspended may "
"result in hung connection.\n"
msgstr ""
-#: src/microhttpd/connection.c:2535
-msgid "Received malformed line (no colon). Closing connection."
-msgstr ""
-
-#: src/microhttpd/connection.c:2698
-msgid "Received HTTP 1.1 request without `Host' header.\n"
+#: src/microhttpd/connection.c:3573
+msgid "Received HTTP/1.1 request without `Host' header.\n"
msgstr ""
-#: src/microhttpd/connection.c:2710
-msgid "Closing connection (failed to create response)."
+#: src/microhttpd/connection.c:3620
+msgid "Too large value of 'Content-Length' header. Closing connection.\n"
msgstr ""
-#: src/microhttpd/connection.c:2760
+#: src/microhttpd/connection.c:3631
msgid "Failed to parse `Content-Length' header. Closing connection.\n"
msgstr ""
-#: src/microhttpd/connection.c:2861
-msgid "Socket disconnected while reading request."
+#: src/microhttpd/connection.c:3744
+msgid "Socket has been disconnected when reading request.\n"
msgstr ""
-#: src/microhttpd/connection.c:2868
+#: src/microhttpd/connection.c:3756
#, c-format
msgid "Connection socket is closed when reading request due to the error: %s\n"
msgstr ""
-#: src/microhttpd/connection.c:2983
+#: src/microhttpd/connection.c:3774
+msgid "Connection was closed by remote side with incomplete request.\n"
+msgstr ""
+
+#: src/microhttpd/connection.c:3900
#, c-format
msgid "Failed to send data in request for %s.\n"
msgstr ""
-#: src/microhttpd/connection.c:2992
+#: src/microhttpd/connection.c:3909
#, c-format
msgid "Sent 100 continue response: `%.*s'\n"
msgstr ""
-#: src/microhttpd/connection.c:3063
+#: src/microhttpd/connection.c:3986
#, c-format
msgid ""
"Failed to send the response headers for the request for `%s'. Error: %s\n"
msgstr ""
-#: src/microhttpd/connection.c:3128
+#: src/microhttpd/connection.c:4052
msgid "Data offset exceeds limit.\n"
msgstr ""
-#: src/microhttpd/connection.c:3138
+#: src/microhttpd/connection.c:4062
#, c-format
msgid "Sent %d-byte DATA response: `%.*s'\n"
msgstr ""
-#: src/microhttpd/connection.c:3155
+#: src/microhttpd/connection.c:4079
#, c-format
msgid "Failed to send the response body for the request for `%s'. Error: %s\n"
msgstr ""
-#: src/microhttpd/connection.c:3187
+#: src/microhttpd/connection.c:4111
#, c-format
msgid ""
"Failed to send the chunked response body for the request for `%s'. Error: "
"%s\n"
msgstr ""
-#: src/microhttpd/connection.c:3223
+#: src/microhttpd/connection.c:4147
#, c-format
msgid "Failed to send the footers for the request for `%s'. Error: %s\n"
msgstr ""
-#: src/microhttpd/connection.c:3252
+#: src/microhttpd/connection.c:4176
msgid "Internal error.\n"
msgstr ""
-#: src/microhttpd/connection.c:3330
+#: src/microhttpd/connection.c:4215
+#, c-format
+msgid "Detected system clock %u milliseconds jump back.\n"
+msgstr ""
+
+#: src/microhttpd/connection.c:4222
+#, c-format
+msgid "Detected too large system clock %<PRIu64> milliseconds jump back.\n"
+msgstr ""
+
+#: src/microhttpd/connection.c:4304
msgid ""
"Failed to signal end of connection via inter-thread communication channel.\n"
msgstr ""
-#: src/microhttpd/connection.c:3599
+#: src/microhttpd/connection.c:4688
msgid "Closing connection (failed to create response header).\n"
msgstr ""
-#: src/microhttpd/connection.c:4060
+#: src/microhttpd/connection.c:4807
+msgid "Closing connection (failed to create response footer)."
+msgstr ""
+
+#: src/microhttpd/connection.c:5117
msgid "Attempted to queue response on wrong thread!\n"
msgstr ""
-#: src/microhttpd/connection.c:4072
+#: src/microhttpd/connection.c:5140
msgid ""
"Attempted 'upgrade' connection on daemon without MHD_ALLOW_UPGRADE option!\n"
msgstr ""
-#: src/microhttpd/connection.c:4082
+#: src/microhttpd/connection.c:5149
msgid "Application used invalid status code for 'upgrade' response!\n"
msgstr ""
-#: src/microhttpd/response.c:1096
+#: src/microhttpd/connection.c:5158
+msgid "Application used invalid response without \"Connection\" header!\n"
+msgstr ""
+
+#: src/microhttpd/connection.c:5172
+msgid ""
+"Application used invalid response without \"upgrade\" token in \"Connection"
+"\" header!\n"
+msgstr ""
+
+#: src/microhttpd/connection.c:5182
+msgid "Connection \"Upgrade\" can be used with HTTP/1.1 connections!\n"
+msgstr ""
+
+#: src/microhttpd/connection.c:5194
+#, c-format
+msgid ""
+"Refused wrong status code (%u). HTTP requires three digits status code!\n"
+msgstr ""
+
+#: src/microhttpd/connection.c:5206
+#, c-format
+msgid ""
+"Wrong status code (%u) refused. HTTP/1.0 clients do not support 1xx status "
+"codes!\n"
+msgstr ""
+
+#: src/microhttpd/connection.c:5217
+#, c-format
+msgid ""
+"Wrong status code (%u) refused. HTTP/1.0 reply mode does not support 1xx "
+"status codes!\n"
+msgstr ""
+
+#: src/microhttpd/response.c:1646
msgid ""
"Invalid response for upgrade: application failed to set the 'Upgrade' "
"header!\n"
msgstr ""
-#: src/microhttpd/response.c:1139
+#: src/microhttpd/response.c:1690
msgid "Failed to make loopback sockets non-blocking.\n"
msgstr ""
-#: src/microhttpd/response.c:1158
+#: src/microhttpd/response.c:1709
msgid "Failed to set SO_NOSIGPIPE on loopback sockets.\n"
msgstr ""
-#: src/microhttpd/response.c:1178
+#: src/microhttpd/response.c:1729
#, c-format
msgid "Socketpair descriptor larger than FD_SETSIZE: %d > %d\n"
msgstr ""
-#: src/microhttpd/response.c:1259
+#: src/microhttpd/response.c:1810
msgid "Error cleaning up while handling epoll error.\n"
msgstr ""
-#: src/microhttpd/mhd_itc.h:355
+#: src/microhttpd/mhd_itc.h:357
msgid "Failed to destroy ITC.\n"
msgstr ""
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/Makefile.am
^
|
@@ -10,17 +10,18 @@
SUBDIRS = include microhttpd $(curltests) $(zzuftests) .
+# Finally (last!) also build experimental lib...
+if HAVE_EXPERIMENTAL
+SUBDIRS += microhttpd_ws lib
+endif
+
if BUILD_EXAMPLES
SUBDIRS += examples
endif
-# Finally (last!) also build experimental lib...
-if HAVE_EXPERIMENTAL
-SUBDIRS += lib
-endif
EXTRA_DIST = \
datadir/cert-and-key.pem \
- datadir/cert-and-key-for-wireshark.pem
+ datadir/cert-and-key-for-wireshark.pem
.NOTPARALLEL:
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/Makefile.am
^
|
@@ -30,6 +30,11 @@
fileserver_example_external_select \
refuse_post_example
+if HAVE_EXPERIMENTAL
+noinst_PROGRAMS += \
+ websocket_chatserver_example
+endif
+
if MHD_HAVE_EPOLL
noinst_PROGRAMS += \
suspend_resume_epoll
@@ -123,6 +128,12 @@
chunked_example_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
+websocket_chatserver_example_SOURCES = \
+ websocket_chatserver_example.c
+websocket_chatserver_example_LDADD = \
+ $(top_builddir)/src/microhttpd_ws/libmicrohttpd_ws.la \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
demo_SOURCES = \
demo.c
demo_CFLAGS = \
@@ -132,7 +143,7 @@
demo_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la \
$(PTHREAD_LIBS)
-if HAVE_LIBMAGIC
+if MHD_HAVE_LIBMAGIC
demo_LDADD += -lmagic
endif
@@ -145,7 +156,7 @@
demo_https_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la \
$(PTHREAD_LIBS)
-if HAVE_LIBMAGIC
+if MHD_HAVE_LIBMAGIC
demo_https_LDADD += -lmagic
endif
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/benchmark.c
^
|
@@ -25,6 +25,13 @@
#include "platform.h"
#include <microhttpd.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+#ifndef PRIu64
+#define PRIu64 "llu"
+#endif /* ! PRIu64 */
+
#if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
#undef MHD_CPU_COUNT
#endif
@@ -87,7 +94,7 @@
if (delta < SMALL)
small_deltas[delta]++;
else
- fprintf (stdout, "D: %llu 1\n", (unsigned long long) delta);
+ fprintf (stdout, "D: %" PRIu64 " 1\n", delta);
free (tv);
}
@@ -166,7 +173,7 @@
(void) getc (stdin);
MHD_stop_daemon (d);
MHD_destroy_response (response);
- for (i = 0; i<SMALL; i++)
+ for (i = 0; i < SMALL; i++)
if (0 != small_deltas[i])
fprintf (stdout, "D: %d %u\n", i, small_deltas[i]);
return 0;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/benchmark_https.c
^
|
@@ -25,6 +25,13 @@
#include "platform.h"
#include <microhttpd.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+#ifndef PRIu64
+#define PRIu64 "llu"
+#endif /* ! PRIu64 */
+
#if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
#undef MHD_CPU_COUNT
#endif
@@ -87,7 +94,7 @@
if (delta < SMALL)
small_deltas[delta]++;
else
- fprintf (stdout, "D: %llu 1\n", (unsigned long long) delta);
+ fprintf (stdout, "D: %" PRIu64 " 1\n", delta);
free (tv);
}
@@ -230,7 +237,7 @@
(void) getc (stdin);
MHD_stop_daemon (d);
MHD_destroy_response (response);
- for (i = 0; i<SMALL; i++)
+ for (i = 0; i < SMALL; i++)
if (0 != small_deltas[i])
fprintf (stdout, "D: %d %u\n", i, small_deltas[i]);
return 0;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/chunked_example.c
^
|
@@ -137,6 +137,15 @@
free (callback_param);
return MHD_NO;
}
+ /* Enforce chunked response, even for non-keep-alive connection. */
+ if (MHD_NO == MHD_add_response_header (response,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ "chunked"))
+ {
+ free (callback_param);
+ MHD_destroy_response (response);
+ return MHD_NO;
+ }
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
MHD_destroy_response (response);
return ret;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/connection_close.c
^
|
@@ -25,6 +25,13 @@
#include "platform.h"
#include <microhttpd.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+#ifndef PRIu64
+#define PRIu64 "llu"
+#endif /* ! PRIu64 */
+
#define PAGE \
"<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
@@ -72,8 +79,8 @@
enum MHD_RequestTerminationCode toe)
{
fprintf (stderr,
- "%llu - RC: %d\n",
- (unsigned long long) __rdtsc (),
+ "%" PRIu64 " - RC: %d\n",
+ (uint64_t) __rdtsc (),
toe);
}
@@ -85,8 +92,8 @@
enum MHD_ConnectionNotificationCode toe)
{
fprintf (stderr,
- "%llu - CC: %d\n",
- (unsigned long long) __rdtsc (),
+ "%" PRIu64 " - CC: %d\n",
+ (uint64_t) __rdtsc (),
toe);
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/demo.c
^
|
@@ -28,6 +28,7 @@
* to be adjusted depending on the number of available cores.
* @author Christian Grothoff
*/
+#include "MHD_config.h"
#include "platform.h"
#include <microhttpd.h>
#include <unistd.h>
@@ -568,7 +569,7 @@
uc->language,
uc->category,
filename);
- for (i = strlen (fn) - 1; i>=0; i--)
+ for (i = strlen (fn) - 1; i >= 0; i--)
if (! isprint ((unsigned char) fn[i]))
fn[i] = '_';
uc->fd = open (fn,
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/demo_https.c
^
|
@@ -571,7 +571,7 @@
uc->language,
uc->category,
filename);
- for (i = strlen (fn) - 1; i>=0; i--)
+ for (i = strlen (fn) - 1; i >= 0; i--)
if (! isprint ((unsigned char) fn[i]))
fn[i] = '_';
uc->fd = open (fn,
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/pthread_windows.c
^
|
@@ -0,0 +1,252 @@
+#include "pthread_windows.h"
+#include <Windows.h>
+
+struct _pthread_t
+{
+ HANDLE thread;
+};
+
+struct _pthread_cond_t
+{
+ HANDLE event;
+};
+
+struct _pthread_mutex_t
+{
+ HANDLE mutex;
+};
+
+struct StdCallThread
+{
+ void *(__cdecl *start) (void *);
+ void *arg;
+};
+
+DWORD WINAPI
+ThreadProc (LPVOID lpParameter)
+{
+ struct StdCallThread st = *((struct StdCallThread *) lpParameter);
+ free (lpParameter);
+ st.start (st.arg);
+ return 0;
+}
+
+
+int
+pthread_create (pthread_t *pt,
+ const void *attr,
+ void *(__cdecl *start)(void *),
+ void *arg)
+{
+ pthread_t pt_ = (pthread_t) malloc (sizeof(struct _pthread_t));
+ if (NULL == pt_)
+ return 1;
+ struct StdCallThread *sct;
+ sct = (struct StdCallThread *) malloc (sizeof(struct StdCallThread));
+ if (NULL == sct)
+ {
+ free (pt_);
+ return 1;
+ }
+
+ sct->start = start;
+ sct->arg = arg;
+ pt_->thread = CreateThread (NULL, 0, ThreadProc, sct, 0, NULL);
+ if (NULL == pt_->thread)
+ {
+ free (sct);
+ free (pt_);
+ return 1;
+ }
+ *pt = pt_;
+
+ return 0;
+}
+
+
+int
+pthread_detach (pthread_t pt)
+{
+ if (pt)
+ {
+ CloseHandle (pt->thread);
+ free (pt);
+ }
+ return 0;
+}
+
+
+int
+pthread_join (pthread_t pt,
+ void **value_ptr)
+{
+ if (NULL == pt)
+ return 1;
+
+ if (value_ptr)
+ {
+ *value_ptr = NULL;
+ }
+ WaitForSingleObject (pt->thread, INFINITE);
+ CloseHandle (pt->thread);
+ free (pt);
+
+ return 0;
+}
+
+
+int
+pthread_mutex_init (pthread_mutex_t *mutex,
+ const void *attr)
+{
+ pthread_mutex_t mutex_ = (pthread_mutex_t) malloc (sizeof(struct
+ _pthread_mutex_t));
+ if (NULL == mutex_)
+ return 1;
+ mutex_->mutex = CreateMutex (NULL, FALSE, NULL);
+ if (NULL == mutex_->mutex)
+ {
+ free (mutex_);
+ return 1;
+ }
+ *mutex = mutex_;
+
+ return 0;
+}
+
+
+int
+pthread_mutex_destroy (pthread_mutex_t *mutex)
+{
+ if (NULL == mutex)
+ return 1;
+ if ((NULL == *mutex) || (PTHREAD_MUTEX_INITIALIZER == *mutex))
+ return 0;
+
+ CloseHandle ((*mutex)->mutex);
+ free (*mutex);
+ *mutex = NULL;
+
+ return 0;
+}
+
+
+int
+pthread_mutex_lock (pthread_mutex_t *mutex)
+{
+ if (NULL == mutex)
+ return 1;
+ if (NULL == *mutex)
+ return 1;
+ if (PTHREAD_MUTEX_INITIALIZER == *mutex)
+ {
+ int ret = pthread_mutex_init (mutex, NULL);
+ if (0 != ret)
+ return ret;
+ }
+ if (WAIT_OBJECT_0 != WaitForSingleObject ((*mutex)->mutex, INFINITE))
+ return 1;
+ return 0;
+}
+
+
+int
+pthread_mutex_unlock (pthread_mutex_t *mutex)
+{
+ if (NULL == mutex)
+ return 1;
+ if ((NULL == *mutex) || (PTHREAD_MUTEX_INITIALIZER == *mutex))
+ return 1;
+
+ if (0 == ReleaseMutex ((*mutex)->mutex))
+ return 1;
+
+ return 0;
+}
+
+
+int
+pthread_cond_init (pthread_cond_t *cond,
+ const void *attr)
+{
+ pthread_cond_t cond_ = (pthread_cond_t) malloc (sizeof(struct
+ _pthread_cond_t));
+ if (NULL == cond_)
+ return 1;
+ cond_->event = CreateEvent (NULL, FALSE, FALSE, NULL);
+ if (NULL == cond_->event)
+ {
+ free (cond_);
+ return 1;
+ }
+ *cond = cond_;
+
+ return 0;
+}
+
+
+int
+pthread_cond_destroy (pthread_cond_t *cond)
+{
+ if (NULL == cond)
+ return 1;
+ if ((NULL == *cond) || (PTHREAD_COND_INITIALIZER == *cond))
+ return 1;
+
+ CloseHandle ((*cond)->event);
+ free (*cond);
+
+ return 0;
+}
+
+
+int
+pthread_cond_wait (pthread_cond_t *cond,
+ pthread_mutex_t *mutex)
+{
+ if ((NULL == cond) || (NULL == mutex))
+ return 1;
+ if ((NULL == *cond) || (NULL == *mutex))
+ return 1;
+ if (PTHREAD_COND_INITIALIZER == *cond)
+ {
+ int ret = pthread_cond_init (cond, NULL);
+ if (0 != ret)
+ return ret;
+ }
+ if (PTHREAD_MUTEX_INITIALIZER == *mutex)
+ {
+ int ret = pthread_mutex_init (mutex, NULL);
+ if (0 != ret)
+ return ret;
+ }
+ ReleaseMutex ((*mutex)->mutex);
+ if (WAIT_OBJECT_0 != WaitForSingleObject ((*cond)->event, INFINITE))
+ return 1;
+ if (WAIT_OBJECT_0 != WaitForSingleObject ((*mutex)->mutex, INFINITE))
+ return 1;
+
+ return 0;
+}
+
+
+int
+pthread_cond_signal (pthread_cond_t *cond)
+{
+ if (NULL == cond)
+ return 1;
+ if ((NULL == *cond) || (PTHREAD_COND_INITIALIZER == *cond))
+ return 1;
+
+ if (0 == SetEvent ((*cond)->event))
+ return 1;
+
+ return 0;
+}
+
+
+int
+pthread_cond_broadcast (pthread_cond_t *cond)
+{
+ return pthread_cond_signal (cond);
+}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/pthread_windows.h
^
|
@@ -0,0 +1,46 @@
+#ifndef pthread_windows_H
+#define pthread_windows_H
+
+struct _pthread_t;
+struct _pthread_cond_t;
+struct _pthread_mutex_t;
+
+typedef struct _pthread_t *pthread_t;
+typedef struct _pthread_cond_t *pthread_cond_t;
+typedef struct _pthread_mutex_t *pthread_mutex_t;
+
+#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -1)
+#define PTHREAD_COND_INITIALIZER ((pthread_cond_t)(size_t) -1)
+
+int pthread_create (pthread_t * pt,
+ const void *attr,
+ void *(__cdecl * start)(void *),
+ void *arg);
+
+int pthread_detach (pthread_t pt);
+
+int pthread_join (pthread_t pt,
+ void **value_ptr);
+
+int pthread_mutex_init (pthread_mutex_t *mutex,
+ const void *attr);
+
+int pthread_mutex_destroy (pthread_mutex_t *mutex);
+
+int pthread_mutex_lock (pthread_mutex_t *mutex);
+
+int pthread_mutex_unlock (pthread_mutex_t *mutex);
+
+int pthread_cond_init (pthread_cond_t *cond,
+ const void *attr);
+
+int pthread_cond_destroy (pthread_cond_t *cond);
+
+int pthread_cond_wait (pthread_cond_t *cond,
+ pthread_mutex_t *mutex);
+
+int pthread_cond_signal (pthread_cond_t *cond);
+
+int pthread_cond_broadcast (pthread_cond_t *cond);
+
+#endif // !pthread_windows_H
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/querystring_example.c
^
|
@@ -43,6 +43,7 @@
char *me;
struct MHD_Response *response;
enum MHD_Result ret;
+ int resp_len;
(void) url; /* Unused. Silent compiler warning. */
(void) version; /* Unused. Silent compiler warning. */
(void) upload_data; /* Unused. Silent compiler warning. */
@@ -56,13 +57,24 @@
*ptr = &aptr;
return MHD_YES;
}
- *ptr = NULL; /* reset when done */
+ *ptr = NULL; /* reset when done */
+ if (NULL == fmt)
+ return MHD_NO; /* The cls must not be NULL */
val = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "q");
- me = malloc (snprintf (NULL, 0, fmt, "q", val) + 1);
+ if (NULL == val)
+ return MHD_NO; /* No "q" argument was found */
+ resp_len = snprintf (NULL, 0, fmt, "q", val);
+ if (0 > resp_len)
+ return MHD_NO; /* Error calculating response size */
+ me = malloc (resp_len + 1);
if (me == NULL)
- return MHD_NO;
- sprintf (me, fmt, "q", val);
- response = MHD_create_response_from_buffer (strlen (me), me,
+ return MHD_NO; /* Error allocating memory */
+ if (resp_len != snprintf (me, resp_len + 1, fmt, "q", val))
+ {
+ free (me);
+ return MHD_NO; /* Error forming the response body */
+ }
+ response = MHD_create_response_from_buffer (resp_len, me,
MHD_RESPMEM_MUST_FREE);
if (response == NULL)
{
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/suspend_resume_epoll.c
^
|
@@ -53,7 +53,7 @@
{
struct MHD_Response *response;
enum MHD_Result ret;
- struct Request*req;
+ struct Request *req;
struct itimerspec ts;
(void) cls;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/upgrade_example.c
^
|
@@ -40,7 +40,6 @@
* Change socket to blocking.
*
* @param fd the socket to manipulate
- * @return non-zero if succeeded, zero otherwise
*/
static void
make_blocking (MHD_socket fd)
@@ -50,16 +49,16 @@
flags = fcntl (fd, F_GETFL);
if (-1 == flags)
- return;
+ abort ();
if ((flags & ~O_NONBLOCK) != flags)
if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
abort ();
#elif defined(MHD_WINSOCK_SOCKETS)
- unsigned long flags = 1;
+ unsigned long flags = 0;
- ioctlsocket (fd, FIONBIO, &flags);
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ abort ();
#endif /* MHD_WINSOCK_SOCKETS */
-
}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/websocket_chatserver_example.c
^
|
@@ -0,0 +1,2363 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2021 David Gausmann (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 websocket_chatserver_example.c
+ * @brief example for how to use websockets
+ * @author David Gausmann
+ *
+ * Access the HTTP server with your webbrowser.
+ * The webbrowser must support JavaScript and WebSockets.
+ * The websocket access will be initiated via the JavaScript on the website.
+ * You will get an example chat room, which uses websockets.
+ * For testing with multiple users, just start several instances of your webbrowser.
+ *
+ */
+
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+#include "platform.h"
+#include <microhttpd.h>
+#include <microhttpd_ws.h>
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
+/*
+ Workaround for Windows systems, because the NuGet version of pthreads is buggy.
+ This is a simple replacement. It doesn't offer all functions of pthread, but
+ has everything, what is required for this example.
+ See: https://github.com/coapp-packages/pthreads/issues/2
+*/
+#include "pthread_windows.h"
+
+/*
+ On Windows we will use stricmp instead of strcasecmp (strcasecmp is undefined there).
+*/
+#define strcasecmp stricmp
+
+#else
+/*
+ On Unix systems we can use pthread.
+*/
+#include <pthread.h>
+#endif
+
+
+/*
+ * Specify with this constant whether or not to use HTTPS.
+ * 0 means HTTP, 1 means HTTPS.
+ * Please note that you must enter a valid private key/certificate pair
+ * in the main procedure to running this example with HTTPS.
+ */
+#define USE_HTTPS 0
+
+/**
+ * This is the main website.
+ * The HTML, CSS and JavaScript code is all in there.
+ */
+#define PAGE \
+ "<!DOCTYPE html>" \
+ "<html>" \
+ "<head>" \
+ "<meta charset='UTF-8'>" \
+ "<title>libmicrohttpd websocket chatserver demo</title>" \
+ "<style>" \
+ " html" \
+ " {\n" \
+ " font: 11pt sans-serif;\n" \
+ " }\n" \
+ " html, body" \
+ " {\n" \
+ " margin: 0;\n" \
+ " width: 100vw;\n" \
+ " height: 100vh;\n" \
+ " }\n" \
+ " div#Chat\n" \
+ " {\n" \
+ " display: flex;\n" \
+ " flex-direction: row;\n" \
+ " }\n" \
+ " div#Chat > div.MessagesAndInput\n" \
+ " {\n" \
+ " flex: 1 1 auto;" \
+ " display: flex;\n" \
+ " flex-direction: column;\n" \
+ " width: calc(100vw - 20em);\n" \
+ " }\n" \
+ " div#Chat > div.MessagesAndInput > div#Messages\n" \
+ " {\n" \
+ " flex: 1 1 auto;" \
+ " display: flex;\n" \
+ " flex-direction: column;\n" \
+ " justify-content: flex-start;\n" \
+ " box-sizing: border-box;\n" \
+ " overflow-y: scroll;\n" \
+ " border: 2pt solid #888;\n" \
+ " background-color: #eee;\n" \
+ " height: calc(100vh - 2em);\n" \
+ " }\n" \
+ " div#Chat > div.MessagesAndInput > div#Messages > div.Message > span\n" \
+ " {\n" \
+ " white-space: pre\n" \
+ " }\n" \
+ " div#Chat > div.MessagesAndInput > div#Messages > div.Message.error > span\n" \
+ " {\n" \
+ " color: red;\n" \
+ " }\n" \
+ " div#Chat > div.MessagesAndInput > div#Messages > div.Message.system > span\n" \
+ " {\n" \
+ " color: green;\n" \
+ " }\n" \
+ " div#Chat > div.MessagesAndInput > div#Messages > div.Message.moderator > span\n" \
+ " {\n" \
+ " color: #808000;\n" \
+ " }\n" \
+ " div#Chat > div.MessagesAndInput > div#Messages > div.Message.private > span\n" \
+ " {\n" \
+ " color: blue;\n" \
+ " }\n" \
+ " div#Chat > div.MessagesAndInput > div.Input\n" \
+ " {\n" \
+ " flex: 0 0 auto;" \
+ " height: 2em;" \
+ " display: flex;" \
+ " flex-direction: row;" \
+ " background-color: #eee;\n" \
+ " }\n" \
+ " div#Chat > div.MessagesAndInput > div.Input > input#InputMessage\n" \
+ " {\n" \
+ " flex: 1 1 auto;" \
+ " }\n" \
+ " div#Chat > div.MessagesAndInput > div.Input > button\n" \
+ " {\n" \
+ " flex: 0 0 auto;" \
+ " width: 5em;" \
+ " margin-left: 4pt;" \
+ " }\n" \
+ " div#Chat > div#Users\n" \
+ " {\n" \
+ " flex: 0 0 auto;" \
+ " width: 20em;" \
+ " display: flex;\n" \
+ " flex-direction: column;\n" \
+ " justify-content: flex-start;\n" \
+ " box-sizing: border-box;\n" \
+ " overflow-y: scroll;\n" \
+ " border: 2pt solid #888;\n" \
+ " background-color: #eee;\n" \
+ " }\n" \
+ " div#Chat > div#Users > div\n" \
+ " {\n" \
+ " cursor: pointer;\n" \
+ " user-select: none;\n" \
+ " -webkit-user-select: none;\n" \
+ " }\n" \
+ " div#Chat > div#Users > div.selected\n" \
+ " {\n" \
+ " background-color: #7bf;\n" \
+ " }\n" \
+ "</style>" \
+ "<script>\n" \
+ " 'use strict'\n;" \
+ "\n" \
+ " let baseUrl;\n" \
+ " let socket;\n" \
+ " let connectedUsers = new Map();\n" \
+ "\n" \
+ " window.addEventListener('load', window_onload);\n" \
+ "\n" \
+ " /**\n" \
+ " This is the main procedure which initializes the chat and connects the first socket\n" \
+ " */\n" \
+ " function window_onload(event)\n" \
+ " {\n" \
+ " /* Determine the base url (for http:/" "/ this is ws:/" "/ for https:/" \
+ "/ this must be wss:/" "/) */\n" \
+ " baseUrl = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + ':/" "/' + window.location.host + '/ChatServerWebSocket';\n" \
+ " chat_generate();\n" \
+ " chat_connect();\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This function generates the chat using DOM\n" \
+ " */\n" \
+ " function chat_generate()\n" \
+ " {\n" \
+ " document.body.innerHTML = '';\n" \
+ " let chat = document.createElement('div');\n" \
+ " document.body.appendChild(chat);\n" \
+ " chat.id = 'Chat';\n" \
+ " let messagesAndInput = document.createElement('div');\n" \
+ " chat.appendChild(messagesAndInput);\n" \
+ " messagesAndInput.classList.add('MessagesAndInput');\n" \
+ " let messages = document.createElement('div');\n" \
+ " messagesAndInput.appendChild(messages);\n" \
+ " messages.id = 'Messages';\n" \
+ " let input = document.createElement('div');\n" \
+ " messagesAndInput.appendChild(input);\n" \
+ " input.classList.add('Input');\n" \
+ " let inputMessage = document.createElement('input');\n" \
+ " input.appendChild(inputMessage);\n" \
+ " inputMessage.type = 'text';\n" \
+ " inputMessage.id = 'InputMessage';\n" \
+ " inputMessage.disabled = true;\n" \
+ " inputMessage.addEventListener('keydown', chat_onKeyDown);\n" \
+ " let inputMessageSend = document.createElement('button');\n" \
+ " input.appendChild(inputMessageSend);\n" \
+ " inputMessageSend.id = 'InputMessageButton';\n" \
+ " inputMessageSend.disabled = true;\n" \
+ " inputMessageSend.innerText = 'send';\n" \
+ " inputMessageSend.addEventListener('click', chat_onSendClicked);\n" \
+ " let inputImage = document.createElement('input');\n" \
+ " input.appendChild(inputImage);\n" \
+ " inputImage.id = 'InputImage';\n" \
+ " inputImage.type = 'file';\n" \
+ " inputImage.accept = 'image /*';\n" \
+ " inputImage.style.display = 'none';\n" \
+ " inputImage.addEventListener('change', chat_onImageSelected);\n" \
+ " let inputImageButton = document.createElement('button');\n" \
+ " input.appendChild(inputImageButton);\n" \
+ " inputImageButton.id = 'InputImageButton';\n" \
+ " inputImageButton.disabled = true;\n" \
+ " inputImageButton.innerText = 'image';\n" \
+ " inputImageButton.addEventListener('click', chat_onImageClicked);\n" \
+ " let users = document.createElement('div');\n" \
+ " chat.appendChild(users);\n" \
+ " users.id = 'Users';\n" \
+ " users.addEventListener('click', chat_onUserClicked);\n" \
+ " let allUsers = document.createElement('div');\n" \
+ " users.appendChild(allUsers);\n" \
+ " allUsers.classList.add('selected');\n" \
+ " allUsers.innerText = '<everyone>';\n" \
+ " allUsers.setAttribute('data-user', '0');\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This function creates and connects a WebSocket\n" \
+ " */\n" \
+ " function chat_connect()\n" \
+ " {\n" \
+ " chat_addMessage(`Connecting to libmicrohttpd chat server demo (${baseUrl})...`, { type: 'system' });\n" \
+ " socket = new WebSocket(baseUrl);\n" \
+ " socket.binaryType = 'arraybuffer';\n" \
+ " socket.onopen = socket_onopen;\n" \
+ " socket.onclose = socket_onclose;\n" \
+ " socket.onerror = socket_onerror;\n" \
+ " socket.onmessage = socket_onmessage;\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This function adds new text to the chat list\n" \
+ " */\n" \
+ " function chat_addMessage(text, options)\n" \
+ " {\n" \
+ " let type = options && options.type || 'regular';\n" \
+ " if(!/^(?:regular|system|error|private|moderator)$/.test(type))\n" \
+ " type = 'regular';\n" \
+ " let message = document.createElement('div');\n" \
+ " message.classList.add('Message');\n" \
+ " message.classList.add(type);\n" \
+ " if(typeof(text) === 'string')\n" \
+ " {\n" \
+ " let content = document.createElement('span');\n" \
+ " message.appendChild(content);\n" \
+ " if(options && options.from)\n" \
+ " content.innerText = `${options.from}: ${text}`;\n" \
+ " else\n" \
+ " content.innerText = text;\n" \
+ " if(options && options.reconnect)\n" \
+ " {\n" \
+ " let span = document.createElement('span');\n" \
+ " span.appendChild(document.createTextNode(' ('));\n" \
+ " let reconnect = document.createElement('a');\n" \
+ " reconnect.href = 'javascript:chat_connect()';\n" \
+ " reconnect.innerText = 'reconnect';\n" \
+ " span.appendChild(reconnect);\n" \
+ " span.appendChild(document.createTextNode(')'));\n" \
+ " message.appendChild(span);\n" \
+ " }\n" \
+ " }\n" \
+ " else\n" \
+ " {\n" \
+ " let content = document.createElement('span');\n" \
+ " message.appendChild(content);\n" \
+ " if(options && options.from)\n" \
+ " {\n" \
+ " content.innerText = `${options.from}:\\n`;\n" \
+ " }\n" \
+ " if(options && options.pictureType && text instanceof Uint8Array)\n" \
+ " {\n" \
+ " let img = document.createElement('img');\n" \
+ " content.appendChild(img);\n" \
+ " img.src = URL.createObjectURL(new Blob([ text.buffer ], { type: options.pictureType }));\n" \
+ " }\n" \
+ " }\n" \
+ " document.getElementById('Messages').appendChild(message);\n" \
+ " message.scrollIntoView();\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This is a keydown event handler, which allows that you can just press enter instead of clicking the 'send' button\n" \
+ " */\n" \
+ " function chat_onKeyDown(event)\n" \
+ " {\n" \
+ " if(event.key == 'Enter')\n" \
+ " chat_onSendClicked();\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This is the code to send a message or command, when clicking the 'send' button\n" \
+ " */\n" \
+ " function chat_onSendClicked(event)\n" \
+ " {\n" \
+ " let message = document.getElementById('InputMessage').value;\n" \
+ " if(message.length == 0)\n" \
+ " return;\n" \
+ " if(message.substr(0, 1) == '/')\n" \
+ " {\n" \
+ " /* command */ \n" \
+ " let match;\n" \
+ " if(/^\\/disconnect\\s*$/.test(message))\n" \
+ " {\n" \
+ " socket.close(1000);\n" \
+ " }\n" \
+ " else if((match = /^\\/m\\s+(\\S+)\\s+/.exec(message)))\n" \
+ " {\n" \
+ " message = message.substr(match[0].length);\n" \
+ " let userId = chat_getUserIdByName(match[1]);\n" \
+ " if(userId !== null)\n" \
+ " {\n" \
+ " socket.send(`private|${userId}|${message}`);\n" \
+ " }\n" \
+ " else\n" \
+ " {\n" \
+ " chat_addMessage(`Unknown user \"${match[1]}\" for private message: ${message}`, { type: 'error' });\n" \
+ " }\n" \
+ " }\n" \
+ " else if((match = /^\\/ping\\s+(\\S+)\\s*$/.exec(message)))\n" \
+ " {\n" \
+ " let userId = chat_getUserIdByName(match[1]);\n" \
+ " if(userId !== null)\n" \
+ " {\n" \
+ " socket.send(`ping|${userId}|`);\n" \
+ " }\n" \
+ " else\n" \
+ " {\n" \
+ " chat_addMessage(`Unknown user \"${match[1]}\" for ping`, { type: 'error' });\n" \
+ " }\n" \
+ " }\n" \
+ " else if((match = /^\\/name\\s+(\\S+)\\s*$/.exec(message)))\n" \
+ " {\n" \
+ " socket.send(`name||${match[1]}`);\n" \
+ " }\n" \
+ " else\n" \
+ " {\n" \
+ " chat_addMessage(`Unsupported command or invalid syntax: ${message}`, { type: 'error' });\n" \
+ " }\n" \
+ " }\n" \
+ " else\n" \
+ " {\n" \
+ " /* regular chat message to the selected user */ \n" \
+ " let selectedUser = document.querySelector('div#Users > div.selected');\n" \
+ " let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \
+ " if(selectedUserId == 0)\n" \
+ " socket.send(`||${message}`);\n" \
+ " else\n" \
+ " socket.send(`private|${selectedUserId}|${message}`);\n" \
+ " }\n" \
+ " document.getElementById('InputMessage').value = '';\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This is the event when the user hits the 'image' button\n" \
+ " */\n" \
+ " function chat_onImageClicked(event)\n" \
+ " {\n" \
+ " document.getElementById('InputImage').click();\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This is the event when the user selected an image.\n" \
+ " The image will be read with the FileReader (allowed in web, because the user selected the file).\n" \
+ " */\n" \
+ " function chat_onImageSelected(event)\n" \
+ " {\n" \
+ " let file = event.target.files[0];\n" \
+ " if(!file || !/^image\\/" "/.test(file.type))\n" \
+ " return;\n" \
+ " let selectedUser = document.querySelector('div#Users > div.selected');\n" \
+ " let selectedUserId = parseInt(selectedUser.getAttribute('data-user') || '0', 10);\n" \
+ " let reader = new FileReader();\n" \
+ " reader.onload = function(event) {\n" \
+ " chat_onImageRead(event, file.type, selectedUserId);\n" \
+ " };\n" \
+ " reader.readAsArrayBuffer(file);\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This is the event when the user selected image has been read.\n" \
+ " This will add our chat protocol prefix and send it via the websocket.\n" \
+ " */\n" \
+ " function chat_onImageRead(event, fileType, selectedUserId)\n" \
+ " {\n" \
+ " let encoder = new TextEncoder();\n" \
+ " let prefix = ((selectedUserId == 0 ? '||' : `private|${selectedUserId}|`) + fileType + '|');\n" \
+ " prefix = encoder.encode(prefix);\n" \
+ " let byteData = new Uint8Array(event.target.result);\n" \
+ " let totalLength = prefix.length + byteData.length;\n" \
+ " let resultByteData = new Uint8Array(totalLength);\n" \
+ " resultByteData.set(prefix, 0);\n" \
+ " resultByteData.set(byteData, prefix.length);\n" \
+ " socket.send(resultByteData);\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This is the event when the user clicked a name in the user list.\n" \
+ " This is useful to send private messages or images without needing to add the /m prefix.\n" \
+ " */\n" \
+ " function chat_onUserClicked(event, selectedUserId)\n" \
+ " {\n" \
+ " let newSelected = event.target.closest('div#Users > div');\n" \
+ " if(newSelected === null)\n" \
+ " return;\n" \
+ " for(let div of this.querySelectorAll(':scope > div.selected'))\n" \
+ " div.classList.remove('selected');\n" \
+ " newSelected.classList.add('selected');\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This functions returns the current id of a user identified by its name.\n" \
+ " */\n" \
+ " function chat_getUserIdByName(name)\n" \
+ " {\n" \
+ " let nameUpper = name.toUpperCase();\n" \
+ " for(let pair of connectedUsers)\n" \
+ " {\n" \
+ " if(pair[1].toUpperCase() == nameUpper)\n" \
+ " return pair[0];\n" \
+ " }\n" \
+ " return null;\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This functions clears the entire user list (needed for reconnecting).\n" \
+ " */\n" \
+ " function chat_clearUserList()\n" \
+ " {\n" \
+ " let users = document.getElementById('Users');\n" \
+ " for(let div of users.querySelectorAll(':scope > div'))\n" \
+ " {\n" \
+ " if(div.getAttribute('data-user') === '0')\n" \
+ " {\n" \
+ " div.classList.add('selected');\n" \
+ " }\n" \
+ " else\n" \
+ " {\n" \
+ " div.parentNode.removeChild(div);\n" \
+ " }\n" \
+ " }\n" \
+ " return null;\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This is the event when the socket has established a connection.\n" \
+ " This will initialize an empty chat and enable the controls.\n" \
+ " */\n" \
+ " function socket_onopen(event)\n" \
+ " {\n" \
+ " connectedUsers.clear();\n" \
+ " chat_clearUserList();\n" \
+ " chat_addMessage('Connected!', { type: 'system' });\n" \
+ " document.getElementById('InputMessage').disabled = false;\n" \
+ " document.getElementById('InputMessageButton').disabled = false;\n" \
+ " document.getElementById('InputImageButton').disabled = false;\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This is the event when the socket has been closed.\n" \
+ " This will lock the controls.\n" \
+ " */\n" \
+ " function socket_onclose(event)\n" \
+ " {\n" \
+ " chat_addMessage('Connection closed!', { type: 'system', reconnect: true });\n" \
+ " document.getElementById('InputMessage').disabled = true;\n" \
+ " document.getElementById('InputMessageButton').disabled = true;\n" \
+ " document.getElementById('InputImageButton').disabled = true;\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This is the event when the socket reported an error.\n" \
+ " This will just make an output.\n" \
+ " In the web browser console (F12 on many browsers) will show you more detailed error information.\n" \
+ " */\n" \
+ " function socket_onerror(event)\n" \
+ " {\n" \
+ " console.error('WebSocket error reported: ', event);\n" \
+ " chat_addMessage('The socket reported an error!', { type: 'error' });\n" \
+ " }\n" \
+ "\n" \
+ " /**\n" \
+ " This is the event when the socket has received a message.\n" \
+ " This will parse the message and execute the corresponding command (or add the message).\n" \
+ " */\n" \
+ " function socket_onmessage(event)\n" \
+ " {\n" \
+ " if(typeof(event.data) === 'string')\n" \
+ " {\n" \
+ " /* text message or command */ \n" \
+ " let message = event.data.split('|', 3);\n" \
+ " switch(message[0])\n" \
+ " {\n" \
+ " case 'userinit':\n" \
+ " connectedUsers.set(message[1], message[2]);\n" \
+ " {\n" \
+ " let users = document.getElementById('Users');\n" \
+ " let div = document.createElement('div');\n" \
+ " users.appendChild(div);\n" \
+ " div.innerText = message[2];\n" \
+ " div.setAttribute('data-user', message[1]);\n" \
+ " }\n" \
+ " break;\n" \
+ " case 'useradd':\n" \
+ " connectedUsers.set(message[1], message[2]);\n" \
+ " chat_addMessage(`The user '${message[2]}' has joined our lovely chatroom.`, { type: 'moderator' });\n" \
+ " {\n" \
+ " let users = document.getElementById('Users');\n" \
+ " let div = document.createElement('div');\n" \
+ " users.appendChild(div);\n" \
+ " div.innerText = message[2];\n" \
+ " div.setAttribute('data-user', message[1]);\n" \
+ " }\n" \
+ " break;\n" \
+ " case 'userdel':\n" \
+ " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has left our chatroom. We will miss you.`, { type: 'moderator' });\n" \
+ " connectedUsers.delete(message[1]);\n" \
+ " {\n" \
+ " let users = document.getElementById('Users');\n" \
+ " let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \
+ " if(div !== null)\n" \
+ " {\n" \
+ " users.removeChild(div);\n" \
+ " if(div.classList.contains('selected'))\n" \
+ " users.querySelector('div[data-user=\\'0\\']').classList.add('selected');\n" \
+ " }\n" \
+ " }\n" \
+ " break;\n" \
+ " case 'username':\n" \
+ " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has changed his name to '${message[2]}'.`, { type: 'moderator' });\n" \
+ " connectedUsers.set(message[1], message[2]);\n" \
+ " {\n" \
+ " let users = document.getElementById('Users');\n" \
+ " let div = users.querySelector(`div[data-user='${message[1]}']`);\n" \
+ " if(div !== null)\n" \
+ " {\n" \
+ " div.innerText = message[2];\n" \
+ " }\n" \
+ " }\n" \
+ " break;\n" \
+ " case 'ping':\n" \
+ " chat_addMessage(`The user '${connectedUsers.get(message[1])}' has a ping of ${message[2]} ms.`, { type: 'moderator' });\n" \
+ " break;\n" \
+ " default:\n" \
+ " chat_addMessage(message[2], { type: message[0], from: connectedUsers.get(message[1]) });\n" \
+ " break;\n" \
+ " }\n" \
+ " }\n" \
+ " else\n" \
+ " {\n" \
+ " /* We received a binary frame, which means a picture here */ \n" \
+ " let byteData = new Uint8Array(event.data);\n" \
+ " let decoder = new TextDecoder();\n" \
+ " let message = [ ];\n" \
+ " /* message type */ \n" \
+ " let j = 0;\n" \
+ " let i = byteData.indexOf(0x7C, j); /* | = 0x7C;*/ \n"\
+ " if(i < 0)\n" \
+ " return;\n" \
+ " message.push(decoder.decode(byteData.slice(0, i)));\n" \
+ " /* picture from */ \n" \
+ " j = i + 1;\n" \
+ " i = byteData.indexOf(0x7C, j);\n" \
+ " if(i < 0)\n" \
+ " return;\n" \
+ " message.push(decoder.decode(byteData.slice(j, i)));\n" \
+ " /* picture encoding */ \n" \
+ " j = i + 1;\n" \
+ " i = byteData.indexOf(0x7C, j);\n" \
+ " if(i < 0)\n" \
+ " return;\n" \
+ " message.push(decoder.decode(byteData.slice(j, i)));\n" \
+ " /* picture */ \n" \
+ " byteData = byteData.slice(i + 1);\n" \
+ " chat_addMessage(byteData, { type: message[0], from: connectedUsers.get(message[1]), pictureType: message[2] });\n" \
+ " }\n" \
+ " }\n" \
+ "</script>" \
+ "</head>" \
+ "<body><noscript>Please enable JavaScript to test the libmicrohttpd Websocket chatserver demo!</noscript></body>" \
+ "</html>"
+
+#define PAGE_NOT_FOUND \
+ "404 Not Found"
+
+#define PAGE_INVALID_WEBSOCKET_REQUEST \
+ "Invalid WebSocket request!"
+
+/**
+ * This struct is used to keep the data of a connected chat user.
+ * It is passed to the socket-receive thread (connecteduser_receive_messages) as well as to
+ * the socket-send thread (connecteduser_send_messages).
+ * It can also be accessed via the global array users (mutex protected).
+ */
+struct ConnectedUser
+{
+ /* the TCP/IP socket for reading/writing */
+ MHD_socket fd;
+ /* the UpgradeResponseHandle of libmicrohttpd (needed for closing the socket) */
+ struct MHD_UpgradeResponseHandle *urh;
+ /* the websocket encode/decode stream */
+ struct MHD_WebSocketStream *ws;
+ /* the possibly read data at the start (only used once) */
+ char *extra_in;
+ size_t extra_in_size;
+ /* the unique user id (counting from 1, ids will never be re-used) */
+ size_t user_id;
+ /* the current user name */
+ char *user_name;
+ size_t user_name_len;
+ /* the zero-based index of the next message;
+ may be decremented when old messages are deleted */
+ size_t next_message_index;
+ /* specifies whether the websocket shall be closed (1) or not (0) */
+ int disconnect;
+ /* condition variable to wake up the sender of this connection */
+ pthread_cond_t wake_up_sender;
+ /* mutex to ensure that no send actions are mixed
+ (sending can be done by send and recv thread;
+ may not be simultaneously locked with chat_mutex by the same thread) */
+ pthread_mutex_t send_mutex;
+ /* specifies whether a ping shall be executed (1), is being executed (2) or
+ no ping is pending (0) */
+ int ping_status;
+ /* the start time of the ping, if a ping is running */
+ struct timespec ping_start;
+ /* the message used for the ping (must match the pong response)*/
+ char ping_message[128];
+ /* the length of the ping message (may not exceed 125) */
+ size_t ping_message_len;
+ /* the numeric ping message suffix to detect ping messages, which are too old */
+ int ping_counter;
+};
+
+/**
+ * A single message, which has been send via the chat.
+ * This can be text, an image or a command.
+ */
+struct Message
+{
+ /* The user id of the sender. This is 0 if it is a system message- */
+ size_t from_user_id;
+ /* The user id of the recipient. This is 0 if every connected user shall receive it */
+ size_t to_user_id;
+ /* The data of the message. */
+ char *data;
+ size_t data_len;
+ /* Specifies whether the data is UTF-8 encoded text (0) or binary data (1) */
+ int is_binary;
+};
+
+/* the unique user counter for new users (only accessed by main thread) */
+size_t unique_user_id = 0;
+
+/* the chat data (users and messages; may be accessed by all threads, but is protected by mutex) */
+pthread_mutex_t chat_mutex;
+struct ConnectedUser **users = NULL;
+size_t user_count = 0;
+struct Message **messages = NULL;
+size_t message_count = 0;
+/* specifies whether all websockets must close (1) or not (0) */
+volatile int disconnect_all = 0;
+/* a counter for cleaning old messages (each 10 messages we will try to clean the list */
+int clean_count = 0;
+#define CLEANUP_LIMIT 10
+
+/**
+ * Change socket to blocking.
+ *
+ * @param fd the socket to manipulate
+ */
+static void
+make_blocking (MHD_socket fd)
+{
+#if defined(MHD_POSIX_SOCKETS)
+ int flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (-1 == flags)
+ abort ();
+ if ((flags & ~O_NONBLOCK) != flags)
+ if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+ abort ();
+#elif defined(MHD_WINSOCK_SOCKETS)
+ unsigned long flags = 0;
+
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ abort ();
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+
+/**
+ * Sends all data of the given buffer via the TCP/IP socket
+ *
+ * @param fd The TCP/IP socket which is used for sending
+ * @param buf The buffer with the data to send
+ * @param len The length in bytes of the data in the buffer
+ */
+static void
+send_all (struct ConnectedUser *cu,
+ const char *buf,
+ size_t len)
+{
+ ssize_t ret;
+ size_t off;
+
+ if (0 == pthread_mutex_lock (&cu->send_mutex))
+ {
+ for (off = 0; off < len; off += ret)
+ {
+ ret = send (cu->fd,
+ &buf[off],
+ (int) (len - off),
+ 0);
+ if (0 > ret)
+ {
+ if (EAGAIN == errno)
+ {
+ ret = 0;
+ continue;
+ }
+ break;
+ }
+ if (0 == ret)
+ break;
+ }
+ pthread_mutex_unlock (&cu->send_mutex);
+ }
+}
+
+
+/**
+ * Adds a new chat message to the list of messages.
+ *
+ * @param from_user_id the user id of the sender (0 means system)
+ * @param to_user_id the user id of the recipiend (0 means everyone)
+ * @param data the data to send (UTF-8 text or binary; will be copied)
+ * @param data_len the length of the data to send
+ * @param is_binary specifies whether the data is UTF-8 text (0) or binary (1)
+ * @param needs_lock specifies whether the caller has already locked the global chat mutex (0) or
+ * if this procedure needs to lock it (1)
+ *
+ * @return 0 on success, other values on error
+ */
+static int
+chat_addmessage (size_t from_user_id,
+ size_t to_user_id,
+ char *data,
+ size_t data_len,
+ int is_binary,
+ int needs_lock)
+{
+ /* allocate the buffer and fill it with data */
+ struct Message *message = (struct Message *) malloc (sizeof (struct Message));
+ if (NULL == message)
+ return 1;
+
+ memset (message, 0, sizeof (struct Message));
+ message->from_user_id = from_user_id;
+ message->to_user_id = to_user_id;
+ message->is_binary = is_binary;
+ message->data_len = data_len;
+ message->data = malloc (data_len + 1);
+ if (NULL == message->data)
+ {
+ free (message);
+ return 1;
+ }
+ memcpy (message->data, data, data_len);
+ message->data[data_len] = 0;
+
+ /* lock the global mutex if needed */
+ if (0 != needs_lock)
+ {
+ if (0 != pthread_mutex_lock (&chat_mutex))
+ return 1;
+ }
+
+ /* add the new message to the global message list */
+ size_t message_count_ = message_count + 1;
+ struct Message **messages_ = (struct Message **) realloc (messages,
+ message_count_
+ * sizeof (struct
+ Message *));
+ if (NULL == messages_)
+ {
+ free (message);
+ if (0 != needs_lock)
+ pthread_mutex_unlock (&chat_mutex);
+ return 1;
+ }
+ messages_[message_count] = message;
+ messages = messages_;
+ message_count = message_count_;
+
+ /* inform the sender threads about the new message */
+ for (size_t i = 0; i < user_count; ++i)
+ pthread_cond_signal (&users[i]->wake_up_sender);
+
+ /* unlock the global mutex if needed */
+ if (0 != needs_lock)
+ {
+ if (0 != needs_lock)
+ pthread_mutex_unlock (&chat_mutex);
+ }
+ return 0;
+}
+
+
+/**
+ * Cleans up old messages
+ *
+ * @param needs_lock specifies whether the caller has already locked the global chat mutex (0) or
+ * if this procedure needs to lock it (1)
+ * @return 0 on success, other values on error
+ */
+static int
+chat_clearmessages (int needs_lock)
+{
+ /* lock the global mutex if needed */
+ if (0 != needs_lock)
+ {
+ if (0 != pthread_mutex_lock (&chat_mutex))
+ return 1;
+ }
+
+ /* update the clean counter and check whether we need cleaning */
+ ++clean_count;
+ if (CLEANUP_LIMIT > clean_count)
+ {
+ /* no cleanup required */
+ if (0 != needs_lock)
+ {
+ pthread_mutex_unlock (&chat_mutex);
+ }
+ return 0;
+ }
+ clean_count = 0;
+
+ /* check whether we got any messages (without them no cleaning is required */
+ if (0 < message_count)
+ {
+ /* then check whether we got any connected users */
+ if (0 < user_count)
+ {
+ /* determine the minimum index for the next message of all connected users */
+ size_t min_message = users[0]->next_message_index;
+ for (size_t i = 1; i < user_count; ++i)
+ {
+ if (min_message > users[i]->next_message_index)
+ min_message = users[i]->next_message_index;
+ }
+ if (0 < min_message)
+ {
+ /* remove all messages with index below min_message and update
+ the message indices of the users */
+ for (size_t i = 0; i < min_message; ++i)
+ {
+ free (messages[i]->data);
+ free (messages[i]);
+ }
+ for (size_t i = min_message; i < message_count; ++i)
+ messages[i - min_message] = messages[i];
+ message_count -= min_message;
+ for (size_t i = 0; i < user_count; ++i)
+ users[i]->next_message_index -= min_message;
+ }
+ }
+ else
+ {
+ /* without connected users, simply remove all messages */
+ for (size_t i = 0; i < message_count; ++i)
+ {
+ free (messages[i]->data);
+ free (messages[i]);
+ }
+ free (messages);
+ messages = NULL;
+ message_count = 0;
+ }
+ }
+
+ /* unlock the global mutex if needed */
+ if (0 != needs_lock)
+ {
+ pthread_mutex_unlock (&chat_mutex);
+ }
+ return 0;
+}
+
+
+/**
+ * Adds a new chat user to the global user list.
+ * This will be called at the start of connecteduser_receive_messages.
+ *
+ * @param cu The connected user
+ * @return 0 on success, other values on error
+ */
+static int
+chat_adduser (struct ConnectedUser *cu)
+{
+ /* initialize the notification message of the new user */
+ char user_index[32];
+ snprintf (user_index, 32, "%d", (int) cu->user_id);
+ size_t user_index_len = strlen (user_index);
+ size_t data_len = user_index_len + cu->user_name_len + 9;
+ char *data = (char *) malloc (data_len + 1);
+ if (NULL == data)
+ return 1;
+ strcpy (data, "useradd|");
+ strcat (data, user_index);
+ strcat (data, "|");
+ strcat (data, cu->user_name);
+
+ /* lock the mutex */
+ if (0 != pthread_mutex_lock (&chat_mutex))
+ {
+ free (data);
+ return 1;
+ }
+ /* inform the other chat users about the new user */
+ if (0 != chat_addmessage (0,
+ 0,
+ data,
+ data_len,
+ 0,
+ 0))
+ {
+ free (data);
+ pthread_mutex_unlock (&chat_mutex);
+ return 1;
+ }
+ free (data);
+
+ /* add the new user to the list */
+ size_t user_count_ = user_count + 1;
+ struct ConnectedUser **users_ = (struct ConnectedUser **) realloc (users,
+ user_count_
+ * sizeof (
+ struct
+ ConnectedUser
+ *));
+ if (NULL == users_)
+ {
+ /* realloc failed */
+ pthread_mutex_unlock (&chat_mutex);
+ return 1;
+ }
+ users_[user_count] = cu;
+ users = users_;
+ user_count = user_count_;
+
+ /* Initialize the next message index to the current message count. */
+ /* This will skip all old messages for this new connected user. */
+ cu->next_message_index = message_count;
+
+ /* unlock the mutex */
+ pthread_mutex_unlock (&chat_mutex);
+ return 0;
+}
+
+
+/**
+ * Removes a chat user from the global user list.
+ *
+ * @param cu The connected user
+ * @return 0 on success, other values on error
+ */
+static int
+chat_removeuser (struct ConnectedUser *cu)
+{
+ char user_index[32];
+
+ /* initialize the chat message for the removed user */
+ snprintf (user_index, 32, "%d", (int) cu->user_id);
+ size_t user_index_len = strlen (user_index);
+ size_t data_len = user_index_len + 9;
+ char *data = (char *) malloc (data_len + 1);
+ if (NULL == data)
+ return 1;
+ strcpy (data, "userdel|");
+ strcat (data, user_index);
+ strcat (data, "|");
+
+ /* lock the mutex */
+ if (0 != pthread_mutex_lock (&chat_mutex))
+ {
+ free (data);
+ return 1;
+ }
+ /* inform the other chat users that the user is gone */
+ int got_error = 0;
+ if (0 != chat_addmessage (0, 0, data, data_len, 0, 0))
+ {
+ free (data);
+ got_error = 1;
+ }
+
+ /* remove the user from the list */
+ int found = 0;
+ for (size_t i = 0; i < user_count; ++i)
+ {
+ if (cu == users[i])
+ {
+ found = 1;
+ for (size_t j = i + 1; j < user_count; ++j)
+ {
+ users[j - 1] = users[j];
+ }
+ --user_count;
+ break;
+ }
+ }
+ if (0 == found)
+ got_error = 1;
+
+ /* unlock the mutex */
+ pthread_mutex_unlock (&chat_mutex);
+
+ return got_error;
+}
+
+
+/**
+ * Renames a chat user
+ *
+ * @param cu The connected user
+ * @param new_name The new user name. On success this pointer will be taken.
+ * @param new_name_len The length of the new name
+ * @return 0 on success, other values on error. 2 means name already in use.
+ */
+static int
+chat_renameuser (struct ConnectedUser *cu,
+ char *new_name,
+ size_t new_name_len)
+{
+ /* lock the mutex */
+ if (0 != pthread_mutex_lock (&chat_mutex))
+ {
+ return 1;
+ }
+
+ /* check whether the name is already in use */
+ for (size_t i = 0; i < user_count; ++i)
+ {
+ if (cu != users[i])
+ {
+ if ((users[i]->user_name_len == new_name_len) &&
+ (0 == strcasecmp (users[i]->user_name, new_name)))
+ {
+ pthread_mutex_unlock (&chat_mutex);
+ return 2;
+ }
+ }
+ }
+
+ /* generate the notification message */
+ char user_index[32];
+ snprintf (user_index, 32, "%d", (int) cu->user_id);
+ size_t user_index_len = strlen (user_index);
+ size_t data_len = user_index_len + new_name_len + 10;
+ char *data = (char *) malloc (data_len + 1);
+ if (NULL == data)
+ return 1;
+ strcpy (data, "username|");
+ strcat (data, user_index);
+ strcat (data, "|");
+ strcat (data, new_name);
+
+ /* inform the other chat users about the new name */
+ if (0 != chat_addmessage (0, 0, data, data_len, 0, 0))
+ {
+ free (data);
+ pthread_mutex_unlock (&chat_mutex);
+ return 1;
+ }
+ free (data);
+
+ /* accept the new user name */
+ free (cu->user_name);
+ cu->user_name = new_name;
+ cu->user_name_len = new_name_len;
+
+ /* unlock the mutex */
+ pthread_mutex_unlock (&chat_mutex);
+
+ return 0;
+}
+
+
+/**
+* Parses received data from the TCP/IP socket with the websocket stream
+*
+* @param cu The connected user
+* @param new_name The new user name
+* @param new_name_len The length of the new name
+* @return 0 on success, other values on error
+*/
+static int
+connecteduser_parse_received_websocket_stream (struct ConnectedUser *cu,
+ char *buf,
+ size_t buf_len)
+{
+ size_t buf_offset = 0;
+ while (buf_offset < buf_len)
+ {
+ size_t new_offset = 0;
+ char *frame_data = NULL;
+ size_t frame_len = 0;
+ int status = MHD_websocket_decode (cu->ws,
+ buf + buf_offset,
+ buf_len - buf_offset,
+ &new_offset,
+ &frame_data,
+ &frame_len);
+ if (0 > status)
+ {
+ /* an error occurred and the connection must be closed */
+ if (NULL != frame_data)
+ {
+ /* depending on the WebSocket flag */
+ /* MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR */
+ /* close frames might be generated on errors */
+ send_all (cu,
+ frame_data,
+ frame_len);
+ MHD_websocket_free (cu->ws, frame_data);
+ }
+ return 1;
+ }
+ else
+ {
+ buf_offset += new_offset;
+
+ if (0 < status)
+ {
+ /* the frame is complete */
+ switch (status)
+ {
+ case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
+ case MHD_WEBSOCKET_STATUS_BINARY_FRAME:
+ /**
+ * a text or binary frame has been received.
+ * in this chat server example we use a simple protocol where
+ * the JavaScript added a prefix like "<command>|<to_user_id>|data".
+ * Some examples:
+ * "||test" means a regular chat message to everyone with the message "test".
+ * "private|1|secret" means a private chat message to user with id 1 with the message "secret".
+ * "name||MyNewName" means that the user requests a rename to "MyNewName"
+ * "ping|1|" means that the user with id 1 shall get a ping
+ *
+ * Binary data is handled here like text data.
+ * The difference in the data is only checked by the JavaScript.
+ */
+ {
+ size_t command = 1000;
+ size_t from_user_id = cu->user_id;
+ size_t to_user_id = 0;
+ size_t i;
+
+ /* parse the command */
+ for (i = 0; i < frame_len; ++i)
+ {
+ if ('|' == frame_data[i])
+ {
+ frame_data[i] = 0;
+ ++i;
+ break;
+ }
+ }
+ if (0 < i)
+ {
+ if (i == 1)
+ {
+ /* no command means regular message */
+ command = 0;
+ }
+ else if (0 == strcasecmp (frame_data, "private"))
+ {
+ /* private means private message */
+ command = 1;
+ }
+ else if (0 == strcasecmp (frame_data, "name"))
+ {
+ /* name means chat user rename */
+ command = 2;
+ }
+ else if (0 == strcasecmp (frame_data, "ping"))
+ {
+ /* ping means a ping request */
+ command = 3;
+ }
+ else
+ {
+ /* no other commands supported, so this means invalid */
+ command = 1000;
+ }
+ }
+
+ /* parse the to_user_id, if given */
+ size_t j = i;
+ for (; j < frame_len; ++j)
+ {
+ if ('|' == frame_data[j])
+ {
+ frame_data[j] = 0;
+ ++j;
+ break;
+ }
+ }
+ if (i + 1 < j)
+ {
+ to_user_id = (size_t) atoi (frame_data + i);
+ }
+
+ /* decide via the command what action to do */
+ if (frame_len >= j)
+ {
+ int is_binary = (MHD_WEBSOCKET_STATUS_BINARY_FRAME == status ? 1 :
+ 0);
+ switch (command)
+ {
+ case 0:
+ /* regular chat message */
+ {
+ /**
+ * Generate the message for the message list.
+ * Regular chat messages get the command "regular".
+ * After that we add the from_user_id, followed by the content.
+ * The content must always be copied with memcpy instead of strcat,
+ * because the data (binary as well as UTF-8 encoded) is allowed
+ * to contain the NUL character.
+ * However we will add a terminating NUL character,
+ * which is not included in the data length
+ * (and thus will not be send to the recipients).
+ * This is useful for debugging with an IDE.
+ */
+ char user_index[32];
+ snprintf (user_index, 32, "%d", (int) cu->user_id);
+ size_t user_index_len = strlen (user_index);
+ size_t data_len = user_index_len + frame_len - j + 9;
+ char *data = (char *) malloc (data_len + 1);
+ if (NULL != data)
+ {
+ strcpy (data, "regular|");
+ strcat (data, user_index);
+ strcat (data, "|");
+ size_t offset = strlen (data);
+ memcpy (data + offset,
+ frame_data + j,
+ frame_len - j);
+ data[data_len] = 0;
+
+ /* add the chat message to the global list */
+ chat_addmessage (from_user_id,
+ 0,
+ data,
+ data_len,
+ is_binary,
+ 1);
+ free (data);
+ }
+ }
+ break;
+
+ case 1:
+ /* private chat message */
+ if (0 != to_user_id)
+ {
+ /**
+ * Generate the message for the message list.
+ * This is similar to the code for regular messages above.
+ * The difference is the prefix "private"
+ */
+ char user_index[32];
+ snprintf (user_index, 32, "%d", (int) cu->user_id);
+ size_t user_index_len = strlen (user_index);
+ size_t data_len = user_index_len + frame_len - j + 9;
+ char *data = (char *) malloc (data_len + 1);
+ if (NULL != data)
+ {
+
+ strcpy (data, "private|");
+ strcat (data, user_index);
+ strcat (data, "|");
+ size_t offset = strlen (data);
+ memcpy (data + offset,
+ frame_data + j,
+ frame_len - j);
+ data[data_len] = 0;
+
+ /* add the chat message to the global list */
+ chat_addmessage (from_user_id,
+ to_user_id,
+ data,
+ data_len,
+ is_binary,
+ 1);
+ free (data);
+ }
+ }
+ break;
+
+ case 2:
+ /* rename */
+ {
+ /* check whether the new name is valid and allocate a new buffer for it */
+ size_t new_name_len = frame_len - j;
+ if (0 == new_name_len)
+ {
+ chat_addmessage (0,
+ from_user_id,
+ "error||Your new name is invalid. You haven't been renamed.",
+ 58,
+ 0,
+ 1);
+ break;
+ }
+ char *new_name = (char *) malloc (new_name_len + 1);
+ if (NULL == new_name)
+ {
+ chat_addmessage (0,
+ from_user_id,
+ "error||Error while renaming. You haven't been renamed.",
+ 54,
+ 0,
+ 1);
+ break;
+ }
+ new_name[new_name_len] = 0;
+ for (size_t k = 0; k < new_name_len; ++k)
+ {
+ char c = frame_data[j + k];
+ if ((32 >= c) || (c >= 127))
+ {
+ free (new_name);
+ new_name = NULL;
+ chat_addmessage (0,
+ from_user_id,
+ "error||Your new name contains invalid characters. You haven't been renamed.",
+ 75,
+ 0,
+ 1);
+ break;
+ }
+ new_name[k] = c;
+ }
+ if (NULL == new_name)
+ break;
+
+ /* rename the user */
+ int rename_result = chat_renameuser (cu,
+ new_name,
+ new_name_len);
+ if (0 != rename_result)
+ {
+ /* the buffer will only be freed if no rename was possible */
+ free (new_name);
+ if (2 == rename_result)
+ {
+ chat_addmessage (0,
+ from_user_id,
+ "error||Your new name is already in use by another user. You haven't been renamed.",
+ 81,
+ 0,
+ 1);
+ }
+ else
+ {
+ chat_addmessage (0,
+ from_user_id,
+ "error||Error while renaming. You haven't been renamed.",
+ 54,
+ 0,
+ 1);
+ }
+ }
+ }
+ break;
+
+ case 3:
+ /* ping */
+ {
+ if (0 == pthread_mutex_lock (&chat_mutex))
+ {
+ /* check whether the to_user exists */
+ struct ConnectedUser *ping_user = NULL;
+ for (size_t k = 0; k < user_count; ++k)
+ {
+ if (users[k]->user_id == to_user_id)
+ {
+ ping_user = users[k];
+ break;
+ }
+ }
+ if (NULL == ping_user)
+ {
+ chat_addmessage (0,
+ from_user_id,
+ "error||Couldn't find the specified user for pinging.",
+ 52,
+ 0,
+ 0);
+ }
+ else
+ {
+ /* if pinging is requested, */
+ /* we mark the user and inform the sender about this */
+ if (0 == ping_user->ping_status)
+ {
+ ping_user->ping_status = 1;
+ pthread_cond_signal (&ping_user->wake_up_sender);
+ }
+ }
+ pthread_mutex_unlock (&chat_mutex);
+ }
+ else
+ {
+ chat_addmessage (0,
+ from_user_id,
+ "error||Error while pinging.",
+ 27,
+ 0,
+ 1);
+ }
+ }
+ break;
+
+ default:
+ /* invalid command */
+ chat_addmessage (0,
+ from_user_id,
+ "error||You sent an invalid command.",
+ 35,
+ 0,
+ 1);
+ break;
+ }
+ }
+ }
+ MHD_websocket_free (cu->ws,
+ frame_data);
+ return 0;
+
+ case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
+ /* if we receive a close frame, we will respond with one */
+ MHD_websocket_free (cu->ws,
+ frame_data);
+ {
+ char *result = NULL;
+ size_t result_len = 0;
+ int er = MHD_websocket_encode_close (cu->ws,
+ MHD_WEBSOCKET_CLOSEREASON_REGULAR,
+ NULL,
+ 0,
+ &result,
+ &result_len);
+ if (MHD_WEBSOCKET_STATUS_OK == er)
+ {
+ send_all (cu,
+ result,
+ result_len);
+ MHD_websocket_free (cu->ws, result);
+ }
+ }
+ return 1;
+
+ case MHD_WEBSOCKET_STATUS_PING_FRAME:
+ /* if we receive a ping frame, we will respond */
+ /* with the corresponding pong frame */
+ {
+ char *pong = NULL;
+ size_t pong_len = 0;
+ int er = MHD_websocket_encode_pong (cu->ws,
+ frame_data,
+ frame_len,
+ &pong,
+ &pong_len);
+
+ MHD_websocket_free (cu->ws,
+ frame_data);
+ if (MHD_WEBSOCKET_STATUS_OK == er)
+ {
+ send_all (cu,
+ pong,
+ pong_len);
+ MHD_websocket_free (cu->ws,
+ pong);
+ }
+ }
+ return 0;
+
+ case MHD_WEBSOCKET_STATUS_PONG_FRAME:
+ /* if we receive a pong frame, */
+ /* we will check whether we requested this frame and */
+ /* whether it is the last requested pong */
+ if (2 == cu->ping_status)
+ {
+ cu->ping_status = 0;
+ struct timespec now;
+ timespec_get (&now, TIME_UTC);
+ if ((cu->ping_message_len == frame_len) &&
+ (0 == strcmp (frame_data,
+ cu->ping_message)))
+ {
+ int ping = (int) (((int64_t) (now.tv_sec
+ - cu->ping_start.tv_sec)) * 1000
+ + ((int64_t) (now.tv_nsec
+ - cu->ping_start.tv_nsec))
+ / 1000000);
+ char result_text[240];
+ strcpy (result_text,
+ "ping|");
+ snprintf (result_text + 5, 235, "%d", (int) cu->user_id);
+ strcat (result_text,
+ "|");
+ snprintf (result_text + strlen (result_text), 240 - strlen (
+ result_text), "%d", (int) ping);
+ chat_addmessage (0,
+ 0,
+ result_text,
+ strlen (result_text),
+ 0,
+ 1);
+ }
+ }
+ MHD_websocket_free (cu->ws,
+ frame_data);
+ return 0;
+
+ default:
+ /* This case should really never happen, */
+ /* because there are only five types of (finished) websocket frames. */
+ /* If it is ever reached, it means that there is memory corruption. */
+ MHD_websocket_free (cu->ws,
+ frame_data);
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Sends messages from the message list over the TCP/IP socket
+ * after encoding it with the websocket stream.
+ * This is also used for server-side actions,
+ * because the thread for receiving messages waits for
+ * incoming data and cannot be woken up.
+ * But the sender thread can be woken up easily.
+ *
+ * @param cls The connected user
+ * @return Always NULL
+ */
+static void *
+connecteduser_send_messages (void *cls)
+{
+ struct ConnectedUser *cu = cls;
+
+ /* the main loop of sending messages requires to lock the mutex */
+ if (0 == pthread_mutex_lock (&chat_mutex))
+ {
+ for (;;)
+ {
+ /* loop while not all messages processed */
+ int all_messages_read = 0;
+ while (0 == all_messages_read)
+ {
+ if (1 == disconnect_all)
+ {
+ /* the application closes and want that we disconnect all users */
+ struct MHD_UpgradeResponseHandle *urh = cu->urh;
+ if (NULL != urh)
+ {
+ /* Close the TCP/IP socket. */
+ /* This will also wake-up the waiting receive-thread for this connected user. */
+ cu->urh = NULL;
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ }
+ pthread_mutex_unlock (&chat_mutex);
+ return NULL;
+ }
+ else if (1 == cu->disconnect)
+ {
+ /* The sender thread shall close. */
+ /* This is only requested by the receive thread, so we can just leave. */
+ pthread_mutex_unlock (&chat_mutex);
+ return NULL;
+ }
+ else if (1 == cu->ping_status)
+ {
+ /* A pending ping is requested */
+ ++cu->ping_counter;
+ strcpy (cu->ping_message,
+ "libmicrohttpdchatserverpingdata");
+ snprintf (cu->ping_message + 31, 97, "%d", (int) cu->ping_counter);
+ cu->ping_message_len = strlen (cu->ping_message);
+ char *frame_data = NULL;
+ size_t frame_len = 0;
+ int er = MHD_websocket_encode_ping (cu->ws,
+ cu->ping_message,
+ cu->ping_message_len,
+ &frame_data,
+ &frame_len);
+ if (MHD_WEBSOCKET_STATUS_OK == er)
+ {
+ cu->ping_status = 2;
+ timespec_get (&cu->ping_start, TIME_UTC);
+
+ /* send the data via the TCP/IP socket and */
+ /* unlock the mutex while sending */
+ pthread_mutex_unlock (&chat_mutex);
+ send_all (cu,
+ frame_data,
+ frame_len);
+ if (0 != pthread_mutex_lock (&chat_mutex))
+ {
+ return NULL;
+ }
+ }
+ MHD_websocket_free (cu->ws, frame_data);
+ }
+ else if (cu->next_message_index < message_count)
+ {
+ /* a chat message or command is pending */
+ char *frame_data = NULL;
+ size_t frame_len = 0;
+ int er = 0;
+ {
+ struct Message *msg = messages[cu->next_message_index];
+ if ((0 == msg->to_user_id) ||
+ (cu->user_id == msg->to_user_id) ||
+ (cu->user_id == msg->from_user_id) )
+ {
+ if (0 == msg->is_binary)
+ {
+ er = MHD_websocket_encode_text (cu->ws,
+ msg->data,
+ msg->data_len,
+ MHD_WEBSOCKET_FRAGMENTATION_NONE,
+ &frame_data,
+ &frame_len,
+ NULL);
+ }
+ else
+ {
+ er = MHD_websocket_encode_binary (cu->ws,
+ msg->data,
+ msg->data_len,
+ MHD_WEBSOCKET_FRAGMENTATION_NONE,
+ &frame_data,
+ &frame_len);
+ }
+ }
+ }
+ ++cu->next_message_index;
+
+ /* send the data via the TCP/IP socket and */
+ /* unlock the mutex while sending */
+ pthread_mutex_unlock (&chat_mutex);
+ if (MHD_WEBSOCKET_STATUS_OK == er)
+ {
+ send_all (cu,
+ frame_data,
+ frame_len);
+ }
+ MHD_websocket_free (cu->ws,
+ frame_data);
+ if (0 != pthread_mutex_lock (&chat_mutex))
+ {
+ return NULL;
+ }
+ /* check whether there are still pending messages */
+ all_messages_read = (cu->next_message_index < message_count) ? 0 : 1;
+ }
+ else
+ {
+ all_messages_read = 1;
+ }
+ }
+ /* clear old messages */
+ chat_clearmessages (0);
+
+ /* Wait for wake up. */
+ /* This will automatically unlock the mutex while waiting and */
+ /* lock the mutex after waiting */
+ pthread_cond_wait (&cu->wake_up_sender, &chat_mutex);
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Receives messages from the TCP/IP socket and
+ * initializes the connected user.
+ *
+ * @param cls The connected user
+ * @return Always NULL
+ */
+static void *
+connecteduser_receive_messages (void *cls)
+{
+ struct ConnectedUser *cu = cls;
+ char buf[128];
+ ssize_t got;
+ int result;
+
+ /* make the socket blocking */
+ make_blocking (cu->fd);
+
+ /* generate the user name */
+ {
+ char user_name[32];
+ strcpy (user_name, "User");
+ snprintf (user_name + 4, 28, "%d", (int) cu->user_id);
+ cu->user_name_len = strlen (user_name);
+ cu->user_name = malloc (cu->user_name_len + 1);
+ if (NULL == cu->user_name)
+ {
+ free (cu->extra_in);
+ free (cu);
+ MHD_upgrade_action (cu->urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ return NULL;
+ }
+ strcpy (cu->user_name, user_name);
+ }
+
+ /* initialize the wake-up-sender condition variable */
+ if (0 != pthread_cond_init (&cu->wake_up_sender, NULL))
+ {
+ MHD_upgrade_action (cu->urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ free (cu->user_name);
+ free (cu->extra_in);
+ free (cu);
+ return NULL;
+ }
+
+ /* initialize the send mutex */
+ if (0 != pthread_mutex_init (&cu->send_mutex, NULL))
+ {
+ MHD_upgrade_action (cu->urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ pthread_cond_destroy (&cu->wake_up_sender);
+ free (cu->user_name);
+ free (cu->extra_in);
+ free (cu);
+ return NULL;
+ }
+
+ /* add the user to the chat user list */
+ chat_adduser (cu);
+
+ /* initialize the web socket stream for encoding/decoding */
+ result = MHD_websocket_stream_init (&cu->ws,
+ MHD_WEBSOCKET_FLAG_SERVER
+ | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+ 0);
+ if (MHD_WEBSOCKET_STATUS_OK != result)
+ {
+ chat_removeuser (cu);
+ pthread_cond_destroy (&cu->wake_up_sender);
+ pthread_mutex_destroy (&cu->send_mutex);
+ MHD_upgrade_action (cu->urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ free (cu->user_name);
+ free (cu->extra_in);
+ free (cu);
+ return NULL;
+ }
+
+ /* send a list of all currently connected users (bypassing the messaging system) */
+ {
+ struct UserInit
+ {
+ char *user_init;
+ size_t user_init_len;
+ };
+ struct UserInit *init_users = NULL;
+ size_t init_users_len = 0;
+
+ /* first collect all users without sending (so the mutex isn't locked too long) */
+ if (0 == pthread_mutex_lock (&chat_mutex))
+ {
+ if (0 < user_count)
+ {
+ init_users = (struct UserInit *) malloc (user_count * sizeof (struct
+ UserInit));
+ if (NULL != init_users)
+ {
+ init_users_len = user_count;
+ for (size_t i = 0; i < user_count; ++i)
+ {
+ char user_index[32];
+ snprintf (user_index, 32, "%d", (int) users[i]->user_id);
+ size_t user_index_len = strlen (user_index);
+ struct UserInit iu;
+ iu.user_init_len = user_index_len + users[i]->user_name_len + 10;
+ iu.user_init = (char *) malloc (iu.user_init_len + 1);
+ if (NULL != iu.user_init)
+ {
+ strcpy (iu.user_init, "userinit|");
+ strcat (iu.user_init, user_index);
+ strcat (iu.user_init, "|");
+ if (0 < users[i]->user_name_len)
+ strcat (iu.user_init, users[i]->user_name);
+ }
+ init_users[i] = iu;
+ }
+ }
+ }
+ pthread_mutex_unlock (&chat_mutex);
+ }
+
+ /* then send all users to the connected client */
+ for (size_t i = 0; i < init_users_len; ++i)
+ {
+ char *frame_data = NULL;
+ size_t frame_len = 0;
+ if ((0 < init_users[i].user_init_len) && (NULL !=
+ init_users[i].user_init) )
+ {
+ int status = MHD_websocket_encode_text (cu->ws,
+ init_users[i].user_init,
+ init_users[i].user_init_len,
+ MHD_WEBSOCKET_FRAGMENTATION_NONE,
+ &frame_data,
+ &frame_len,
+ NULL);
+ if (MHD_WEBSOCKET_STATUS_OK == status)
+ {
+ send_all (cu,
+ frame_data,
+ frame_len);
+ MHD_websocket_free (cu->ws,
+ frame_data);
+ }
+ free (init_users[i].user_init);
+ }
+ }
+ free (init_users);
+ }
+
+ /* send the welcome message to the user (bypassing the messaging system) */
+ {
+ char *frame_data = NULL;
+ size_t frame_len = 0;
+ const char *welcome_msg = "moderator||" \
+ "Welcome to the libmicrohttpd WebSocket chatserver example.\n" \
+ "Supported commands are:\n" \
+ " /m <user> <text> - sends a private message to the specified user\n" \
+ " /ping <user> - sends a ping to the specified user\n" \
+ " /name <name> - changes your name to the specified name\n" \
+ " /disconnect - disconnects your websocket\n\n" \
+ "All messages, which does not start with a slash, " \
+ "are regular messages and will be sent to the selected user.\n\n" \
+ "Have fun!";
+ MHD_websocket_encode_text (cu->ws,
+ welcome_msg,
+ strlen (welcome_msg),
+ MHD_WEBSOCKET_FRAGMENTATION_NONE,
+ &frame_data,
+ &frame_len,
+ NULL);
+ send_all (cu,
+ frame_data,
+ frame_len);
+ MHD_websocket_free (cu->ws,
+ frame_data);
+ }
+
+ /* start the message-send thread */
+ pthread_t pt;
+ if (0 != pthread_create (&pt,
+ NULL,
+ &connecteduser_send_messages,
+ cu))
+ abort ();
+
+ /* start by parsing extra data MHD may have already read, if any */
+ if (0 != cu->extra_in_size)
+ {
+ if (0 != connecteduser_parse_received_websocket_stream (cu,
+ cu->extra_in,
+ cu->extra_in_size))
+ {
+ chat_removeuser (cu);
+ if (0 == pthread_mutex_lock (&chat_mutex))
+ {
+ cu->disconnect = 1;
+ pthread_cond_signal (&cu->wake_up_sender);
+ pthread_mutex_unlock (&chat_mutex);
+ pthread_join (pt, NULL);
+ }
+ struct MHD_UpgradeResponseHandle *urh = cu->urh;
+ if (NULL != urh)
+ {
+ cu->urh = NULL;
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ }
+ pthread_cond_destroy (&cu->wake_up_sender);
+ pthread_mutex_destroy (&cu->send_mutex);
+ MHD_websocket_stream_free (cu->ws);
+ free (cu->user_name);
+ free (cu->extra_in);
+ free (cu);
+ return NULL;
+ }
+ free (cu->extra_in);
+ cu->extra_in = NULL;
+ }
+
+ /* the main loop for receiving data */
+ while (1)
+ {
+ got = recv (cu->fd,
+ buf,
+ sizeof (buf),
+ 0);
+ if (0 >= got)
+ {
+ /* the TCP/IP socket has been closed */
+ break;
+ }
+ if (0 < got)
+ {
+ if (0 != connecteduser_parse_received_websocket_stream (cu, buf,
+ (size_t) got))
+ {
+ /* A websocket protocol error occurred */
+ chat_removeuser (cu);
+ if (0 == pthread_mutex_lock (&chat_mutex))
+ {
+ cu->disconnect = 1;
+ pthread_cond_signal (&cu->wake_up_sender);
+ pthread_mutex_unlock (&chat_mutex);
+ pthread_join (pt, NULL);
+ }
+ struct MHD_UpgradeResponseHandle *urh = cu->urh;
+ if (NULL != urh)
+ {
+ cu->urh = NULL;
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ }
+ pthread_cond_destroy (&cu->wake_up_sender);
+ pthread_mutex_destroy (&cu->send_mutex);
+ MHD_websocket_stream_free (cu->ws);
+ free (cu->user_name);
+ free (cu);
+ return NULL;
+ }
+ }
+ }
+
+ /* cleanup */
+ chat_removeuser (cu);
+ if (0 == pthread_mutex_lock (&chat_mutex))
+ {
+ cu->disconnect = 1;
+ pthread_cond_signal (&cu->wake_up_sender);
+ pthread_mutex_unlock (&chat_mutex);
+ pthread_join (pt, NULL);
+ }
+ struct MHD_UpgradeResponseHandle *urh = cu->urh;
+ if (NULL != urh)
+ {
+ cu->urh = NULL;
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ }
+ pthread_cond_destroy (&cu->wake_up_sender);
+ pthread_mutex_destroy (&cu->send_mutex);
+ MHD_websocket_stream_free (cu->ws);
+ free (cu->user_name);
+ free (cu);
+
+ return NULL;
+}
+
+
+/**
+ * Function called after a protocol "upgrade" response was sent
+ * successfully and the socket should now be controlled by some
+ * protocol other than HTTP.
+ *
+ * Any data already received on the socket will be made available in
+ * @e extra_in. This can happen if the application sent extra data
+ * before MHD send the upgrade response. The application should
+ * treat data from @a extra_in as if it had read it from the socket.
+ *
+ * Note that the application must not close() @a sock directly,
+ * but instead use #MHD_upgrade_action() for special operations
+ * on @a sock.
+ *
+ * Data forwarding to "upgraded" @a sock will be started as soon
+ * as this function return.
+ *
+ * Except when in 'thread-per-connection' mode, implementations
+ * of this function should never block (as it will still be called
+ * from within the main event loop).
+ *
+ * @param cls closure, whatever was given to #MHD_create_response_for_upgrade().
+ * @param connection original HTTP connection handle,
+ * giving the function a last chance
+ * to inspect the original HTTP request
+ * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback`
+ * @param extra_in if we happened to have read bytes after the
+ * HTTP header already (because the client sent
+ * more than the HTTP header of the request before
+ * we sent the upgrade response),
+ * these are the extra bytes already read from @a sock
+ * by MHD. The application should treat these as if
+ * it had read them from @a sock.
+ * @param extra_in_size number of bytes in @a extra_in
+ * @param sock socket to use for bi-directional communication
+ * with the client. For HTTPS, this may not be a socket
+ * that is directly connected to the client and thus certain
+ * operations (TCP-specific setsockopt(), getsockopt(), etc.)
+ * may not work as expected (as the socket could be from a
+ * socketpair() or a TCP-loopback). The application is expected
+ * to perform read()/recv() and write()/send() calls on the socket.
+ * The application may also call shutdown(), but must not call
+ * close() directly.
+ * @param urh argument for #MHD_upgrade_action()s on this @a connection.
+ * Applications must eventually use this callback to (indirectly)
+ * perform the close() action on the @a sock.
+ */
+static void
+upgrade_handler (void *cls,
+ struct MHD_Connection *connection,
+ void *con_cls,
+ const char *extra_in,
+ size_t extra_in_size,
+ MHD_socket fd,
+ struct MHD_UpgradeResponseHandle *urh)
+{
+ struct ConnectedUser *cu;
+ pthread_t pt;
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) connection; /* Unused. Silent compiler warning. */
+ (void) con_cls; /* Unused. Silent compiler warning. */
+
+ /* This callback must return as soon as possible. */
+
+ /* allocate new connected user */
+ cu = malloc (sizeof (struct ConnectedUser));
+ if (NULL == cu)
+ abort ();
+ memset (cu, 0, sizeof (struct ConnectedUser));
+ if (0 != extra_in_size)
+ {
+ cu->extra_in = malloc (extra_in_size);
+ if (NULL == cu->extra_in)
+ abort ();
+ memcpy (cu->extra_in,
+ extra_in,
+ extra_in_size);
+ }
+ cu->extra_in_size = extra_in_size;
+ cu->fd = fd;
+ cu->urh = urh;
+ cu->user_id = ++unique_user_id;
+ cu->user_name = NULL;
+ cu->user_name_len = 0;
+
+ /* create thread for the new connected user */
+ if (0 != pthread_create (&pt,
+ NULL,
+ &connecteduser_receive_messages,
+ cu))
+ abort ();
+ pthread_detach (pt);
+}
+
+
+/**
+ * Function called by the MHD_daemon when the client tries to access a page.
+ *
+ * This is used to provide the main page
+ * (in this example HTML + CSS + JavaScript is all in the same file)
+ * and to initialize a websocket connection.
+ * The rules for the initialization of a websocket connection
+ * are listed near the URL check of "/ChatServerWebSocket".
+ *
+ * @param cls closure, whatever was given to #MHD_start_daemon().
+ * @param connection The HTTP connection handle
+ * @param url The requested URL
+ * @param method The request method (typically "GET")
+ * @param version The HTTP version
+ * @param upload_data Given upload data for POST requests
+ * @param upload_data_size The size of the upload data
+ * @param ptr A pointer for request specific data
+ * @return MHD_YES on success or MHD_NO on error.
+ */
+static enum MHD_Result
+access_handler (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **ptr)
+{
+ static int aptr;
+ struct MHD_Response *response;
+ int ret;
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) version; /* Unused. Silent compiler warning. */
+ (void) upload_data; /* Unused. Silent compiler warning. */
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO; /* unexpected method */
+ if (&aptr != *ptr)
+ {
+ /* do never respond on first call */
+ *ptr = &aptr;
+ return MHD_YES;
+ }
+ *ptr = NULL; /* reset when done */
+ if (0 == strcmp (url, "/"))
+ {
+ /* Default page for visiting the server */
+ struct MHD_Response *response = MHD_create_response_from_buffer (strlen (
+ PAGE),
+ PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ }
+ else if (0 == strcmp (url, "/ChatServerWebSocket"))
+ {
+ /**
+ * The path for the chat has been accessed.
+ * For a valid WebSocket request, at least five headers are required:
+ * 1. "Host: <name>"
+ * 2. "Connection: Upgrade"
+ * 3. "Upgrade: websocket"
+ * 4. "Sec-WebSocket-Version: 13"
+ * 5. "Sec-WebSocket-Key: <base64 encoded value>"
+ * Values are compared in a case-insensitive manner.
+ * Furthermore it must be a HTTP/1.1 or higher GET request.
+ * See: https://tools.ietf.org/html/rfc6455#section-4.2.1
+ *
+ * To make this example portable we skip the Host check
+ */
+
+ char is_valid = 1;
+ const char *value = NULL;
+ char sec_websocket_accept[29];
+
+ /* check whether an websocket upgrade is requested */
+ if (0 != MHD_websocket_check_http_version (version))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONNECTION);
+ if (0 != MHD_websocket_check_connection_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_UPGRADE);
+ if (0 != MHD_websocket_check_upgrade_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
+ if (0 != MHD_websocket_check_version_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
+ if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
+ {
+ is_valid = 0;
+ }
+
+ if (1 == is_valid)
+ {
+ /* create the response for upgrade */
+ response = MHD_create_response_for_upgrade (&upgrade_handler,
+ NULL);
+
+ /**
+ * For the response we need at least the following headers:
+ * 1. "Connection: Upgrade"
+ * 2. "Upgrade: websocket"
+ * 3. "Sec-WebSocket-Accept: <base64value>"
+ * The value for Sec-WebSocket-Accept can be generated with MHD_websocket_create_accept_header.
+ * It requires the value of the Sec-WebSocket-Key header of the request.
+ * See also: https://tools.ietf.org/html/rfc6455#section-4.2.2
+ */
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONNECTION,
+ "Upgrade");
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_UPGRADE,
+ "websocket");
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
+ sec_websocket_accept);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_SWITCHING_PROTOCOLS,
+ response);
+ MHD_destroy_response (response);
+ }
+ else
+ {
+ /* return error page */
+ struct MHD_Response *response = MHD_create_response_from_buffer (strlen (
+ PAGE_INVALID_WEBSOCKET_REQUEST),
+ PAGE_INVALID_WEBSOCKET_REQUEST,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_BAD_REQUEST,
+ response);
+ MHD_destroy_response (response);
+ }
+ }
+ else
+ {
+ struct MHD_Response *response = MHD_create_response_from_buffer (strlen (
+ PAGE_NOT_FOUND),
+ PAGE_NOT_FOUND,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_FOUND,
+ response);
+ MHD_destroy_response (response);
+ }
+ return ret;
+}
+
+
+/**
+ * The main routine for this example
+ *
+ * This starts the daemon and waits for a key hit.
+ * After this it will shutdown the daemon.
+ */
+int
+main (int argc,
+ char *const *argv)
+{
+ (void) argc; /* Unused. Silent compiler warning. */
+ (void) argv; /* Unused. Silent compiler warning. */
+ struct MHD_Daemon *d;
+
+ if (0 != pthread_mutex_init (&chat_mutex, NULL))
+ return 1;
+
+#if USE_HTTPS == 1
+ const char private_key[] = "TODO: Enter your key in PEM format here";
+ const char certificate[] = "TODO: Enter your certificate in PEM format here";
+ d = MHD_start_daemon (MHD_ALLOW_UPGRADE | MHD_USE_AUTO
+ | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
+ | MHD_USE_TLS,
+ 443,
+ NULL, NULL,
+ &access_handler, NULL,
+ MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
+ MHD_OPTION_HTTPS_MEM_KEY, private_key,
+ MHD_OPTION_HTTPS_MEM_CERT, certificate,
+ MHD_OPTION_END);
+#else
+ d = MHD_start_daemon (MHD_ALLOW_UPGRADE | MHD_USE_AUTO
+ | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
+ 80,
+ NULL, NULL,
+ &access_handler, NULL,
+ MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
+ MHD_OPTION_END);
+#endif
+
+ if (NULL == d)
+ return 1;
+ (void) getc (stdin);
+
+ if (0 == pthread_mutex_lock (&chat_mutex))
+ {
+ disconnect_all = 1;
+ for (size_t i = 0; i < user_count; ++i)
+ pthread_cond_signal (&users[i]->wake_up_sender);
+ pthread_mutex_unlock (&chat_mutex);
+ }
+ sleep (2);
+ if (0 == pthread_mutex_lock (&chat_mutex))
+ {
+ for (size_t i = 0; i < user_count; ++i)
+ {
+ struct MHD_UpgradeResponseHandle *urh = users[i]->urh;
+ if (NULL != urh)
+ {
+ users[i]->urh = NULL;
+ MHD_upgrade_action (users[i]->urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ }
+ }
+ pthread_mutex_unlock (&chat_mutex);
+ }
+ sleep (2);
+
+ /* usually we should wait here in a safe way for all threads to disconnect, */
+ /* but we skip this in the example */
+
+ pthread_mutex_destroy (&chat_mutex);
+
+ MHD_stop_daemon (d);
+
+ return 0;
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/examples/websocket_threaded_example.c
^
|
@@ -36,7 +36,7 @@
"<title>WebSocket chat</title>\n" \
"<script>\n" \
"document.addEventListener('DOMContentLoaded', function() {\n" \
- " const ws = new WebSocket('ws:// ' + window.location.host);\n" /* \
+ " const ws = new WebSocket('ws:/" "/ ' + window.location.host);\n" /* \
" const btn = document.getElementById('send');\n" \
" const msg = document.getElementById('msg');\n" \
" const log = document.getElementById('log');\n" \
@@ -420,9 +420,9 @@
(void) con; /* Unused. Silent compiler warning. */
- return (upg_header != NULL) && (con_header != NULL)
- && (0 == strcmp (upg_header, WS_UPGRADE_VALUE))
- && (NULL != strstr (con_header, "Upgrade"))
+ return ((upg_header != NULL) && (con_header != NULL)
+ && (0 == strcmp (upg_header, WS_UPGRADE_VALUE))
+ && (NULL != strstr (con_header, "Upgrade")))
? MHD_YES
: MHD_NO;
}
@@ -495,7 +495,11 @@
strncpy (str + WS_KEY_LEN, WS_GUID, WS_GUID_LEN + 1);
SHA1Reset (&ctx);
SHA1Input (&ctx, (const unsigned char *) str, WS_KEY_GUID_LEN);
- SHA1Result (&ctx, hash);
+ if (SHA1_RESULT_SUCCESS != SHA1Result (&ctx, hash))
+ {
+ free (str);
+ return MHD_NO;
+ }
free (str);
len = BASE64Encode (hash, SHA1HashSize, val);
if (-1 == len)
@@ -515,14 +519,15 @@
flags = fcntl (fd, F_GETFL);
if (-1 == flags)
- return;
+ abort ();
if ((flags & ~O_NONBLOCK) != flags)
if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
abort ();
#elif defined(MHD_WINSOCK_SOCKETS)
- unsigned long flags = 1;
+ unsigned long flags = 0;
- ioctlsocket (fd, FIONBIO, &flags);
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ abort ();
#endif /* MHD_WINSOCK_SOCKETS */
}
@@ -535,7 +540,7 @@
for (off = 0; off < len; off += ret)
{
- ret = send (sock, (const void*) &buf[off], len - off, 0);
+ ret = send (sock, (const void *) &buf[off], len - off, 0);
if (0 > ret)
{
if (EAGAIN == errno)
@@ -571,14 +576,8 @@
frame[1] = length & 0x7F;
idx_first_rdata = 2;
}
- else if ((length >= 126) && (length <= 0xFFFF))
- {
- frame[1] = 126;
- frame[2] = (length >> 8) & 0xFF;
- frame[3] = length & 0xFF;
- idx_first_rdata = 4;
- }
- else
+#if SIZEOF_SIZE_T > 4
+ else if (0xFFFF < length)
{
frame[1] = 127;
frame[2] = (unsigned char) ((length >> 56) & 0xFF);
@@ -591,6 +590,14 @@
frame[9] = (unsigned char) (length & 0xFF);
idx_first_rdata = 10;
}
+#endif /* SIZEOF_SIZE_T > 4 */
+ else
+ {
+ frame[1] = 126;
+ frame[2] = (length >> 8) & 0xFF;
+ frame[3] = length & 0xFF;
+ idx_first_rdata = 4;
+ }
idx_response = 0;
response = malloc (idx_first_rdata + length + 1);
if (NULL == response)
@@ -699,7 +706,7 @@
make_blocking (ws->sock);
while (1)
{
- got = recv (ws->sock, (void*) buf, sizeof (buf), 0);
+ got = recv (ws->sock, (void *) buf, sizeof (buf), 0);
if (0 >= got)
{
break;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/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
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/include/mhd_options.h
^
|
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2016 Karlson2k (Evgeny Grin)
+ Copyright (C) 2016-2021 Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -142,4 +142,28 @@
#define MHD_FAVOR_FAST_CODE 1
#endif /* !MHD_FAVOR_FAST_CODE && !MHD_FAVOR_SMALL_CODE */
+#ifndef MHD_ASAN_ACTIVE
+#if (defined(__GNUC__) || defined(_MSC_VER)) && defined(__SANITIZE_ADDRESS__)
+#define MHD_ASAN_ACTIVE 1
+#elif defined(__has_feature)
+#if __has_feature (address_sanitizer)
+#define MHD_ASAN_ACTIVE 1
+#endif /* __has_feature(address_sanitizer) */
+#endif /* __has_feature */
+#endif /* MHD_ASAN_ACTIVE */
+
+#if defined(MHD_ASAN_ACTIVE) && defined(HAVE_SANITIZER_ASAN_INTERFACE_H) && \
+ (defined(FUNC_ATTR_PTRCOMPARE_WOKRS) || defined(FUNC_ATTR_NOSANITIZE_WORKS))
+#ifndef MHD_ASAN_POISON_ACTIVE
+/* Manual ASAN poisoning could be used */
+#warning User memory poisoning is not active
+#endif /* ! MHD_ASAN_POISON_ACTIVE */
+#else /* ! (MHD_ASAN_ACTIVE && HAVE_SANITIZER_ASAN_INTERFACE_H &&
+ (FUNC_ATTR_PTRCOMPARE_WOKRS || FUNC_ATTR_NOSANITIZE_WORKS)) */
+#ifdef MHD_ASAN_POISON_ACTIVE
+#error User memory poisoning is active, but conditions are not suitable
+#endif /* MHD_ASAN_POISON_ACTIVE */
+#endif /* ! (MHD_ASAN_ACTIVE && HAVE_SANITIZER_ASAN_INTERFACE_H &&
+ (FUNC_ATTR_PTRCOMPARE_WOKRS || FUNC_ATTR_NOSANITIZE_WORKS)) */
+
#endif /* MHD_OPTIONS_H */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/include/microhttpd.h
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2006--2020 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2006-2021 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -87,6 +88,16 @@
#endif
#endif
+
+/**
+ * Current version of the library in packed BCD form.
+ * @note Version number components are coded as Simple Binary-Coded Decimal
+ * (also called Natural BCD or BCD 8421). While they are hexadecimal numbers,
+ * they are parsed as decimal numbers.
+ * Example: 0x01093001 = 1.9.30-1.
+ */
+#define MHD_VERSION 0x00097500
+
/* If generic headers don't work on your platform, include headers
which define 'va_list', 'size_t', 'ssize_t', 'intptr_t',
'uint16_t', 'uint32_t', 'uint64_t', 'off_t', 'struct sockaddr',
@@ -98,6 +109,7 @@
#ifndef MHD_PLATFORM_H
#if defined(_WIN32) && ! defined(__CYGWIN__) && \
! defined(_CRT_DECLARE_NONSTDC_NAMES)
+/* Declare POSIX-compatible names */
#define _CRT_DECLARE_NONSTDC_NAMES 1
#endif /* _WIN32 && ! __CYGWIN__ && ! _CRT_DECLARE_NONSTDC_NAMES */
#include <stdarg.h>
@@ -108,8 +120,6 @@
#include <sys/time.h>
#include <sys/socket.h>
#else /* _WIN32 && ! __CYGWIN__ */
-/* Declare POSIX-compatible names */
-#define _CRT_DECLARE_NONSTDC_NAMES 1
#include <ws2tcpip.h>
#if defined(_MSC_FULL_VER) && ! defined (_SSIZE_T_DEFINED)
#define _SSIZE_T_DEFINED
@@ -123,14 +133,23 @@
#error Cygwin with winsock fd_set is not supported
#endif
-/**
- * Current version of the library in packed BCD form.
- * @note Version number components are coded as Simple Binary-Coded Decimal
- * (also called Natural BCD or BCD 8421). While they are hexadecimal numbers,
- * they are parsed as decimal numbers.
- * Example: 0x01093001 = 1.9.30-1.
- */
-#define MHD_VERSION 0x00097300
+#ifdef __has_attribute
+#if __has_attribute (flag_enum)
+#define _MHD_FLAGS_ENUM __attribute__((flag_enum))
+#endif /* flag_enum */
+#if __has_attribute (enum_extensibility)
+#define _MHD_FIXED_ENUM __attribute__((enum_extensibility (closed)))
+#endif /* enum_extensibility */
+#endif /* __has_attribute */
+
+#ifndef _MHD_FLAGS_ENUM
+#define _MHD_FLAGS_ENUM
+#endif /* _MHD_FLAGS_ENUM */
+#ifndef _MHD_FIXED_ENUM
+#define _MHD_FIXED_ENUM
+#endif /* _MHD_FIXED_ENUM */
+
+#define _MHD_FIXED_FLAGS_ENUM _MHD_FIXED_ENUM _MHD_FLAGS_ENUM
/**
* Operational results from MHD calls.
@@ -147,7 +166,7 @@
*/
MHD_YES = 1
-};
+} _MHD_FIXED_ENUM;
/**
@@ -165,13 +184,8 @@
#define MHD_SIZE_UNKNOWN ((uint64_t) -1LL)
#endif
-#ifdef SIZE_MAX
-#define MHD_CONTENT_READER_END_OF_STREAM SIZE_MAX
-#define MHD_CONTENT_READER_END_WITH_ERROR (SIZE_MAX - 1)
-#else
-#define MHD_CONTENT_READER_END_OF_STREAM ((size_t) -1LL)
-#define MHD_CONTENT_READER_END_WITH_ERROR (((size_t) -1LL) - 1)
-#endif
+#define MHD_CONTENT_READER_END_OF_STREAM ((ssize_t) -1)
+#define MHD_CONTENT_READER_END_WITH_ERROR ((ssize_t) -2)
#ifndef _MHD_EXTERN
#if defined(_WIN32) && defined(MHD_W32LIB)
@@ -219,16 +233,17 @@
#define _MHD_INSTRMACRO(a) #a
#define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
/* deprecation message */
-#define _MHD_DEPR_MACRO(msg) __pragma(message (__FILE__ "(" _MHD_STRMACRO ( \
- __LINE__) "): warning: " msg))
+#define _MHD_DEPR_MACRO(msg) \
+ __pragma(message (__FILE__ "(" _MHD_STRMACRO ( __LINE__) "): warning: " msg))
#define _MHD_DEPR_IN_MACRO(msg) _MHD_DEPR_MACRO (msg)
#elif defined(__clang__) || defined (__GNUC_PATCHLEVEL__)
/* clang or GCC since 3.0 */
#define _MHD_GCC_PRAG(x) _Pragma(#x)
-#if (defined(__clang__) && (__clang_major__ + 0 >= 5 || \
- (! defined(__apple_build_version__) && \
- (__clang_major__ + 0 > 3 || (__clang_major__ + 0 == 3 && __clang_minor__ >= \
- 3))))) || \
+#if (defined(__clang__) && \
+ (__clang_major__ + 0 >= 5 || \
+ (! defined(__apple_build_version__) && \
+ (__clang_major__ + 0 > 3 || \
+ (__clang_major__ + 0 == 3 && __clang_minor__ >= 3))))) || \
__GNUC__ + 0 > 4 || (__GNUC__ + 0 == 4 && __GNUC_MINOR__ + 0 >= 8)
/* clang >= 3.3 (or XCode's clang >= 5.0) or
GCC >= 4.8 */
@@ -237,9 +252,9 @@
#else /* older clang or GCC */
/* clang < 3.3, XCode's clang < 5.0, 3.0 <= GCC < 4.8 */
#define _MHD_DEPR_MACRO(msg) _MHD_GCC_PRAG (message msg)
-#if (defined(__clang__) && (__clang_major__ + 0 > 2 || (__clang_major__ + 0 == \
- 2 && __clang_minor__ >= \
- 9))) /* FIXME: clang >= 2.9, earlier versions not tested */
+#if (defined(__clang__) && \
+ (__clang_major__ + 0 > 2 || \
+ (__clang_major__ + 0 == 2 && __clang_minor__ >= 9))) /* clang >= 2.9 */
/* clang handles inline pragmas better than GCC */
#define _MHD_DEPR_IN_MACRO(msg) _MHD_DEPR_MACRO (msg)
#endif /* clang >= 2.9 */
@@ -262,17 +277,17 @@
/* VS 2005 or later */
#define _MHD_DEPR_FUNC(msg) __declspec(deprecated (msg))
#elif defined(_MSC_FULL_VER) && _MSC_VER + 0 >= 1310
-/* VS .NET 2003 deprecation do not support custom messages */
+/* VS .NET 2003 deprecation does not support custom messages */
#define _MHD_DEPR_FUNC(msg) __declspec(deprecated)
#elif (__GNUC__ + 0 >= 5) || (defined (__clang__) && \
- (__clang_major__ + 0 > 2 || (__clang_major__ + 0 == 2 && __clang_minor__ >= \
- 9))) /* FIXME: earlier versions not tested */
+ (__clang_major__ + 0 > 2 || \
+ (__clang_major__ + 0 == 2 && __clang_minor__ >= 9)))
/* GCC >= 5.0 or clang >= 2.9 */
#define _MHD_DEPR_FUNC(msg) __attribute__((deprecated (msg)))
-#elif defined (__clang__) || __GNUC__ + 0 > 3 || (__GNUC__ + 0 == 3 && \
- __GNUC_MINOR__ + 0 >= 1)
+#elif defined (__clang__) || __GNUC__ + 0 > 3 || \
+ (__GNUC__ + 0 == 3 && __GNUC_MINOR__ + 0 >= 1)
/* 3.1 <= GCC < 5.0 or clang < 2.9 */
-/* old GCC-style deprecation do not support custom messages */
+/* old GCC-style deprecation does not support custom messages */
#define _MHD_DEPR_FUNC(msg) __attribute__((__deprecated__))
/* #elif defined(SOMEMACRO) */ /* add compiler-specific macros here if required */
#endif /* clang < 2.9 || GCC >= 3.1 */
@@ -295,7 +310,7 @@
#define MHD_LONG_LONG long long
#define MHD_UNSIGNED_LONG_LONG unsigned long long
#else /* MHD_LONG_LONG */
-_MHD_DEPR_MACRO (
+_MHD_DEPR_MACRO ( \
"Macro MHD_LONG_LONG is deprecated, use MHD_UNSIGNED_LONG_LONG")
#endif
/**
@@ -309,7 +324,7 @@
#define MHD_LONG_LONG_PRINTF "ll"
#define MHD_UNSIGNED_LONG_LONG_PRINTF "%llu"
#else /* MHD_LONG_LONG_PRINTF */
-_MHD_DEPR_MACRO (
+_MHD_DEPR_MACRO ( \
"Macro MHD_LONG_LONG_PRINTF is deprecated, use MHD_UNSIGNED_LONG_LONG_PRINTF")
#endif
@@ -324,114 +339,115 @@
* @defgroup httpcode HTTP response codes.
* These are the status codes defined for HTTP responses.
* See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
- * Registry export date: 2019-06-09
+ * Registry export date: 2021-12-19
* @{
*/
-/* 100 "Continue". RFC7231, Section 6.2.1. */
-#define MHD_HTTP_CONTINUE 100
-/* 101 "Switching Protocols". RFC7231, Section 6.2.2. */
-#define MHD_HTTP_SWITCHING_PROTOCOLS 101
+/* 100 "Continue". RFC-ietf-httpbis-semantics, Section 15.2.1. */
+#define MHD_HTTP_CONTINUE 100
+/* 101 "Switching Protocols". RFC-ietf-httpbis-semantics, Section 15.2.2. */
+#define MHD_HTTP_SWITCHING_PROTOCOLS 101
/* 102 "Processing". RFC2518. */
-#define MHD_HTTP_PROCESSING 102
+#define MHD_HTTP_PROCESSING 102
/* 103 "Early Hints". RFC8297. */
-#define MHD_HTTP_EARLY_HINTS 103
+#define MHD_HTTP_EARLY_HINTS 103
-/* 200 "OK". RFC7231, Section 6.3.1. */
-#define MHD_HTTP_OK 200
-/* 201 "Created". RFC7231, Section 6.3.2. */
-#define MHD_HTTP_CREATED 201
-/* 202 "Accepted". RFC7231, Section 6.3.3. */
-#define MHD_HTTP_ACCEPTED 202
-/* 203 "Non-Authoritative Information". RFC7231, Section 6.3.4. */
+/* 200 "OK". RFC-ietf-httpbis-semantics, Section 15.3.1. */
+#define MHD_HTTP_OK 200
+/* 201 "Created". RFC-ietf-httpbis-semantics, Section 15.3.2. */
+#define MHD_HTTP_CREATED 201
+/* 202 "Accepted". RFC-ietf-httpbis-semantics, Section 15.3.3. */
+#define MHD_HTTP_ACCEPTED 202
+/* 203 "Non-Authoritative Information". RFC-ietf-httpbis-semantics, Section 15.3.4. */
#define MHD_HTTP_NON_AUTHORITATIVE_INFORMATION 203
-/* 204 "No Content". RFC7231, Section 6.3.5. */
-#define MHD_HTTP_NO_CONTENT 204
-/* 205 "Reset Content". RFC7231, Section 6.3.6. */
-#define MHD_HTTP_RESET_CONTENT 205
-/* 206 "Partial Content". RFC7233, Section 4.1. */
-#define MHD_HTTP_PARTIAL_CONTENT 206
+/* 204 "No Content". RFC-ietf-httpbis-semantics, Section 15.3.5. */
+#define MHD_HTTP_NO_CONTENT 204
+/* 205 "Reset Content". RFC-ietf-httpbis-semantics, Section 15.3.6. */
+#define MHD_HTTP_RESET_CONTENT 205
+/* 206 "Partial Content". RFC-ietf-httpbis-semantics, Section 15.3.7. */
+#define MHD_HTTP_PARTIAL_CONTENT 206
/* 207 "Multi-Status". RFC4918. */
-#define MHD_HTTP_MULTI_STATUS 207
+#define MHD_HTTP_MULTI_STATUS 207
/* 208 "Already Reported". RFC5842. */
-#define MHD_HTTP_ALREADY_REPORTED 208
+#define MHD_HTTP_ALREADY_REPORTED 208
/* 226 "IM Used". RFC3229. */
-#define MHD_HTTP_IM_USED 226
+#define MHD_HTTP_IM_USED 226
-/* 300 "Multiple Choices". RFC7231, Section 6.4.1. */
-#define MHD_HTTP_MULTIPLE_CHOICES 300
-/* 301 "Moved Permanently". RFC7231, Section 6.4.2. */
-#define MHD_HTTP_MOVED_PERMANENTLY 301
-/* 302 "Found". RFC7231, Section 6.4.3. */
-#define MHD_HTTP_FOUND 302
-/* 303 "See Other". RFC7231, Section 6.4.4. */
-#define MHD_HTTP_SEE_OTHER 303
-/* 304 "Not Modified". RFC7232, Section 4.1. */
-#define MHD_HTTP_NOT_MODIFIED 304
-/* 305 "Use Proxy". RFC7231, Section 6.4.5. */
-#define MHD_HTTP_USE_PROXY 305
-/* 306 "Switch Proxy". Not used! RFC7231, Section 6.4.6. */
-#define MHD_HTTP_SWITCH_PROXY 306
-/* 307 "Temporary Redirect". RFC7231, Section 6.4.7. */
-#define MHD_HTTP_TEMPORARY_REDIRECT 307
-/* 308 "Permanent Redirect". RFC7538. */
-#define MHD_HTTP_PERMANENT_REDIRECT 308
-
-/* 400 "Bad Request". RFC7231, Section 6.5.1. */
-#define MHD_HTTP_BAD_REQUEST 400
-/* 401 "Unauthorized". RFC7235, Section 3.1. */
-#define MHD_HTTP_UNAUTHORIZED 401
-/* 402 "Payment Required". RFC7231, Section 6.5.2. */
-#define MHD_HTTP_PAYMENT_REQUIRED 402
-/* 403 "Forbidden". RFC7231, Section 6.5.3. */
-#define MHD_HTTP_FORBIDDEN 403
-/* 404 "Not Found". RFC7231, Section 6.5.4. */
-#define MHD_HTTP_NOT_FOUND 404
-/* 405 "Method Not Allowed". RFC7231, Section 6.5.5. */
-#define MHD_HTTP_METHOD_NOT_ALLOWED 405
-/* 406 "Not Acceptable". RFC7231, Section 6.5.6. */
-#define MHD_HTTP_NOT_ACCEPTABLE 406
-/* 407 "Proxy Authentication Required". RFC7235, Section 3.2. */
+/* 300 "Multiple Choices". RFC-ietf-httpbis-semantics, Section 15.4.1. */
+#define MHD_HTTP_MULTIPLE_CHOICES 300
+/* 301 "Moved Permanently". RFC-ietf-httpbis-semantics, Section 15.4.2. */
+#define MHD_HTTP_MOVED_PERMANENTLY 301
+/* 302 "Found". RFC-ietf-httpbis-semantics, Section 15.4.3. */
+#define MHD_HTTP_FOUND 302
+/* 303 "See Other". RFC-ietf-httpbis-semantics, Section 15.4.4. */
+#define MHD_HTTP_SEE_OTHER 303
+/* 304 "Not Modified". RFC-ietf-httpbis-semantics, Section 15.4.5. */
+#define MHD_HTTP_NOT_MODIFIED 304
+/* 305 "Use Proxy". RFC-ietf-httpbis-semantics, Section 15.4.6. */
+#define MHD_HTTP_USE_PROXY 305
+/* 306 "Switch Proxy". Not used! RFC-ietf-httpbis-semantics, Section 15.4.7. */
+#define MHD_HTTP_SWITCH_PROXY 306
+/* 307 "Temporary Redirect". RFC-ietf-httpbis-semantics, Section 15.4.8. */
+#define MHD_HTTP_TEMPORARY_REDIRECT 307
+/* 308 "Permanent Redirect". RFC-ietf-httpbis-semantics, Section 15.4.9. */
+#define MHD_HTTP_PERMANENT_REDIRECT 308
+
+/* 400 "Bad Request". RFC-ietf-httpbis-semantics, Section 15.5.1. */
+#define MHD_HTTP_BAD_REQUEST 400
+/* 401 "Unauthorized". RFC-ietf-httpbis-semantics, Section 15.5.2. */
+#define MHD_HTTP_UNAUTHORIZED 401
+/* 402 "Payment Required". RFC-ietf-httpbis-semantics, Section 15.5.3. */
+#define MHD_HTTP_PAYMENT_REQUIRED 402
+/* 403 "Forbidden". RFC-ietf-httpbis-semantics, Section 15.5.4. */
+#define MHD_HTTP_FORBIDDEN 403
+/* 404 "Not Found". RFC-ietf-httpbis-semantics, Section 15.5.5. */
+#define MHD_HTTP_NOT_FOUND 404
+/* 405 "Method Not Allowed". RFC-ietf-httpbis-semantics, Section 15.5.6. */
+#define MHD_HTTP_METHOD_NOT_ALLOWED 405
+/* 406 "Not Acceptable". RFC-ietf-httpbis-semantics, Section 15.5.7. */
+#define MHD_HTTP_NOT_ACCEPTABLE 406
+/* 407 "Proxy Authentication Required". RFC-ietf-httpbis-semantics, Section 15.5.8. */
#define MHD_HTTP_PROXY_AUTHENTICATION_REQUIRED 407
-/* 408 "Request Timeout". RFC7231, Section 6.5.7. */
-#define MHD_HTTP_REQUEST_TIMEOUT 408
-/* 409 "Conflict". RFC7231, Section 6.5.8. */
-#define MHD_HTTP_CONFLICT 409
-/* 410 "Gone". RFC7231, Section 6.5.9. */
-#define MHD_HTTP_GONE 410
-/* 411 "Length Required". RFC7231, Section 6.5.10. */
-#define MHD_HTTP_LENGTH_REQUIRED 411
-/* 412 "Precondition Failed". RFC7232, Section 4.2; RFC8144, Section 3.2. */
-#define MHD_HTTP_PRECONDITION_FAILED 412
-/* 413 "Payload Too Large". RFC7231, Section 6.5.11. */
-#define MHD_HTTP_PAYLOAD_TOO_LARGE 413
-/* 414 "URI Too Long". RFC7231, Section 6.5.12. */
-#define MHD_HTTP_URI_TOO_LONG 414
-/* 415 "Unsupported Media Type". RFC7231, Section 6.5.13; RFC7694, Section 3. */
-#define MHD_HTTP_UNSUPPORTED_MEDIA_TYPE 415
-/* 416 "Range Not Satisfiable". RFC7233, Section 4.4. */
-#define MHD_HTTP_RANGE_NOT_SATISFIABLE 416
-/* 417 "Expectation Failed". RFC7231, Section 6.5.14. */
-#define MHD_HTTP_EXPECTATION_FAILED 417
-
-/* 421 "Misdirected Request". RFC7540, Section 9.1.2. */
-#define MHD_HTTP_MISDIRECTED_REQUEST 421
-/* 422 "Unprocessable Entity". RFC4918. */
-#define MHD_HTTP_UNPROCESSABLE_ENTITY 422
+/* 408 "Request Timeout". RFC-ietf-httpbis-semantics, Section 15.5.9. */
+#define MHD_HTTP_REQUEST_TIMEOUT 408
+/* 409 "Conflict". RFC-ietf-httpbis-semantics, Section 15.5.10. */
+#define MHD_HTTP_CONFLICT 409
+/* 410 "Gone". RFC-ietf-httpbis-semantics, Section 15.5.11. */
+#define MHD_HTTP_GONE 410
+/* 411 "Length Required". RFC-ietf-httpbis-semantics, Section 15.5.12. */
+#define MHD_HTTP_LENGTH_REQUIRED 411
+/* 412 "Precondition Failed". RFC-ietf-httpbis-semantics, Section 15.5.13. */
+#define MHD_HTTP_PRECONDITION_FAILED 412
+/* 413 "Content Too Large". RFC-ietf-httpbis-semantics, Section 15.5.14. */
+#define MHD_HTTP_CONTENT_TOO_LARGE 413
+/* 414 "URI Too Long". RFC-ietf-httpbis-semantics, Section 15.5.15. */
+#define MHD_HTTP_URI_TOO_LONG 414
+/* 415 "Unsupported Media Type". RFC-ietf-httpbis-semantics, Section 15.5.16. */
+#define MHD_HTTP_UNSUPPORTED_MEDIA_TYPE 415
+/* 416 "Range Not Satisfiable". RFC-ietf-httpbis-semantics, Section 15.5.17. */
+#define MHD_HTTP_RANGE_NOT_SATISFIABLE 416
+/* 417 "Expectation Failed". RFC-ietf-httpbis-semantics, Section 15.5.18. */
+#define MHD_HTTP_EXPECTATION_FAILED 417
+
+
+/* 421 "Misdirected Request". RFC-ietf-httpbis-semantics, Section 15.5.20. */
+#define MHD_HTTP_MISDIRECTED_REQUEST 421
+/* 422 "Unprocessable Content". RFC-ietf-httpbis-semantics, Section 15.5.21. */
+#define MHD_HTTP_UNPROCESSABLE_CONTENT 422
/* 423 "Locked". RFC4918. */
-#define MHD_HTTP_LOCKED 423
+#define MHD_HTTP_LOCKED 423
/* 424 "Failed Dependency". RFC4918. */
-#define MHD_HTTP_FAILED_DEPENDENCY 424
+#define MHD_HTTP_FAILED_DEPENDENCY 424
/* 425 "Too Early". RFC8470. */
-#define MHD_HTTP_TOO_EARLY 425
-/* 426 "Upgrade Required". RFC7231, Section 6.5.15. */
-#define MHD_HTTP_UPGRADE_REQUIRED 426
+#define MHD_HTTP_TOO_EARLY 425
+/* 426 "Upgrade Required". RFC-ietf-httpbis-semantics, Section 15.5.22. */
+#define MHD_HTTP_UPGRADE_REQUIRED 426
/* 428 "Precondition Required". RFC6585. */
-#define MHD_HTTP_PRECONDITION_REQUIRED 428
+#define MHD_HTTP_PRECONDITION_REQUIRED 428
/* 429 "Too Many Requests". RFC6585. */
-#define MHD_HTTP_TOO_MANY_REQUESTS 429
+#define MHD_HTTP_TOO_MANY_REQUESTS 429
/* 431 "Request Header Fields Too Large". RFC6585. */
#define MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE 431
@@ -439,43 +455,42 @@
/* 451 "Unavailable For Legal Reasons". RFC7725. */
#define MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS 451
-/* 500 "Internal Server Error". RFC7231, Section 6.6.1. */
-#define MHD_HTTP_INTERNAL_SERVER_ERROR 500
-/* 501 "Not Implemented". RFC7231, Section 6.6.2. */
-#define MHD_HTTP_NOT_IMPLEMENTED 501
-/* 502 "Bad Gateway". RFC7231, Section 6.6.3. */
-#define MHD_HTTP_BAD_GATEWAY 502
-/* 503 "Service Unavailable". RFC7231, Section 6.6.4. */
-#define MHD_HTTP_SERVICE_UNAVAILABLE 503
-/* 504 "Gateway Timeout". RFC7231, Section 6.6.5. */
-#define MHD_HTTP_GATEWAY_TIMEOUT 504
-/* 505 "HTTP Version Not Supported". RFC7231, Section 6.6.6. */
-#define MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED 505
+/* 500 "Internal Server Error". RFC-ietf-httpbis-semantics, Section 15.6.1. */
+#define MHD_HTTP_INTERNAL_SERVER_ERROR 500
+/* 501 "Not Implemented". RFC-ietf-httpbis-semantics, Section 15.6.2. */
+#define MHD_HTTP_NOT_IMPLEMENTED 501
+/* 502 "Bad Gateway". RFC-ietf-httpbis-semantics, Section 15.6.3. */
+#define MHD_HTTP_BAD_GATEWAY 502
+/* 503 "Service Unavailable". RFC-ietf-httpbis-semantics, Section 15.6.4. */
+#define MHD_HTTP_SERVICE_UNAVAILABLE 503
+/* 504 "Gateway Timeout". RFC-ietf-httpbis-semantics, Section 15.6.5. */
+#define MHD_HTTP_GATEWAY_TIMEOUT 504
+/* 505 "HTTP Version Not Supported". RFC-ietf-httpbis-semantics, Section 15.6.6. */
+#define MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED 505
/* 506 "Variant Also Negotiates". RFC2295. */
-#define MHD_HTTP_VARIANT_ALSO_NEGOTIATES 506
+#define MHD_HTTP_VARIANT_ALSO_NEGOTIATES 506
/* 507 "Insufficient Storage". RFC4918. */
-#define MHD_HTTP_INSUFFICIENT_STORAGE 507
+#define MHD_HTTP_INSUFFICIENT_STORAGE 507
/* 508 "Loop Detected". RFC5842. */
-#define MHD_HTTP_LOOP_DETECTED 508
+#define MHD_HTTP_LOOP_DETECTED 508
/* 510 "Not Extended". RFC2774. */
-#define MHD_HTTP_NOT_EXTENDED 510
+#define MHD_HTTP_NOT_EXTENDED 510
/* 511 "Network Authentication Required". RFC6585. */
#define MHD_HTTP_NETWORK_AUTHENTICATION_REQUIRED 511
/* Not registered non-standard codes */
/* 449 "Reply With". MS IIS extension. */
-#define MHD_HTTP_RETRY_WITH 449
+#define MHD_HTTP_RETRY_WITH 449
/* 450 "Blocked by Windows Parental Controls". MS extension. */
#define MHD_HTTP_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS 450
/* 509 "Bandwidth Limit Exceeded". Apache extension. */
-#define MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED 509
-
+#define MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED 509
-/* Deprecated codes */
+/* Deprecated names and codes */
/** @deprecated */
#define MHD_HTTP_METHOD_NOT_ACCEPTABLE \
_MHD_DEPR_IN_MACRO ( \
@@ -485,7 +500,13 @@
/** @deprecated */
#define MHD_HTTP_REQUEST_ENTITY_TOO_LARGE \
_MHD_DEPR_IN_MACRO ( \
- "Value MHD_HTTP_REQUEST_ENTITY_TOO_LARGE is deprecated, use MHD_HTTP_PAYLOAD_TOO_LARGE") \
+ "Value MHD_HTTP_REQUEST_ENTITY_TOO_LARGE is deprecated, use MHD_HTTP_CONTENT_TOO_LARGE") \
+ 413
+
+/** @deprecated */
+#define MHD_HTTP_PAYLOAD_TOO_LARGE \
+ _MHD_DEPR_IN_MACRO ( \
+ "Value MHD_HTTP_PAYLOAD_TOO_LARGE is deprecated, use MHD_HTTP_CONTENT_TOO_LARGE") \
413
/** @deprecated */
@@ -501,6 +522,12 @@
416
/** @deprecated */
+#define MHD_HTTP_UNPROCESSABLE_ENTITY \
+ _MHD_DEPR_IN_MACRO ( \
+ "Value MHD_HTTP_UNPROCESSABLE_ENTITY is deprecated, use MHD_HTTP_UNPROCESSABLE_CONTENT") \
+ 422
+
+/** @deprecated */
#define MHD_HTTP_UNORDERED_COLLECTION \
_MHD_DEPR_IN_MACRO ( \
"Value MHD_HTTP_UNORDERED_COLLECTION is deprecated as it was removed from RFC") \
@@ -526,6 +553,15 @@
/**
+ * Returns the length of the string reason phrase for a response code.
+ *
+ * If message string is not available for a status code,
+ * 0 is returned.
+ */
+_MHD_EXTERN size_t
+MHD_get_reason_phrase_len_for (unsigned int code);
+
+/**
* Flag to be or-ed with MHD_HTTP status code for
* SHOUTcast. This will cause the response to begin
* with the SHOUTcast "ICY" line instead of "HTTP".
@@ -536,354 +572,490 @@
/**
* @defgroup headers HTTP headers
* These are the standard headers found in HTTP requests and responses.
- * See: http://www.iana.org/assignments/message-headers/message-headers.xml
- * Registry export date: 2020-09-20
+ * See: https://www.iana.org/assignments/http-fields/http-fields.xhtml
+ * Registry export date: 2021-12-19
* @{
*/
/* Main HTTP headers. */
-/* Standard. RFC7231, Section 5.3.2 */
-#define MHD_HTTP_HEADER_ACCEPT "Accept"
-/* Standard. RFC7231, Section 5.3.3 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 12.5.1 */
+#define MHD_HTTP_HEADER_ACCEPT "Accept"
+/* Deprecated. RFC-ietf-httpbis-semantics-19, Section 12.5.2 */
#define MHD_HTTP_HEADER_ACCEPT_CHARSET "Accept-Charset"
-/* Standard. RFC7231, Section 5.3.4; RFC7694, Section 3 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 12.5.3 */
#define MHD_HTTP_HEADER_ACCEPT_ENCODING "Accept-Encoding"
-/* Standard. RFC7231, Section 5.3.5 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 12.5.4 */
#define MHD_HTTP_HEADER_ACCEPT_LANGUAGE "Accept-Language"
-/* Standard. RFC7233, Section 2.3 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 14.3 */
#define MHD_HTTP_HEADER_ACCEPT_RANGES "Accept-Ranges"
-/* Standard. RFC7234, Section 5.1 */
-#define MHD_HTTP_HEADER_AGE "Age"
-/* Standard. RFC7231, Section 7.4.1 */
-#define MHD_HTTP_HEADER_ALLOW "Allow"
-/* Standard. RFC7235, Section 4.2 */
+/* Permanent. RFC-ietf-httpbis-cache-19, Section 5.1 */
+#define MHD_HTTP_HEADER_AGE "Age"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 10.2.1 */
+#define MHD_HTTP_HEADER_ALLOW "Allow"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 11.6.3 */
+#define MHD_HTTP_HEADER_AUTHENTICATION_INFO "Authentication-Info"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 11.6.2 */
#define MHD_HTTP_HEADER_AUTHORIZATION "Authorization"
-/* Standard. RFC7234, Section 5.2 */
+/* Permanent. RFC-ietf-httpbis-cache-19, Section 5.2 */
#define MHD_HTTP_HEADER_CACHE_CONTROL "Cache-Control"
-/* Reserved. RFC7230, Section 8.1 */
-#define MHD_HTTP_HEADER_CLOSE "Close"
-/* Standard. RFC7230, Section 6.1 */
-#define MHD_HTTP_HEADER_CONNECTION "Connection"
-/* Standard. RFC7231, Section 3.1.2.2 */
+/* Permanent. RFC-ietf-httpbis-cache-header-10 */
+#define MHD_HTTP_HEADER_CACHE_STATUS "Cache-Status"
+/* Permanent. RFC-ietf-httpbis-messaging-19, Section 9.6 */
+#define MHD_HTTP_HEADER_CLOSE "Close"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 7.6.1 */
+#define MHD_HTTP_HEADER_CONNECTION "Connection"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 8.4 */
#define MHD_HTTP_HEADER_CONTENT_ENCODING "Content-Encoding"
-/* Standard. RFC7231, Section 3.1.3.2 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 8.5 */
#define MHD_HTTP_HEADER_CONTENT_LANGUAGE "Content-Language"
-/* Standard. RFC7230, Section 3.3.2 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 8.6 */
#define MHD_HTTP_HEADER_CONTENT_LENGTH "Content-Length"
-/* Standard. RFC7231, Section 3.1.4.2 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 8.7 */
#define MHD_HTTP_HEADER_CONTENT_LOCATION "Content-Location"
-/* Standard. RFC7233, Section 4.2 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 14.4 */
#define MHD_HTTP_HEADER_CONTENT_RANGE "Content-Range"
-/* Standard. RFC7231, Section 3.1.1.5 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 8.3 */
#define MHD_HTTP_HEADER_CONTENT_TYPE "Content-Type"
-/* Standard. RFC7231, Section 7.1.1.2 */
-#define MHD_HTTP_HEADER_DATE "Date"
-/* Standard. RFC7232, Section 2.3 */
-#define MHD_HTTP_HEADER_ETAG "ETag"
-/* Standard. RFC7231, Section 5.1.1 */
-#define MHD_HTTP_HEADER_EXPECT "Expect"
-/* Standard. RFC7234, Section 5.3 */
-#define MHD_HTTP_HEADER_EXPIRES "Expires"
-/* Standard. RFC7231, Section 5.5.1 */
-#define MHD_HTTP_HEADER_FROM "From"
-/* Standard. RFC7230, Section 5.4 */
-#define MHD_HTTP_HEADER_HOST "Host"
-/* Standard. RFC7232, Section 3.1 */
-#define MHD_HTTP_HEADER_IF_MATCH "If-Match"
-/* Standard. RFC7232, Section 3.3 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 6.6.1 */
+#define MHD_HTTP_HEADER_DATE "Date"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 8.8.3 */
+#define MHD_HTTP_HEADER_ETAG "ETag"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 10.1.1 */
+#define MHD_HTTP_HEADER_EXPECT "Expect"
+/* Permanent. RFC-ietf-httpbis-expect-ct-08 */
+#define MHD_HTTP_HEADER_EXPECT_CT "Expect-CT"
+/* Permanent. RFC-ietf-httpbis-cache-19, Section 5.3 */
+#define MHD_HTTP_HEADER_EXPIRES "Expires"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 10.1.2 */
+#define MHD_HTTP_HEADER_FROM "From"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 7.2 */
+#define MHD_HTTP_HEADER_HOST "Host"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 13.1.1 */
+#define MHD_HTTP_HEADER_IF_MATCH "If-Match"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 13.1.3 */
#define MHD_HTTP_HEADER_IF_MODIFIED_SINCE "If-Modified-Since"
-/* Standard. RFC7232, Section 3.2 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 13.1.2 */
#define MHD_HTTP_HEADER_IF_NONE_MATCH "If-None-Match"
-/* Standard. RFC7233, Section 3.2 */
-#define MHD_HTTP_HEADER_IF_RANGE "If-Range"
-/* Standard. RFC7232, Section 3.4 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 13.1.5 */
+#define MHD_HTTP_HEADER_IF_RANGE "If-Range"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 13.1.4 */
#define MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE "If-Unmodified-Since"
-/* Standard. RFC7232, Section 2.2 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 8.8.2 */
#define MHD_HTTP_HEADER_LAST_MODIFIED "Last-Modified"
-/* Standard. RFC7231, Section 7.1.2 */
-#define MHD_HTTP_HEADER_LOCATION "Location"
-/* Standard. RFC7231, Section 5.1.2 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 10.2.2 */
+#define MHD_HTTP_HEADER_LOCATION "Location"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 7.6.2 */
#define MHD_HTTP_HEADER_MAX_FORWARDS "Max-Forwards"
-/* Standard. RFC7231, Appendix A.1 */
+/* Permanent. RFC-ietf-httpbis-messaging-19, Appendix B.1 */
#define MHD_HTTP_HEADER_MIME_VERSION "MIME-Version"
-/* Standard. RFC7234, Section 5.4 */
-#define MHD_HTTP_HEADER_PRAGMA "Pragma"
-/* Standard. RFC7235, Section 4.3 */
+/* Permanent. RFC-ietf-httpbis-cache-19, Section 5.4 */
+#define MHD_HTTP_HEADER_PRAGMA "Pragma"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 11.7.1 */
#define MHD_HTTP_HEADER_PROXY_AUTHENTICATE "Proxy-Authenticate"
-/* Standard. RFC7235, Section 4.4 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 11.7.3 */
+#define MHD_HTTP_HEADER_PROXY_AUTHENTICATION_INFO "Proxy-Authentication-Info"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 11.7.2 */
#define MHD_HTTP_HEADER_PROXY_AUTHORIZATION "Proxy-Authorization"
-/* Standard. RFC7233, Section 3.1 */
-#define MHD_HTTP_HEADER_RANGE "Range"
-/* Standard. RFC7231, Section 5.5.2 */
-#define MHD_HTTP_HEADER_REFERER "Referer"
-/* Standard. RFC7231, Section 7.1.3 */
-#define MHD_HTTP_HEADER_RETRY_AFTER "Retry-After"
-/* Standard. RFC7231, Section 7.4.2 */
-#define MHD_HTTP_HEADER_SERVER "Server"
-/* Standard. RFC7230, Section 4.3 */
-#define MHD_HTTP_HEADER_TE "TE"
-/* Standard. RFC7230, Section 4.4 */
-#define MHD_HTTP_HEADER_TRAILER "Trailer"
-/* Standard. RFC7230, Section 3.3.1 */
+/* Permanent. RFC-ietf-httpbis-proxy-status-08 */
+#define MHD_HTTP_HEADER_PROXY_STATUS "Proxy-Status"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 14.2 */
+#define MHD_HTTP_HEADER_RANGE "Range"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 10.1.3 */
+#define MHD_HTTP_HEADER_REFERER "Referer"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 10.2.3 */
+#define MHD_HTTP_HEADER_RETRY_AFTER "Retry-After"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 10.2.4 */
+#define MHD_HTTP_HEADER_SERVER "Server"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 10.1.4 */
+#define MHD_HTTP_HEADER_TE "TE"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 6.6.2 */
+#define MHD_HTTP_HEADER_TRAILER "Trailer"
+/* Permanent. RFC-ietf-httpbis-messaging-19, Section 6.1 */
#define MHD_HTTP_HEADER_TRANSFER_ENCODING "Transfer-Encoding"
-/* Standard. RFC7230, Section 6.7 */
-#define MHD_HTTP_HEADER_UPGRADE "Upgrade"
-/* Standard. RFC7231, Section 5.5.3 */
-#define MHD_HTTP_HEADER_USER_AGENT "User-Agent"
-/* Standard. RFC7231, Section 7.1.4 */
-#define MHD_HTTP_HEADER_VARY "Vary"
-/* Standard. RFC7230, Section 5.7.1 */
-#define MHD_HTTP_HEADER_VIA "Via"
-/* Standard. RFC7235, Section 4.1 */
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 7.8 */
+#define MHD_HTTP_HEADER_UPGRADE "Upgrade"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 10.1.5 */
+#define MHD_HTTP_HEADER_USER_AGENT "User-Agent"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 12.5.5 */
+#define MHD_HTTP_HEADER_VARY "Vary"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 7.6.3 */
+#define MHD_HTTP_HEADER_VIA "Via"
+/* Obsoleted. RFC-ietf-httpbis-cache-19, Section 5.5 */
+#define MHD_HTTP_HEADER_WARNING "Warning"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 11.6.1 */
#define MHD_HTTP_HEADER_WWW_AUTHENTICATE "WWW-Authenticate"
-/* Standard. RFC7234, Section 5.5 */
-#define MHD_HTTP_HEADER_WARNING "Warning"
+/* Permanent. RFC-ietf-httpbis-semantics-19, Section 12.5.5 */
+#define MHD_HTTP_HEADER_ASTERISK "*"
/* Additional HTTP headers. */
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_A_IM "A-IM"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_A_IM "A-IM"
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_ACCEPT_ADDITIONS "Accept-Additions"
-/* Experimental. RFC-ietf-httpbis-client-hints-15, Section 3.1 */
-#define MHD_HTTP_HEADER_ACCEPT_CH "Accept-CH"
-/* Informational. RFC7089 */
+/* Permanent. RFC8942, Section 3.1 */
+#define MHD_HTTP_HEADER_ACCEPT_CH "Accept-CH"
+/* Permanent. RFC7089 */
#define MHD_HTTP_HEADER_ACCEPT_DATETIME "Accept-Datetime"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_ACCEPT_FEATURES "Accept-Features"
-/* No category. RFC5789 */
-#define MHD_HTTP_HEADER_ACCEPT_PATCH "Accept-Patch"
-/* Standard. https://www.w3.org/TR/ldp/ */
-#define MHD_HTTP_HEADER_ACCEPT_POST "Accept-Post"
-/* Standard. RFC7639, Section 2 */
-#define MHD_HTTP_HEADER_ALPN "ALPN"
-/* Standard. RFC7838 */
-#define MHD_HTTP_HEADER_ALT_SVC "Alt-Svc"
-/* Standard. RFC7838 */
-#define MHD_HTTP_HEADER_ALT_USED "Alt-Used"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_ALTERNATES "Alternates"
-/* No category. RFC4437 */
+/* Permanent. https://www.w3.org/TR/ldp/ */
+#define MHD_HTTP_HEADER_ACCEPT_POST "Accept-Post"
+/* Permanent. https://fetch.spec.whatwg.org/#http-access-control-allow-credentials */
+#define MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS \
+ "Access-Control-Allow-Credentials"
+/* Permanent. https://fetch.spec.whatwg.org/#http-access-control-allow-headers */
+#define MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS \
+ "Access-Control-Allow-Headers"
+/* Permanent. https://fetch.spec.whatwg.org/#http-access-control-allow-methods */
+#define MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_METHODS \
+ "Access-Control-Allow-Methods"
+/* Permanent. https://fetch.spec.whatwg.org/#http-access-control-allow-origin */
+#define MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN \
+ "Access-Control-Allow-Origin"
+/* Permanent. https://fetch.spec.whatwg.org/#http-access-control-expose-headers */
+#define MHD_HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS \
+ "Access-Control-Expose-Headers"
+/* Permanent. https://fetch.spec.whatwg.org/#http-access-control-max-age */
+#define MHD_HTTP_HEADER_ACCESS_CONTROL_MAX_AGE "Access-Control-Max-Age"
+/* Permanent. https://fetch.spec.whatwg.org/#http-access-control-request-headers */
+#define MHD_HTTP_HEADER_ACCESS_CONTROL_REQUEST_HEADERS \
+ "Access-Control-Request-Headers"
+/* Permanent. https://fetch.spec.whatwg.org/#http-access-control-request-method */
+#define MHD_HTTP_HEADER_ACCESS_CONTROL_REQUEST_METHOD \
+ "Access-Control-Request-Method"
+/* Permanent. RFC7639, Section 2 */
+#define MHD_HTTP_HEADER_ALPN "ALPN"
+/* Permanent. RFC7838 */
+#define MHD_HTTP_HEADER_ALT_SVC "Alt-Svc"
+/* Permanent. RFC7838 */
+#define MHD_HTTP_HEADER_ALT_USED "Alt-Used"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_ALTERNATES "Alternates"
+/* Permanent. RFC4437 */
#define MHD_HTTP_HEADER_APPLY_TO_REDIRECT_REF "Apply-To-Redirect-Ref"
-/* Experimental. RFC8053, Section 4 */
+/* Permanent. RFC8053, Section 4 */
#define MHD_HTTP_HEADER_AUTHENTICATION_CONTROL "Authentication-Control"
-/* Standard. RFC7615, Section 3 */
-#define MHD_HTTP_HEADER_AUTHENTICATION_INFO "Authentication-Info"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_C_EXT "C-Ext"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_C_MAN "C-Man"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_C_OPT "C-Opt"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_C_PEP "C-PEP"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_C_PEP_INFO "C-PEP-Info"
-/* Standard. RFC8607, Section 5.1 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_C_EXT "C-Ext"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_C_MAN "C-Man"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_C_OPT "C-Opt"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_C_PEP "C-PEP"
+/* Permanent. RFC8607, Section 5.1 */
#define MHD_HTTP_HEADER_CAL_MANAGED_ID "Cal-Managed-ID"
-/* Standard. RFC7809, Section 7.1 */
+/* Permanent. RFC7809, Section 7.1 */
#define MHD_HTTP_HEADER_CALDAV_TIMEZONES "CalDAV-Timezones"
-/* Standard. RFC8586 */
-#define MHD_HTTP_HEADER_CDN_LOOP "CDN-Loop"
-/* Standard. RFC8739, Section 3.3 */
+/* Permanent. RFC8586 */
+#define MHD_HTTP_HEADER_CDN_LOOP "CDN-Loop"
+/* Permanent. RFC8739, Section 3.3 */
#define MHD_HTTP_HEADER_CERT_NOT_AFTER "Cert-Not-After"
-/* Standard. RFC8739, Section 3.3 */
+/* Permanent. RFC8739, Section 3.3 */
#define MHD_HTTP_HEADER_CERT_NOT_BEFORE "Cert-Not-Before"
-/* Obsoleted. RFC2068; RFC2616 */
-#define MHD_HTTP_HEADER_CONTENT_BASE "Content-Base"
-/* Standard. RFC6266 */
+/* Permanent. RFC6266 */
#define MHD_HTTP_HEADER_CONTENT_DISPOSITION "Content-Disposition"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_CONTENT_ID "Content-ID"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_CONTENT_MD5 "Content-MD5"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_CONTENT_ID "Content-ID"
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_CONTENT_SCRIPT_TYPE "Content-Script-Type"
-/* No category. RFC4229 */
+/* Permanent. https://www.w3.org/TR/CSP/#csp-header */
+#define MHD_HTTP_HEADER_CONTENT_SECURITY_POLICY "Content-Security-Policy"
+/* Permanent. https://www.w3.org/TR/CSP/#cspro-header */
+#define MHD_HTTP_HEADER_CONTENT_SECURITY_POLICY_REPORT_ONLY \
+ "Content-Security-Policy-Report-Only"
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_CONTENT_STYLE_TYPE "Content-Style-Type"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_CONTENT_VERSION "Content-Version"
-/* Standard. RFC6265 */
-#define MHD_HTTP_HEADER_COOKIE "Cookie"
-/* Obsoleted. RFC2965; RFC6265 */
-#define MHD_HTTP_HEADER_COOKIE2 "Cookie2"
-/* Standard. RFC5323 */
-#define MHD_HTTP_HEADER_DASL "DASL"
-/* Standard. RFC4918 */
-#define MHD_HTTP_HEADER_DAV "DAV"
-/* No category. RFC4229 */
+/* Permanent. RFC6265 */
+#define MHD_HTTP_HEADER_COOKIE "Cookie"
+/* Permanent. https://html.spec.whatwg.org/multipage/origin.html#cross-origin-embedder-policy */
+#define MHD_HTTP_HEADER_CROSS_ORIGIN_EMBEDDER_POLICY \
+ "Cross-Origin-Embedder-Policy"
+/* Permanent. https://html.spec.whatwg.org/multipage/origin.html#cross-origin-embedder-policy-report-only */
+#define MHD_HTTP_HEADER_CROSS_ORIGIN_EMBEDDER_POLICY_REPORT_ONLY \
+ "Cross-Origin-Embedder-Policy-Report-Only"
+/* Permanent. https://html.spec.whatwg.org/multipage/origin.html#cross-origin-opener-policy-2 */
+#define MHD_HTTP_HEADER_CROSS_ORIGIN_OPENER_POLICY "Cross-Origin-Opener-Policy"
+/* Permanent. https://html.spec.whatwg.org/multipage/origin.html#cross-origin-opener-policy-report-only */
+#define MHD_HTTP_HEADER_CROSS_ORIGIN_OPENER_POLICY_REPORT_ONLY \
+ "Cross-Origin-Opener-Policy-Report-Only"
+/* Permanent. https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header */
+#define MHD_HTTP_HEADER_CROSS_ORIGIN_RESOURCE_POLICY \
+ "Cross-Origin-Resource-Policy"
+/* Permanent. RFC5323 */
+#define MHD_HTTP_HEADER_DASL "DASL"
+/* Permanent. RFC4918 */
+#define MHD_HTTP_HEADER_DAV "DAV"
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_DEFAULT_STYLE "Default-Style"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_DELTA_BASE "Delta-Base"
-/* Standard. RFC4918 */
-#define MHD_HTTP_HEADER_DEPTH "Depth"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_DELTA_BASE "Delta-Base"
+/* Permanent. RFC4918 */
+#define MHD_HTTP_HEADER_DEPTH "Depth"
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_DERIVED_FROM "Derived-From"
-/* Standard. RFC4918 */
-#define MHD_HTTP_HEADER_DESTINATION "Destination"
-/* No category. RFC4229 */
+/* Permanent. RFC4918 */
+#define MHD_HTTP_HEADER_DESTINATION "Destination"
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_DIFFERENTIAL_ID "Differential-ID"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_DIGEST "Digest"
-/* Standard. RFC8470 */
-#define MHD_HTTP_HEADER_EARLY_DATA "Early-Data"
-/* Experimental. RFC-ietf-httpbis-expect-ct-08 */
-#define MHD_HTTP_HEADER_EXPECT_CT "Expect-CT"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_EXT "Ext"
-/* Standard. RFC7239 */
-#define MHD_HTTP_HEADER_FORWARDED "Forwarded"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_GETPROFILE "GetProfile"
-/* Experimental. RFC7486, Section 6.1.1 */
-#define MHD_HTTP_HEADER_HOBAREG "Hobareg"
-/* Standard. RFC7540, Section 3.2.1 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_DIGEST "Digest"
+/* Permanent. RFC8470 */
+#define MHD_HTTP_HEADER_EARLY_DATA "Early-Data"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_EXT "Ext"
+/* Permanent. RFC7239 */
+#define MHD_HTTP_HEADER_FORWARDED "Forwarded"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_GETPROFILE "GetProfile"
+/* Permanent. RFC7486, Section 6.1.1 */
+#define MHD_HTTP_HEADER_HOBAREG "Hobareg"
+/* Permanent. RFC7540, Section 3.2.1 */
#define MHD_HTTP_HEADER_HTTP2_SETTINGS "HTTP2-Settings"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_IM "IM"
-/* Standard. RFC4918 */
-#define MHD_HTTP_HEADER_IF "If"
-/* Standard. RFC6638 */
+/* Permanent. RFC4918 */
+#define MHD_HTTP_HEADER_IF "If"
+/* Permanent. RFC6638 */
#define MHD_HTTP_HEADER_IF_SCHEDULE_TAG_MATCH "If-Schedule-Tag-Match"
-/* Standard. RFC8473 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_IM "IM"
+/* Permanent. RFC8473 */
#define MHD_HTTP_HEADER_INCLUDE_REFERRED_TOKEN_BINDING_ID \
"Include-Referred-Token-Binding-ID"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_KEEP_ALIVE "Keep-Alive"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_LABEL "Label"
-/* Standard. RFC8288 */
-#define MHD_HTTP_HEADER_LINK "Link"
-/* Standard. RFC4918 */
-#define MHD_HTTP_HEADER_LOCK_TOKEN "Lock-Token"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_MAN "Man"
-/* Informational. RFC7089 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_KEEP_ALIVE "Keep-Alive"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_LABEL "Label"
+/* Permanent. https://html.spec.whatwg.org/multipage/server-sent-events.html#last-event-id */
+#define MHD_HTTP_HEADER_LAST_EVENT_ID "Last-Event-ID"
+/* Permanent. RFC8288 */
+#define MHD_HTTP_HEADER_LINK "Link"
+/* Permanent. RFC4918 */
+#define MHD_HTTP_HEADER_LOCK_TOKEN "Lock-Token"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_MAN "Man"
+/* Permanent. RFC7089 */
#define MHD_HTTP_HEADER_MEMENTO_DATETIME "Memento-Datetime"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_METER "Meter"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_NEGOTIATE "Negotiate"
-/* Standard. OData Version 4.01 Part 1: Protocol; OASIS; Chet_Ensign */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_METER "Meter"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_NEGOTIATE "Negotiate"
+/* Permanent. OData Version 4.01 Part 1: Protocol; OASIS; Chet_Ensign */
#define MHD_HTTP_HEADER_ODATA_ENTITYID "OData-EntityId"
-/* Standard. OData Version 4.01 Part 1: Protocol; OASIS; Chet_Ensign */
+/* Permanent. OData Version 4.01 Part 1: Protocol; OASIS; Chet_Ensign */
#define MHD_HTTP_HEADER_ODATA_ISOLATION "OData-Isolation"
-/* Standard. OData Version 4.01 Part 1: Protocol; OASIS; Chet_Ensign */
+/* Permanent. OData Version 4.01 Part 1: Protocol; OASIS; Chet_Ensign */
#define MHD_HTTP_HEADER_ODATA_MAXVERSION "OData-MaxVersion"
-/* Standard. OData Version 4.01 Part 1: Protocol; OASIS; Chet_Ensign */
+/* Permanent. OData Version 4.01 Part 1: Protocol; OASIS; Chet_Ensign */
#define MHD_HTTP_HEADER_ODATA_VERSION "OData-Version"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_OPT "Opt"
-/* Experimental. RFC8053, Section 3 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_OPT "Opt"
+/* Permanent. RFC8053, Section 3 */
#define MHD_HTTP_HEADER_OPTIONAL_WWW_AUTHENTICATE "Optional-WWW-Authenticate"
-/* Standard. RFC4229 */
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_ORDERING_TYPE "Ordering-Type"
-/* Standard. RFC6454 */
-#define MHD_HTTP_HEADER_ORIGIN "Origin"
-/* Standard. RFC8613, Section 11.1 */
-#define MHD_HTTP_HEADER_OSCORE "OSCORE"
-/* Standard. RFC4918 */
-#define MHD_HTTP_HEADER_OVERWRITE "Overwrite"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_P3P "P3P"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_PEP "PEP"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_PICS_LABEL "PICS-Label"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_PEP_INFO "Pep-Info"
-/* Standard. RFC4229 */
-#define MHD_HTTP_HEADER_POSITION "Position"
-/* Standard. RFC7240 */
-#define MHD_HTTP_HEADER_PREFER "Prefer"
-/* Standard. RFC7240 */
+/* Permanent. RFC6454 */
+#define MHD_HTTP_HEADER_ORIGIN "Origin"
+/* Permanent. https://html.spec.whatwg.org/multipage/origin.html#origin-agent-cluster */
+#define MHD_HTTP_HEADER_ORIGIN_AGENT_CLUSTER "Origin-Agent-Cluster"
+/* Permanent. RFC8613, Section 11.1 */
+#define MHD_HTTP_HEADER_OSCORE "OSCORE"
+/* Permanent. OASIS Project Specification 01; OASIS; Chet_Ensign */
+#define MHD_HTTP_HEADER_OSLC_CORE_VERSION "OSLC-Core-Version"
+/* Permanent. RFC4918 */
+#define MHD_HTTP_HEADER_OVERWRITE "Overwrite"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_P3P "P3P"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_PEP "PEP"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_PEP_INFO "Pep-Info"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_PICS_LABEL "PICS-Label"
+/* Permanent. https://html.spec.whatwg.org/multipage/links.html#ping-from */
+#define MHD_HTTP_HEADER_PING_FROM "Ping-From"
+/* Permanent. https://html.spec.whatwg.org/multipage/links.html#ping-to */
+#define MHD_HTTP_HEADER_PING_TO "Ping-To"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_POSITION "Position"
+/* Permanent. RFC7240 */
+#define MHD_HTTP_HEADER_PREFER "Prefer"
+/* Permanent. RFC7240 */
#define MHD_HTTP_HEADER_PREFERENCE_APPLIED "Preference-Applied"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_PROFILEOBJECT "ProfileObject"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_PROTOCOL "Protocol"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_PROTOCOL_INFO "Protocol-Info"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_PROTOCOL_QUERY "Protocol-Query"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_PROTOCOL "Protocol"
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_PROTOCOL_REQUEST "Protocol-Request"
-/* Standard. RFC7615, Section 4 */
-#define MHD_HTTP_HEADER_PROXY_AUTHENTICATION_INFO "Proxy-Authentication-Info"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_PROXY_FEATURES "Proxy-Features"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_PROXY_INSTRUCTION "Proxy-Instruction"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_PUBLIC "Public"
-/* Standard. RFC7469 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_PUBLIC "Public"
+/* Permanent. RFC7469 */
#define MHD_HTTP_HEADER_PUBLIC_KEY_PINS "Public-Key-Pins"
-/* Standard. RFC7469 */
+/* Permanent. RFC7469 */
#define MHD_HTTP_HEADER_PUBLIC_KEY_PINS_REPORT_ONLY \
"Public-Key-Pins-Report-Only"
-/* No category. RFC4437 */
+/* Permanent. RFC4437 */
#define MHD_HTTP_HEADER_REDIRECT_REF "Redirect-Ref"
-/* Standard. RFC8555, Section 6.5.1 */
+/* Permanent. https://html.spec.whatwg.org/multipage/browsing-the-web.html#refresh */
+#define MHD_HTTP_HEADER_REFRESH "Refresh"
+/* Permanent. RFC8555, Section 6.5.1 */
#define MHD_HTTP_HEADER_REPLAY_NONCE "Replay-Nonce"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_SAFE "Safe"
-/* Standard. RFC6638 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_SAFE "Safe"
+/* Permanent. RFC6638 */
#define MHD_HTTP_HEADER_SCHEDULE_REPLY "Schedule-Reply"
-/* Standard. RFC6638 */
+/* Permanent. RFC6638 */
#define MHD_HTTP_HEADER_SCHEDULE_TAG "Schedule-Tag"
-/* Standard. RFC8473 */
+/* Permanent. RFC8473 */
#define MHD_HTTP_HEADER_SEC_TOKEN_BINDING "Sec-Token-Binding"
-/* Standard. RFC6455 */
+/* Permanent. RFC6455 */
#define MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT "Sec-WebSocket-Accept"
-/* Standard. RFC6455 */
+/* Permanent. RFC6455 */
#define MHD_HTTP_HEADER_SEC_WEBSOCKET_EXTENSIONS "Sec-WebSocket-Extensions"
-/* Standard. RFC6455 */
+/* Permanent. RFC6455 */
#define MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY "Sec-WebSocket-Key"
-/* Standard. RFC6455 */
+/* Permanent. RFC6455 */
#define MHD_HTTP_HEADER_SEC_WEBSOCKET_PROTOCOL "Sec-WebSocket-Protocol"
-/* Standard. RFC6455 */
+/* Permanent. RFC6455 */
#define MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION "Sec-WebSocket-Version"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_SECURITY_SCHEME "Security-Scheme"
-/* Standard. RFC6265 */
-#define MHD_HTTP_HEADER_SET_COOKIE "Set-Cookie"
-/* Obsoleted. RFC2965; RFC6265 */
-#define MHD_HTTP_HEADER_SET_COOKIE2 "Set-Cookie2"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_SETPROFILE "SetProfile"
-/* Standard. RFC5023 */
-#define MHD_HTTP_HEADER_SLUG "SLUG"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_SOAPACTION "SoapAction"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_STATUS_URI "Status-URI"
-/* Standard. RFC6797 */
+/* Permanent. https://www.w3.org/TR/server-timing/ */
+#define MHD_HTTP_HEADER_SERVER_TIMING "Server-Timing"
+/* Permanent. RFC6265 */
+#define MHD_HTTP_HEADER_SET_COOKIE "Set-Cookie"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_SETPROFILE "SetProfile"
+/* Permanent. RFC5023 */
+#define MHD_HTTP_HEADER_SLUG "SLUG"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_SOAPACTION "SoapAction"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_STATUS_URI "Status-URI"
+/* Permanent. RFC6797 */
#define MHD_HTTP_HEADER_STRICT_TRANSPORT_SECURITY "Strict-Transport-Security"
-/* Informational. RFC8594 */
-#define MHD_HTTP_HEADER_SUNSET "Sunset"
-/* No category. RFC4229 */
+/* Permanent. RFC8594 */
+#define MHD_HTTP_HEADER_SUNSET "Sunset"
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_SURROGATE_CAPABILITY "Surrogate-Capability"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_SURROGATE_CONTROL "Surrogate-Control"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_TCN "TCN"
-/* Standard. RFC4918 */
-#define MHD_HTTP_HEADER_TIMEOUT "Timeout"
-/* Standard. RFC8030, Section 5.4 */
-#define MHD_HTTP_HEADER_TOPIC "Topic"
-/* Standard. RFC8030, Section 5.2 */
-#define MHD_HTTP_HEADER_TTL "TTL"
-/* Standard. RFC8030, Section 5.3 */
-#define MHD_HTTP_HEADER_URGENCY "Urgency"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_URI "URI"
-/* No category. RFC4229 */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_TCN "TCN"
+/* Permanent. RFC4918 */
+#define MHD_HTTP_HEADER_TIMEOUT "Timeout"
+/* Permanent. RFC8030, Section 5.4 */
+#define MHD_HTTP_HEADER_TOPIC "Topic"
+/* Permanent. RFC8030, Section 5.2 */
+#define MHD_HTTP_HEADER_TTL "TTL"
+/* Permanent. RFC8030, Section 5.3 */
+#define MHD_HTTP_HEADER_URGENCY "Urgency"
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_URI "URI"
+/* Permanent. RFC4229 */
#define MHD_HTTP_HEADER_VARIANT_VARY "Variant-Vary"
-/* No category. RFC4229 */
-#define MHD_HTTP_HEADER_WANT_DIGEST "Want-Digest"
-/* Standard. https://fetch.spec.whatwg.org/#x-content-type-options-header */
+/* Permanent. RFC4229 */
+#define MHD_HTTP_HEADER_WANT_DIGEST "Want-Digest"
+/* Permanent. https://fetch.spec.whatwg.org/#x-content-type-options-header */
#define MHD_HTTP_HEADER_X_CONTENT_TYPE_OPTIONS "X-Content-Type-Options"
-/* Informational. RFC7034 */
+/* Permanent. https://html.spec.whatwg.org/multipage/browsing-the-web.html#x-frame-options */
#define MHD_HTTP_HEADER_X_FRAME_OPTIONS "X-Frame-Options"
+/* Provisional. RFC5789 */
+#define MHD_HTTP_HEADER_ACCEPT_PATCH "Accept-Patch"
+/* Provisional. https://github.com/ampproject/amphtml/blob/master/spec/amp-cache-transform.md */
+#define MHD_HTTP_HEADER_AMP_CACHE_TRANSFORM "AMP-Cache-Transform"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_COMPLIANCE "Compliance"
+/* Provisional. https://docs.oasis-open-projects.org/oslc-op/config/v1.0/psd01/config-resources.html#configcontext */
+#define MHD_HTTP_HEADER_CONFIGURATION_CONTEXT "Configuration-Context"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_CONTENT_TRANSFER_ENCODING "Content-Transfer-Encoding"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_COST "Cost"
+/* Provisional. RFC6017 */
+#define MHD_HTTP_HEADER_EDIINT_FEATURES "EDIINT-Features"
+/* Provisional. OData Version 4.01 Part 1: Protocol; OASIS; Chet_Ensign */
+#define MHD_HTTP_HEADER_ISOLATION "Isolation"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_MESSAGE_ID "Message-ID"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_NON_COMPLIANCE "Non-Compliance"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_OPTIONAL "Optional"
+/* Provisional. Repeatable Requests Version 1.0; OASIS; Chet_Ensign */
+#define MHD_HTTP_HEADER_REPEATABILITY_CLIENT_ID "Repeatability-Client-ID"
+/* Provisional. Repeatable Requests Version 1.0; OASIS; Chet_Ensign */
+#define MHD_HTTP_HEADER_REPEATABILITY_FIRST_SENT "Repeatability-First-Sent"
+/* Provisional. Repeatable Requests Version 1.0; OASIS; Chet_Ensign */
+#define MHD_HTTP_HEADER_REPEATABILITY_REQUEST_ID "Repeatability-Request-ID"
+/* Provisional. Repeatable Requests Version 1.0; OASIS; Chet_Ensign */
+#define MHD_HTTP_HEADER_REPEATABILITY_RESULT "Repeatability-Result"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_RESOLUTION_HINT "Resolution-Hint"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_RESOLVER_LOCATION "Resolver-Location"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_SUBOK "SubOK"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_SUBST "Subst"
+/* Provisional. https://www.w3.org/TR/resource-timing-1/#timing-allow-origin */
+#define MHD_HTTP_HEADER_TIMING_ALLOW_ORIGIN "Timing-Allow-Origin"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_TITLE "Title"
+/* Provisional. https://www.w3.org/TR/trace-context/#traceparent-field */
+#define MHD_HTTP_HEADER_TRACEPARENT "Traceparent"
+/* Provisional. https://www.w3.org/TR/trace-context/#tracestate-field */
+#define MHD_HTTP_HEADER_TRACESTATE "Tracestate"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_UA_COLOR "UA-Color"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_UA_MEDIA "UA-Media"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_UA_PIXELS "UA-Pixels"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_UA_RESOLUTION "UA-Resolution"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_UA_WINDOWPIXELS "UA-Windowpixels"
+/* Provisional. RFC4229 */
+#define MHD_HTTP_HEADER_VERSION "Version"
+/* Provisional. W3C Mobile Web Best Practices Working Group */
+#define MHD_HTTP_HEADER_X_DEVICE_ACCEPT "X-Device-Accept"
+/* Provisional. W3C Mobile Web Best Practices Working Group */
+#define MHD_HTTP_HEADER_X_DEVICE_ACCEPT_CHARSET "X-Device-Accept-Charset"
+/* Provisional. W3C Mobile Web Best Practices Working Group */
+#define MHD_HTTP_HEADER_X_DEVICE_ACCEPT_ENCODING "X-Device-Accept-Encoding"
+/* Provisional. W3C Mobile Web Best Practices Working Group */
+#define MHD_HTTP_HEADER_X_DEVICE_ACCEPT_LANGUAGE "X-Device-Accept-Language"
+/* Provisional. W3C Mobile Web Best Practices Working Group */
+#define MHD_HTTP_HEADER_X_DEVICE_USER_AGENT "X-Device-User-Agent"
+/* Deprecated. RFC4229 */
+#define MHD_HTTP_HEADER_C_PEP_INFO "C-PEP-Info"
+/* Deprecated. RFC4229 */
+#define MHD_HTTP_HEADER_PROTOCOL_INFO "Protocol-Info"
+/* Deprecated. RFC4229 */
+#define MHD_HTTP_HEADER_PROTOCOL_QUERY "Protocol-Query"
+/* Obsoleted. https://www.w3.org/TR/2007/WD-access-control-20071126/#access-control0 */
+#define MHD_HTTP_HEADER_ACCESS_CONTROL "Access-Control"
+/* Obsoleted. RFC2068; RFC2616 */
+#define MHD_HTTP_HEADER_CONTENT_BASE "Content-Base"
+/* Obsoleted. RFC2616, Section 14.15; RFC7231, Appendix B */
+#define MHD_HTTP_HEADER_CONTENT_MD5 "Content-MD5"
+/* Obsoleted. RFC2965; RFC6265 */
+#define MHD_HTTP_HEADER_COOKIE2 "Cookie2"
+/* Obsoleted. https://www.w3.org/TR/2007/WD-access-control-20071126/#method-check */
+#define MHD_HTTP_HEADER_METHOD_CHECK "Method-Check"
+/* Obsoleted. https://www.w3.org/TR/2007/WD-access-control-20071126/#method-check-expires */
+#define MHD_HTTP_HEADER_METHOD_CHECK_EXPIRES "Method-Check-Expires"
+/* Obsoleted. https://www.w3.org/TR/2007/WD-access-control-20071126/#referer-root */
+#define MHD_HTTP_HEADER_REFERER_ROOT "Referer-Root"
+/* Obsoleted. RFC2965; RFC6265 */
+#define MHD_HTTP_HEADER_SET_COOKIE2 "Set-Cookie2"
/* Some provisional headers. */
#define MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN \
@@ -905,87 +1077,89 @@
* @defgroup methods HTTP methods
* HTTP methods (as strings).
* See: http://www.iana.org/assignments/http-methods/http-methods.xml
- * Registry export date: 2020-09-20
+ * Registry export date: 2021-12-19
* @{
*/
/* Main HTTP methods. */
-/* Not safe. Not idempotent. RFC7231, Section 4.3.6. */
-#define MHD_HTTP_METHOD_CONNECT "CONNECT"
-/* Not safe. Idempotent. RFC7231, Section 4.3.5. */
-#define MHD_HTTP_METHOD_DELETE "DELETE"
-/* Safe. Idempotent. RFC7231, Section 4.3.1. */
-#define MHD_HTTP_METHOD_GET "GET"
-/* Safe. Idempotent. RFC7231, Section 4.3.2. */
-#define MHD_HTTP_METHOD_HEAD "HEAD"
-/* Safe. Idempotent. RFC7231, Section 4.3.7. */
-#define MHD_HTTP_METHOD_OPTIONS "OPTIONS"
-/* Not safe. Not idempotent. RFC7231, Section 4.3.3. */
-#define MHD_HTTP_METHOD_POST "POST"
-/* Not safe. Idempotent. RFC7231, Section 4.3.4. */
-#define MHD_HTTP_METHOD_PUT "PUT"
-/* Safe. Idempotent. RFC7231, Section 4.3.8. */
-#define MHD_HTTP_METHOD_TRACE "TRACE"
+/* Not safe. Not idempotent. RFC-ietf-httpbis-semantics, Section 9.3.6. */
+#define MHD_HTTP_METHOD_CONNECT "CONNECT"
+/* Not safe. Idempotent. RFC-ietf-httpbis-semantics, Section 9.3.5. */
+#define MHD_HTTP_METHOD_DELETE "DELETE"
+/* Safe. Idempotent. RFC-ietf-httpbis-semantics, Section 9.3.1. */
+#define MHD_HTTP_METHOD_GET "GET"
+/* Safe. Idempotent. RFC-ietf-httpbis-semantics, Section 9.3.2. */
+#define MHD_HTTP_METHOD_HEAD "HEAD"
+/* Safe. Idempotent. RFC-ietf-httpbis-semantics, Section 9.3.7. */
+#define MHD_HTTP_METHOD_OPTIONS "OPTIONS"
+/* Not safe. Not idempotent. RFC-ietf-httpbis-semantics, Section 9.3.3. */
+#define MHD_HTTP_METHOD_POST "POST"
+/* Not safe. Idempotent. RFC-ietf-httpbis-semantics, Section 9.3.4. */
+#define MHD_HTTP_METHOD_PUT "PUT"
+/* Safe. Idempotent. RFC-ietf-httpbis-semantics, Section 9.3.8. */
+#define MHD_HTTP_METHOD_TRACE "TRACE"
+/* Not safe. Not idempotent. RFC-ietf-httpbis-semantics, Section 18.2. */
+#define MHD_HTTP_METHOD_ASTERISK "*"
/* Additional HTTP methods. */
/* Not safe. Idempotent. RFC3744, Section 8.1. */
-#define MHD_HTTP_METHOD_ACL "ACL"
+#define MHD_HTTP_METHOD_ACL "ACL"
/* Not safe. Idempotent. RFC3253, Section 12.6. */
#define MHD_HTTP_METHOD_BASELINE_CONTROL "BASELINE-CONTROL"
/* Not safe. Idempotent. RFC5842, Section 4. */
-#define MHD_HTTP_METHOD_BIND "BIND"
+#define MHD_HTTP_METHOD_BIND "BIND"
/* Not safe. Idempotent. RFC3253, Section 4.4, Section 9.4. */
-#define MHD_HTTP_METHOD_CHECKIN "CHECKIN"
+#define MHD_HTTP_METHOD_CHECKIN "CHECKIN"
/* Not safe. Idempotent. RFC3253, Section 4.3, Section 8.8. */
-#define MHD_HTTP_METHOD_CHECKOUT "CHECKOUT"
+#define MHD_HTTP_METHOD_CHECKOUT "CHECKOUT"
/* Not safe. Idempotent. RFC4918, Section 9.8. */
-#define MHD_HTTP_METHOD_COPY "COPY"
+#define MHD_HTTP_METHOD_COPY "COPY"
/* Not safe. Idempotent. RFC3253, Section 8.2. */
-#define MHD_HTTP_METHOD_LABEL "LABEL"
+#define MHD_HTTP_METHOD_LABEL "LABEL"
/* Not safe. Idempotent. RFC2068, Section 19.6.1.2. */
-#define MHD_HTTP_METHOD_LINK "LINK"
+#define MHD_HTTP_METHOD_LINK "LINK"
/* Not safe. Not idempotent. RFC4918, Section 9.10. */
-#define MHD_HTTP_METHOD_LOCK "LOCK"
+#define MHD_HTTP_METHOD_LOCK "LOCK"
/* Not safe. Idempotent. RFC3253, Section 11.2. */
-#define MHD_HTTP_METHOD_MERGE "MERGE"
+#define MHD_HTTP_METHOD_MERGE "MERGE"
/* Not safe. Idempotent. RFC3253, Section 13.5. */
-#define MHD_HTTP_METHOD_MKACTIVITY "MKACTIVITY"
+#define MHD_HTTP_METHOD_MKACTIVITY "MKACTIVITY"
/* Not safe. Idempotent. RFC4791, Section 5.3.1; RFC8144, Section 2.3. */
-#define MHD_HTTP_METHOD_MKCALENDAR "MKCALENDAR"
+#define MHD_HTTP_METHOD_MKCALENDAR "MKCALENDAR"
/* Not safe. Idempotent. RFC4918, Section 9.3; RFC5689, Section 3; RFC8144, Section 2.3. */
-#define MHD_HTTP_METHOD_MKCOL "MKCOL"
+#define MHD_HTTP_METHOD_MKCOL "MKCOL"
/* Not safe. Idempotent. RFC4437, Section 6. */
-#define MHD_HTTP_METHOD_MKREDIRECTREF "MKREDIRECTREF"
+#define MHD_HTTP_METHOD_MKREDIRECTREF "MKREDIRECTREF"
/* Not safe. Idempotent. RFC3253, Section 6.3. */
-#define MHD_HTTP_METHOD_MKWORKSPACE "MKWORKSPACE"
+#define MHD_HTTP_METHOD_MKWORKSPACE "MKWORKSPACE"
/* Not safe. Idempotent. RFC4918, Section 9.9. */
-#define MHD_HTTP_METHOD_MOVE "MOVE"
+#define MHD_HTTP_METHOD_MOVE "MOVE"
/* Not safe. Idempotent. RFC3648, Section 7. */
-#define MHD_HTTP_METHOD_ORDERPATCH "ORDERPATCH"
+#define MHD_HTTP_METHOD_ORDERPATCH "ORDERPATCH"
/* Not safe. Not idempotent. RFC5789, Section 2. */
-#define MHD_HTTP_METHOD_PATCH "PATCH"
+#define MHD_HTTP_METHOD_PATCH "PATCH"
/* Safe. Idempotent. RFC7540, Section 3.5. */
-#define MHD_HTTP_METHOD_PRI "PRI"
+#define MHD_HTTP_METHOD_PRI "PRI"
/* Safe. Idempotent. RFC4918, Section 9.1; RFC8144, Section 2.1. */
-#define MHD_HTTP_METHOD_PROPFIND "PROPFIND"
+#define MHD_HTTP_METHOD_PROPFIND "PROPFIND"
/* Not safe. Idempotent. RFC4918, Section 9.2; RFC8144, Section 2.2. */
-#define MHD_HTTP_METHOD_PROPPATCH "PROPPATCH"
+#define MHD_HTTP_METHOD_PROPPATCH "PROPPATCH"
/* Not safe. Idempotent. RFC5842, Section 6. */
-#define MHD_HTTP_METHOD_REBIND "REBIND"
+#define MHD_HTTP_METHOD_REBIND "REBIND"
/* Safe. Idempotent. RFC3253, Section 3.6; RFC8144, Section 2.1. */
-#define MHD_HTTP_METHOD_REPORT "REPORT"
+#define MHD_HTTP_METHOD_REPORT "REPORT"
/* Safe. Idempotent. RFC5323, Section 2. */
-#define MHD_HTTP_METHOD_SEARCH "SEARCH"
+#define MHD_HTTP_METHOD_SEARCH "SEARCH"
/* Not safe. Idempotent. RFC5842, Section 5. */
-#define MHD_HTTP_METHOD_UNBIND "UNBIND"
+#define MHD_HTTP_METHOD_UNBIND "UNBIND"
/* Not safe. Idempotent. RFC3253, Section 4.5. */
-#define MHD_HTTP_METHOD_UNCHECKOUT "UNCHECKOUT"
+#define MHD_HTTP_METHOD_UNCHECKOUT "UNCHECKOUT"
/* Not safe. Idempotent. RFC2068, Section 19.6.1.3. */
-#define MHD_HTTP_METHOD_UNLINK "UNLINK"
+#define MHD_HTTP_METHOD_UNLINK "UNLINK"
/* Not safe. Idempotent. RFC4918, Section 9.11. */
-#define MHD_HTTP_METHOD_UNLOCK "UNLOCK"
+#define MHD_HTTP_METHOD_UNLOCK "UNLOCK"
/* Not safe. Idempotent. RFC3253, Section 7.1. */
-#define MHD_HTTP_METHOD_UPDATE "UPDATE"
+#define MHD_HTTP_METHOD_UPDATE "UPDATE"
/* Not safe. Idempotent. RFC4437, Section 7. */
#define MHD_HTTP_METHOD_UPDATEREDIRECTREF "UPDATEREDIRECTREF"
/* Not safe. Idempotent. RFC3253, Section 3.5. */
@@ -1094,6 +1268,7 @@
* be used.
* This flag is set explicitly by #MHD_USE_POLL_INTERNAL_THREAD and
* by #MHD_USE_EPOLL_INTERNAL_THREAD.
+ * When this flag is not set, MHD run in "external" polling mode.
*/
MHD_USE_INTERNAL_POLLING_THREAD = 8,
@@ -1133,11 +1308,12 @@
#endif /* 0 */
/**
- * Use `poll()` instead of `select()`. This allows sockets with `fd >=
- * FD_SETSIZE`. This option is not compatible with using an
- * 'external' polling mode (as there is no API to get the file
- * descriptors for the external poll() from MHD) and must also not
- * be used in combination with #MHD_USE_EPOLL.
+ * Use `poll()` instead of `select()` for polling sockets.
+ * This allows sockets with `fd >= FD_SETSIZE`.
+ * This option is not compatible with an "external" polling mode
+ * (as there is no API to get the file descriptors for the external
+ * poll() from MHD) and must also not be used in combination
+ * with #MHD_USE_EPOLL.
* @sa ::MHD_FEATURE_POLL, #MHD_USE_POLL_INTERNAL_THREAD
*/
MHD_USE_POLL = 64,
@@ -1202,7 +1378,7 @@
#endif /* 0 */
/**
- * Run using an internal thread (or thread pool) doing `epoll()`.
+ * Run using an internal thread (or thread pool) doing `epoll` polling.
* This option is only available on certain platforms; using the option on
* platform without `epoll` support will cause #MHD_start_daemon to fail.
* @sa ::MHD_FEATURE_EPOLL, #MHD_USE_EPOLL, #MHD_USE_INTERNAL_POLLING_THREAD
@@ -1230,7 +1406,7 @@
/**
* Use inter-thread communication channel.
* #MHD_USE_ITC can be used with #MHD_USE_INTERNAL_POLLING_THREAD
- * and is ignored with any "external" mode.
+ * and is ignored with any "external" sockets polling.
* It's required for use of #MHD_quiesce_daemon
* or #MHD_add_connection.
* This option is enforced by #MHD_ALLOW_SUSPEND_RESUME or
@@ -1403,6 +1579,8 @@
* After how many seconds of inactivity should a
* connection automatically be timed out? (followed
* by an `unsigned int`; use zero for no timeout).
+ * Values larger than (UINT64_MAX / 2000 - 1) will
+ * be clipped to this number.
*/
MHD_OPTION_CONNECTION_TIMEOUT = 3,
@@ -1416,7 +1594,7 @@
* This option should be followed by TWO pointers. First a pointer
* to a function of type #MHD_RequestCompletedCallback and second a
* pointer to a closure to pass to the request completed callback.
- * The second pointer maybe NULL.
+ * The second pointer may be NULL.
*/
MHD_OPTION_NOTIFY_COMPLETED = 4,
@@ -1671,7 +1849,7 @@
* This option should be followed by TWO pointers. First a pointer
* to a function of type #MHD_NotifyConnectionCallback and second a
* pointer to a closure to pass to the request completed callback.
- * The second pointer maybe NULL.
+ * The second pointer may be NULL.
*/
MHD_OPTION_NOTIFY_CONNECTION = 27,
@@ -1748,7 +1926,7 @@
* @note Available since #MHD_VERSION 0x00097207
*/
MHD_OPTION_TLS_NO_ALPN = 34
-};
+} _MHD_FIXED_ENUM;
/**
@@ -1762,7 +1940,7 @@
*/
MHD_DSC_SANE = 0
-};
+} _MHD_FIXED_FLAGS_ENUM;
/**
@@ -1839,7 +2017,7 @@
* HTTP footer (only for HTTP 1.1 chunked encodings).
*/
MHD_FOOTER_KIND = 16
-};
+} _MHD_FIXED_ENUM;
/**
@@ -1858,8 +2036,9 @@
/**
* Error handling the connection (resources
- * exhausted, other side closed connection,
- * application error accepting request, etc.)
+ * exhausted, application error accepting request,
+ * decrypt error (for HTTPS), connection died when
+ * sending the response etc.)
* @ingroup request
*/
MHD_REQUEST_TERMINATED_WITH_ERROR = 1,
@@ -1880,24 +2059,23 @@
MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN = 3,
/**
- * We tried to read additional data, but the other side closed the
- * connection. This error is similar to
- * #MHD_REQUEST_TERMINATED_WITH_ERROR, but specific to the case where
- * the connection died because the other side did not send expected
- * data.
+ * We tried to read additional data, but the connection became broken or
+ * the other side hard closed the connection.
+ * This error is similar to #MHD_REQUEST_TERMINATED_WITH_ERROR, but
+ * specific to the case where the connection died before request completely
+ * received.
* @ingroup request
*/
MHD_REQUEST_TERMINATED_READ_ERROR = 4,
/**
* The client terminated the connection by closing the socket
- * for writing (TCP half-closed); MHD aborted sending the
- * response according to RFC 2616, section 8.1.4.
+ * for writing (TCP half-closed) while still sending request.
* @ingroup request
*/
MHD_REQUEST_TERMINATED_CLIENT_ABORT = 5
-};
+} _MHD_FIXED_ENUM;
/**
@@ -1920,7 +2098,7 @@
*/
MHD_CONNECTION_NOTIFY_CLOSED = 1
-};
+} _MHD_FIXED_ENUM;
/**
@@ -1952,6 +2130,11 @@
unsigned int connection_timeout;
/**
+ * HTTP status queued with the response, for #MHD_CONNECTION_INFO_HTTP_STATUS.
+ */
+ unsigned int http_status;
+
+ /**
* Connect socket
*/
MHD_socket connect_fd;
@@ -2092,8 +2275,15 @@
* Return length of the client's HTTP request header.
* @ingroup request
*/
- MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE
-};
+ MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE,
+
+ /**
+ * Return HTTP status queued with the response. NULL
+ * if no HTTP response has been queued yet.
+ */
+ MHD_CONNECTION_INFO_HTTP_STATUS
+
+} _MHD_FIXED_ENUM;
/**
@@ -2119,10 +2309,16 @@
MHD_DAEMON_INFO_LISTEN_FD,
/**
- * Request the file descriptor for the external epoll.
+ * Request the file descriptor for the "external" sockets polling
+ * when 'epoll' mode is used.
* No extra arguments should be passed.
+ *
* Waiting on epoll FD must not block longer than value
- * returned by #MHD_get_timeout().
+ * returned by #MHD_get_timeout() otherwise connections
+ * will "hung" with unprocessed data in network buffers
+ * and timed-out connections will not be closed.
+ *
+ * @sa #MHD_get_timeout(), #MHD_run()
*/
MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY,
MHD_DAEMON_INFO_EPOLL_FD = MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY,
@@ -2130,7 +2326,7 @@
/**
* Request the number of current connections handled by the daemon.
* No extra arguments should be passed.
- * Note: when using MHD in external polling mode, this type of request
+ * Note: when using MHD in "external" polling mode, this type of request
* could be used only when #MHD_run()/#MHD_run_from_select is not
* working in other thread at the same time.
*/
@@ -2151,7 +2347,7 @@
* value will be real port number.
*/
MHD_DAEMON_INFO_BIND_PORT
-};
+} _MHD_FIXED_ENUM;
/**
@@ -2185,12 +2381,34 @@
/**
- * A client has requested the given url using the given method
- * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
- * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
- * must call MHD callbacks to provide content to give back to the
- * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
- * #MHD_HTTP_NOT_FOUND, etc.).
+ * A client has requested the given @a url using the given @a method
+ * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, #MHD_HTTP_METHOD_DELETE,
+ * #MHD_HTTP_METHOD_POST, etc).
+ *
+ * The callback must call MHD function MHD_queue_response() to provide content
+ * to give back to the client and return an HTTP status code (i.e.
+ * #MHD_HTTP_OK, #MHD_HTTP_NOT_FOUND, etc.). The response can be created
+ * in this callback or prepared in advance.
+ * Alternatively, callback may call MHD_suspend_connection() to temporarily
+ * suspend data processing for this connection.
+ *
+ * As soon as response is provided this callback will not be called anymore
+ * for the current request.
+ *
+ * For each HTTP request this callback is called several times:
+ * * after request headers are fully received and decoded,
+ * * for each received part of request body (optional, if request has body),
+ * * when request is fully received.
+ *
+ * If response is provided before request is fully received, the rest
+ * of the request is discarded and connection is automatically closed
+ * after sending response.
+ *
+ * If the request is fully received, but response hasn't been provided and
+ * connection is not suspended, the callback can be called again immediately.
+ *
+ * The response cannot be queued when this callback is called to process
+ * the client upload data (when @a upload_data is not NULL).
*
* @param cls argument given together with the function
* pointer when the handler was registered with MHD
@@ -2222,6 +2440,8 @@
* @return #MHD_YES if the connection was handled successfully,
* #MHD_NO if the socket must be closed due to a serious
* error while handling the request
+ *
+ * @sa #MHD_queue_response()
*/
typedef enum MHD_Result
(*MHD_AccessHandlerCallback)(void *cls,
@@ -2313,7 +2533,7 @@
* @param kind kind of the header we are looking at
* @param key key for the value, can be an empty string
* @param value corresponding value, can be NULL
- * @param value_size number of bytes in @a value, NEW since #MHD_VERSION 0x00096301;
+ * @param value_size number of bytes in @a value;
* for C-strings, the length excludes the 0-terminator
* @return #MHD_YES to continue iterating,
* #MHD_NO to abort the iteration
@@ -2329,9 +2549,10 @@
/**
- * Callback used by libmicrohttpd in order to obtain content. The
- * callback is to copy at most @a max bytes of content into @a buf. The
- * total number of bytes that has been placed into @a buf should be
+ * Callback used by libmicrohttpd in order to obtain content.
+ *
+ * The callback is to copy at most @a max bytes of content into @a buf.
+ * The total number of bytes that has been placed into @a buf should be
* returned.
*
* Note that returning zero will cause libmicrohttpd to try again.
@@ -2350,10 +2571,10 @@
* @param buf where to copy the data
* @param max maximum number of bytes to copy to @a buf (size of @a buf)
* @return number of bytes written to @a buf;
- * 0 is legal unless we are running in internal select mode (since
- * this would cause busy-waiting); 0 in external select mode
- * will cause this function to be called again once the external
- * select calls MHD again;
+ * 0 is legal unless MHD is started in "internal" sockets polling mode
+ * (since this would cause busy-waiting); 0 in "external" sockets
+ * polling mode will cause this function to be called again once
+ * any MHD_run*() function is called;
* #MHD_CONTENT_READER_END_OF_STREAM (-1) for the regular
* end of transmission (with chunked encoding, MHD will then
* terminate the chunk and send any HTTP footers that might be
@@ -2394,7 +2615,7 @@
/**
* Iterator over key-value pairs where the value
- * maybe made available in increments and/or may
+ * may be made available in increments and/or may
* not be zero-terminated. Used for processing
* POST data.
*
@@ -2486,7 +2707,7 @@
* clients to continue processing, but stops accepting new
* connections. Note that the caller is responsible for closing the
* returned socket; however, if MHD is run using threads (anything but
- * external select mode), it must not be closed until AFTER
+ * "external" sockets polling mode), it must not be closed until AFTER
* #MHD_stop_daemon has been called (as it is theoretically possible
* that an existing thread is still using it).
*
@@ -2521,9 +2742,8 @@
* for example if your HTTP server is behind NAT and needs to connect
* out to the HTTP client, or if you are building a proxy.
*
- * If you use this API in conjunction with a internal select or a
- * thread pool, you must set the option
- * #MHD_USE_ITC to ensure that the freshly added
+ * If you use this API in conjunction with an "internal" socket polling,
+ * you must set the option #MHD_USE_ITC to ensure that the freshly added
* connection is immediately processed by MHD.
*
* The given client socket will be managed (and closed!) by MHD after
@@ -2555,12 +2775,14 @@
* before calling this function. FD_SETSIZE is assumed
* to be platform's default.
*
- * This function should only be called in when MHD is configured to
- * use external select with 'select()' or with 'epoll'.
- * In the latter case, it will only add the single 'epoll()' file
+ * This function should be called only when MHD is configured to
+ * use "external" sockets polling with 'select()' or with 'epoll'.
+ * In the latter case, it will only add the single 'epoll' file
* descriptor used by MHD to the sets.
- * It's necessary to use #MHD_get_timeout() in combination with
- * this function.
+ * It's necessary to use #MHD_get_timeout() to get maximum timeout
+ * value for `select()`. Usage of `select()` with indefinite timeout
+ * (or timeout larger than returned by #MHD_get_timeout()) will
+ * violate MHD API and may results in pending unprocessed data.
*
* This function must be called only for daemon started
* without #MHD_USE_INTERNAL_POLLING_THREAD flag.
@@ -2594,12 +2816,14 @@
* Passing custom FD_SETSIZE as @a fd_setsize allow usage of
* larger/smaller than platform's default fd_sets.
*
- * This function should only be called in when MHD is configured to
- * use external select with 'select()' or with 'epoll'.
+ * This function should be called only when MHD is configured to
+ * use "external" sockets polling with 'select()' or with 'epoll'.
* In the latter case, it will only add the single 'epoll' file
* descriptor used by MHD to the sets.
- * It's necessary to use #MHD_get_timeout() in combination with
- * this function.
+ * It's necessary to use #MHD_get_timeout() to get maximum timeout
+ * value for `select()`. Usage of `select()` with indefinite timeout
+ * (or timeout larger than returned by #MHD_get_timeout()) will
+ * violate MHD API and may results in pending unprocessed data.
*
* This function must be called only for daemon started
* without #MHD_USE_INTERNAL_POLLING_THREAD flag.
@@ -2632,10 +2856,17 @@
* daemon FDs in fd_sets, call FD_ZERO for each fd_set
* before calling this function. Size of fd_set is
* determined by current value of FD_SETSIZE.
- * It's necessary to use #MHD_get_timeout() in combination with
- * this function.
*
- * This function could be called only for daemon started
+ * This function should be called only when MHD is configured to
+ * use "external" sockets polling with 'select()' or with 'epoll'.
+ * In the latter case, it will only add the single 'epoll' file
+ * descriptor used by MHD to the sets.
+ * It's necessary to use #MHD_get_timeout() to get maximum timeout
+ * value for `select()`. Usage of `select()` with indefinite timeout
+ * (or timeout larger than returned by #MHD_get_timeout()) will
+ * violate MHD API and may results in pending unprocessed data.
+ *
+ * This function must be called only for daemon started
* without #MHD_USE_INTERNAL_POLLING_THREAD flag.
*
* @param daemon daemon to get sets from
@@ -2657,20 +2888,32 @@
/**
* Obtain timeout value for polling function for this daemon.
- * This function set value to amount of milliseconds for which polling
- * function (`select()` or `poll()`) should at most block, not the
+ *
+ * This function set value to the amount of milliseconds for which polling
+ * function (`select()`, `poll()` or epoll) should at most block, not the
* timeout value set for connections.
- * It is important to always use this function, even if connection
- * timeout is not set, as in some cases MHD may already have more
- * data to process on next turn (data pending in TLS buffers,
- * connections are already ready with epoll etc.) and returned timeout
- * will be zero.
+ *
+ * Any "external" sockets polling function must be called with the timeout
+ * value provided by this function. Smaller timeout values can be used for
+ * polling function if it is required for any reason, but using larger
+ * timeout value or no timeout (indefinite timeout) when this function
+ * return #MHD_YES will break MHD processing logic and result in "hung"
+ * connections with data pending in network buffers and other problems.
+ *
+ * It is important to always use this function when "external" polling is
+ * used. If this function returns #MHD_YES then #MHD_run() (or
+ * #MHD_run_from_select()) must be called right after return from polling
+ * function, regardless of the states of MHD fds.
+ *
+ * In practice, if #MHD_YES is returned then #MHD_run() (or
+ * #MHD_run_from_select()) must be called not later than @a timeout
+ * millisecond even if not activity is detected on sockets by
+ * sockets polling function.
*
* @param daemon daemon to query for timeout
* @param timeout set to the timeout (in milliseconds)
* @return #MHD_YES on success, #MHD_NO if timeouts are
- * not used (or no connections exist that would
- * necessitate the use of a timeout right now).
+ * not used and no data processing is pending.
* @ingroup event
*/
_MHD_EXTERN enum MHD_Result
@@ -2679,19 +2922,26 @@
/**
- * Run webserver operations (without blocking unless in client
- * callbacks). This method should be called by clients in combination
- * with #MHD_get_fdset if the client-controlled select method is used and
- * #MHD_get_timeout().
+ * Run webserver operations (without blocking unless in client callbacks).
+ *
+ * This method should be called by clients in combination with
+ * #MHD_get_fdset() (or #MHD_get_daemon_info() with MHD_DAEMON_INFO_EPOLL_FD
+ * if epoll is used) and #MHD_get_timeout() if the client-controlled
+ * connection polling method is used (i.e. daemon was started without
+ * #MHD_USE_INTERNAL_POLLING_THREAD flag).
*
* This function is a convenience method, which is useful if the
* fd_sets from #MHD_get_fdset were not directly passed to `select()`;
* with this function, MHD will internally do the appropriate `select()`
- * call itself again. While it is always safe to call #MHD_run (if
- * #MHD_USE_INTERNAL_POLLING_THREAD is not set), you should call
- * #MHD_run_from_select if performance is important (as it saves an
+ * call itself again. While it is acceptable to call #MHD_run (if
+ * #MHD_USE_INTERNAL_POLLING_THREAD is not set) at any moment, you should
+ * call #MHD_run_from_select() if performance is important (as it saves an
* expensive call to `select()`).
*
+ * If #MHD_get_timeout() returned #MHD_YES, than this function must be called
+ * right after polling function returns regardless of detected activity on
+ * the daemon's FDs.
+ *
* @param daemon daemon to run
* @return #MHD_YES on success, #MHD_NO if this
* daemon was not started with the right
@@ -2704,25 +2954,35 @@
/**
* Run websever operation with possible blocking.
- * This function do the following: waits for any network event not more than
- * specified number of milliseconds, processes all incoming and outgoing
- * data, processes new connections, processes any timed-out connection, and
- * do other things required to run webserver.
+ *
+ * This function does the following: waits for any network event not more than
+ * specified number of milliseconds, processes all incoming and outgoing data,
+ * processes new connections, processes any timed-out connection, and does
+ * other things required to run webserver.
* Once all connections are processed, function returns.
- * This function is useful for quick and simple webserver implementation if
- * application needs to run a single thread only and does not have any other
+ *
+ * This function is useful for quick and simple (lazy) webserver implementation
+ * if application needs to run a single thread only and does not have any other
* network activity.
+ *
+ * This function calls MHD_get_timeout() internally and use returned value as
+ * maximum wait time if it less than value of @a millisec parameter.
+ *
+ * It is expected that the "external" socket polling function is not used in
+ * conjunction with this function unless the @a millisec is set to zero.
+ *
* @param daemon the daemon to run
* @param millisec the maximum time in milliseconds to wait for network and
* other events. Note: there is no guarantee that function
- * blocks for specified amount of time. The real processing
- * time can be shorter (if some data comes earlier) or
- * longer (if data processing requires more time, especially
- * in the user callbacks).
+ * blocks for the specified amount of time. The real processing
+ * time can be shorter (if some data or connection timeout
+ * comes earlier) or longer (if data processing requires more
+ * time, especially in user callbacks).
* If set to '0' then function does not block and processes
* only already available data (if any).
* If set to '-1' then function waits for events
- * indefinitely (blocks until next network activity).
+ * indefinitely (blocks until next network activity or
+ * connection timeout).
* @return #MHD_YES on success, #MHD_NO if this
* daemon was not started with the right
* options for this call or some serious
@@ -2747,6 +3007,10 @@
* not have to call `select()` again to determine which operations are
* ready.
*
+ * If #MHD_get_timeout() returned #MHD_YES, than this function must be
+ * called right after `select()` returns regardless of detected activity
+ * on the daemon's FDs.
+ *
* This function cannot be used with daemon started with
* #MHD_USE_INTERNAL_POLLING_THREAD flag.
*
@@ -2772,7 +3036,7 @@
* @param connection connection to get values from
* @param kind types of values to iterate over, can be a bitmask
* @param iterator callback to call on each header;
- * maybe NULL (then just count headers)
+ * may be NULL (then just count headers)
* @param iterator_cls extra argument to @a iterator
* @return number of entries iterated over,
* -1 if connection is NULL.
@@ -2791,7 +3055,7 @@
* @param connection connection to get values from
* @param kind types of values to iterate over, can be a bitmask
* @param iterator callback to call on each header;
- * maybe NULL (then just count headers)
+ * may be NULL (then just count headers)
* @param iterator_cls extra argument to @a iterator
* @return number of entries iterated over,
* -1 if connection is NULL.
@@ -2954,12 +3218,18 @@
* Queue a response to be transmitted to the client (as soon as
* possible but after #MHD_AccessHandlerCallback returns).
*
+ * For any active connection this function must be called
+ * only by #MHD_AccessHandlerCallback callback.
+ * For suspended connection this function can be called at any moment. Response
+ * will be sent as soon as connection is resumed.
+ *
* @param connection the connection identifying the client
* @param status_code HTTP status code (i.e. #MHD_HTTP_OK)
* @param response response to transmit
* @return #MHD_NO on error (i.e. reply already sent),
* #MHD_YES on success or if message has been queued
* @ingroup response
+ * @sa #MHD_AccessHandlerCallback
*/
_MHD_EXTERN enum MHD_Result
MHD_queue_response (struct MHD_Connection *connection,
@@ -2968,12 +3238,13 @@
/**
- * Suspend handling of network data for a given connection. This can
- * be used to dequeue a connection from MHD's event loop for a while.
+ * Suspend handling of network data for a given connection.
+ * This can be used to dequeue a connection from MHD's event loop
+ * (not applicable to thread-per-connection!) for a while.
*
- * If you use this API in conjunction with a internal select or a
- * thread pool, you must set the option #MHD_USE_ITC to
- * ensure that a resumed connection is immediately processed by MHD.
+ * If you use this API in conjunction with an "internal" socket polling,
+ * you must set the option #MHD_USE_ITC to ensure that a resumed
+ * connection is immediately processed by MHD.
*
* Suspended connections continue to count against the total number of
* connections allowed (per daemon, as well as per IP, if such limits
@@ -2982,8 +3253,8 @@
* connection is suspended, MHD will not detect disconnects by the
* client.
*
- * The only safe time to suspend a connection is from the
- * #MHD_AccessHandlerCallback.
+ * The only safe way to call this function is to call it from the
+ * #MHD_AccessHandlerCallback or #MHD_ContentReaderCallback.
*
* Finally, it is an API violation to call #MHD_stop_daemon while
* having suspended connections (this will at least create memory and
@@ -2991,6 +3262,8 @@
* resume all connections before stopping the daemon.
*
* @param connection the connection to suspend
+ *
+ * @sa #MHD_AccessHandlerCallback
*/
_MHD_EXTERN void
MHD_suspend_connection (struct MHD_Connection *connection);
@@ -3002,7 +3275,7 @@
* function on a connection that was not previously suspended will
* result in undefined behavior.
*
- * If you are using this function in ``external'' select mode, you must
+ * If you are using this function in "external" sockets polling mode, you must
* make sure to run #MHD_run() and #MHD_get_timeout() afterwards (before
* again calling #MHD_get_fdset()), as otherwise the change may not be
* reflected in the set returned by #MHD_get_fdset() and you may end up
@@ -3024,35 +3297,65 @@
{
/**
* Default: no special flags.
+ * @note Available since #MHD_VERSION 0x00093701
*/
MHD_RF_NONE = 0,
/**
- * Only respond in conservative HTTP 1.0-mode. In particular,
- * do not (automatically) sent "Connection" headers and always
- * close the connection after generating the response.
- * By default, MHD will respond using the same HTTP version which
- * was set in the request. You can also set the
- * #MHD_RF_HTTP_VERSION_1_0_RESPONSE flag to force version 1.0
- * in the response.
- */
- MHD_RF_HTTP_VERSION_1_0_ONLY = 1,
-
- /**
- * Only respond in HTTP 1.0-mode. Contrary to the
- * #MHD_RF_HTTP_VERSION_1_0_ONLY flag, the response's HTTP version will
- * always be set to 1.0 and "Connection" headers are still supported.
+ * Only respond in conservative (dumb) HTTP/1.0-compatible mode.
+ * Response still use HTTP/1.1 version in header, but always close
+ * the connection after sending the response and do not use chunked
+ * encoding for the response.
+ * You can also set the #MHD_RF_HTTP_1_0_SERVER flag to force
+ * HTTP/1.0 version in the response.
+ * Responses are still compatible with HTTP/1.1.
+ * This option can be used to communicate with some broken client, which
+ * does not implement HTTP/1.1 features, but advertises HTTP/1.1 support.
+ * @note Available since #MHD_VERSION 0x00097308
+ */
+ MHD_RF_HTTP_1_0_COMPATIBLE_STRICT = 1 << 0,
+ /**
+ * The same as #MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
+ * @note Available since #MHD_VERSION 0x00093701
+ */
+ MHD_RF_HTTP_VERSION_1_0_ONLY = 1 << 0,
+
+ /**
+ * Only respond in HTTP 1.0-mode.
+ * Contrary to the #MHD_RF_HTTP_1_0_COMPATIBLE_STRICT flag, the response's
+ * HTTP version will always be set to 1.0 and keep-alive connections
+ * will be used if explicitly requested by the client.
+ * The "Connection:" header will be added for both "close" and "keep-alive"
+ * connections.
+ * Chunked encoding will not be used for the response.
+ * Due to backward compatibility, responses still can be used with
+ * HTTP/1.1 clients.
+ * This option can be used to emulate HTTP/1.0 server (for response part
+ * only as chunked encoding in requests (if any) is processed by MHD).
+ * @note Available since #MHD_VERSION 0x00097308
+ */
+ MHD_RF_HTTP_1_0_SERVER = 1 << 1,
+ /**
+ * The same as #MHD_RF_HTTP_1_0_SERVER
+ * @note Available since #MHD_VERSION 0x00096000
*/
- MHD_RF_HTTP_VERSION_1_0_RESPONSE = 2,
+ MHD_RF_HTTP_VERSION_1_0_RESPONSE = 1 << 1,
/**
* Disable sanity check preventing clients from manually
* setting the HTTP content length option.
+ * @note Available since #MHD_VERSION 0x00096702
*/
- MHD_RF_INSANITY_HEADER_CONTENT_LENGTH = 4
-
+ MHD_RF_INSANITY_HEADER_CONTENT_LENGTH = 1 << 2,
-};
+ /**
+ * Enable sending of "Connection: keep-alive" header even for
+ * HTTP/1.1 clients when "Keep-Alive" connection is used.
+ * Disabled by default for HTTP/1.1 clients as per RFC.
+ * @note Available since #MHD_VERSION 0x00097310
+ */
+ MHD_RF_SEND_KEEP_ALIVE_HEADER = 1 << 3
+} _MHD_FIXED_FLAGS_ENUM;
/**
@@ -3064,7 +3367,7 @@
* End of the list of options.
*/
MHD_RO_END = 0
-};
+} _MHD_FIXED_ENUM;
/**
@@ -3112,7 +3415,7 @@
* @param data the data itself
* @param must_free libmicrohttpd should free data when done
* @param must_copy libmicrohttpd must make a copy of @a data
- * right away, the data maybe released anytime after
+ * right away, the data may be released anytime after
* this call returns
* @return NULL on error (i.e. invalid arguments, out of memory)
* @deprecated use #MHD_create_response_from_buffer instead
@@ -3160,12 +3463,19 @@
*/
MHD_RESPMEM_MUST_COPY
-};
+} _MHD_FIXED_ENUM;
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided buffer used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response
* @param buffer size bytes containing the response's data portion
@@ -3180,8 +3490,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided buffer used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response
* @param buffer size bytes containing the response's data portion
@@ -3198,8 +3515,43 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided buffer used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
+ *
+ * @param size size of the data portion of the response
+ * @param buffer size bytes containing the response's data portion
+ * @param crfc function to call to cleanup, if set to NULL then callback
+ * is not called
+ * @param crfc_cls an argument for @a crfc
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @note Available since #MHD_VERSION 0x00097302
+ * @ingroup response
+ */
+_MHD_EXTERN struct MHD_Response *
+MHD_create_response_from_buffer_with_free_callback_cls (size_t size,
+ void *buffer,
+ MHD_ContentReaderFreeCallback
+ crfc,
+ void *crfc_cls);
+
+
+/**
+ * Create a response object with the content of provided file used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response
* @param fd file descriptor referring to a file on disk with the
@@ -3214,8 +3566,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used ONLY ONCE.
+ * Create a response object with the response body created by reading
+ * the provided pipe.
+ *
+ * The response object can be extended with header information and
+ * then be used ONLY ONCE.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param fd file descriptor referring to a read-end of a pipe with the
* data; will be closed when response is destroyed;
@@ -3229,8 +3588,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided file used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response;
* sizes larger than 2 GiB may be not supported by OS or
@@ -3247,8 +3613,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided file with
+ * specified offset used as the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response
* @param fd file descriptor referring to a file on disk with the
@@ -3280,8 +3653,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided file with
+ * specified offset used as the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response;
* sizes larger than 2 GiB may be not supported by OS or
@@ -3302,9 +3682,15 @@
/**
- * Create a response object from an array of memory buffers.
- * The response object can be extended with header information and then be used
- * any number of times.
+ * Create a response object with an array of memory buffers
+ * used as the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param iov the array for response data buffers, an internal copy of this
* will be made
@@ -3349,7 +3735,7 @@
*/
MHD_UPGRADE_ACTION_CORK_OFF = 2
-};
+} _MHD_FIXED_ENUM;
/**
@@ -3485,10 +3871,49 @@
/**
* Add a header line to the response.
*
- * @param response response to add a header to
- * @param header the header to add
- * @param content value to add
- * @return #MHD_NO on error (i.e. invalid header or content format),
+ * When reply is generated with queued response, some headers are generated
+ * automatically. Automatically generated headers are only sent to the client,
+ * but not added back to the response object.
+ *
+ * The list of automatic headers:
+ * + "Date" header is added automatically unless already set by
+ * this function
+ * @see #MHD_USE_SUPPRESS_DATE_NO_CLOCK
+ * + "Content-Length" is added automatically when required, attempt to set
+ * it manually by this function is ignored.
+ * @see #MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
+ * + "Transfer-Encoding" with value "chunked" is added automatically,
+ * when chunked transfer encoding is used automatically. Same header with
+ * the same value can be set manually by this function to enforce chunked
+ * encoding, however for HTTP/1.0 clients chunked encoding will not be used
+ * and manually set "Transfer-Encoding" header is automatically removed
+ * for HTTP/1.0 clients
+ * + "Connection" may be added automatically with value "Keep-Alive" (only
+ * for HTTP/1.0 clients) or "Close". The header "Connection" with value
+ * "Close" could be set by this function to enforce closure of
+ * the connection after sending this response. "Keep-Alive" cannot be
+ * enforced and will be removed automatically.
+ * @see #MHD_RF_SEND_KEEP_ALIVE_HEADER
+ *
+ * Some headers are pre-processed by this function:
+ * * "Connection" headers are combined into single header entry, value is
+ * normilised, "Keep-Alive" tokens are removed.
+ * * "Transfer-Encoding" header: the only one header is allowed, the only
+ * allowed value is "chunked".
+ * * "Date" header: the only one header is allowed, the second added header
+ * replaces the first one.
+ * * "Content-Length" application-defined header is not allowed.
+ * @see #MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
+ *
+ * Headers are used in order as they were added.
+ *
+ * @param response the response to add a header to
+ * @param header the header name to add, no need to be static, an internal copy
+ * will be created automatically
+ * @param content the header value to add, no need to be static, an internal
+ * copy will be created automatically
+ * @return #MHD_YES on success,
+ * #MHD_NO on error (i.e. invalid header or content format),
* or out of memory
* @ingroup response
*/
@@ -3516,6 +3941,11 @@
/**
* Delete a header (or footer) line from the response.
*
+ * For "Connection" headers this function remove all tokens from existing
+ * value. Successful result means that at least one token has been removed.
+ * If all tokens are removed from "Connection" header, the empty "Connection"
+ * header removed.
+ *
* @param response response to remove a header from
* @param header the header to delete
* @param content value to delete
@@ -3533,7 +3963,7 @@
*
* @param response response to query
* @param iterator callback to call on each header;
- * maybe NULL (then just count headers)
+ * may be NULL (then just count headers)
* @param iterator_cls extra argument to @a iterator
* @return number of entries iterated over
* @ingroup response
@@ -3679,7 +4109,7 @@
*/
MHD_DIGEST_ALG_SHA256
-};
+} _MHD_FIXED_ENUM;
/**
@@ -3848,7 +4278,7 @@
*/
_MHD_EXTERN char *
MHD_basic_auth_get_username_password (struct MHD_Connection *connection,
- char**password);
+ char **password);
/**
@@ -3900,10 +4330,12 @@
* zero for no timeout.
* If timeout was set to zero (or unset) before, setup of new value by
* MHD_set_connection_option() will reset timeout timer.
+ * Values larger than (UINT64_MAX / 2000 - 1) will
+ * be clipped to this number.
*/
MHD_CONNECTION_OPTION_TIMEOUT
-};
+} _MHD_FIXED_ENUM;
/**
@@ -3991,7 +4423,7 @@
* @return static version string, e.g. "0.9.9"
* @ingroup specialized
*/
-_MHD_EXTERN const char*
+_MHD_EXTERN const char *
MHD_get_version (void);
@@ -4143,7 +4575,7 @@
MHD_FEATURE_AUTODETECT_BIND_PORT = 19,
/**
- * Get whether MHD support SIGPIPE suppression.
+ * Get whether MHD supports automatic SIGPIPE suppression.
* If SIGPIPE suppression is not supported, application must handle
* SIGPIPE signal by itself.
*/
@@ -4166,7 +4598,7 @@
* supported.
*/
MHD_FEATURE_HTTPS_CERT_CALLBACK2 = 23
-};
+} _MHD_FIXED_ENUM;
/**
@@ -4184,10 +4616,10 @@
MHD_is_feature_supported (enum MHD_FEATURE feature);
+#ifdef __cplusplus
#if 0 /* keep Emacsens' auto-indent happy */
{
#endif
-#ifdef __cplusplus
}
#endif
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/include/microhttpd_ws.h
^
|
@@ -0,0 +1,1124 @@
+/*
+ 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
+ */
+/*
+ * *** WARNING! ***
+ * * The websockets interface is currenly in "experimental" stage. *
+ * * It does not work on architectures with endianness different from *
+ * * big endian and little endian and may have some portability issues.*
+ * * API and ABI are not yet stable. *
+ */
+#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 stream is initialized in server mode (default).
+ * Thus all outgoing payload will not be "masked".
+ * All incoming payload must be masked.
+ * This flag cannot be used together with #MHD_WEBSOCKET_FLAG_CLIENT
+ */
+ MHD_WEBSOCKET_FLAG_SERVER = 0,
+ /**
+ * The websocket stream is initialized in client mode.
+ * You will usually never use that mode in combination with libmicrohttpd,
+ * because libmicrohttpd provides a server and not a client.
+ * In client mode all outgoing payload will be "masked"
+ * (XOR-ed with random values).
+ * All incoming payload must be unmasked.
+ * If you use this mode, you must always call #MHD_websocket_stream_init2()
+ * instead of #MHD_websocket_stream_init(), because you need
+ * to pass a random number generator callback function for masking.
+ * This flag cannot be used together with #MHD_WEBSOCKET_FLAG_SERVER
+ */
+ MHD_WEBSOCKET_FLAG_CLIENT = 1,
+ /**
+ * You don't want to get fragmented data while decoding (default).
+ * 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 sequence (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_NO_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
+ * `payload` and `payload_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 types 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.
+ * For other functions this means simply a success.
+ */
+ MHD_WEBSOCKET_STATUS_OK = 0,
+ /**
+ * #MHD_websocket_decode() has decoded a text frame.
+ * The parameters `payload` and `payload_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 `payload` and `payload_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 `payload` and `payload_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.
+ * You can call #MHD_websocket_split_close_reason() to split that
+ * 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 `payload` and `payload_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 `payload` and `payload_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 `payload` and `payload_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_FIRST_FRAGMENT = 0x11,
+ /**
+ * #MHD_websocket_decode() has decoded a binary frame fragment.
+ * The parameters `payload` and `payload_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_FIRST_FRAGMENT = 0x12,
+ /**
+ * #MHD_websocket_decode() has decoded the next text frame fragment.
+ * The parameters `payload` and `payload_len` are filled with
+ * the decoded text (if any).
+ * This is like #MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT, but it appears
+ * only after the first and before 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_NEXT_FRAGMENT = 0x21,
+ /**
+ * #MHD_websocket_decode() has decoded the next binary frame fragment.
+ * The parameters `payload` and `payload_len` are filled with
+ * the decoded binary data (if any).
+ * This is like #MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT, but it appears
+ * only after the first and before 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_NEXT_FRAGMENT = 0x22,
+ /**
+ * #MHD_websocket_decode() has decoded the last text frame fragment.
+ * The parameters `payload` and `payload_len` are filled with
+ * the decoded text (if any).
+ * This is like #MHD_WEBSOCKET_STATUS_TEXT_FIRST_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 = 0x41,
+ /**
+ * #MHD_websocket_decode() has decoded the last binary frame fragment.
+ * The parameters `payload` and `payload_len` are filled with
+ * the decoded binary data (if any).
+ * This is like #MHD_WEBSOCKET_STATUS_BINARY_FIRST_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 = 0x42,
+ /**
+ * 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 may 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 may send a close frame before closing.
+ * This is only used by #MHD_websocket_decode() and happens
+ * if you call #MDM_websocket_decode() again after
+ * has been invalidated.
+ * You can call #MHD_websocket_stream_is_valid() at any time
+ * to check whether a stream is invalid or not.
+ */
+ 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() to
+ * avoid this and close the websocket 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 may 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 GiB 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 sequence 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 may 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,
+ /**
+ * A check routine for the HTTP headers came to the conclusion that
+ * the header value isn't valid for a websocket handshake request.
+ * This value can only be returned from the following functions:
+ * * #MHD_websocket_check_http_version()
+ * * #MHD_websocket_check_connection_header()
+ * * #MHD_websocket_check_upgrade_header()
+ * * #MHD_websocket_check_version_header()
+ * * #MHD_websocket_create_accept_header()
+ */
+ MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER = -7
+};
+
+/**
+ * @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 be 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 because it fulfilled its purpose and shall
+ * now be closed in a normal, planned way.
+ */
+ MHD_WEBSOCKET_CLOSEREASON_REGULAR = 1000,
+ /**
+ * You close the websocket because you are shutting down the server or
+ * something similar.
+ */
+ MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY = 1001,
+ /**
+ * You close the websocket because 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 specify 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 callback function is used internally by many websocket functions
+ * for allocating data.
+ * By default `malloc()` is used.
+ * You can use your own allocation function with
+ * #MHD_websocket_stream_init2() if you wish to.
+ * This can be useful for operating systems like Windows
+ * where `malloc()`, `realloc()` and `free()` are compiler-dependent.
+ * You can call the associated `malloc()` callback of
+ * a websocket stream with #MHD_websocket_malloc().
+ *
+ * @param buf_len buffer size in bytes
+ * @return allocated memory
+ * @ingroup websocket
+ */
+typedef void *
+(*MHD_WebSocketMallocCallback) (size_t buf_len);
+/**
+ * This callback function is used internally by many websocket
+ * functions for reallocating data.
+ * By default `realloc()` is used.
+ * You can use your own reallocation function with
+ * #MHD_websocket_stream_init2() if you wish to.
+ * This can be useful for operating systems like Windows
+ * where `malloc()`, `realloc()` and `free()` are compiler-dependent.
+ * You can call the associated `realloc()` callback of
+ * a websocket stream with #MHD_websocket_realloc().
+ *
+ * @param buf buffer
+ * @param new_buf_len new buffer size in bytes
+ * @return reallocated memory
+ * @ingroup websocket
+ */
+typedef void *
+(*MHD_WebSocketReallocCallback) (void *buf, size_t new_buf_len);
+/**
+ * This callback function is used internally by many websocket
+ * functions for freeing data.
+ * By default `free()` is used.
+ * You can use your own free function with
+ * #MHD_websocket_stream_init2() if you wish to.
+ * This can be useful for operating systems like Windows
+ * where `malloc()`, `realloc()` and `free()` are compiler-dependent.
+ * You can call the associated `free()` callback of
+ * a websocket stream with #MHD_websocket_free().
+ *
+ * @param buf buffer
+ * @ingroup websocket
+ */
+typedef void
+(*MHD_WebSocketFreeCallback) (void *buf);
+/**
+ * This callback function is used for generating random numbers
+ * for masking payload data in client mode.
+ * If you use websockets in server mode with libmicrohttpd then
+ * you don't need a random number generator, because
+ * the server doesn't mask its outgoing messageses.
+ * However if you wish to use a websocket stream in client mode,
+ * you must pass this callback function to #MHD_websocket_stream_init2().
+ *
+ * @param cls closure specified in #MHD_websocket_stream_init2()
+ * @param buf buffer to fill with random values
+ * @param buf_len size of buffer in bytes
+ * @return The number of generated random bytes.
+ * Should usually equal to buf_len.
+ * @ingroup websocket
+ */
+typedef size_t
+(*MHD_WebSocketRandomNumberGenerator) (void *cls, void *buf, size_t buf_len);
+
+/**
+ * Checks the HTTP version of the incoming request.
+ * Websocket requests are only allowed for HTTP/1.1 or above.
+ *
+ * @param http_version The value of the 'version' parameter of your
+ * access_handler callback
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ * 0 means the HTTP version is correct for a websocket request,
+ * a value less than zero means that the HTTP version isn't
+ * valid for a websocket request.
+ * @ingroup websocket
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_http_version (const char *http_version);
+
+/**
+ * Checks the value of the 'Connection' HTTP request header.
+ * Websocket requests require the token 'Upgrade' in
+ * the 'Connection' HTTP request header.
+ *
+ * @param connection_header The value of the 'Connection' request header.
+ * You can get this request header value by passing
+ * #MHD_HTTP_HEADER_CONNECTION to
+ * #MHD_lookup_connection_value().
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ * 0 means the 'Connection' request header is correct
+ * for a websocket request,
+ * a value less than zero means that the 'Connection' header isn't
+ * valid for a websocket request.
+ * @ingroup websocket
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_connection_header (const char *connection_header);
+
+/**
+ * Checks the value of the 'Upgrade' HTTP request header.
+ * Websocket requests require the value 'websocket' in
+ * the 'Upgrade' HTTP request header.
+ *
+ * @param upgrade_header The value of the 'Upgrade' request header.
+ * You can get this request header value by passing
+ * #MHD_HTTP_HEADER_UPGRADE to
+ * #MHD_lookup_connection_value().
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ * 0 means the 'Upgrade' request header is correct
+ * for a websocket request,
+ * a value less than zero means that the 'Upgrade' header isn't
+ * valid for a websocket request.
+ * @ingroup websocket
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_upgrade_header (const char *upgrade_header);
+
+/**
+ * Checks the value of the 'Sec-WebSocket-Version' HTTP request header.
+ * Websocket requests require the value '13'
+ * in the 'Sec-WebSocket-Version' HTTP request header.
+ *
+ * @param version_header The value of the 'Sec-WebSocket-Version'
+ * request header.
+ * You can get this request header value by passing
+ * #MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION to
+ * #MHD_lookup_connection_value().
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ * 0 means the 'Sec-WebSocket-Version' request header is correct
+ * for a websocket request,
+ * a value less than zero means that the 'Sec-WebSocket-Version'
+ * header isn't valid for a websocket request.
+ * @ingroup websocket
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_version_header (const char *version_header);
+
+/**
+ * Creates the response value for the 'Sec-WebSocket-Key' HTTP request header.
+ * The generated value must be sent to the client
+ * as 'Sec-WebSocket-Accept' HTTP response header.
+ *
+ * @param sec_websocket_key The value of the 'Sec-WebSocket-Key'
+ * request header.
+ * You can get this request header value by passing
+ * #MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY to
+ * #MHD_lookup_connection_value().
+ * @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 the response value plus
+ * a terminating NUL on success.
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ * Typically 0 on success or less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_create_accept_header (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_payload_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 enum MHD_WEBSOCKET_STATUS
+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.
+ * Also a random number generator can be specified for client mode.
+ *
+ * @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_payload_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()`.
+ * @param cls_rng A closure for the random number generator callback.
+ * This is only required when
+ * MHD_WEBSOCKET_FLAG_CLIENT is passed in `flags`.
+ * The given value is passed to
+ * the random number generator.
+ * May be NULL if not needed.
+ * Should be NULL when you are
+ * not using MHD_WEBSOCKET_FLAG_CLIENT.
+ * @param callback_rng A callback function for a
+ * secure random number generator.
+ * This is only required when
+ * MHD_WEBSOCKET_FLAG_CLIENT is passed in `flags`.
+ * Should be NULL otherwise.
+ * @return A value of `enum MHD_WEBSOCKET_STATUS`.
+ * Typically 0 on success or less than 0 on errors.
+ * @ingroup websocket
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+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,
+ void *cls_rng,
+ MHD_WebSocketRandomNumberGenerator callback_rng);
+
+/**
+ * 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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_VALIDITY
+MHD_websocket_stream_is_valid (struct MHD_WebSocketStream *ws);
+
+/**
+ * Decodes a byte sequence for a 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
+ * to the next call of this function.
+ * @param[out] payload Pointer to a variable, which receives a buffer
+ * with the decoded payload data.
+ * If no decoded data is available this is NULL.
+ * When the returned value 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
+ * (function 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 the 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 enum MHD_WEBSOCKET_STATUS
+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 a decoded close frame.
+ *
+ * @param payload The payload of the close frame.
+ * This parameter may only 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 may 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 may 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 may 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 enum MHD_WEBSOCKET_STATUS
+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 may 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 specify 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
+ * should 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.
+ * `enum MHD_WEBSOCKET_UTF8STEP` is for this value.
+ * 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 enum MHD_WEBSOCKET_STATUS
+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 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 specify 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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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 should be
+ * 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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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);
+
+/**
+ * Allocates memory with the associated 'malloc' function
+ * of the websocket stream
+ *
+ * @param ws The websocket stream.
+ * @param buf_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 buf_len);
+
+/**
+ * Reallocates memory with the associated 'realloc' function
+ * of the websocket stream
+ *
+ * @param ws The websocket stream.
+ * @param buf The previously allocated memory or NULL
+ * @param new_buf_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 *buf,
+ size_t new_buf_len);
+
+/**
+ * Frees memory with the associated 'free' function
+ * of the websocket stream
+ *
+ * @param ws The websocket stream.
+ * @param buf 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 *buf);
+
+#if 0 /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/include/platform.h
^
|
@@ -37,7 +37,9 @@
#include "mhd_options.h"
#include <stdio.h>
+#ifdef HAVE_STDLIB_H
#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
#include <stdint.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
@@ -47,7 +49,9 @@
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
+#ifdef HAVE_STDDEF_H
#include <stddef.h>
+#endif /* HAVE_STDDEF_H */
/* different OSes have fd_set in
a broad range of header files;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/base64.c
^
|
@@ -24,11 +24,11 @@
char *
-BASE64Decode (const char*src)
+BASE64Decode (const char *src)
{
size_t in_len = strlen (src);
- char*dest;
- char*result;
+ char *dest;
+ char *result;
if (in_len % 4)
{
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/base64.h
^
|
@@ -12,6 +12,6 @@
#include "platform.h"
char *
-BASE64Decode (const char*src);
+BASE64Decode (const char *src);
#endif /* !BASE64_H */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/connection_call_handlers.c
^
|
@@ -1134,7 +1134,7 @@
time_t t;
#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
! defined(HAVE_GMTIME_R)
- struct tm*pNow;
+ struct tm *pNow;
#endif
date[0] = 0;
@@ -2283,7 +2283,7 @@
if ( (0 != getsockopt (connection->socket_fd,
IPPROTO_TCP,
TCP_CORK,
- (void*) &cork_val,
+ (void *) &cork_val,
¶m_size)) ||
(0 != cork_val))
res &= (0 == setsockopt (connection->socket_fd,
@@ -3582,8 +3582,8 @@
time_t timeout;
timeout = connection->connection_timeout;
if ( (0 != timeout) &&
- (timeout < (MHD_monotonic_sec_counter ()
- - connection->last_activity)) )
+ (timeout <= (MHD_monotonic_sec_counter ()
+ - connection->last_activity)) )
{
MHD_connection_close_ (connection,
MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/daemon_epoll.c
^
|
@@ -358,7 +358,7 @@
#endif
return MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR;
}
- for (i = 0; i<(unsigned int) num_events; i++)
+ for (i = 0; i < (unsigned int) num_events; i++)
{
/* First, check for the values of `ptr` that would indicate
that this event is not about a normal connection. */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/daemon_ip_limit.c
^
|
@@ -72,7 +72,7 @@
* @param daemon handle to a daemon
* @return master daemon handle
*/
-static struct MHD_Daemon*
+static struct MHD_Daemon *
get_master (struct MHD_Daemon *daemon)
{
while (NULL != daemon->master)
@@ -145,7 +145,7 @@
/* IPv4 addresses */
if (sizeof (struct sockaddr_in) == addrlen)
{
- const struct sockaddr_in *addr4 = (const struct sockaddr_in*) addr;
+ const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr;
key->family = AF_INET;
memcpy (&key->addr.ipv4,
@@ -158,7 +158,7 @@
/* IPv6 addresses */
if (sizeof (struct sockaddr_in6) == addrlen)
{
- const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*) addr;
+ const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr;
key->family = AF_INET6;
memcpy (&key->addr.ipv6,
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/daemon_select.c
^
|
@@ -538,7 +538,7 @@
/* FIXME: does this check really needed? */
if (MHD_INVALID_SOCKET != max_fd)
{
- struct timeval*tvp;
+ struct timeval *tvp;
struct timeval tv;
if ( (con->tls_read_ready) &&
(urh->in_buffer_used < urh->in_buffer_size))
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/mhd_itc.c
^
|
@@ -48,7 +48,7 @@
{
unsigned int i;
- for (i = 0; i<2; i++)
+ for (i = 0; i < 2; i++)
{
int flags;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/mhd_sockets.c
^
|
@@ -37,7 +37,7 @@
* @param err the WinSock error code.
* @return pointer to string description of specified WinSock error.
*/
-const char*
+const char *
MHD_W32_strerror_winsock_ (int err)
{
switch (err)
@@ -277,12 +277,12 @@
listen_addr.sin_port = 0; /* same as htons(0) */
listen_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
if ( ((0 == bind (listen_s,
- (struct sockaddr*) &listen_addr,
+ (struct sockaddr *) &listen_addr,
c_addinlen)) &&
(0 == listen (listen_s,
1) ) &&
(0 == getsockname (listen_s,
- (struct sockaddr*) &listen_addr,
+ (struct sockaddr *) &listen_addr,
&addr_len))) )
{
SOCKET client_s = socket (AF_INET,
@@ -303,7 +303,7 @@
FIONBIO,
&on_val)) ||
( (0 != connect (client_s,
- (struct sockaddr*) &listen_addr,
+ (struct sockaddr *) &listen_addr,
c_addinlen)) &&
(WSAGetLastError () != WSAEWOULDBLOCK)) )
{
@@ -315,7 +315,7 @@
addr_len = c_addinlen;
server_s = accept (listen_s,
- (struct sockaddr*) &accepted_from_addr,
+ (struct sockaddr *) &accepted_from_addr,
&addr_len);
if (INVALID_SOCKET == server_s)
{
@@ -327,7 +327,7 @@
addr_len = c_addinlen;
if ( (0 == getsockname (client_s,
- (struct sockaddr*) &client_addr,
+ (struct sockaddr *) &client_addr,
&addr_len)) &&
(accepted_from_addr.sin_family == client_addr.sin_family) &&
(accepted_from_addr.sin_port == client_addr.sin_port) &&
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/mhd_sockets.h
^
|
@@ -533,7 +533,7 @@
* @param err the WinSock error code.
* @return pointer to string description of specified WinSock error.
*/
-const char*MHD_W32_strerror_winsock_ (int err);
+const char *MHD_W32_strerror_winsock_ (int err);
#endif /* MHD_WINSOCK_SOCKETS */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/mhd_str.c
^
|
@@ -775,9 +775,9 @@
if (i)
{
if (8 == val_size)
- *(uint64_t*) out_val = res;
+ *(uint64_t *) out_val = res;
else if (4 == val_size)
- *(uint32_t*) out_val = (uint32_t) res;
+ *(uint32_t *) out_val = (uint32_t) res;
else
return 0;
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/mhd_threads.c
^
|
@@ -294,7 +294,7 @@
*/
int
MHD_create_named_thread_ (MHD_thread_handle_ID_ *thread,
- const char*thread_name,
+ const char *thread_name,
size_t stack_size,
MHD_THREAD_START_ROUTINE_ start_routine,
void *arg)
@@ -354,7 +354,7 @@
if (! MHD_create_thread_ (thread,
stack_size,
&named_thread_starter,
- (void*) param))
+ (void *) param))
{
free (param);
return 0;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/mhd_threads.h
^
|
@@ -227,7 +227,7 @@
*/
int
MHD_create_named_thread_ (MHD_thread_handle_ID_ *thread,
- const char*thread_name,
+ const char *thread_name,
size_t stack_size,
MHD_THREAD_START_ROUTINE_ start_routine,
void *arg);
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/reason_phrase.c
^
|
@@ -156,7 +156,7 @@
struct MHD_Reason_Block
{
size_t max;
- const char *const*data;
+ const char *const *data;
};
#define BLOCK(m) { (sizeof(m) / sizeof(char*)), m }
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/response_from_fd.c
^
|
@@ -125,7 +125,7 @@
f_ol.Offset = pos_uli.LowPart;
f_ol.OffsetHigh = pos_uli.HighPart;
if (! ReadFile (fh,
- (void*) buf,
+ (void *) buf,
toRead,
&resRead,
&f_ol))
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/tsearch.c
^
|
@@ -64,7 +64,7 @@
void *const *vrootp, /* address of the tree root */
int (*compar)(const void *, const void *))
{
- node_t *const *rootp = (node_t *const*) vrootp;
+ node_t *const *rootp = (node_t *const *) vrootp;
if (NULL == rootp)
return NULL;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/lib/tsearch.h
^
|
@@ -15,21 +15,21 @@
void *
- tdelete (const void *__restrict,
- void **__restrict,
- int (*)(const void *, const void *));
+tdelete (const void *__restrict,
+ void **__restrict,
+ int (*)(const void *, const void *));
void *
- tfind (const void *,
- void *const *,
- int (*)(const void *, const void *));
+tfind (const void *,
+ void *const *,
+ int (*)(const void *, const void *));
void *
- tsearch (const void *,
- void **,
- int (*)(const void *, const void *));
+tsearch (const void *,
+ void **,
+ int (*)(const void *, const void *));
#if defined(__cplusplus)
};
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/.gitignore
^
|
@@ -48,15 +48,32 @@
/test_shutdown_select_ignore
/test_str_compare
/test_str_to_value
+/test_str_from_value
/test_upgrade
/test_upgrade_ssl
/test_options
/test_start_stop
/test_str_token
+/test_str_token_remove
+/test_str_tokens_remove
+/test_response_entries
test_shutdown_poll
test_shutdown_select
test_md5
test_sha256
+/test_sha1
test_upgrade_large
test_upgrade_large_tls
test_postprocessor_md
+/test_client_put_shutdown
+/test_client_put_close
+/test_client_put_hard_close
+/test_client_put_steps_shutdown
+/test_client_put_steps_close
+/test_client_put_steps_hard_close
+/test_client_put_chunked_shutdown
+/test_client_put_chunked_close
+/test_client_put_chunked_hard_close
+/test_client_put_chunked_steps_shutdown
+/test_client_put_chunked_steps_close
+/test_client_put_chunked_steps_hard_close
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/Makefile.am
^
|
@@ -1,4 +1,5 @@
# This Makefile.am is in the public domain
+
AM_CPPFLAGS = \
-I$(top_srcdir)/src/include \
-I$(top_srcdir)/src/microhttpd
@@ -11,32 +12,50 @@
noinst_DATA =
MOSTLYCLEANFILES =
+AM_V_LIB = $(am__v_LIB_$(V))
+am__v_LIB_ = $(am__v_LIB_$(AM_DEFAULT_VERBOSITY))
+am__v_LIB_0 = @echo " LIB " $@;
+am__v_LIB_1 =
+
if W32_SHARED_LIB_EXP
+AM_V_DLLTOOL = $(am__v_DLLTOOL_$(V))
+am__v_DLLTOOL_ = $(am__v_DLLTOOL_$(AM_DEFAULT_VERBOSITY))
+am__v_DLLTOOL_0 = @echo " DLLTOOL " $@;
+am__v_DLLTOOL_1 =
+
W32_MHD_LIB_LDFLAGS = -Wl,--output-def,$(lt_cv_objdir)/libmicrohttpd.def -XCClinker -static-libgcc
-noinst_DATA += $(lt_cv_objdir)/libmicrohttpd.lib $(lt_cv_objdir)/libmicrohttpd.def $(lt_cv_objdir)/libmicrohttpd.exp
+noinst_DATA += $(lt_cv_objdir)/libmicrohttpd.lib $(lt_cv_objdir)/libmicrohttpd.def
MOSTLYCLEANFILES += $(lt_cv_objdir)/libmicrohttpd.lib $(lt_cv_objdir)/libmicrohttpd.def $(lt_cv_objdir)/libmicrohttpd.exp
$(lt_cv_objdir)/libmicrohttpd.def: libmicrohttpd.la
+ $(AM_V_at)test -f $@ && touch $@ || \
+ ( rm -f libmicrohttpd.la ; $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd.la && touch $@ )
+
+if USE_EXPORT_FILE
+noinst_DATA += $(lt_cv_objdir)/libmicrohttpd.exp
$(lt_cv_objdir)/libmicrohttpd.exp: $(lt_cv_objdir)/libmicrohttpd.lib
+ $(AM_V_at)test -f $@ && touch $@ || \
+ ( rm -f $(lt_cv_objdir)/libmicrohttpd.lib ; $(MAKE) $(AM_MAKEFLAGS) $(lt_cv_objdir)/libmicrohttpd.lib && touch $@ )
+endif
-$(lt_cv_objdir)/libmicrohttpd.lib: $(lt_cv_objdir)/libmicrohttpd.def libmicrohttpd.la $(libmicrohttpd_la_OBJECTS)
if USE_MS_LIB_TOOL
- @echo Creating $@ and libmicrohttpd.exp by $(MS_LIB_TOOL)... && \
- dll_name=`$(EGREP) -o dlname=\'.+\' libmicrohttpd.la` && \
- dll_name=$${dll_name#*\'} && dll_name=$${dll_name%\'} && test -n "$$dll_name" && \
- echo Creating $$dll_name by $(MS_LIB_TOOL).. && cd "$(lt_cv_objdir)" && \
- $(MS_LIB_TOOL) -def:libmicrohttpd.def -name:$$dll_name -out:libmicrohttpd.lib $(libmicrohttpd_la_OBJECTS:.lo=.o) && cd ..
+$(lt_cv_objdir)/libmicrohttpd.lib: $(lt_cv_objdir)/libmicrohttpd.def libmicrohttpd.la $(libmicrohttpd_la_OBJECTS)
+ $(AM_V_LIB) cd "$(lt_cv_objdir)" && dll_name=`$(SED) -n -e "s/^dlname='\(.*\)'/\1/p" libmicrohttpd.la` && test -n "$$dll_name" && \
+ $(MS_LIB_TOOL) -nologo -def:libmicrohttpd.def -name:$$dll_name -out:libmicrohttpd.lib $(libmicrohttpd_la_OBJECTS:.lo=.o) -ignore:4221
else
- @echo Creating $@ and libmicrohttpd.exp by $(DLLTOOL)... && \
- dll_name=`$(EGREP) -o dlname=\'.+\' libmicrohttpd.la` && \
- dll_name=$${dll_name#*\'} && dll_name=$${dll_name%\'} && test -n "$$dll_name" && \
- echo Creating $$dll_name by $(DLLTOOL).. && cd "$(lt_cv_objdir)" && \
- $(DLLTOOL) -d ./libmicrohttpd.def -D $$dll_name -l libmicrohttpd.lib $(libmicrohttpd_la_OBJECTS:.lo=.o) -e ./libmicrohttpd.exp && cd .. &&\
- echo Created libmicrohttpd.exp and libmicrohttpd.lib.
+if USE_EXPORT_FILE
+$(lt_cv_objdir)/libmicrohttpd.lib: $(lt_cv_objdir)/libmicrohttpd.def libmicrohttpd.la $(libmicrohttpd_la_OBJECTS)
+ $(AM_V_DLLTOOL) cd "$(lt_cv_objdir)" && dll_name=`$(SED) -n -e "s/^dlname='\(.*\)'/\1/p" libmicrohttpd.la` && test -n "$$dll_name" && \
+ $(DLLTOOL) -d libmicrohttpd.def -D $$dll_name -l libmicrohttpd.lib $(libmicrohttpd_la_OBJECTS:.lo=.o) -e ./libmicrohttpd.exp
+else
+$(lt_cv_objdir)/libmicrohttpd.lib: $(lt_cv_objdir)/libmicrohttpd.def libmicrohttpd.la
+ $(AM_V_DLLTOOL) cd "$(lt_cv_objdir)" && dll_name=`$(SED) -n -e "s/^dlname='\(.*\)'/\1/p" libmicrohttpd.la` && test -n "$$dll_name" && \
+ $(DLLTOOL) -d libmicrohttpd.def -D $$dll_name -l libmicrohttpd.lib
+endif
endif
else
- W32_MHD_LIB_LDFLAGS =
+W32_MHD_LIB_LDFLAGS =
endif
if W32_STATIC_LIB
@@ -45,9 +64,9 @@
$(lt_cv_objdir)/libmicrohttpd-static.lib: libmicrohttpd.la $(libmicrohttpd_la_OBJECTS)
if USE_MS_LIB_TOOL
- $(MS_LIB_TOOL) -out:$@ $(libmicrohttpd_la_OBJECTS:.lo=.o)
+ $(AM_V_LIB) $(MS_LIB_TOOL) -nologo -out:$@ $(libmicrohttpd_la_OBJECTS:.lo=.o)
else
- cp $(lt_cv_objdir)/libmicrohttpd.a $@
+ $(AM_V_at)cp $(lt_cv_objdir)/libmicrohttpd.a $@
endif
endif
@@ -59,7 +78,7 @@
internal.c internal.h \
memorypool.c memorypool.h \
mhd_mono_clock.c mhd_mono_clock.h \
- mhd_limits.h mhd_byteorder.h \
+ mhd_limits.h \
sysfdsetsize.c sysfdsetsize.h \
mhd_str.c mhd_str.h \
mhd_send.h mhd_send.c \
@@ -80,6 +99,7 @@
mhd_locks.h
endif
+
libmicrohttpd_la_CPPFLAGS = \
$(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) \
-DBUILDING_MHD_LIB=1
@@ -92,6 +112,7 @@
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)
@@ -99,9 +120,14 @@
EXTRA_libmicrohttpd_la_DEPENDENCIES = $(MHD_DLL_RES_LO)
libmicrohttpd_la_LIBADD += $(MHD_DLL_RES_LO)
+AM_V_RC = $(am__v_RC_$(V))
+am__v_RC_ = $(am__v_RC_$(AM_DEFAULT_VERBOSITY))
+am__v_RC_0 = @echo " RC " $@;
+am__v_RC_1 =
+
# General rule is not required, but keep it just in case
.rc.lo:
- $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $< -o $@
+ $(AM_V_RC) $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $< -o $@
# To add dll resource only to .dll file and exclude it form static
# lib, a little trick was used. Allow libtool to create file.lo,
@@ -112,9 +138,9 @@
# Note: windres does not understand '-isystem' flag, so all
# possible '-isystem' flags are replaced by simple '-I' flags.
$(MHD_DLL_RES_LO): $(MHD_DLL_RES_SRC)
- RC_CPP_FLAGS=" $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) " && \
- $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $${RC_CPP_FLAGS// -isystem / -I } $< -o $@ && \
- echo > $@-empty.c && $(CC) $(AM_CFLAGS) $(CFLAGS) -c $@-empty.c -o $(@:.lo=.o) && rm -f $@-empty.c
+ $(AM_V_RC) RC_CPP_FLAGS=" $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) " && \
+ $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $${RC_CPP_FLAGS// -isystem / -I} $< -o $@ && \
+ echo > $@-empty.c && $(CC) $(DEFS) $(AM_CFLAGS) $(CFLAGS) -c $@-empty.c -o $(@:.lo=.o) && rm -f $@-empty.c
endif
if USE_COVERAGE
@@ -134,7 +160,7 @@
if ENABLE_DAUTH
libmicrohttpd_la_SOURCES += \
digestauth.c \
- mhd_bithelpers.h \
+ mhd_bithelpers.h mhd_byteorder.h mhd_align.h \
md5.c md5.h \
sha256.c sha256.h
endif
@@ -153,13 +179,30 @@
check_PROGRAMS = \
test_str_compare \
test_str_to_value \
+ test_str_from_value \
test_str_token \
+ test_str_token_remove \
+ test_str_tokens_remove \
test_http_reasons \
test_md5 \
+ test_sha1 \
test_sha256 \
test_start_stop \
test_daemon \
+ test_response_entries \
test_postprocessor_md \
+ test_client_put_shutdown \
+ test_client_put_close \
+ test_client_put_hard_close \
+ test_client_put_steps_shutdown \
+ test_client_put_steps_close \
+ test_client_put_steps_hard_close \
+ test_client_put_chunked_shutdown \
+ test_client_put_chunked_close \
+ test_client_put_chunked_hard_close \
+ test_client_put_chunked_steps_shutdown \
+ test_client_put_chunked_steps_close \
+ test_client_put_chunked_steps_hard_close \
test_options
if HAVE_POSIX_THREADS
@@ -171,11 +214,10 @@
check_PROGRAMS += test_upgrade test_upgrade_large
endif
if ENABLE_HTTPS
-if USE_POSIX_THREADS
+if USE_THREADS
+if USE_UPGRADE_TLS_TESTS
check_PROGRAMS += test_upgrade_tls test_upgrade_large_tls
endif
-if USE_W32_THREADS
-check_PROGRAMS += test_upgrade_tls test_upgrade_large_tls
endif
endif
endif
@@ -188,8 +230,6 @@
test_postprocessor_amp
endif
-TESTS = $(check_PROGRAMS)
-
# Do not test trigger of select by shutdown of listen socket
# on Cygwin as this ability is deliberately ignored on Cygwin
# to improve compatibility with core OS.
@@ -207,16 +247,23 @@
endif
endif
+TESTS = $(check_PROGRAMS)
+
test_start_stop_SOURCES = \
test_start_stop.c
test_start_stop_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la
+ libmicrohttpd.la
test_daemon_SOURCES = \
test_daemon.c
test_daemon_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
+test_response_entries_SOURCES = \
+ test_response_entries.c
+test_response_entries_LDADD = \
+ libmicrohttpd.la
+
test_upgrade_SOURCES = \
test_upgrade.c test_helpers.h mhd_sockets.h
test_upgrade_CPPFLAGS = \
@@ -340,13 +387,22 @@
endif
test_str_compare_SOURCES = \
- test_str.c test_helpers.h mhd_str.c
+ test_str.c test_helpers.h mhd_str.c mhd_str.h
test_str_to_value_SOURCES = \
- test_str.c test_helpers.h mhd_str.c
+ test_str.c test_helpers.h mhd_str.c mhd_str.h
+
+test_str_from_value_SOURCES = \
+ test_str.c test_helpers.h mhd_str.c mhd_str.h
test_str_token_SOURCES = \
- test_str_token.c mhd_str.c
+ test_str_token.c mhd_str.c mhd_str.h
+
+test_str_token_remove_SOURCES = \
+ test_str_token_remove.c mhd_str.c mhd_str.h mhd_assert.h ../include/mhd_options.h
+
+test_str_tokens_remove_SOURCES = \
+ test_str_tokens_remove.c mhd_str.c mhd_str.h mhd_assert.h ../include/mhd_options.h
test_http_reasons_SOURCES = \
test_http_reasons.c \
@@ -354,13 +410,77 @@
test_md5_SOURCES = \
test_md5.c test_helpers.h \
- md5.c md5.h mhd_bithelpers.h
+ md5.c md5.h mhd_bithelpers.h mhd_byteorder.h mhd_align.h
test_sha256_SOURCES = \
test_sha256.c test_helpers.h \
- sha256.c sha256.h mhd_bithelpers.h
+ sha256.c sha256.h mhd_bithelpers.h mhd_byteorder.h mhd_align.h
+
+test_sha1_SOURCES = \
+ test_sha1.c test_helpers.h \
+ sha1.c sha1.h mhd_bithelpers.h mhd_byteorder.h mhd_align.h
test_options_SOURCES = \
test_options.c
test_options_LDADD = \
$(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_client_put_shutdown_SOURCES = \
+ test_client_put_stop.c
+test_client_put_shutdown_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_close_SOURCES = \
+ test_client_put_stop.c
+test_client_put_close_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_hard_close_SOURCES = \
+ test_client_put_stop.c
+test_client_put_hard_close_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_steps_shutdown_SOURCES = \
+ test_client_put_stop.c
+test_client_put_steps_shutdown_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_steps_close_SOURCES = \
+ test_client_put_stop.c
+test_client_put_steps_close_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_steps_hard_close_SOURCES = \
+ test_client_put_stop.c
+test_client_put_steps_hard_close_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_chunked_shutdown_SOURCES = \
+ test_client_put_stop.c
+test_client_put_chunked_shutdown_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_chunked_close_SOURCES = \
+ test_client_put_stop.c
+test_client_put_chunked_close_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_chunked_hard_close_SOURCES = \
+ test_client_put_stop.c
+test_client_put_chunked_hard_close_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_chunked_steps_shutdown_SOURCES = \
+ test_client_put_stop.c
+test_client_put_chunked_steps_shutdown_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_chunked_steps_close_SOURCES = \
+ test_client_put_stop.c
+test_client_put_chunked_steps_close_LDADD = \
+ libmicrohttpd.la
+
+test_client_put_chunked_steps_hard_close_SOURCES = \
+ test_client_put_stop.c
+test_client_put_chunked_steps_hard_close_LDADD = \
+ libmicrohttpd.la
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/connection.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
+ Copyright (C) 2015-2021 Evgeny Grin (Karlson2k)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -53,6 +54,7 @@
#include <sys/param.h>
#endif /* HAVE_SYS_PARAM_H */
#include "mhd_send.h"
+#include "mhd_assert.h"
/**
* Message to transmit when http 1.1 request is received
@@ -102,6 +104,50 @@
#endif
/**
+ * Response text used when the request HTTP chunked encoding is
+ * malformed.
+ */
+#ifdef HAVE_MESSAGES
+#define REQUEST_CHUNKED_MALFORMED \
+ "<html><head><title>Request malformed</title></head><body>Your HTTP chunked encoding was syntactically incorrect.</body></html>"
+#else
+#define REQUEST_CHUNKED_MALFORMED ""
+#endif
+
+/**
+ * Response text used when the request HTTP chunk is too large.
+ */
+#ifdef HAVE_MESSAGES
+#define REQUEST_CHUNK_TOO_LARGE \
+ "<html><head><title>Request content too large</title></head><body>The chunk size used in your HTTP chunked encoded request is too large.</body></html>"
+#else
+#define REQUEST_CHUNK_TOO_LARGE ""
+#endif
+
+/**
+ * Response text used when the request HTTP content is too large.
+ */
+#ifdef HAVE_MESSAGES
+#define REQUEST_CONTENTLENGTH_TOOLARGE \
+ "<html><head><title>Request content too large</title></head>" \
+ "<body>Your HTTP request has too large value for <b>Content-Length</b> header.</body></html>"
+#else
+#define REQUEST_CONTENTLENGTH_TOOLARGE ""
+#endif
+
+/**
+ * Response text used when the request HTTP chunked encoding is
+ * malformed.
+ */
+#ifdef HAVE_MESSAGES
+#define REQUEST_CONTENTLENGTH_MALFORMED \
+ "<html><head><title>Request malformed</title></head>" \
+ "<body>Your HTTP request has wrong value for <b>Content-Length</b> header.</body></html>"
+#else
+#define REQUEST_CONTENTLENGTH_MALFORMED ""
+#endif
+
+/**
* Response text used when there is an internal server error.
*
* Intentionally empty here to keep our memory footprint
@@ -114,6 +160,26 @@
#define INTERNAL_ERROR ""
#endif
+/**
+ * Response text used when the request HTTP version is too old.
+ */
+#ifdef HAVE_MESSAGES
+#define REQ_HTTP_VER_IS_TOO_OLD \
+ "<html><head><title>Requested HTTP version is not supported</title></head><body>Requested HTTP version is too old and not supported.</body></html>"
+#else
+#define REQ_HTTP_VER_IS_TOO_OLD ""
+#endif
+
+/**
+ * Response text used when the request HTTP version is not supported.
+ */
+#ifdef HAVE_MESSAGES
+#define REQ_HTTP_VER_IS_NOT_SUPPORTED \
+ "<html><head><title>Requested HTTP version is not supported</title></head><body>Requested HTTP version is not supported.</body></html>"
+#else
+#define REQ_HTTP_VER_IS_NOT_SUPPORTED ""
+#endif
+
/**
* sendfile() chuck size
@@ -168,6 +234,76 @@
#endif /* HAVE_MESSAGES */
/**
+ * Allocate memory from connection's memory pool.
+ * If memory pool doesn't have enough free memory but read of write buffer
+ * have some unused memory, the size of the buffer will be reduced as needed.
+ * @param connection the connection to use
+ * @param size the size of allocated memory area
+ * @return pointer to allocated memory region in the pool or
+ * NULL if no memory is available
+ */
+static void *
+connection_alloc_memory (struct MHD_Connection *connection,
+ size_t size)
+{
+ struct MHD_Connection *const c = connection; /* a short alias */
+ struct MemoryPool *const pool = c->pool; /* a short alias */
+ size_t need_to_free; /**< The required amount of free memory */
+ void *res;
+
+ res = MHD_pool_try_alloc (pool, size, &need_to_free);
+ if (NULL == res)
+ {
+ if (NULL != c->write_buffer)
+ {
+ /* The connection is in the sending phase */
+ mhd_assert (MHD_CONNECTION_START_REPLY <= c->state);
+ if (c->write_buffer_size - c->write_buffer_append_offset >= need_to_free)
+ {
+ char *buf;
+ const size_t new_buf_size = c->write_buffer_size - need_to_free;
+ buf = MHD_pool_reallocate (pool,
+ c->write_buffer,
+ c->write_buffer_size,
+ new_buf_size);
+ mhd_assert (c->write_buffer == buf);
+ mhd_assert (c->write_buffer_append_offset <= new_buf_size);
+ mhd_assert (c->write_buffer_send_offset <= new_buf_size);
+ c->write_buffer_size = new_buf_size;
+ c->write_buffer = buf;
+ }
+ else
+ return NULL;
+ }
+ else if (NULL != c->read_buffer)
+ {
+ /* The connection is in the receiving phase */
+ if (c->read_buffer_size - c->read_buffer_offset >= need_to_free)
+ {
+ char *buf;
+ const size_t new_buf_size = c->read_buffer_size - need_to_free;
+ buf = MHD_pool_reallocate (pool,
+ c->read_buffer,
+ c->read_buffer_size,
+ new_buf_size);
+ mhd_assert (c->read_buffer == buf);
+ mhd_assert (c->read_buffer_offset <= new_buf_size);
+ c->read_buffer_size = new_buf_size;
+ c->read_buffer = buf;
+ }
+ else
+ return NULL;
+ }
+ else
+ return NULL;
+ res = MHD_pool_allocate (pool, size, true);
+ mhd_assert (NULL != res); /* It has been checked that pool has enough space */
+ }
+ return res;
+}
+
+
+/**
* Callback for receiving data from the socket.
*
* @param connection the MHD connection structure
@@ -201,7 +337,8 @@
{
#ifdef EPOLL_SUPPORT
/* Got EAGAIN --- no longer read-ready */
- connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
#endif /* EPOLL_SUPPORT */
return MHD_ERR_AGAIN_;
}
@@ -224,7 +361,8 @@
}
#ifdef EPOLL_SUPPORT
else if (i > (size_t) ret)
- connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
#endif /* EPOLL_SUPPORT */
return ret;
}
@@ -344,9 +482,8 @@
{
struct MHD_HTTP_Header *pos;
- pos = MHD_pool_allocate (connection->pool,
- sizeof (struct MHD_HTTP_Header),
- true);
+ pos = connection_alloc_memory (connection,
+ sizeof (struct MHD_HTTP_Header));
if (NULL == pos)
return MHD_NO;
pos->header = (char *) key;
@@ -632,18 +769,16 @@
{
const char *expect;
- return ( (NULL != connection->version) &&
- (MHD_str_equal_caseless_ (connection->version,
- MHD_HTTP_VERSION_1_1)) &&
- (MHD_NO != MHD_lookup_connection_value_n (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_EXPECT,
- MHD_STATICSTR_LEN_ (
- MHD_HTTP_HEADER_EXPECT),
- &expect,
- NULL)) &&
- (MHD_str_equal_caseless_ (expect,
- "100-continue")) );
+ return (MHD_IS_HTTP_VER_1_1_COMPAT (connection->http_ver) &&
+ (MHD_NO != MHD_lookup_connection_value_n (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_EXPECT,
+ MHD_STATICSTR_LEN_ (
+ MHD_HTTP_HEADER_EXPECT),
+ &expect,
+ NULL)) &&
+ (MHD_str_equal_caseless_ (expect,
+ "100-continue")) );
}
@@ -658,8 +793,6 @@
{
const struct MHD_Daemon *daemon = connection->daemon;
- connection->state = MHD_CONNECTION_CLOSED;
- connection->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
if (0 == (daemon->options & MHD_USE_TURBO))
{
#ifdef HTTPS_SUPPORT
@@ -679,6 +812,8 @@
shutdown (connection->socket_fd,
SHUT_WR);
}
+ connection->state = MHD_CONNECTION_CLOSED;
+ connection->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
}
@@ -702,13 +837,6 @@
mhd_assert ( (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) || \
MHD_thread_ID_match_current_ (connection->pid) );
#endif /* MHD_USE_THREADS */
-
- MHD_connection_mark_closed_ (connection);
- if (NULL != resp)
- {
- connection->response = NULL;
- MHD_destroy_response (resp);
- }
if ( (NULL != daemon->notify_completed) &&
(connection->client_aware) )
daemon->notify_completed (daemon->notify_completed_cls,
@@ -716,6 +844,18 @@
&connection->client_context,
termination_code);
connection->client_aware = false;
+ if (NULL != resp)
+ {
+ connection->response = NULL;
+ MHD_destroy_response (resp);
+ }
+ if (NULL != connection->pool)
+ {
+ MHD_pool_destroy (connection->pool);
+ connection->pool = NULL;
+ }
+
+ MHD_connection_mark_closed_ (connection);
}
@@ -804,6 +944,8 @@
connection_close_error (struct MHD_Connection *connection,
const char *emsg)
{
+ connection->stop_with_error = true;
+ connection->discard_request = true;
#ifdef HAVE_MESSAGES
if (NULL != emsg)
MHD_DLOG (connection->daemon,
@@ -829,6 +971,41 @@
/**
+ * A serious error occurred, check whether error response is already
+ * queued and close the connection if response wasn't queued.
+ *
+ * @param connection connection to close with error
+ * @param emsg error message (can be NULL)
+ */
+static void
+connection_close_error_check (struct MHD_Connection *connection,
+ const char *emsg)
+{
+ if ( (NULL != connection->response) &&
+ (400 <= connection->responseCode) &&
+ (NULL == connection->response->crc) && /* Static response only! */
+ (connection->stop_with_error) &&
+ (MHD_CONNECTION_HEADERS_SENDING == connection->state) )
+ return; /* An error response was already queued */
+
+ connection_close_error (connection, emsg);
+}
+
+
+/**
+ * Macro to only include error message in call to
+ * #connection_close_error_check() if we have HAVE_MESSAGES.
+ */
+#ifdef HAVE_MESSAGES
+#define CONNECTION_CLOSE_ERROR_CHECK(c, emsg) \
+ connection_close_error_check (c, emsg)
+#else
+#define CONNECTION_CLOSE_ERROR_CHECK(c, emsg) \
+ connection_close_error_check (c, NULL)
+#endif
+
+
+/**
* Prepare the response buffer of this connection for
* sending. Assumes that the response mutex is
* already held. If the transmission is complete,
@@ -847,9 +1024,12 @@
struct MHD_Response *response;
response = connection->response;
+ mhd_assert (connection->rp_props.send_reply_body);
+
if ( (0 == response->total_size) ||
+ /* TODO: replace the next check with assert */
(connection->response_write_position == response->total_size) )
- return MHD_YES; /* 0-byte response is always ready */
+ return MHD_YES; /* 0-byte response is always ready */
if (NULL != response->data_iov)
{
size_t copy_size;
@@ -857,9 +1037,8 @@
if (NULL != connection->resp_iov.iov)
return MHD_YES;
copy_size = response->data_iovcnt * sizeof(MHD_iovec_);
- connection->resp_iov.iov = MHD_pool_allocate (connection->pool,
- copy_size,
- true);
+ connection->resp_iov.iov = connection_alloc_memory (connection,
+ copy_size);
if (NULL == connection->resp_iov.iov)
{
MHD_mutex_unlock_chk_ (&response->mutex);
@@ -896,15 +1075,17 @@
(size_t) MHD_MIN ((uint64_t) response->data_buffer_size,
response->total_size
- connection->response_write_position));
- if ( (((ssize_t) MHD_CONTENT_READER_END_OF_STREAM) == ret) ||
- (((ssize_t) MHD_CONTENT_READER_END_WITH_ERROR) == ret) )
+ if ( (MHD_CONTENT_READER_END_OF_STREAM == ret) ||
+ (MHD_CONTENT_READER_END_WITH_ERROR == ret) )
{
/* either error or http 1.0 transfer, close socket! */
+ /* TODO: do not update total size, check whether response
+ * was really with unknown size */
response->total_size = connection->response_write_position;
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
MHD_mutex_unlock_chk_ (&response->mutex);
#endif
- if ( ((ssize_t) MHD_CONTENT_READER_END_OF_STREAM) == ret)
+ if (MHD_CONTENT_READER_END_OF_STREAM == ret)
MHD_connection_close_ (connection,
MHD_REQUEST_TERMINATED_COMPLETED_OK);
else
@@ -934,25 +1115,39 @@
* return #MHD_NO).
*
* @param connection the connection
+ * @param[out] p_finished the pointer to variable that will be set to "true"
+ * when application returned indication of the end
+ * of the stream
* @return #MHD_NO if readying the response failed
*/
static enum MHD_Result
-try_ready_chunked_body (struct MHD_Connection *connection)
+try_ready_chunked_body (struct MHD_Connection *connection,
+ bool *p_finished)
{
ssize_t ret;
struct MHD_Response *response;
- char cbuf[10]; /* 10: max strlen of "%x\r\n" */
- int cblen;
+ static const size_t max_chunk = 0xFFFFFF;
+ char chunk_hdr[6]; /* 6: max strlen of "FFFFFF" */
+ /* "FFFFFF" + "\r\n" */
+ static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
+ /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
+ static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
+ size_t chunk_hdr_len;
+ uint64_t left_to_send;
+ size_t size_to_fill;
response = connection->response;
- if (NULL == response->crc)
- return MHD_YES;
- if (0 == connection->write_buffer_size)
+ mhd_assert (NULL != response->crc || NULL != response->data);
+
+ mhd_assert (0 == connection->write_buffer_append_offset);
+
+ /* The buffer must be reasonably large enough */
+ if (128 > connection->write_buffer_size)
{
size_t size;
- size = MHD_pool_get_free (connection->pool);
- if (size < 128)
+ size = connection->write_buffer_size + MHD_pool_get_free (connection->pool);
+ if (128 > size)
{
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
MHD_mutex_unlock_chk_ (&response->mutex);
@@ -962,17 +1157,33 @@
_ ("Closing connection (out of memory)."));
return MHD_NO;
}
- if ( (2 * (0xFFFFFF + sizeof(cbuf) + 2)) < size)
- size = 2 * (0xFFFFFF + sizeof(cbuf) + 2);
- connection->write_buffer = MHD_pool_allocate (connection->pool,
- size,
- false);
+ /* Limit the buffer size to the largest usable size for chunks */
+ if ( (max_chunk + max_chunk_overhead) < size)
+ size = max_chunk + max_chunk_overhead;
+ connection->write_buffer = MHD_pool_reallocate (connection->pool,
+ connection->write_buffer,
+ connection->
+ write_buffer_size, size);
mhd_assert (NULL != connection->write_buffer);
connection->write_buffer_size = size;
}
+ mhd_assert (max_chunk_overhead < connection->write_buffer_size);
+
+ if (MHD_SIZE_UNKNOWN == response->total_size)
+ left_to_send = MHD_SIZE_UNKNOWN;
+ else
+ left_to_send = response->total_size - connection->response_write_position;
- if (0 == response->total_size)
- ret = 0; /* response must be empty, don't bother calling crc */
+ size_to_fill = connection->write_buffer_size - max_chunk_overhead;
+ /* Limit size for the callback to the max usable size */
+ if (max_chunk < size_to_fill)
+ size_to_fill = max_chunk;
+ if (left_to_send < size_to_fill)
+ size_to_fill = (size_t) left_to_send;
+
+ if (0 == left_to_send)
+ /* nothing to send, don't bother calling crc */
+ ret = MHD_CONTENT_READER_END_OF_STREAM;
else if ( (response->data_start <=
connection->response_write_position) &&
(response->data_start + response->data_size >
@@ -984,23 +1195,32 @@
= (size_t) (connection->response_write_position - response->data_start);
/* buffer already ready, use what is there for the chunk */
ret = response->data_size - data_write_offset;
- if ( ((size_t) ret) > connection->write_buffer_size - sizeof (cbuf) - 2)
- ret = connection->write_buffer_size - sizeof (cbuf) - 2;
- memcpy (&connection->write_buffer[sizeof (cbuf)],
+ if ( ((size_t) ret) > size_to_fill)
+ ret = (ssize_t) size_to_fill;
+ memcpy (&connection->write_buffer[max_chunk_hdr_len],
&response->data[data_write_offset],
ret);
}
else
{
- /* buffer not in range, try to fill it */
+ if (NULL == response->crc)
+ { /* There is no way to reach this code */
+#if defined(MHD_USE_THREADS)
+ MHD_mutex_unlock_chk_ (&response->mutex);
+#endif
+ CONNECTION_CLOSE_ERROR (connection,
+ _ ("No callback for the chunked data."));
+ return MHD_NO;
+ }
ret = response->crc (response->crc_cls,
connection->response_write_position,
- &connection->write_buffer[sizeof (cbuf)],
- connection->write_buffer_size - sizeof (cbuf) - 2);
+ &connection->write_buffer[max_chunk_hdr_len],
+ size_to_fill);
}
- if ( ((ssize_t) MHD_CONTENT_READER_END_WITH_ERROR) == ret)
+ if (MHD_CONTENT_READER_END_WITH_ERROR == ret)
{
/* error, close socket! */
+ /* TODO: remove update of the response size */
response->total_size = connection->response_write_position;
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
MHD_mutex_unlock_chk_ (&response->mutex);
@@ -1010,15 +1230,10 @@
"Closing connection (application error generating response)."));
return MHD_NO;
}
- if ( (((ssize_t) MHD_CONTENT_READER_END_OF_STREAM) == ret) ||
- (0 == response->total_size) )
+ if (MHD_CONTENT_READER_END_OF_STREAM == ret)
{
- /* end of message, signal other side! */
- memcpy (connection->write_buffer,
- "0\r\n",
- 3);
- connection->write_buffer_append_offset = 3;
- connection->write_buffer_send_offset = 0;
+ *p_finished = true;
+ /* TODO: remove update of the response size */
response->total_size = connection->response_write_position;
return MHD_YES;
}
@@ -1030,96 +1245,128 @@
#endif
return MHD_NO;
}
- if (ret > 0xFFFFFF)
- ret = 0xFFFFFF;
- cblen = MHD_snprintf_ (cbuf,
- sizeof (cbuf),
- "%X\r\n",
- (unsigned int) ret);
- mhd_assert (cblen > 0);
- mhd_assert ((size_t) cblen < sizeof(cbuf));
- memcpy (&connection->write_buffer[sizeof (cbuf) - cblen],
- cbuf,
- cblen);
- memcpy (&connection->write_buffer[sizeof (cbuf) + ret],
- "\r\n",
- 2);
+ if (size_to_fill < (size_t) ret)
+ {
+#if defined(MHD_USE_THREADS)
+ MHD_mutex_unlock_chk_ (&response->mutex);
+#endif
+ CONNECTION_CLOSE_ERROR (connection,
+ _ ("Closing connection (application returned " \
+ "more data than requested)."));
+ return MHD_NO;
+ }
+ chunk_hdr_len = MHD_uint32_to_strx ((uint32_t) ret, chunk_hdr,
+ sizeof(chunk_hdr));
+ mhd_assert (chunk_hdr_len != 0);
+ mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
+ *p_finished = false;
+ connection->write_buffer_send_offset =
+ (max_chunk_hdr_len - (chunk_hdr_len + 2));
+ memcpy (connection->write_buffer + connection->write_buffer_send_offset,
+ chunk_hdr,
+ chunk_hdr_len);
+ connection->write_buffer[max_chunk_hdr_len - 2] = '\r';
+ connection->write_buffer[max_chunk_hdr_len - 1] = '\n';
+ connection->write_buffer[max_chunk_hdr_len + ret] = '\r';
+ connection->write_buffer[max_chunk_hdr_len + ret + 1] = '\n';
connection->response_write_position += ret;
- connection->write_buffer_send_offset = sizeof (cbuf) - cblen;
- connection->write_buffer_append_offset = sizeof (cbuf) + ret + 2;
+ connection->write_buffer_append_offset = max_chunk_hdr_len + ret + 2;
return MHD_YES;
}
/**
- * Are we allowed to keep the given connection alive? We can use the
- * TCP stream for a second request if the connection is HTTP 1.1 and
- * the "Connection" header either does not exist or is not set to
- * "close", or if the connection is HTTP 1.0 and the "Connection"
- * header is explicitly set to "keep-alive". If no HTTP version is
- * specified (or if it is not 1.0 or 1.1), we definitively close the
- * connection. If the "Connection" header is not exactly "close" or
- * "keep-alive", we proceed to use the default for the respective HTTP
- * version (which is conservative for HTTP 1.0, but might be a bit
- * optimistic for HTTP 1.1).
+ * Are we allowed to keep the given connection alive?
+ * We can use the TCP stream for a second request if the connection
+ * is HTTP 1.1 and the "Connection" header either does not exist or
+ * is not set to "close", or if the connection is HTTP 1.0 and the
+ * "Connection" header is explicitly set to "keep-alive".
+ * If no HTTP version is specified (or if it is not 1.0 or 1.1), we
+ * definitively close the connection. If the "Connection" header is
+ * not exactly "close" or "keep-alive", we proceed to use the default
+ * for the respective HTTP version.
+ * If response has HTTP/1.0 flag or has "Connection: close" header
+ * then connection must be closed.
+ * If full request has not been read then connection must be closed
+ * as well.
*
* @param connection the connection to check for keepalive
- * @return #MHD_YES if (based on the request), a keepalive is
- * legal
+ * @return MHD_CONN_USE_KEEPALIVE if (based on the request and the response),
+ * a keepalive is legal,
+ * MHD_CONN_MUST_CLOSE if connection must be closed after sending
+ * complete reply,
+ * MHD_CONN_MUST_UPGRADE if connection must be upgraded.
*/
-static enum MHD_Result
+static enum MHD_ConnKeepAlive
keepalive_possible (struct MHD_Connection *connection)
{
- if (MHD_CONN_MUST_CLOSE == connection->keepalive)
- return MHD_NO;
- if (NULL == connection->version)
- return MHD_NO;
- if ( (NULL != connection->response) &&
- (0 != (connection->response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) ) )
- return MHD_NO;
+ struct MHD_Connection *const c = connection; /**< a short alias */
+ struct MHD_Response *const r = c->response; /**< a short alias */
- if (MHD_str_equal_caseless_ (connection->version,
- MHD_HTTP_VERSION_1_1) &&
- ( (NULL == connection->response) ||
- (0 == (connection->response->flags
- & MHD_RF_HTTP_VERSION_1_0_RESPONSE) ) ) )
- {
- if (MHD_lookup_header_s_token_ci (connection,
- MHD_HTTP_HEADER_CONNECTION,
- "upgrade"))
- return MHD_NO;
-
- if (MHD_lookup_header_s_token_ci (connection,
- MHD_HTTP_HEADER_CONNECTION,
- "close"))
- return MHD_NO;
+ mhd_assert (NULL != r);
+ if (MHD_CONN_MUST_CLOSE == c->keepalive)
+ return MHD_CONN_MUST_CLOSE;
- return MHD_YES;
+#ifdef UPGRADE_SUPPORT
+ /* TODO: Move below the next check when MHD stops closing connections
+ * when response is queued in first callback */
+ if (NULL != r->upgrade_handler)
+ {
+ /* No "close" token is enforced by 'add_response_header_connection()' */
+ mhd_assert (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE));
+ /* Valid HTTP version is enforced by 'MHD_queue_response()' */
+ mhd_assert (MHD_IS_HTTP_VER_SUPPORTED (c->http_ver));
+ mhd_assert (! c->stop_with_error);
+ return MHD_CONN_MUST_UPGRADE;
}
- if (MHD_str_equal_caseless_ (connection->version,
- MHD_HTTP_VERSION_1_0))
+#endif /* UPGRADE_SUPPORT */
+
+ mhd_assert ( (! c->stop_with_error) || (c->discard_request));
+ if ((c->read_closed) || (c->discard_request))
+ return MHD_CONN_MUST_CLOSE;
+
+ if (0 != (r->flags & MHD_RF_HTTP_1_0_COMPATIBLE_STRICT))
+ return MHD_CONN_MUST_CLOSE;
+ if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
+ return MHD_CONN_MUST_CLOSE;
+
+ if (! MHD_IS_HTTP_VER_SUPPORTED (c->http_ver))
+ return MHD_CONN_MUST_CLOSE;
+
+ if (MHD_lookup_header_s_token_ci (c,
+ MHD_HTTP_HEADER_CONNECTION,
+ "close"))
+ return MHD_CONN_MUST_CLOSE;
+
+ if ((MHD_HTTP_VER_1_0 == connection->http_ver) ||
+ (0 != (connection->response->flags & MHD_RF_HTTP_1_0_SERVER)))
{
if (MHD_lookup_header_s_token_ci (connection,
MHD_HTTP_HEADER_CONNECTION,
"Keep-Alive"))
- return MHD_YES;
+ return MHD_CONN_USE_KEEPALIVE;
- return MHD_NO;
+ return MHD_CONN_MUST_CLOSE;
}
- return MHD_NO;
+
+ if (MHD_IS_HTTP_VER_1_1_COMPAT (c->http_ver))
+ return MHD_CONN_USE_KEEPALIVE;
+
+ return MHD_CONN_MUST_CLOSE;
}
/**
- * Produce HTTP time stamp.
+ * Produce time stamp.
+ *
+ * Result is NOT null-terminated.
+ * Result is always 29 bytes long.
*
- * @param date where to write the header, with
- * at least 128 bytes available space.
- * @param date_len number of bytes in @a date
+ * @param[out] date where to write the time stamp, with
+ * at least 29 bytes available space.
*/
-static void
-get_date_string (char *date,
- size_t date_len)
+static bool
+get_date_str (char *date)
{
static const char *const days[] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
@@ -1128,43 +1375,99 @@
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
+ static const size_t buf_len = 29;
struct tm now;
time_t t;
+ const char *src;
#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
! defined(HAVE_GMTIME_R)
- struct tm*pNow;
+ struct tm *pNow;
#endif
- date[0] = 0;
- time (&t);
+ if ((time_t) -1 == time (&t))
+ return false;
#if defined(HAVE_C11_GMTIME_S)
if (NULL == gmtime_s (&t,
&now))
- return;
+ return false;
#elif defined(HAVE_W32_GMTIME_S)
if (0 != gmtime_s (&now,
&t))
- return;
+ return false;
#elif defined(HAVE_GMTIME_R)
if (NULL == gmtime_r (&t,
&now))
- return;
+ return false;
#else
pNow = gmtime (&t);
if (NULL == pNow)
- return;
+ return false;
now = *pNow;
#endif
- MHD_snprintf_ (date,
- date_len,
- "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n",
- days[now.tm_wday % 7],
- (unsigned int) now.tm_mday,
- mons[now.tm_mon % 12],
- (unsigned int) (1900 + now.tm_year),
- (unsigned int) now.tm_hour,
- (unsigned int) now.tm_min,
- (unsigned int) now.tm_sec);
+
+ /* Day of the week */
+ src = days[now.tm_wday % 7];
+ date[0] = src[0];
+ date[1] = src[1];
+ date[2] = src[2];
+ date[3] = ',';
+ date[4] = ' ';
+ /* Day of the month */
+ if (2 != MHD_uint8_to_str_pad ((uint8_t) now.tm_mday, 2,
+ date + 5, buf_len - 5))
+ return false;
+ date[7] = ' ';
+ /* Month */
+ src = mons[now.tm_mon % 12];
+ date[8] = src[0];
+ date[9] = src[1];
+ date[10] = src[2];
+ date[11] = ' ';
+ /* Year */
+ if (4 != MHD_uint16_to_str ((uint16_t) (1900 + now.tm_year), date + 12,
+ buf_len - 12))
+ return false;
+ date[16] = ' ';
+ /* Time */
+ MHD_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17);
+ date[19] = ':';
+ MHD_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20);
+ date[22] = ':';
+ MHD_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23);
+ date[25] = ' ';
+ date[26] = 'G';
+ date[27] = 'M';
+ date[28] = 'T';
+
+ return true;
+}
+
+
+/**
+ * Produce HTTP DATE header.
+ * Result is always 37 bytes long (plus one terminating null).
+ *
+ * @param[out] header where to write the header, with
+ * at least 38 bytes available space.
+ */
+static bool
+get_date_header (char *header)
+{
+ if (! get_date_str (header + 6))
+ {
+ header[0] = 0;
+ return false;
+ }
+ header[0] = 'D';
+ header[1] = 'a';
+ header[2] = 't';
+ header[3] = 'e';
+ header[4] = ':';
+ header[5] = ' ';
+ header[35] = '\r';
+ header[36] = '\n';
+ header[37] = 0;
+ return true;
}
@@ -1238,376 +1541,719 @@
/**
- * Allocate the connection's write buffer and fill it with all of the
- * headers (or footers, if we have already sent the body) from the
- * HTTPd's response. If headers are missing in the response supplied
- * by the application, additional headers may be added here.
- *
- * @param connection the connection
- * @return #MHD_YES on success, #MHD_NO on failure (out of memory)
+ * Shrink connection read buffer to the zero size of free space in the buffer
+ * @param connection the connection whose read buffer is being manipulated
*/
-static enum MHD_Result
-build_header_response (struct MHD_Connection *connection)
+static void
+connection_shrink_read_buffer (struct MHD_Connection *connection)
{
- struct MHD_Response *response = connection->response;
- size_t size;
- size_t off;
- struct MHD_HTTP_Header *pos;
- char code[256];
- char date[128];
- size_t datelen;
- char content_length_buf[128];
- size_t content_length_len;
- char *data;
- enum MHD_ValueKind kind;
- const char *reason_phrase;
- uint32_t rc;
- bool client_requested_close;
- bool response_has_close;
- bool response_has_keepalive;
- const char *have_encoding;
- bool must_add_close;
- bool must_add_chunked_encoding;
- bool must_add_keep_alive;
- bool must_add_content_length;
- bool may_add_content_length;
+ struct MHD_Connection *const c = connection; /**< a short alias */
+ void *new_buf;
- mhd_assert (NULL != connection->version);
- if (0 == connection->version[0])
+ if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
{
- data = MHD_pool_allocate (connection->pool,
- 0,
- true);
- connection->write_buffer = data;
- connection->write_buffer_append_offset = 0;
- connection->write_buffer_send_offset = 0;
- connection->write_buffer_size = 0;
- return MHD_YES;
+ mhd_assert (0 == c->read_buffer_size);
+ mhd_assert (0 == c->read_buffer_offset);
+ return;
}
- rc = connection->responseCode & (~MHD_ICY_FLAG);
- if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
+
+ mhd_assert (c->read_buffer_offset <= c->read_buffer_size);
+ new_buf = MHD_pool_reallocate (c->pool, c->read_buffer, c->read_buffer_size,
+ c->read_buffer_offset);
+ mhd_assert (c->read_buffer == new_buf);
+ c->read_buffer = new_buf;
+ c->read_buffer_size = c->read_buffer_offset;
+}
+
+
+/**
+ * Allocate the maximum available amount of memory from MemoryPool
+ * for write buffer.
+ * @param connection the connection whose write buffer is being manipulated
+ * @return the size of free space in write buffer, may be smaller
+ * than requested size.
+ */
+static size_t
+connection_maximize_write_buffer (struct MHD_Connection *connection)
+{
+ struct MHD_Connection *const c = connection; /**< a short alias */
+ struct MemoryPool *const pool = connection->pool;
+ void *new_buf;
+ size_t new_size;
+ size_t free_size;
+
+ mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
+ mhd_assert (c->write_buffer_append_offset >= c->write_buffer_send_offset);
+ mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset);
+
+ free_size = MHD_pool_get_free (pool);
+ if (0 != free_size)
{
- reason_phrase = MHD_get_reason_phrase_for (rc);
- off = MHD_snprintf_ (code,
- sizeof (code),
- "%s %u %s\r\n",
- (0 != (connection->responseCode & MHD_ICY_FLAG))
- ? "ICY"
- : ( (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_0,
- connection->version) ||
- (0 != (connection->response->flags
- & MHD_RF_HTTP_VERSION_1_0_RESPONSE)) )
- ? MHD_HTTP_VERSION_1_0
- : MHD_HTTP_VERSION_1_1),
- rc,
- reason_phrase);
- /* estimate size */
- size = off + 2; /* +2 for extra "\r\n" at the end */
- kind = MHD_HEADER_KIND;
- if ( (0 == (connection->daemon->options
- & MHD_USE_SUPPRESS_DATE_NO_CLOCK)) &&
- (NULL == MHD_get_response_header (response,
- MHD_HTTP_HEADER_DATE)) )
- get_date_string (date,
- sizeof (date));
- else
- date[0] = '\0';
- datelen = strlen (date);
- size += datelen;
+ new_size = c->write_buffer_size + free_size;
+ /* This function must not move the buffer position.
+ * MHD_pool_reallocate () may return the new position only if buffer was
+ * allocated 'from_end' or is not the last allocation,
+ * which should not happen. */
+ new_buf = MHD_pool_reallocate (pool,
+ c->write_buffer,
+ c->write_buffer_size,
+ new_size);
+ mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
+ c->write_buffer = new_buf;
+ c->write_buffer_size = new_size;
+ if (c->write_buffer_send_offset == c->write_buffer_append_offset)
+ {
+ /* All data have been sent, reset offsets to zero. */
+ c->write_buffer_send_offset = 0;
+ c->write_buffer_append_offset = 0;
+ }
}
- else
+
+ return c->write_buffer_size - c->write_buffer_append_offset;
+}
+
+
+#if 0 /* disable unused function */
+/**
+ * Shrink connection write buffer to the size of unsent data.
+ *
+ * @note: The number of calls of this function should be limited to avoid extra
+ * zeroing of the memory.
+ * @param connection the connection whose write buffer is being manipulated
+ * @param connection the connection to manipulate write buffer
+ */
+static void
+connection_shrink_write_buffer (struct MHD_Connection *connection)
+{
+ struct MHD_Connection *const c = connection; /**< a short alias */
+ struct MemoryPool *const pool = connection->pool;
+ void *new_buf;
+
+ mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
+ mhd_assert (c->write_buffer_append_offset >= c->write_buffer_send_offset);
+ mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset);
+
+ if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size))
{
- /* 2 bytes for final CRLF of a Chunked-Body */
- size = 2;
- kind = MHD_FOOTER_KIND;
- off = 0;
- datelen = 0;
+ mhd_assert (0 == c->write_buffer_append_offset);
+ mhd_assert (0 == c->write_buffer_send_offset);
+ c->write_buffer = NULL;
+ return;
}
+ if (c->write_buffer_append_offset == c->write_buffer_size)
+ return;
- /* calculate extra headers we need to add, such as 'Connection: close',
- first see what was explicitly requested by the application */
- must_add_close = false;
- must_add_chunked_encoding = false;
- must_add_keep_alive = false;
- must_add_content_length = false;
- content_length_len = 0; /* Mute compiler warning only */
- response_has_close = false;
- switch (connection->state)
- {
- case MHD_CONNECTION_FOOTERS_RECEIVED:
- response_has_close = MHD_check_response_header_s_token_ci (response,
- MHD_HTTP_HEADER_CONNECTION,
- "close");
- response_has_keepalive = MHD_check_response_header_s_token_ci (response,
- MHD_HTTP_HEADER_CONNECTION,
- "Keep-Alive");
- client_requested_close = MHD_lookup_header_s_token_ci (connection,
- MHD_HTTP_HEADER_CONNECTION,
- "close");
+ new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size,
+ c->write_buffer_append_offset);
+ mhd_assert ((c->write_buffer == new_buf) || \
+ (0 == c->write_buffer_append_offset));
+ c->write_buffer_size = c->write_buffer_append_offset;
+ if (0 == c->write_buffer_size)
+ c->write_buffer = NULL;
+ else
+ c->write_buffer = new_buf;
+}
+
+
+#endif /* unused function */
+
+
+/**
+ * Switch connection from recv mode to send mode.
+ *
+ * Current request header or body will not be read anymore,
+ * response must be assigned to connection.
+ * @param connection the connection to prepare for sending.
+ */
+static void
+connection_switch_from_recv_to_send (struct MHD_Connection *connection)
+{
+ /* Read buffer is not needed for this request, shrink it.*/
+ connection_shrink_read_buffer (connection);
+}
+
+
+/**
+ * Check whether reply body-specific headers (namely Content-Length,
+ * Transfer-Encoding) are needed.
+ *
+ * If reply body-specific headers are not needed then body itself
+ * is not allowed as well.
+ * When reply body-specific headers are needed, the body itself
+ * can be present or not, depending on other conditions.
+ *
+ * @param connection the connection to check
+ * @return true if reply body-specific headers are needed,
+ * false otherwise.
+ * @sa is_reply_body_needed()
+ */
+static bool
+is_reply_body_headers_needed (struct MHD_Connection *connection)
+{
+ struct MHD_Connection *const c = connection; /**< a short alias */
+ unsigned rcode; /**< the response code */
+
+ mhd_assert (100 <= (c->responseCode & (~MHD_ICY_FLAG)) && \
+ 999 >= (c->responseCode & (~MHD_ICY_FLAG)));
+
+ rcode = (unsigned) (c->responseCode & (~MHD_ICY_FLAG));
+
+ if (199 >= rcode)
+ return false;
+
+ if (MHD_HTTP_NO_CONTENT == rcode)
+ return false;
- if (0 != (response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY))
- connection->keepalive = MHD_CONN_MUST_CLOSE;
#ifdef UPGRADE_SUPPORT
- else if (NULL != response->upgrade_handler)
- /* If this connection will not be "upgraded", it must be closed. */
- connection->keepalive = MHD_CONN_MUST_CLOSE;
+ if (NULL != c->response->upgrade_handler)
+ return false;
#endif /* UPGRADE_SUPPORT */
- /* now analyze chunked encoding situation */
- connection->have_chunked_upload = false;
- have_encoding = MHD_get_response_header (response,
- MHD_HTTP_HEADER_TRANSFER_ENCODING);
- if (NULL == have_encoding)
- may_add_content_length = true;
+ if ( (MHD_HTTP_MTHD_CONNECT == c->http_mthd) &&
+ (2 == rcode / 100) )
+ return false; /* Actually pass-through CONNECT is not supported by MHD */
+
+ return true;
+}
+
+
+/**
+ * Check whether reply body must be used.
+ *
+ * If reply body is needed, it could be zero-sized.
+ *
+ * @param connection the connection to check
+ * @return true if reply body must be used,
+ * false otherwise
+ * @sa is_reply_body_header_needed()
+ */
+static bool
+is_reply_body_needed (struct MHD_Connection *connection)
+{
+ struct MHD_Connection *const c = connection; /**< a short alias */
+ unsigned rcode; /**< the response code */
+
+ mhd_assert (100 <= (c->responseCode & (~MHD_ICY_FLAG)) && \
+ 999 >= (c->responseCode & (~MHD_ICY_FLAG)));
+
+ if (! is_reply_body_headers_needed (c))
+ return false;
+
+ if (MHD_HTTP_MTHD_HEAD == c->http_mthd)
+ return false;
+
+ rcode = (unsigned) (c->responseCode & (~MHD_ICY_FLAG));
+ if (MHD_HTTP_NOT_MODIFIED == rcode)
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Setup connection reply properties.
+ *
+ * Reply properties include presence of reply body, transfer-encoding
+ * type and other.
+ *
+ * @param connection to connection to process
+ * @param reply_body_allowed
+ */
+static void
+setup_reply_properties (struct MHD_Connection *connection)
+{
+ struct MHD_Connection *const c = connection; /**< a short alias */
+ struct MHD_Response *const r = c->response; /**< a short alias */
+ bool use_chunked;
+
+ mhd_assert (NULL != r);
+
+ /* ** Adjust reply properties ** */
+
+ c->keepalive = keepalive_possible (c);
+ c->rp_props.use_reply_body_headers = is_reply_body_headers_needed (c);
+ if (c->rp_props.use_reply_body_headers)
+ c->rp_props.send_reply_body = is_reply_body_needed (c);
+ else
+ c->rp_props.send_reply_body = false;
+
+ if (c->rp_props.use_reply_body_headers)
+ {
+ if ((MHD_SIZE_UNKNOWN == r->total_size) ||
+ (0 != (r->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED)))
+ { /* Use chunked reply encoding if possible */
+
+ /* Check whether chunked encoding is supported by the client */
+ if (! MHD_IS_HTTP_VER_1_1_COMPAT (c->http_ver))
+ use_chunked = false;
+ /* Check whether chunked encoding is allowed for the reply */
+ else if (0 != (r->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
+ | MHD_RF_HTTP_1_0_SERVER)))
+ use_chunked = false;
+ else
+ /* If chunked encoding is supported and allowed, and response size
+ * is unknown, use chunked even for non-Keep-Alive connections.
+ * See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
+ * Also use chunked if it is enforced by application and supported by
+ * the client. */
+ use_chunked = true;
+ }
else
- may_add_content_length = false; /* RFC 7230, Section 3.3.2 forbids header */
- if ( (MHD_SIZE_UNKNOWN == response->total_size) &&
-#ifdef UPGRADE_SUPPORT
- (NULL == response->upgrade_handler) &&
-#endif /* UPGRADE_SUPPORT */
- (! response_has_close) &&
- (! client_requested_close) )
+ use_chunked = false;
+
+ if ( (MHD_SIZE_UNKNOWN == r->total_size) && ! use_chunked)
{
- /* size is unknown, and close was not explicitly requested;
- need to either to HTTP 1.1 chunked encoding or
- close the connection */
- /* 'close' header doesn't exist yet, see if we need to add one;
- if the client asked for a close, no need to start chunk'ing */
- if ( (MHD_NO != keepalive_possible (connection)) &&
- (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_1,
- connection->version) ) )
- {
- if (NULL == have_encoding)
- {
- must_add_chunked_encoding = true;
- connection->have_chunked_upload = true;
- }
- else
- {
- if (MHD_str_equal_caseless_ (have_encoding,
- "identity"))
- {
- /* application forced identity encoding, can't do 'chunked' */
- must_add_close = true;
- }
- else
- {
- connection->have_chunked_upload = true;
- }
- }
+ /* End of the stream is indicated by closure */
+ c->keepalive = MHD_CONN_MUST_CLOSE;
+ }
+ }
+ else
+ use_chunked = false; /* chunked encoding cannot be used without body */
+
+ c->rp_props.chunked = use_chunked;
+ c->rp_props.set = true;
+}
+
+
+/**
+ * Append data to the buffer if enough space is available,
+ * update position.
+ * @param[out] buf the buffer to append data to
+ * @param[in,out] ppos the pointer to position in the @a buffer
+ * @param buf_size the size of the @a buffer
+ * @param append the data to append
+ * @param append_size the size of the @a append
+ * @return true if data has been added and position has been updated,
+ * false if not enough space is available
+ */
+static bool
+buffer_append (char *buf,
+ size_t *ppos,
+ size_t buf_size,
+ const char *append,
+ size_t append_size)
+{
+ mhd_assert (NULL != buf); /* Mute static analyzer */
+ if (buf_size < *ppos + append_size)
+ return false;
+ memcpy (buf + *ppos, append, append_size);
+ *ppos += append_size;
+ return true;
+}
+
+
+/**
+ * Append static string to the buffer if enough space is available,
+ * update position.
+ * @param[out] buf the buffer to append data to
+ * @param[in,out] ppos the pointer to position in the @a buffer
+ * @param buf_size the size of the @a buffer
+ * @param str the static string to append
+ * @return true if data has been added and position has been updated,
+ * false if not enough space is available
+ */
+#define buffer_append_s(buf,ppos,buf_size,str) \
+ buffer_append(buf,ppos,buf_size,str, MHD_STATICSTR_LEN_(str))
+
+
+/**
+ * Add user-defined headers from response object to
+ * the text buffer.
+ *
+ * @param buf the buffer to add headers to
+ * @param ppos the pointer to the position in the @a buf
+ * @param buf_size the size of the @a buf
+ * @param response the response
+ * @param kind the kind of objects (headers or footers)
+ * @param filter_transf_enc skip "Transfer-Encoding" header if any
+ * @param add_close add "close" token to the
+ * "Connection:" header (if any), ignored if no "Connection:"
+ * header was added by user or if "close" token is already
+ * present in "Connection:" header
+ * @param add_keep_alive add "Keep-Alive" token to the
+ * "Connection:" header (if any)
+ * @return true if succeed,
+ * false if buffer is too small
+ */
+static bool
+add_user_headers (char *buf,
+ size_t *ppos,
+ size_t buf_size,
+ struct MHD_Response *response,
+ enum MHD_ValueKind kind,
+ bool filter_transf_enc,
+ bool add_close,
+ bool add_keep_alive)
+{
+ struct MHD_Response *const r = response; /**< a short alias */
+ struct MHD_HTTP_Header *hdr; /**< Iterates through User-specified headers */
+ size_t el_size; /**< the size of current element to be added to the @a buf */
+
+ mhd_assert ((! filter_transf_enc) || MHD_HEADER_KIND == kind);
+ mhd_assert ((! add_close) || MHD_HEADER_KIND == kind);
+ mhd_assert ((! add_keep_alive) || MHD_HEADER_KIND == kind);
+ mhd_assert (! add_close || ! add_keep_alive);
+
+ if (0 == (r->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED))
+ filter_transf_enc = false; /* No such header */
+ if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
+ {
+ add_close = false; /* No such header */
+ add_keep_alive = false; /* No such header */
+ }
+ else if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
+ add_close = false; /* "close" token was already set */
+
+ for (hdr = r->first_header; NULL != hdr; hdr = hdr->next)
+ {
+ size_t initial_pos = *ppos;
+ if (kind != hdr->kind)
+ continue;
+ if (filter_transf_enc)
+ { /* Need to filter-out "Transfer-Encoding" */
+ if ((MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING) ==
+ hdr->header_size) &&
+ (MHD_str_equal_caseless_bin_n_ (MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ hdr->header, hdr->header_size)) )
+ {
+ filter_transf_enc = false; /* There is the only one such header */
+ continue; /* Skip "Transfer-Encoding" header */
+ }
+ }
+
+ /* Add user header */
+ el_size = hdr->header_size + 2 + hdr->value_size + 2;
+ if (buf_size < *ppos + el_size)
+ return false;
+ memcpy (buf + *ppos, hdr->header, hdr->header_size);
+ (*ppos) += hdr->header_size;
+ buf[(*ppos)++] = ':';
+ buf[(*ppos)++] = ' ';
+ if (add_close || add_keep_alive)
+ {
+ /* "Connection:" header must be always the first one */
+ mhd_assert (MHD_str_equal_caseless_n_ (hdr->header, \
+ MHD_HTTP_HEADER_CONNECTION, \
+ hdr->header_size));
+
+ if (add_close)
+ {
+ el_size += MHD_STATICSTR_LEN_ ("close, ");
+ if (buf_size < initial_pos + el_size)
+ return false;
+ memcpy (buf + *ppos, "close, ",
+ MHD_STATICSTR_LEN_ ("close, "));
+ *ppos += MHD_STATICSTR_LEN_ ("close, ");
}
else
{
- /* Keep alive or chunking not possible
- => set close header (we know response_has_close
- is false here) */
- must_add_close = true;
- }
- }
+ el_size += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
+ if (buf_size < initial_pos + el_size)
+ return false;
+ memcpy (buf + *ppos, "Keep-Alive, ",
+ MHD_STATICSTR_LEN_ ("Keep-Alive, "));
+ *ppos += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
+ }
+ add_close = false;
+ add_keep_alive = false;
+ }
+ if (0 != hdr->value_size)
+ memcpy (buf + *ppos, hdr->value, hdr->value_size);
+ *ppos += hdr->value_size;
+ buf[(*ppos)++] = '\r';
+ buf[(*ppos)++] = '\n';
+ mhd_assert (initial_pos + el_size == (*ppos));
+ }
+ return true;
+}
- /* check for other reasons to add 'close' header */
- if ( ( (client_requested_close) ||
- (connection->read_closed) ||
- (MHD_CONN_MUST_CLOSE == connection->keepalive)) &&
- (! response_has_close) &&
-#ifdef UPGRADE_SUPPORT
- (NULL == response->upgrade_handler) &&
-#endif /* UPGRADE_SUPPORT */
- (0 == (response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) ) )
- must_add_close = true;
- /* check if we must add 'close' header because we cannot add content-length
- because it is forbidden AND we don't have a 'chunked' encoding */
- if ( (! may_add_content_length) &&
- (! connection->have_chunked_upload) &&
- (! response_has_close) )
- must_add_close = true;
- /* #MHD_HTTP_NO_CONTENT, #MHD_HTTP_NOT_MODIFIED and 1xx-status
- codes SHOULD NOT have a Content-Length according to spec;
- also chunked encoding / unknown length or CONNECT... */
- if ( (MHD_SIZE_UNKNOWN != response->total_size) &&
- (MHD_HTTP_NO_CONTENT != rc) &&
- (MHD_HTTP_NOT_MODIFIED != rc) &&
- (MHD_HTTP_OK <= rc) &&
- (NULL == /* this COULD fail if the check in
- MHD_add_response_header() was bypassed
- via #MHD_RF_INSANITY_HEADER_CONTENT_LENGTH */
- MHD_get_response_header (response,
- MHD_HTTP_HEADER_CONTENT_LENGTH)) &&
- (may_add_content_length) &&
- ( (NULL == connection->method) ||
- (! MHD_str_equal_caseless_ (connection->method,
- MHD_HTTP_METHOD_CONNECT)) ) )
- {
- /*
- Here we add a content-length if one is missing; however,
- for 'connect' methods, the responses MUST NOT include a
- content-length header *if* the response code is 2xx (in
- which case we expect there to be no body). Still,
- as we don't know the response code here in some cases, we
- simply only force adding a content-length header if this
- is not a 'connect' or if the response is not empty
- (which is kind of more sane, because if some crazy
- application did return content with a 2xx status code,
- then having a content-length might again be a good idea).
-
- Note that the change from 'SHOULD NOT' to 'MUST NOT' is
- a recent development of the HTTP 1.1 specification.
- */
- content_length_len
- = MHD_snprintf_ (content_length_buf,
- sizeof (content_length_buf),
- MHD_HTTP_HEADER_CONTENT_LENGTH ": "
- MHD_UNSIGNED_LONG_LONG_PRINTF "\r\n",
- (MHD_UNSIGNED_LONG_LONG) response->total_size);
- must_add_content_length = true;
- }
-
- /* check for adding keep alive */
- if ( (! response_has_keepalive) &&
- (! response_has_close) &&
- (! must_add_close) &&
- (MHD_CONN_MUST_CLOSE != connection->keepalive) &&
+/**
+ * Allocate the connection's write buffer and fill it with all of the
+ * headers from the response.
+ * Required headers are added here.
+ *
+ * @param connection the connection
+ * @return #MHD_YES on success, #MHD_NO on failure (out of memory)
+ */
+static enum MHD_Result
+build_header_response (struct MHD_Connection *connection)
+{
+ struct MHD_Connection *const c = connection; /**< a short alias */
+ struct MHD_Response *const r = c->response; /**< a short alias */
+ char *buf; /**< the output buffer */
+ size_t pos; /**< append offset in the @a buf */
+ size_t buf_size; /**< the size of the @a buf */
+ size_t el_size; /**< the size of current element to be added to the @a buf */
+ unsigned rcode; /**< the response code */
+ bool use_conn_close; /**< Use "Connection: close" header */
+ bool use_conn_k_alive; /**< Use "Connection: Keep-Alive" header */
+
+ mhd_assert (NULL != r);
+
+ /* ** Adjust response properties ** */
+
+ setup_reply_properties (c);
+
+ mhd_assert (c->rp_props.set);
+ mhd_assert ((MHD_CONN_MUST_CLOSE == c->keepalive) || \
+ (MHD_CONN_USE_KEEPALIVE == c->keepalive) || \
+ (MHD_CONN_MUST_UPGRADE == c->keepalive));
+#ifdef UPGRADE_SUPPORT
+ mhd_assert ((NULL == r->upgrade_handler) || \
+ (MHD_CONN_MUST_UPGRADE == c->keepalive));
+#else /* ! UPGRADE_SUPPORT */
+ mhd_assert (MHD_CONN_MUST_UPGRADE != c->keepalive);
+#endif /* ! UPGRADE_SUPPORT */
+ mhd_assert ((! c->rp_props.chunked) || c->rp_props.use_reply_body_headers);
+ mhd_assert ((! c->rp_props.send_reply_body) || \
+ c->rp_props.use_reply_body_headers);
#ifdef UPGRADE_SUPPORT
- (NULL == response->upgrade_handler) &&
+ mhd_assert (NULL == r->upgrade_handler || \
+ ! c->rp_props.use_reply_body_headers);
#endif /* UPGRADE_SUPPORT */
- (MHD_NO != keepalive_possible (connection)) )
- must_add_keep_alive = true;
- break;
- case MHD_CONNECTION_BODY_SENT:
- response_has_keepalive = false;
- break;
- default:
- mhd_assert (0);
+
+ rcode = (unsigned) (c->responseCode & (~MHD_ICY_FLAG));
+ if (MHD_CONN_MUST_CLOSE == c->keepalive)
+ {
+ /* The closure of connection must be always indicated by header
+ * to avoid hung connections */
+ use_conn_close = true;
+ use_conn_k_alive = false;
+ }
+ else if (MHD_CONN_USE_KEEPALIVE == c->keepalive)
+ {
+ use_conn_close = false;
+ /* Add "Connection: keep-alive" if request is HTTP/1.0 or
+ * if reply is HTTP/1.0
+ * For HTTP/1.1 add header only if explicitly requested by app
+ * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
+ if ((0 != (r->flags & MHD_RF_SEND_KEEP_ALIVE_HEADER)) ||
+ (MHD_HTTP_VER_1_0 == c->http_ver) ||
+ (0 != (r->flags & MHD_RF_HTTP_1_0_SERVER)))
+ use_conn_k_alive = true;
+ else
+ use_conn_k_alive = false;
+ }
+ else
+ {
+ use_conn_close = false;
+ use_conn_k_alive = false;
+ }
+
+ /* ** Actually build the response header ** */
+
+ /* Get all space available */
+ connection_maximize_write_buffer (c);
+ buf = c->write_buffer;
+ pos = c->write_buffer_append_offset;
+ buf_size = c->write_buffer_size;
+ if (0 == buf_size)
return MHD_NO;
+ mhd_assert (NULL != buf);
+
+ /* * The status line * */
+
+ /* The HTTP version */
+ if (0 == (c->responseCode & MHD_ICY_FLAG))
+ { /* HTTP reply */
+ if (0 == (r->flags & MHD_RF_HTTP_1_0_SERVER))
+ { /* HTTP/1.1 reply */
+ /* Use HTTP/1.1 responses for HTTP/1.0 clients.
+ * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
+ if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1))
+ return MHD_NO;
+ }
+ else
+ { /* HTTP/1.0 reply */
+ if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0))
+ return MHD_NO;
+ }
+ }
+ else
+ { /* ICY reply */
+ if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
+ return MHD_NO;
}
- if (MHD_CONN_MUST_CLOSE != connection->keepalive)
- {
- if ( (must_add_close) || (response_has_close) )
- connection->keepalive = MHD_CONN_MUST_CLOSE;
- else if ( (must_add_keep_alive) || (response_has_keepalive) )
- connection->keepalive = MHD_CONN_USE_KEEPALIVE;
- }
-
- if (must_add_close)
- size += MHD_STATICSTR_LEN_ ("Connection: close\r\n");
- if (must_add_keep_alive)
- size += MHD_STATICSTR_LEN_ ("Connection: Keep-Alive\r\n");
- if (must_add_chunked_encoding)
- size += MHD_STATICSTR_LEN_ ("Transfer-Encoding: chunked\r\n");
- if (must_add_content_length)
- size += content_length_len;
- mhd_assert (! (must_add_close && must_add_keep_alive) );
- mhd_assert (! (must_add_chunked_encoding && must_add_content_length) );
-
- for (pos = response->first_header; NULL != pos; pos = pos->next)
- {
- /* TODO: add proper support for excluding "Keep-Alive" token. */
- if ( (pos->kind == kind) &&
- (! ( (must_add_close) &&
- (response_has_keepalive) &&
- (pos->header_size == MHD_STATICSTR_LEN_ (
- MHD_HTTP_HEADER_CONNECTION)) &&
- (MHD_str_equal_caseless_bin_n_ (pos->header,
- MHD_HTTP_HEADER_CONNECTION,
- MHD_STATICSTR_LEN_ (
- MHD_HTTP_HEADER_CONNECTION))) &&
- (MHD_str_equal_caseless_ (pos->value,
- "Keep-Alive")) ) ) )
- size += pos->header_size + pos->value_size + 4; /* colon, space, linefeeds */
- }
- /* produce data */
- data = MHD_pool_allocate (connection->pool,
- size + 1,
- false);
- if (NULL == data)
+ /* The response code */
+ if (buf_size < pos + 5) /* space + code + space */
+ return MHD_NO;
+ buf[pos++] = ' ';
+ pos += MHD_uint16_to_str (rcode, buf + pos,
+ buf_size - pos);
+ buf[pos++] = ' ';
+
+ /* The reason phrase */
+ el_size = MHD_get_reason_phrase_len_for (rcode);
+ if (0 == el_size)
{
-#ifdef HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Not enough memory for write!\n");
-#endif
+ if (! buffer_append_s (buf, &pos, buf_size, "Non-Standard Status"))
+ return MHD_NO;
+ }
+ else if (! buffer_append (buf, &pos, buf_size,
+ MHD_get_reason_phrase_for (rcode),
+ el_size))
+ return MHD_NO;
+
+ /* The linefeed */
+ if (buf_size < pos + 2)
return MHD_NO;
+ buf[pos++] = '\r';
+ buf[pos++] = '\n';
+
+ /* * The headers * */
+
+ /* Main automatic headers */
+
+ /* The "Date:" header */
+ if ( (0 == (r->flags_auto & MHD_RAF_HAS_DATE_HDR)) &&
+ (0 == (c->daemon->options & MHD_USE_SUPPRESS_DATE_NO_CLOCK)) )
+ {
+ /* Additional byte for unused zero-termination */
+ if (buf_size < pos + 38)
+ return MHD_NO;
+ if (get_date_header (buf + pos))
+ pos += 37;
}
- if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
+ /* The "Connection:" header */
+ mhd_assert (! use_conn_close || ! use_conn_k_alive);
+ mhd_assert (! use_conn_k_alive || ! use_conn_close);
+ if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
{
- memcpy (data,
- code,
- off);
- }
- if (must_add_close)
- {
- /* we must add the 'Connection: close' header */
- memcpy (&data[off],
- "Connection: close\r\n",
- MHD_STATICSTR_LEN_ ("Connection: close\r\n"));
- off += MHD_STATICSTR_LEN_ ("Connection: close\r\n");
- }
- if (must_add_keep_alive)
- {
- /* we must add the 'Connection: Keep-Alive' header */
- memcpy (&data[off],
- "Connection: Keep-Alive\r\n",
- MHD_STATICSTR_LEN_ ("Connection: Keep-Alive\r\n"));
- off += MHD_STATICSTR_LEN_ ("Connection: Keep-Alive\r\n");
- }
- if (must_add_chunked_encoding)
- {
- /* we must add the 'Transfer-Encoding: chunked' header */
- memcpy (&data[off],
- "Transfer-Encoding: chunked\r\n",
- MHD_STATICSTR_LEN_ ("Transfer-Encoding: chunked\r\n"));
- off += MHD_STATICSTR_LEN_ ("Transfer-Encoding: chunked\r\n");
- }
- if (must_add_content_length)
- {
- /* we must add the 'Content-Length' header */
- memcpy (&data[off],
- content_length_buf,
- content_length_len);
- off += content_length_len;
- }
- for (pos = response->first_header; NULL != pos; pos = pos->next)
- {
- /* TODO: add proper support for excluding "Keep-Alive" token. */
- if ( (pos->kind == kind) &&
- (! ( (must_add_close) &&
- (response_has_keepalive) &&
- (pos->header_size == MHD_STATICSTR_LEN_ (
- MHD_HTTP_HEADER_CONNECTION)) &&
- (MHD_str_equal_caseless_bin_n_ (pos->header,
- MHD_HTTP_HEADER_CONNECTION,
- MHD_STATICSTR_LEN_ (
- MHD_HTTP_HEADER_CONNECTION))) &&
- (MHD_str_equal_caseless_ (pos->value,
- "Keep-Alive")) ) ) )
- off += MHD_snprintf_ (&data[off],
- size - off,
- "%s: %s\r\n",
- pos->header,
- pos->value);
- }
- if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
- {
- memcpy (&data[off],
- date,
- datelen);
- off += datelen;
- }
- memcpy (&data[off],
- "\r\n",
- 2);
- off += 2;
-
- if (off != size)
- mhd_panic (mhd_panic_cls,
- __FILE__,
- __LINE__,
- NULL);
- connection->write_buffer = data;
- connection->write_buffer_append_offset = size;
- connection->write_buffer_send_offset = 0;
- connection->write_buffer_size = size + 1;
+ if (use_conn_close)
+ {
+ if (! buffer_append_s (buf, &pos, buf_size,
+ MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
+ return MHD_NO;
+ }
+ else if (use_conn_k_alive)
+ {
+ if (! buffer_append_s (buf, &pos, buf_size,
+ MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
+ return MHD_NO;
+ }
+ }
+
+ /* User-defined headers */
+
+ if (! add_user_headers (buf, &pos, buf_size, r, MHD_HEADER_KIND,
+ ! c->rp_props.chunked,
+ use_conn_close,
+ use_conn_k_alive))
+ return MHD_NO;
+
+ /* Other automatic headers */
+
+ if (c->rp_props.use_reply_body_headers)
+ {
+ /* Body-specific headers */
+ if (c->rp_props.chunked)
+ { /* Chunked encoding is used */
+ if (0 == (r->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED))
+ { /* No chunked encoding header set by user */
+ if (! buffer_append_s (buf, &pos, buf_size,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING ": " \
+ "chunked\r\n"))
+ return MHD_NO;
+ }
+ }
+ else
+ { /* Chunked encoding is not used */
+ if (MHD_SIZE_UNKNOWN != r->total_size)
+ {
+ if (! buffer_append_s (buf, &pos, buf_size,
+ MHD_HTTP_HEADER_CONTENT_LENGTH ": "))
+ return MHD_NO;
+ el_size = MHD_uint64_to_str (r->total_size, buf + pos,
+ buf_size - pos);
+ if (0 == el_size)
+ return MHD_NO;
+ pos += el_size;
+
+ if (buf_size < pos + 2)
+ return MHD_NO;
+ buf[pos++] = '\r';
+ buf[pos++] = '\n';
+ }
+ }
+ }
+
+ /* * Header termination * */
+ if (buf_size < pos + 2)
+ return MHD_NO;
+ buf[pos++] = '\r';
+ buf[pos++] = '\n';
+
+ c->write_buffer_append_offset = pos;
+ return MHD_YES;
+}
+
+
+/**
+ * Allocate the connection's write buffer (if necessary) and fill it
+ * with response footers.
+ * Works only for chunked responses as other responses do not need
+ * and do not support any kind of footers.
+ *
+ * @param connection the connection
+ * @return #MHD_YES on success, #MHD_NO on failure (out of memory)
+ */
+static enum MHD_Result
+build_connection_chunked_response_footer (struct MHD_Connection *connection)
+{
+ char *buf; /**< the buffer to write footers to */
+ size_t buf_size; /**< the size of the @a buf */
+ size_t used_size; /**< the used size of the @a buf */
+ struct MHD_Connection *const c = connection; /**< a short alias */
+ struct MHD_HTTP_Header *pos;
+
+ mhd_assert (connection->rp_props.chunked);
+ /* TODO: allow combining of the final footer with the last chunk,
+ * modify the next assert. */
+ mhd_assert (MHD_CONNECTION_BODY_SENT == connection->state);
+ mhd_assert (NULL != c->response);
+
+ buf_size = connection_maximize_write_buffer (c);
+ /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
+ if (buf_size < 5)
+ return MHD_NO;
+ mhd_assert (NULL != c->write_buffer);
+ buf = c->write_buffer + c->write_buffer_append_offset;
+ mhd_assert (NULL != buf);
+ used_size = 0;
+ buf[used_size++] = '0';
+ buf[used_size++] = '\r';
+ buf[used_size++] = '\n';
+
+ for (pos = c->response->first_header; NULL != pos; pos = pos->next)
+ {
+ if (MHD_FOOTER_KIND == pos->kind)
+ {
+ size_t new_used_size; /* resulting size with this header */
+ /* '4' is colon, space, linefeeds */
+ new_used_size = used_size + pos->header_size + pos->value_size + 4;
+ if (new_used_size > buf_size)
+ return MHD_NO;
+ memcpy (buf + used_size, pos->header, pos->header_size);
+ used_size += pos->header_size;
+ buf[used_size++] = ':';
+ buf[used_size++] = ' ';
+ memcpy (buf + used_size, pos->value, pos->value_size);
+ used_size += pos->value_size;
+ buf[used_size++] = '\r';
+ buf[used_size++] = '\n';
+ mhd_assert (used_size == new_used_size);
+ }
+ }
+ if (used_size + 2 > buf_size)
+ return MHD_NO;
+ buf[used_size++] = '\r';
+ buf[used_size++] = '\n';
+
+ c->write_buffer_append_offset += used_size;
+ mhd_assert (c->write_buffer_append_offset <= c->write_buffer_size);
+
return MHD_YES;
}
@@ -1620,23 +2266,49 @@
* @param connection the connection
* @param status_code the response code to send (400, 413 or 414)
* @param message the error message to send
+ * @param message_len the length of the @a message
*/
static void
-transmit_error_response (struct MHD_Connection *connection,
- unsigned int status_code,
- const char *message)
+transmit_error_response_len (struct MHD_Connection *connection,
+ unsigned int status_code,
+ const char *message,
+ size_t message_len)
{
struct MHD_Response *response;
enum MHD_Result iret;
- if (NULL == connection->version)
+ mhd_assert (! connection->stop_with_error); /* Do not send error twice */
+ if (connection->stop_with_error)
+ { /* Should not happen */
+ if (MHD_CONNECTION_CLOSED > connection->state)
+ connection->state = MHD_CONNECTION_CLOSED;
+
+ return;
+ }
+ connection->stop_with_error = true;
+ connection->discard_request = true;
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ _ ("Error processing request (HTTP response code is %u ('%s')). " \
+ "Closing connection.\n"),
+ status_code,
+ message);
+#endif
+ if (MHD_CONNECTION_START_REPLY < connection->state)
{
- /* we were unable to process the full header line, so we don't
- really know what version the client speaks; assume 1.0 */
- connection->version = MHD_HTTP_VERSION_1_0;
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ _ ("Too late to send an error response, " \
+ "response is being sent already.\n"),
+ status_code,
+ message);
+#endif
+ CONNECTION_CLOSE_ERROR (connection,
+ _ ("Too late for error response."));
+ return;
}
- connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
- connection->read_closed = true;
+ /* TODO: remove when special error queue function is implemented */
+ connection->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
if (0 != connection->read_buffer_size)
{
/* Read buffer is not needed anymore, discard it
@@ -1646,24 +2318,24 @@
connection->read_buffer_size,
0);
connection->read_buffer_size = 0;
+ connection->read_buffer_offset = 0;
}
-#ifdef HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- _ (
- "Error processing request (HTTP response code is %u (`%s')). Closing connection.\n"),
- status_code,
- message);
-#endif
if (NULL != connection->response)
{
MHD_destroy_response (connection->response);
connection->response = NULL;
}
- response = MHD_create_response_from_buffer (strlen (message),
+ response = MHD_create_response_from_buffer (message_len,
(void *) message,
MHD_RESPMEM_PERSISTENT);
if (NULL == response)
{
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ _ ("Failed to create error response.\n"),
+ status_code,
+ message);
+#endif
/* can't even send a reply, at least close the connection */
connection->state = MHD_CONNECTION_CLOSED;
return;
@@ -1676,8 +2348,8 @@
{
/* can't even send a reply, at least close the connection */
CONNECTION_CLOSE_ERROR (connection,
- _ (
- "Closing connection (failed to queue response)."));
+ _ ("Closing connection " \
+ "(failed to queue error response)."));
return;
}
mhd_assert (NULL != connection->response);
@@ -1685,19 +2357,45 @@
connection->keepalive = MHD_CONN_MUST_CLOSE;
if (MHD_NO == build_header_response (connection))
{
- /* oops - close! */
- CONNECTION_CLOSE_ERROR (connection,
- _ (
- "Closing connection (failed to create response header)."));
- }
- else
- {
- connection->state = MHD_CONNECTION_HEADERS_SENDING;
+ /* No memory. Release everything. */
+ connection->version = NULL;
+ connection->method = NULL;
+ connection->url = NULL;
+ connection->last = NULL;
+ connection->colon = NULL;
+ connection->headers_received = NULL;
+ connection->headers_received_tail = NULL;
+ connection->write_buffer = NULL;
+ connection->write_buffer_size = 0;
+ connection->write_buffer_send_offset = 0;
+ connection->write_buffer_append_offset = 0;
+ connection->read_buffer
+ = MHD_pool_reset (connection->pool,
+ NULL,
+ 0,
+ 0);
+ connection->read_buffer_size = 0;
+
+ /* Retry with empty buffer */
+ if (MHD_NO == build_header_response (connection))
+ {
+ CONNECTION_CLOSE_ERROR (connection,
+ _ ("Closing connection " \
+ "(failed to create error response header)."));
+ return;
+ }
}
+ connection->state = MHD_CONNECTION_HEADERS_SENDING;
}
/**
+ * Transmit static string as error response
+ */
+#define transmit_error_response_static(c, code, msg) \
+ transmit_error_response_len (c, code, msg, MHD_STATICSTR_LEN_ (msg))
+
+/**
* Update the 'event_loop_info' field of this connection based on the state
* that the connection is now in. May also close the connection or
* perform other updates to the connection if needed to prepare for
@@ -1741,6 +2439,7 @@
switch (connection->state)
{
case MHD_CONNECTION_INIT:
+ case MHD_CONNECTION_REQ_LINE_RECEIVING:
case MHD_CONNECTION_URL_RECEIVED:
case MHD_CONNECTION_HEADER_PART_RECEIVED:
/* while reading headers, we always grow the
@@ -1748,14 +2447,17 @@
if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
(! try_grow_read_buffer (connection, true)) )
{
- transmit_error_response (connection,
- (connection->url != NULL)
- ? MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
- : MHD_HTTP_URI_TOO_LONG,
- REQUEST_TOO_BIG);
+ if (connection->url != NULL)
+ transmit_error_response_static (connection,
+ MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
+ REQUEST_TOO_BIG);
+ else
+ transmit_error_response_static (connection,
+ MHD_HTTP_URI_TOO_LONG,
+ REQUEST_TOO_BIG);
continue;
}
- if (! connection->read_closed)
+ if (! connection->discard_request)
connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
else
connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
@@ -1788,14 +2490,14 @@
on the connection (if a timeout is even
set!).
Solution: we kill the connection with an error */
- transmit_error_response (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- INTERNAL_ERROR);
+ transmit_error_response_static (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ INTERNAL_ERROR);
continue;
}
}
if ( (connection->read_buffer_offset < connection->read_buffer_size) &&
- (! connection->read_closed) )
+ (! connection->discard_request) )
connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
else
connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
@@ -1815,8 +2517,14 @@
happens in read handler */
break;
case MHD_CONNECTION_FOOTERS_RECEIVED:
+ mhd_assert (0);
+ break;
+ case MHD_CONNECTION_FULL_REQ_RECEIVED:
connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
break;
+ case MHD_CONNECTION_START_REPLY:
+ mhd_assert (0);
+ break;
case MHD_CONNECTION_HEADERS_SENDING:
/* headers in buffer, keep writing */
connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
@@ -1885,39 +2593,54 @@
return NULL;
pos = 0;
rbuf = connection->read_buffer;
- while ( (pos < connection->read_buffer_offset - 1) &&
- ('\r' != rbuf[pos]) &&
- ('\n' != rbuf[pos]) )
- pos++;
- if ( (pos == connection->read_buffer_offset - 1) &&
- ('\n' != rbuf[pos]) )
- {
- /* not found, consider growing... */
- if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
- (! try_grow_read_buffer (connection, true)) )
- {
- transmit_error_response (connection,
- (NULL != connection->url)
- ? MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
- : MHD_HTTP_URI_TOO_LONG,
- REQUEST_TOO_BIG);
- }
- if (line_len)
- *line_len = 0;
- return NULL;
- }
+ mhd_assert (NULL != rbuf);
+ do
+ {
+ const char c = rbuf[pos];
+ bool found;
+ found = false;
+ if ( ('\r' == c) && (pos < connection->read_buffer_offset - 1) &&
+ ('\n' == rbuf[pos + 1]) )
+ { /* Found CRLF */
+ found = true;
+ if (line_len)
+ *line_len = pos;
+ rbuf[pos++] = 0; /* Replace CR with zero */
+ rbuf[pos++] = 0; /* Replace LF with zero */
+ }
+ else if ('\n' == c) /* TODO: Add MHD option to disallow */
+ { /* Found bare LF */
+ found = true;
+ if (line_len)
+ *line_len = pos;
+ rbuf[pos++] = 0; /* Replace LF with zero */
+ }
+ if (found)
+ {
+ connection->read_buffer += pos;
+ connection->read_buffer_size -= pos;
+ connection->read_buffer_offset -= pos;
+ return rbuf;
+ }
+ } while (++pos < connection->read_buffer_offset);
+
+ /* not found, consider growing... */
+ if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
+ (! try_grow_read_buffer (connection, true)) )
+ {
+ if (NULL != connection->url)
+ transmit_error_response_static (connection,
+ MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
+ REQUEST_TOO_BIG);
+ else
+ transmit_error_response_static (connection,
+ MHD_HTTP_URI_TOO_LONG,
+ REQUEST_TOO_BIG);
+ }
if (line_len)
- *line_len = pos;
- /* found, check if we have proper LFCR */
- if ( ('\r' == rbuf[pos]) &&
- ('\n' == rbuf[pos + 1]) )
- rbuf[pos++] = '\0'; /* skip both r and n */
- rbuf[pos++] = '\0';
- connection->read_buffer += pos;
- connection->read_buffer_size -= pos;
- connection->read_buffer_offset -= pos;
- return rbuf;
+ *line_len = 0;
+ return NULL;
}
@@ -1954,9 +2677,9 @@
MHD_DLOG (connection->daemon,
_ ("Not enough memory in pool to allocate header record!\n"));
#endif
- transmit_error_response (connection,
- MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
- REQUEST_TOO_BIG);
+ transmit_error_response_static (connection,
+ MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
+ REQUEST_TOO_BIG);
return MHD_NO;
}
return MHD_YES;
@@ -1992,18 +2715,17 @@
&hdr,
&hdr_len))
return MHD_YES;
- cpy = MHD_pool_allocate (connection->pool,
- hdr_len + 1,
- true);
+ cpy = connection_alloc_memory (connection,
+ hdr_len + 1);
if (NULL == cpy)
{
#ifdef HAVE_MESSAGES
MHD_DLOG (connection->daemon,
_ ("Not enough memory in pool to parse cookies!\n"));
#endif
- transmit_error_response (connection,
- MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
- REQUEST_TOO_BIG);
+ transmit_error_response_static (connection,
+ MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
+ REQUEST_TOO_BIG);
return MHD_NO;
}
memcpy (cpy,
@@ -2088,6 +2810,121 @@
/**
+ * Detect HTTP version
+ *
+ * @param connection the connection
+ * @param http_string the pointer to HTTP version string
+ * @param len the length of @a http_string in bytes
+ * @return #MHD_YES if HTTP version is correct and supported,
+ * #MHD_NO if HTTP version is not correct or unsupported.
+ */
+static enum MHD_Result
+parse_http_version (struct MHD_Connection *connection,
+ const char *http_string,
+ size_t len)
+{
+ const char *const h = http_string; /**< short alias */
+ mhd_assert (NULL != http_string);
+
+ /* String must starts with 'HTTP/d.d', case-sensetive match.
+ * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
+ if ((len != 8) ||
+ (h[0] != 'H') || (h[1] != 'T') || (h[2] != 'T') || (h[3] != 'P') ||
+ (h[4] != '/')
+ || (h[6] != '.') ||
+ ((h[5] < '0') || (h[5] > '9')) ||
+ ((h[7] < '0') || (h[7] > '9')))
+ {
+ connection->http_ver = MHD_HTTP_VER_INVALID;
+ transmit_error_response_static (connection,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_MALFORMED);
+ return MHD_NO;
+ }
+ if (1 == h[5] - '0')
+ {
+ /* HTTP/1.x */
+ if (1 == h[7] - '0')
+ connection->http_ver = MHD_HTTP_VER_1_1;
+ else if (0 == h[7] - '0')
+ connection->http_ver = MHD_HTTP_VER_1_0;
+ else
+ connection->http_ver = MHD_HTTP_VER_1_2__1_9;
+
+ return MHD_YES;
+ }
+
+ if (0 == h[5] - '0')
+ {
+ /* Too old major version */
+ connection->http_ver = MHD_HTTP_VER_TOO_OLD;
+ transmit_error_response_static (connection,
+ MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED,
+ REQ_HTTP_VER_IS_TOO_OLD);
+ return MHD_NO;
+ }
+
+ connection->http_ver = MHD_HTTP_VER_FUTURE;
+ transmit_error_response_static (connection,
+ MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED,
+ REQ_HTTP_VER_IS_NOT_SUPPORTED);
+ return MHD_NO;
+}
+
+
+/**
+ * Detect standard HTTP request method
+ *
+ * @param connection the connection
+ * @param method the pointer to HTTP request method string
+ * @param len the length of @a method in bytes
+ * @return #MHD_YES if HTTP method is valid string,
+ * #MHD_NO if HTTP method string is not valid.
+ */
+static enum MHD_Result
+parse_http_std_method (struct MHD_Connection *connection,
+ const char *method,
+ size_t len)
+{
+ const char *const m = method; /**< short alias */
+ mhd_assert (NULL != m);
+
+ if (0 == len)
+ return MHD_NO;
+
+ if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
+ connection->http_mthd = MHD_HTTP_MTHD_GET;
+ else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_HEAD) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_HEAD, len)))
+ connection->http_mthd = MHD_HTTP_MTHD_HEAD;
+ else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_POST) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_POST, len)))
+ connection->http_mthd = MHD_HTTP_MTHD_POST;
+ else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_PUT) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_PUT, len)))
+ connection->http_mthd = MHD_HTTP_MTHD_PUT;
+ else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_DELETE) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_DELETE, len)))
+ connection->http_mthd = MHD_HTTP_MTHD_DELETE;
+ else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_CONNECT) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_CONNECT, len)))
+ connection->http_mthd = MHD_HTTP_MTHD_CONNECT;
+ else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_OPTIONS) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_OPTIONS, len)))
+ connection->http_mthd = MHD_HTTP_MTHD_OPTIONS;
+ else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_TRACE) == len) &&
+ (0 == memcmp (m, MHD_HTTP_METHOD_TRACE, len)))
+ connection->http_mthd = MHD_HTTP_MTHD_TRACE;
+ else
+ connection->http_mthd = MHD_HTTP_MTHD_OTHER;
+
+ /* Any method string with non-zero length is valid */
+ return MHD_YES;
+}
+
+
+/**
* Parse the first line of the HTTP HEADER.
*
* @param connection the connection (updated)
@@ -2113,9 +2950,13 @@
return MHD_NO; /* serious error */
uri[0] = '\0';
connection->method = line;
+ if (MHD_NO == parse_http_std_method (connection, connection->method,
+ (size_t) (uri - line)))
+ return MHD_NO;
uri++;
/* Skip any spaces. Not required by standard but allow
to be more tolerant. */
+ /* TODO: do not skip them in standard mode */
while ( (' ' == uri[0]) &&
( (size_t) (uri - line) < line_len) )
uri++;
@@ -2126,6 +2967,8 @@
uri = NULL;
connection->version = "";
args = NULL;
+ if (MHD_NO == parse_http_version (connection, connection->version, 0))
+ return MHD_NO;
}
else
{
@@ -2134,6 +2977,7 @@
/* Search from back to accept malformed URI with space */
http_version = line + line_len - 1;
/* Skip any trailing spaces */
+ /* TODO: do not skip them in standard mode */
while ( (' ' == http_version[0]) &&
(http_version > uri) )
http_version--;
@@ -2146,11 +2990,17 @@
/* http_version points to character before HTTP version string */
http_version[0] = '\0';
connection->version = http_version + 1;
+ if (MHD_NO == parse_http_version (connection, connection->version,
+ line_len
+ - (connection->version - line)))
+ return MHD_NO;
uri_len = http_version - uri;
}
else
{
connection->version = "";
+ if (MHD_NO == parse_http_version (connection, connection->version, 0))
+ return MHD_NO;
uri_len = line_len - (uri - line);
}
/* check for spaces in URI if we are "strict" */
@@ -2248,11 +3098,13 @@
{
struct MHD_Daemon *daemon = connection->daemon;
size_t available;
- int instant_retry;
+ bool instant_retry;
char *buffer_head;
if (NULL != connection->response)
{
+ /* TODO: discard all read buffer as early response
+ * means that connection have to be closed. */
/* already queued a response, discard remaining upload
(but not more, there might be another request after it) */
size_t purge;
@@ -2276,41 +3128,46 @@
size_t left_unprocessed;
size_t processed_size;
- instant_retry = MHD_NO;
- if ( (connection->have_chunked_upload) &&
- (MHD_SIZE_UNKNOWN == connection->remaining_upload_size) )
+ instant_retry = false;
+ if (connection->have_chunked_upload)
{
+ mhd_assert (MHD_SIZE_UNKNOWN == connection->remaining_upload_size);
if ( (connection->current_chunk_offset ==
connection->current_chunk_size) &&
- (0LLU != connection->current_chunk_offset) &&
- (available >= 2) )
+ (0 != connection->current_chunk_size) )
{
size_t i;
+ mhd_assert (0 != available);
/* skip new line at the *end* of a chunk */
i = 0;
- if ( ('\r' == buffer_head[i]) ||
- ('\n' == buffer_head[i]) )
- i++; /* skip 1st part of line feed */
- if ( ('\r' == buffer_head[i]) ||
- ('\n' == buffer_head[i]) )
- i++; /* skip 2nd part of line feed */
+ if ( (2 <= available) &&
+ ('\r' == buffer_head[0]) &&
+ ('\n' == buffer_head[1]) )
+ i += 2; /* skip CRLF */
+ else if ('\n' == buffer_head[0]) /* TODO: Add MHD option to disallow */
+ i++; /* skip bare LF */
+ else if (2 > available)
+ break; /* need more upload data */
if (0 == i)
{
/* malformed encoding */
- CONNECTION_CLOSE_ERROR (connection,
- _ (
- "Received malformed HTTP request (bad chunked encoding). Closing connection."));
+ transmit_error_response_static (connection,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_CHUNKED_MALFORMED);
return;
}
available -= i;
buffer_head += i;
connection->current_chunk_offset = 0;
connection->current_chunk_size = 0;
+ if (0 == available)
+ break;
}
- if (connection->current_chunk_offset <
- connection->current_chunk_size)
+ if (0 != connection->current_chunk_size)
{
uint64_t cur_chunk_left;
+ mhd_assert (connection->current_chunk_offset < \
+ connection->current_chunk_size);
/* we are in the middle of a chunk, give
as much as possible to the client (without
crossing chunk boundaries) */
@@ -2322,102 +3179,131 @@
{ /* cur_chunk_left <= (size_t)available */
to_be_processed = (size_t) cur_chunk_left;
if (available > to_be_processed)
- instant_retry = MHD_YES;
+ instant_retry = true;
}
}
else
{
size_t i;
- size_t end_size;
+ /** The length of the string with the number of the chunk size */
+ size_t chunk_size_len;
+ bool found_chunk_size_str;
bool malformed;
/* we need to read chunk boundaries */
i = 0;
- while (i < available)
- {
- if ( ('\r' == buffer_head[i]) ||
- ('\n' == buffer_head[i]) ||
- (';' == buffer_head[i]) )
- break;
- i++;
- if (i >= 16)
- break;
- }
- end_size = i;
- /* find beginning of CRLF (skip over chunk extensions) */
- if (';' == buffer_head[i])
+ found_chunk_size_str = false;
+ chunk_size_len = 0;
+ mhd_assert (0 != available);
+ do
{
- while (i < available)
+ if ('\n' == buffer_head[i])
{
- if ( ('\r' == buffer_head[i]) ||
- ('\n' == buffer_head[i]) )
- break;
- i++;
+ if ((0 < i) && ('\r' == buffer_head[i - 1]))
+ { /* CRLF */
+ if (! found_chunk_size_str)
+ chunk_size_len = i - 1;
+ }
+ else
+ { /* bare LF */
+ /* TODO: Add an option to disallow bare LF */
+ if (! found_chunk_size_str)
+ chunk_size_len = i;
+ }
+ found_chunk_size_str = true;
+ break; /* Found the end of the string */
}
- }
- /* take '\n' into account; if '\n' is the unavailable
- character, we will need to wait until we have it
- before going further */
- if ( (i + 1 >= available) &&
- ! ( (1 == i) &&
- (2 == available) &&
- ('0' == buffer_head[0]) ) )
- break; /* need more data... */
- i++;
- malformed = (end_size >= 16);
+ else if (! found_chunk_size_str && (';' == buffer_head[i]))
+ { /* Found chunk extension */
+ chunk_size_len = i;
+ found_chunk_size_str = true;
+ }
+ } while (available > ++i);
+ mhd_assert ((i == available) || found_chunk_size_str);
+ mhd_assert ((0 == chunk_size_len) || found_chunk_size_str);
+ malformed = ((0 == chunk_size_len) && found_chunk_size_str);
if (! malformed)
{
- size_t num_dig = MHD_strx_to_uint64_n_ (buffer_head,
- end_size,
- &connection->
- current_chunk_size);
- malformed = (end_size != num_dig);
+ /* Check whether size is valid hexadecimal number
+ * even if end of the string is not found yet. */
+ size_t num_dig;
+ uint64_t chunk_size;
+ mhd_assert (0 < i);
+ if (! found_chunk_size_str)
+ {
+ mhd_assert (i == available);
+ /* Check already available part of the size string for valid
+ * hexadecimal digits. */
+ chunk_size_len = i;
+ if ('\r' == buffer_head[i - 1])
+ {
+ chunk_size_len--;
+ malformed = (0 == chunk_size_len);
+ }
+ }
+ num_dig = MHD_strx_to_uint64_n_ (buffer_head,
+ chunk_size_len,
+ &chunk_size);
+ malformed = malformed || (chunk_size_len != num_dig);
+
+ if ((available != i) && ! malformed)
+ {
+ /* Found end of the string and the size of the chunk is valid */
+
+ mhd_assert (found_chunk_size_str);
+ /* Start reading payload data of the chunk */
+ connection->current_chunk_offset = 0;
+ connection->current_chunk_size = chunk_size;
+ i++; /* Consume the last checked char */
+ available -= i;
+ buffer_head += i;
+
+ if (0 == connection->current_chunk_size)
+ { /* The final (termination) chunk */
+ connection->remaining_upload_size = 0;
+ break;
+ }
+ if (available > 0)
+ instant_retry = true;
+ continue;
+ }
+
+ if ((0 == num_dig) && (0 != chunk_size_len))
+ { /* Check whether result is invalid due to uint64_t overflow */
+ /* At least one byte is always available
+ * in the input buffer here. */
+ const char d = buffer_head[0]; /**< first digit */
+ if ((('0' <= d) && ('9' >= d)) ||
+ (('A' <= d) && ('F' >= d)) ||
+ (('a' <= d) && ('f' >= d)))
+ { /* The first char is a valid hexadecimal digit */
+ transmit_error_response_static (connection,
+ MHD_HTTP_CONTENT_TOO_LARGE,
+ REQUEST_CHUNK_TOO_LARGE);
+ return;
+ }
+ }
}
if (malformed)
{
- /* malformed encoding */
- CONNECTION_CLOSE_ERROR (connection,
- _ (
- "Received malformed HTTP request (bad chunked encoding). Closing connection."));
+ transmit_error_response_static (connection,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_CHUNKED_MALFORMED);
return;
}
- /* skip 2nd part of line feed */
- if ( (i < available) &&
- ( ('\r' == buffer_head[i]) ||
- ('\n' == buffer_head[i]) ) )
- i++;
-
- buffer_head += i;
- available -= i;
- connection->current_chunk_offset = 0;
-
- if (available > 0)
- instant_retry = MHD_YES;
- if (0LLU == connection->current_chunk_size)
- {
- connection->remaining_upload_size = 0;
- break;
- }
- continue;
+ mhd_assert (available == i);
+ break; /* The end of the string not found, need more upload data */
}
}
else
{
/* no chunked encoding, give all to the client */
- if ( (0 != connection->remaining_upload_size) &&
- (MHD_SIZE_UNKNOWN != connection->remaining_upload_size) &&
- (connection->remaining_upload_size < available) )
- {
+ mhd_assert (MHD_SIZE_UNKNOWN != connection->remaining_upload_size);
+ mhd_assert (0 != connection->remaining_upload_size);
+ if (connection->remaining_upload_size < available)
to_be_processed = (size_t) connection->remaining_upload_size;
- }
else
- {
- /**
- * 1. no chunked encoding, give all to the client
- * 2. client may send large chunked data, but only a smaller part is available at one time.
- */
to_be_processed = available;
- }
}
left_unprocessed = to_be_processed;
connection->client_aware = true;
@@ -2433,8 +3319,8 @@
{
/* serious internal error, close connection */
CONNECTION_CLOSE_ERROR (connection,
- _ (
- "Application reported internal error, closing connection."));
+ _ ("Application reported internal error, " \
+ "closing connection."));
return;
}
if (left_unprocessed > to_be_processed)
@@ -2449,7 +3335,7 @@
);
if (0 != left_unprocessed)
{
- instant_retry = MHD_NO; /* client did not process everything */
+ instant_retry = false; /* client did not process everything */
#ifdef HAVE_MESSAGES
/* client did not process all upload data, complain if
the setup was incorrect, which may prevent us from
@@ -2457,8 +3343,8 @@
if ( (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) &&
(! connection->suspended) )
MHD_DLOG (daemon,
- _ (
- "WARNING: incomplete upload processing and connection not suspended may result in hung connection.\n"));
+ _ ("WARNING: incomplete upload processing and connection " \
+ "not suspended may result in hung connection.\n"));
#endif
}
processed_size = to_be_processed - left_unprocessed;
@@ -2467,15 +3353,23 @@
/* dh left "processed" bytes in buffer for next time... */
buffer_head += processed_size;
available -= processed_size;
- if (MHD_SIZE_UNKNOWN != connection->remaining_upload_size)
+ if (! connection->have_chunked_upload)
+ {
+ mhd_assert (MHD_SIZE_UNKNOWN != connection->remaining_upload_size);
connection->remaining_upload_size -= processed_size;
- }
- while (MHD_NO != instant_retry);
+ }
+ else
+ mhd_assert (MHD_SIZE_UNKNOWN == connection->remaining_upload_size);
+ } while (instant_retry);
+ /* TODO: zero out reused memory region */
if ( (available > 0) &&
(buffer_head != connection->read_buffer) )
memmove (connection->read_buffer,
buffer_head,
available);
+ else
+ mhd_assert ((0 == available) || \
+ (connection->read_buffer_offset == available));
connection->read_buffer_offset = available;
}
@@ -2500,12 +3394,6 @@
connection->write_buffer_append_offset = 0;
connection->write_buffer_send_offset = 0;
connection->state = next_state;
- MHD_pool_reallocate (connection->pool,
- connection->write_buffer,
- connection->write_buffer_size,
- 0);
- connection->write_buffer = NULL;
- connection->write_buffer_size = 0;
return MHD_YES;
}
@@ -2530,9 +3418,6 @@
if (NULL == colon)
{
/* error in header line, die hard */
- CONNECTION_CLOSE_ERROR (connection,
- _ (
- "Received malformed line (no colon). Closing connection."));
return MHD_NO;
}
if (-1 >= connection->daemon->strict_for_client)
@@ -2617,9 +3502,9 @@
last_len + tmp_len + 1);
if (NULL == last)
{
- transmit_error_response (connection,
- MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
- REQUEST_TOO_BIG);
+ transmit_error_response_static (connection,
+ MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
+ REQUEST_TOO_BIG);
return MHD_NO;
}
memcpy (&last[last_len],
@@ -2638,9 +3523,7 @@
strlen (connection->colon),
kind))
{
- transmit_error_response (connection,
- MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
- REQUEST_TOO_BIG);
+ /* Error has been queued by connection_add_header() */
return MHD_NO;
}
/* we still have the current line to deal with... */
@@ -2649,9 +3532,9 @@
if (MHD_NO == process_header_line (connection,
line))
{
- transmit_error_response (connection,
- MHD_HTTP_BAD_REQUEST,
- REQUEST_MALFORMED);
+ transmit_error_response_static (connection,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_MALFORMED);
return MHD_NO;
}
}
@@ -2670,15 +3553,12 @@
parse_connection_headers (struct MHD_Connection *connection)
{
const char *clen;
- struct MHD_Response *response;
const char *enc;
- const char *end;
+ size_t val_len;
parse_cookie_header (connection);
if ( (1 <= connection->daemon->strict_for_client) &&
- (NULL != connection->version) &&
- (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_1,
- connection->version)) &&
+ (MHD_IS_HTTP_VER_1_1_COMPAT (connection->http_ver)) &&
(MHD_NO ==
MHD_lookup_connection_value_n (connection,
MHD_HEADER_KIND,
@@ -2688,39 +3568,13 @@
NULL,
NULL)) )
{
- enum MHD_Result iret;
-
- /* die, http 1.1 request without host and we are pedantic */
- connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
- connection->read_closed = true;
#ifdef HAVE_MESSAGES
MHD_DLOG (connection->daemon,
- _ ("Received HTTP 1.1 request without `Host' header.\n"));
+ _ ("Received HTTP/1.1 request without `Host' header.\n"));
#endif
- mhd_assert (NULL == connection->response);
- response =
- MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (REQUEST_LACKS_HOST),
- REQUEST_LACKS_HOST,
- MHD_RESPMEM_PERSISTENT);
- if (NULL == response)
- {
- /* can't even send a reply, at least close the connection */
- CONNECTION_CLOSE_ERROR (connection,
- _ (
- "Closing connection (failed to create response)."));
- return;
- }
- iret = MHD_queue_response (connection,
- MHD_HTTP_BAD_REQUEST,
- response);
- MHD_destroy_response (response);
- if (MHD_NO == iret)
- {
- /* can't even send a reply, at least close the connection */
- CONNECTION_CLOSE_ERROR (connection,
- _ (
- "Closing connection (failed to queue response)."));
- }
+ transmit_error_response_static (connection,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_LACKS_HOST);
return;
}
@@ -2746,22 +3600,41 @@
MHD_STATICSTR_LEN_ (
MHD_HTTP_HEADER_CONTENT_LENGTH),
&clen,
- NULL))
+ &val_len))
{
- end = clen + MHD_str_to_uint64_ (clen,
- &connection->remaining_upload_size);
- if ( (clen == end) ||
- ('\0' != *end) )
+ size_t num_digits;
+
+ num_digits = MHD_str_to_uint64_n_ (clen,
+ val_len,
+ &connection->remaining_upload_size);
+ if ( (val_len != num_digits) ||
+ (0 == num_digits) )
{
connection->remaining_upload_size = 0;
+ if ((0 == num_digits) &&
+ (0 != val_len) &&
+ ('0' <= clen[0]) && ('9' >= clen[0]))
+ {
#ifdef HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- _ (
- "Failed to parse `Content-Length' header. Closing connection.\n"));
-#endif
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- return;
+ MHD_DLOG (connection->daemon,
+ _ ("Too large value of 'Content-Length' header. " \
+ "Closing connection.\n"));
+#endif
+ transmit_error_response_static (connection,
+ MHD_HTTP_CONTENT_TOO_LARGE,
+ REQUEST_CONTENTLENGTH_TOOLARGE);
+ }
+ else
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ _ ("Failed to parse `Content-Length' header. " \
+ "Closing connection.\n"));
+#endif
+ transmit_error_response_static (connection,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_CONTENTLENGTH_MALFORMED);
+ }
}
}
}
@@ -2780,17 +3653,17 @@
{
struct MHD_Daemon *daemon = connection->daemon;
- if (0 == connection->connection_timeout)
+ if (0 == connection->connection_timeout_ms)
return; /* Skip update of activity for connections
without timeout timer. */
if (connection->suspended)
return; /* no activity on suspended connections */
- connection->last_activity = MHD_monotonic_sec_counter ();
+ connection->last_activity = MHD_monotonic_msec_counter ();
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
return; /* each connection has personal timeout */
- if (connection->connection_timeout != daemon->connection_timeout)
+ if (connection->connection_timeout_ms != daemon->connection_timeout_ms)
return; /* custom timeout, no need to move it in "normal" DLL */
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
@@ -2810,12 +3683,16 @@
/**
* This function handles a particular connection when it has been
- * determined that there is data to be read off a socket.
+ * determined that there is data to be read off a socket. All
+ * implementations (multithreaded, external polling, internal polling)
+ * call this function to handle reads.
*
* @param connection connection to handle
+ * @param socket_error set to true if socket error was detected
*/
void
-MHD_connection_handle_read (struct MHD_Connection *connection)
+MHD_connection_handle_read (struct MHD_Connection *connection,
+ bool socket_error)
{
ssize_t bytes_read;
@@ -2848,17 +3725,28 @@
[connection->read_buffer_offset],
connection->read_buffer_size
- connection->read_buffer_offset);
- if (bytes_read < 0)
+ if ((bytes_read < 0) || socket_error)
{
- if (MHD_ERR_AGAIN_ == bytes_read)
+ if ((MHD_ERR_AGAIN_ == bytes_read) && ! socket_error)
return; /* No new data to process. */
+ if ((bytes_read > 0) && connection->sk_nonblck)
+ { /* Try to detect the socket error */
+ int dummy;
+ bytes_read = connection->recv_cls (connection, &dummy, sizeof (dummy));
+ }
if (MHD_ERR_CONNRESET_ == bytes_read)
{
- CONNECTION_CLOSE_ERROR (connection,
- (MHD_CONNECTION_INIT == connection->state) ?
- NULL :
- _ (
- "Socket disconnected while reading request."));
+ if ( (MHD_CONNECTION_INIT < connection->state) &&
+ (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ _ ("Socket has been disconnected when reading request.\n"));
+#endif
+ connection->discard_request = true;
+ }
+ MHD_connection_close_ (connection,
+ MHD_REQUEST_TERMINATED_READ_ERROR);
return;
}
@@ -2867,7 +3755,8 @@
MHD_DLOG (connection->daemon,
_ ("Connection socket is closed when reading " \
"request due to the error: %s\n"),
- str_conn_error_ (bytes_read));
+ (bytes_read < 0) ? str_conn_error_ (bytes_read) :
+ "detected connection closure");
#endif
CONNECTION_CLOSE_ERROR (connection,
NULL);
@@ -2877,8 +3766,26 @@
if (0 == bytes_read)
{ /* Remote side closed connection. */
connection->read_closed = true;
- MHD_connection_close_ (connection,
- MHD_REQUEST_TERMINATED_CLIENT_ABORT);
+ if ( (MHD_CONNECTION_INIT < connection->state) &&
+ (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ _ ("Connection was closed by remote side with incomplete "
+ "request.\n"));
+#endif
+ connection->discard_request = true;
+ MHD_connection_close_ (connection,
+ MHD_REQUEST_TERMINATED_CLIENT_ABORT);
+ }
+ else if (MHD_CONNECTION_INIT == connection->state)
+ /* This termination code cannot be reported to the application
+ * because application has not been informed yet about this request */
+ MHD_connection_close_ (connection,
+ MHD_REQUEST_TERMINATED_COMPLETED_OK);
+ else
+ MHD_connection_close_ (connection,
+ MHD_REQUEST_TERMINATED_WITH_ERROR);
return;
}
connection->read_buffer_offset += bytes_read;
@@ -2892,6 +3799,7 @@
switch (connection->state)
{
case MHD_CONNECTION_INIT:
+ case MHD_CONNECTION_REQ_LINE_RECEIVING:
case MHD_CONNECTION_URL_RECEIVED:
case MHD_CONNECTION_HEADER_PART_RECEIVED:
case MHD_CONNECTION_HEADERS_RECEIVED:
@@ -2903,6 +3811,7 @@
/* nothing to do but default action */
if (connection->read_closed)
{
+ /* TODO: check whether this really needed */
MHD_connection_close_ (connection,
MHD_REQUEST_TERMINATED_READ_ERROR);
}
@@ -2916,10 +3825,17 @@
#endif /* UPGRADE_SUPPORT */
default:
/* shrink read buffer to how much is actually used */
- MHD_pool_reallocate (connection->pool,
- connection->read_buffer,
- connection->read_buffer_size + 1,
- connection->read_buffer_offset);
+ if ((0 != connection->read_buffer_size) &&
+ (connection->read_buffer_size != connection->read_buffer_offset))
+ {
+ mhd_assert (NULL != connection->read_buffer);
+ connection->read_buffer =
+ MHD_pool_reallocate (connection->pool,
+ connection->read_buffer,
+ connection->read_buffer_size,
+ connection->read_buffer_offset);
+ connection->read_buffer_size = connection->read_buffer_offset;
+ }
break;
}
return;
@@ -2960,6 +3876,7 @@
switch (connection->state)
{
case MHD_CONNECTION_INIT:
+ case MHD_CONNECTION_REQ_LINE_RECEIVING:
case MHD_CONNECTION_URL_RECEIVED:
case MHD_CONNECTION_HEADER_PART_RECEIVED:
case MHD_CONNECTION_HEADERS_RECEIVED:
@@ -3000,6 +3917,8 @@
case MHD_CONNECTION_BODY_RECEIVED:
case MHD_CONNECTION_FOOTER_PART_RECEIVED:
case MHD_CONNECTION_FOOTERS_RECEIVED:
+ case MHD_CONNECTION_FULL_REQ_RECEIVED:
+ case MHD_CONNECTION_START_REPLY:
mhd_assert (0);
return;
case MHD_CONNECTION_HEADERS_SENDING:
@@ -3018,12 +3937,18 @@
connection->response_write_position) || \
(MHD_SIZE_UNKNOWN ==
connection->response_write_position) );
+ mhd_assert ((MHD_CONN_MUST_UPGRADE != connection->keepalive) || \
+ (! connection->rp_props.send_reply_body));
- if ( (NULL == resp->crc) &&
+ if ( (connection->rp_props.send_reply_body) &&
+ (NULL == resp->crc) &&
(NULL == resp->data_iov) &&
- (0 == connection->response_write_position) )
+ /* TODO: remove the next check as 'send_reply_body' is used */
+ (0 == connection->response_write_position) &&
+ (! connection->rp_props.chunked) )
{
mhd_assert (resp->total_size >= resp->data_size);
+ mhd_assert (0 == resp->data_start);
/* Send response headers alongside the response body, if the body
* data is available. */
ret = MHD_send_hdr_and_body_ (connection,
@@ -3048,10 +3973,8 @@
NULL,
0,
((0 == resp->total_size) ||
- (resp->total_size ==
- connection->response_write_position) ||
- (MHD_SIZE_UNKNOWN ==
- connection->response_write_position)));
+ (! connection->rp_props.send_reply_body)
+ ));
}
if (ret < 0)
@@ -3075,7 +3998,8 @@
/* The complete header and some response data have been sent,
* update both offsets. */
mhd_assert (0 == connection->response_write_position);
- mhd_assert (! connection->have_chunked_upload);
+ mhd_assert (! connection->rp_props.chunked);
+ mhd_assert (connection->rp_props.send_reply_body);
connection->write_buffer_send_offset += wb_ready;
connection->response_write_position = ret - wb_ready;
}
@@ -3257,6 +4181,56 @@
/**
+ * Check whether connection has timed out.
+ * @param c the connection to check
+ * @return true if connection has timeout and needs to be closed,
+ * false otherwise.
+ */
+static bool
+connection_check_timedout (struct MHD_Connection *c)
+{
+ const uint64_t timeout = c->connection_timeout_ms;
+ uint64_t now;
+ uint64_t since_actv;
+
+ if (c->suspended)
+ return false;
+ if (0 == timeout)
+ return false;
+ now = MHD_monotonic_msec_counter ();
+ since_actv = now - c->last_activity;
+ /* Keep the next lines in sync with #connection_get_wait() to avoid
+ * undesired side-effects like busy-waiting. */
+ if (timeout < since_actv)
+ {
+ if (UINT64_MAX / 2 < since_actv)
+ {
+ const uint64_t jump_back = c->last_activity - now;
+ /* Very unlikely that it is more than quarter-million years pause.
+ * More likely that system clock jumps back. */
+ if (5000 >= jump_back)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (c->daemon,
+ _ ("Detected system clock %u milliseconds jump back.\n"),
+ (unsigned int) jump_back);
+#endif
+ return false;
+ }
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (c->daemon,
+ _ ("Detected too large system clock %" PRIu64 " milliseconds "
+ "jump back.\n"),
+ jump_back);
+#endif
+ }
+ return true;
+ }
+ return false;
+}
+
+
+/**
* Clean up the state of the given connection and move it into the
* clean up queue for final disposal.
* @remark To be called only from thread that process connection's
@@ -3295,7 +4269,7 @@
{
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
{
- if (connection->connection_timeout == daemon->connection_timeout)
+ if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
XDLL_remove (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
connection);
@@ -3335,6 +4309,95 @@
/**
+ * Reset connection after request-reply cycle.
+ * @param connection the connection to process
+ * @param reuse the flag to choose whether to close connection or
+ * prepare connection for the next request processing
+ */
+static void
+connection_reset (struct MHD_Connection *connection,
+ bool reuse)
+{
+ struct MHD_Connection *const c = connection; /**< a short alias */
+ struct MHD_Daemon *const d = connection->daemon;
+
+ if (! reuse)
+ {
+ /* Next function will destroy response, notify client,
+ * destroy memory pool, and set connection state to "CLOSED" */
+ MHD_connection_close_ (connection,
+ c->stop_with_error ?
+ MHD_REQUEST_TERMINATED_WITH_ERROR :
+ MHD_REQUEST_TERMINATED_COMPLETED_OK);
+ c->read_buffer = NULL;
+ c->read_buffer_size = 0;
+ c->read_buffer_offset = 0;
+ c->write_buffer = NULL;
+ c->write_buffer_size = 0;
+ c->write_buffer_send_offset = 0;
+ c->write_buffer_append_offset = 0;
+ }
+ else
+ {
+ /* Reset connection to process the next request */
+ size_t new_read_buf_size;
+ mhd_assert (! c->stop_with_error);
+ mhd_assert (! c->discard_request);
+
+ if ( (NULL != d->notify_completed) &&
+ (c->client_aware) )
+ d->notify_completed (d->notify_completed_cls,
+ c,
+ &c->client_context,
+ MHD_REQUEST_TERMINATED_COMPLETED_OK);
+ c->client_aware = false;
+
+ if (NULL != c->response)
+ MHD_destroy_response (c->response);
+ c->response = NULL;
+ c->version = NULL;
+ c->http_ver = MHD_HTTP_VER_UNKNOWN;
+ c->last = NULL;
+ c->colon = NULL;
+ c->header_size = 0;
+ c->keepalive = MHD_CONN_KEEPALIVE_UNKOWN;
+ /* Reset the read buffer to the starting size,
+ preserving the bytes we have already read. */
+ new_read_buf_size = c->daemon->pool_size / 2;
+ if (c->read_buffer_offset > new_read_buf_size)
+ new_read_buf_size = c->read_buffer_offset;
+
+ connection->read_buffer
+ = MHD_pool_reset (c->pool,
+ c->read_buffer,
+ c->read_buffer_offset,
+ new_read_buf_size);
+ c->read_buffer_size = new_read_buf_size;
+ c->continue_message_write_offset = 0;
+ c->headers_received = NULL;
+ c->headers_received_tail = NULL;
+ c->have_chunked_upload = false;
+ c->current_chunk_size = 0;
+ c->current_chunk_offset = 0;
+ c->responseCode = 0;
+ c->response_write_position = 0;
+ c->method = NULL;
+ c->http_mthd = MHD_HTTP_MTHD_NO_METHOD;
+ c->url = NULL;
+ memset (&c->rp_props, 0, sizeof(c->rp_props));
+ c->write_buffer = NULL;
+ c->write_buffer_size = 0;
+ c->write_buffer_send_offset = 0;
+ c->write_buffer_append_offset = 0;
+ /* iov (if any) was deallocated by MHD_pool_reset */
+ memset (&connection->resp_iov, 0, sizeof(connection->resp_iov));
+ c->state = MHD_CONNECTION_INIT;
+ }
+ connection->client_context = NULL;
+}
+
+
+/**
* This function was created to handle per-connection processing that
* has to happen even if the socket cannot be read or written to.
* @remark To be called only from thread that process connection's
@@ -3355,6 +4418,8 @@
mhd_assert ( (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) || \
MHD_thread_ID_match_current_ (connection->pid) );
#endif /* MHD_USE_THREADS */
+ /* 'daemon' is not used if epoll is not available and asserts are disabled */
+ (void) daemon; /* Mute compiler warning */
connection->in_idle = true;
while (! connection->suspended)
@@ -3376,33 +4441,40 @@
switch (connection->state)
{
case MHD_CONNECTION_INIT:
+ case MHD_CONNECTION_REQ_LINE_RECEIVING:
line = get_next_header_line (connection,
&line_len);
- /* Check for empty string, as we might want
- to tolerate 'spurious' empty lines; also
- NULL means we didn't get a full line yet;
- line is not 0-terminated here. */
- if ( (NULL == line) ||
- (0 == line[0]) )
+ if (NULL != line)
{
- if (MHD_CONNECTION_INIT != connection->state)
- continue;
- if (connection->read_closed)
+ /* Check for empty string, as we might want
+ to tolerate 'spurious' empty lines */
+ if (0 == line[0])
{
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- continue;
+ /* TODO: Add MHD option to not tolerate it */
+ connection->state = MHD_CONNECTION_INIT;
+ continue; /* Process the next line */
}
- break;
+ if (MHD_NO == parse_initial_message_line (connection,
+ line,
+ line_len))
+ CONNECTION_CLOSE_ERROR_CHECK (connection,
+ NULL);
+ else
+ {
+ mhd_assert (MHD_IS_HTTP_VER_SUPPORTED (connection->http_ver));
+ connection->state = MHD_CONNECTION_URL_RECEIVED;
+ }
+ continue;
}
- if (MHD_NO == parse_initial_message_line (connection,
- line,
- line_len))
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- else
- connection->state = MHD_CONNECTION_URL_RECEIVED;
- continue;
+ /* NULL means we didn't get a full line yet */
+ if (connection->discard_request)
+ {
+ mhd_assert (MHD_CONNECTION_INIT != connection->state);
+ continue;
+ }
+ if (0 < connection->read_buffer_offset)
+ connection->state = MHD_CONNECTION_REQ_LINE_RECEIVING;
+ break;
case MHD_CONNECTION_URL_RECEIVED:
line = get_next_header_line (connection,
NULL);
@@ -3421,15 +4493,16 @@
if (0 == line[0])
{
connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
- connection->header_size = (size_t) (line - connection->read_buffer);
+ connection->header_size = (size_t) (connection->read_buffer
+ - connection->method);
continue;
}
if (MHD_NO == process_header_line (connection,
line))
{
- transmit_error_response (connection,
- MHD_HTTP_BAD_REQUEST,
- REQUEST_MALFORMED);
+ transmit_error_response_static (connection,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_MALFORMED);
break;
}
connection->state = MHD_CONNECTION_HEADER_PART_RECEIVED;
@@ -3457,7 +4530,8 @@
if (0 == line[0])
{
connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
- connection->header_size = (size_t) (line - connection->read_buffer);
+ connection->header_size = (size_t) (connection->read_buffer
+ - connection->method);
continue;
}
continue;
@@ -3487,10 +4561,10 @@
/* we refused (no upload allowed!) */
connection->remaining_upload_size = 0;
/* force close, in case client still tries to upload... */
- connection->read_closed = true;
+ connection->discard_request = true;
}
connection->state = (0 == connection->remaining_upload_size)
- ? MHD_CONNECTION_FOOTERS_RECEIVED
+ ? MHD_CONNECTION_FULL_REQ_RECEIVED
: MHD_CONNECTION_CONTINUE_SENT;
if (connection->suspended)
break;
@@ -3507,19 +4581,22 @@
if (0 != connection->read_buffer_offset)
{
process_request_body (connection); /* loop call */
- if (MHD_CONNECTION_CLOSED == connection->state)
+ if (connection->discard_request)
+ {
+ mhd_assert (MHD_CONNECTION_CONTINUE_SENT != connection->state);
continue;
+ }
}
if ( (0 == connection->remaining_upload_size) ||
( (MHD_SIZE_UNKNOWN == connection->remaining_upload_size) &&
(0 == connection->read_buffer_offset) &&
- (connection->read_closed) ) )
+ (connection->discard_request) ) )
{
if ( (connection->have_chunked_upload) &&
- (! connection->read_closed) )
+ (! connection->discard_request) )
connection->state = MHD_CONNECTION_BODY_RECEIVED;
else
- connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ connection->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
if (connection->suspended)
break;
continue;
@@ -3538,6 +4615,8 @@
NULL);
continue;
}
+ if (0 < connection->read_buffer_offset)
+ connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
break;
}
if (0 == line[0])
@@ -3550,9 +4629,9 @@
if (MHD_NO == process_header_line (connection,
line))
{
- transmit_error_response (connection,
- MHD_HTTP_BAD_REQUEST,
- REQUEST_MALFORMED);
+ transmit_error_response_static (connection,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_MALFORMED);
break;
}
connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
@@ -3586,21 +4665,33 @@
}
continue;
case MHD_CONNECTION_FOOTERS_RECEIVED:
+ /* The header, the body, and the footers of the request has been received,
+ * switch to the final processing of the request. */
+ connection->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ continue;
+ case MHD_CONNECTION_FULL_REQ_RECEIVED:
call_connection_handler (connection); /* "final" call */
if (connection->state == MHD_CONNECTION_CLOSED)
continue;
if (NULL == connection->response)
break; /* try again next time */
+ /* Response is ready, start reply */
+ connection->state = MHD_CONNECTION_START_REPLY;
+ continue;
+ case MHD_CONNECTION_START_REPLY:
+ mhd_assert (NULL != connection->response);
+ connection_switch_from_recv_to_send (connection);
if (MHD_NO == build_header_response (connection))
{
/* oops - close! */
CONNECTION_CLOSE_ERROR (connection,
- _ (
- "Closing connection (failed to create response header).\n"));
+ _ ("Closing connection (failed to create "
+ "response header).\n"));
continue;
}
connection->state = MHD_CONNECTION_HEADERS_SENDING;
break;
+
case MHD_CONNECTION_HEADERS_SENDING:
/* no default action */
break;
@@ -3631,7 +4722,7 @@
}
#endif /* UPGRADE_SUPPORT */
- if (connection->have_chunked_upload)
+ if (connection->rp_props.chunked)
connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
else
connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
@@ -3650,7 +4741,10 @@
if (NULL != connection->response->crc)
MHD_mutex_unlock_chk_ (&connection->response->mutex);
#endif
- connection->state = MHD_CONNECTION_BODY_SENT;
+ if (connection->rp_props.chunked)
+ connection->state = MHD_CONNECTION_BODY_SENT;
+ else
+ connection->state = MHD_CONNECTION_FOOTERS_SENT;
continue;
}
if (MHD_NO != try_ready_normal_body (connection))
@@ -3686,29 +4780,35 @@
connection->state = MHD_CONNECTION_BODY_SENT;
continue;
}
- if (MHD_NO != try_ready_chunked_body (connection))
- {
+ if (1)
+ { /* pseudo-branch for local variables scope */
+ bool finished;
+ if (MHD_NO != try_ready_chunked_body (connection, &finished))
+ {
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
- if (NULL != connection->response->crc)
- MHD_mutex_unlock_chk_ (&connection->response->mutex);
+ if (NULL != connection->response->crc)
+ MHD_mutex_unlock_chk_ (&connection->response->mutex);
#endif
- connection->state = MHD_CONNECTION_CHUNKED_BODY_READY;
- /* Buffering for flushable socket was already enabled */
-
- continue;
+ connection->state = finished ? MHD_CONNECTION_BODY_SENT :
+ MHD_CONNECTION_CHUNKED_BODY_READY;
+ continue;
+ }
+ /* mutex was already unlocked by try_ready_chunked_body */
}
- /* mutex was already unlocked by try_ready_chunked_body */
break;
case MHD_CONNECTION_BODY_SENT:
- if (MHD_NO == build_header_response (connection))
+ mhd_assert (connection->rp_props.chunked);
+
+ if (MHD_NO == build_connection_chunked_response_footer (connection))
{
/* oops - close! */
CONNECTION_CLOSE_ERROR (connection,
_ (
- "Closing connection (failed to create response header)."));
+ "Closing connection (failed to create response footer)."));
continue;
}
- if ( (! connection->have_chunked_upload) ||
+ /* TODO: remove next 'if' */
+ if ( (! connection->rp_props.chunked) ||
(connection->write_buffer_send_offset ==
connection->write_buffer_append_offset) )
connection->state = MHD_CONNECTION_FOOTERS_SENT;
@@ -3728,66 +4828,11 @@
/* FIXME: maybe partially reset memory pool? */
continue;
}
- MHD_destroy_response (connection->response);
- connection->response = NULL;
- if ( (NULL != daemon->notify_completed) &&
- (connection->client_aware) )
- {
- daemon->notify_completed (daemon->notify_completed_cls,
- connection,
- &connection->client_context,
- MHD_REQUEST_TERMINATED_COMPLETED_OK);
- }
- connection->client_aware = false;
- if ( (MHD_CONN_USE_KEEPALIVE != connection->keepalive) ||
- (connection->read_closed) )
- {
- /* have to close for some reason */
- MHD_connection_close_ (connection,
- MHD_REQUEST_TERMINATED_COMPLETED_OK);
- MHD_pool_destroy (connection->pool);
- connection->pool = NULL;
- connection->read_buffer = NULL;
- connection->read_buffer_size = 0;
- connection->read_buffer_offset = 0;
- }
- else
- {
- /* can try to keep-alive */
-
- connection->version = NULL;
- connection->state = MHD_CONNECTION_INIT;
- connection->last = NULL;
- connection->colon = NULL;
- connection->header_size = 0;
- connection->keepalive = MHD_CONN_KEEPALIVE_UNKOWN;
- /* Reset the read buffer to the starting size,
- preserving the bytes we have already read. */
- connection->read_buffer
- = MHD_pool_reset (connection->pool,
- connection->read_buffer,
- connection->read_buffer_offset,
- connection->daemon->pool_size / 2);
- connection->read_buffer_size
- = connection->daemon->pool_size / 2;
- }
- connection->client_context = NULL;
- connection->continue_message_write_offset = 0;
- connection->responseCode = 0;
- connection->headers_received = NULL;
- connection->headers_received_tail = NULL;
- connection->response_write_position = 0;
- connection->have_chunked_upload = false;
- connection->current_chunk_size = 0;
- connection->current_chunk_offset = 0;
- connection->method = NULL;
- connection->url = NULL;
- connection->write_buffer = NULL;
- connection->write_buffer_size = 0;
- connection->write_buffer_send_offset = 0;
- connection->write_buffer_append_offset = 0;
- /* iov (if any) was deallocated by MHD_pool_reset */
- memset (&connection->resp_iov, 0, sizeof(connection->resp_iov));
+ /* Reset connection after complete reply */
+ connection_reset (connection,
+ MHD_CONN_USE_KEEPALIVE == connection->keepalive &&
+ ! connection->read_closed &&
+ ! connection->discard_request);
continue;
case MHD_CONNECTION_CLOSED:
cleanup_connection (connection);
@@ -3804,19 +4849,12 @@
}
break;
}
- if (! connection->suspended)
+ if (connection_check_timedout (connection))
{
- time_t timeout;
- timeout = connection->connection_timeout;
- if ( (0 != timeout) &&
- (timeout < (MHD_monotonic_sec_counter ()
- - connection->last_activity)) )
- {
- MHD_connection_close_ (connection,
- MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
- connection->in_idle = false;
- return MHD_YES;
- }
+ MHD_connection_close_ (connection,
+ MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
+ connection->in_idle = false;
+ return MHD_YES;
}
MHD_connection_update_event_loop_info (connection);
ret = MHD_YES;
@@ -3941,8 +4979,8 @@
connection->suspended_dummy = connection->suspended ? MHD_YES : MHD_NO;
return (const union MHD_ConnectionInfo *) &connection->suspended_dummy;
case MHD_CONNECTION_INFO_CONNECTION_TIMEOUT:
- connection->connection_timeout_dummy = (unsigned
- int) connection->connection_timeout;
+ connection->connection_timeout_dummy =
+ (unsigned int) connection->connection_timeout_ms / 1000;
return (const union MHD_ConnectionInfo *) &connection->
connection_timeout_dummy;
case MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE:
@@ -3950,6 +4988,10 @@
(MHD_CONNECTION_CLOSED == connection->state) )
return NULL; /* invalid, too early! */
return (const union MHD_ConnectionInfo *) &connection->header_size;
+ case MHD_CONNECTION_INFO_HTTP_STATUS:
+ if (NULL == connection->response)
+ return NULL;
+ return (const union MHD_ConnectionInfo *) &connection->responseCode;
default:
return NULL;
}
@@ -3972,20 +5014,21 @@
{
va_list ap;
struct MHD_Daemon *daemon;
+ unsigned int ui_val;
daemon = connection->daemon;
switch (option)
{
case MHD_CONNECTION_OPTION_TIMEOUT:
- if (0 == connection->connection_timeout)
- connection->last_activity = MHD_monotonic_sec_counter ();
+ if (0 == connection->connection_timeout_ms)
+ connection->last_activity = MHD_monotonic_msec_counter ();
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
#endif
if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(! connection->suspended) )
{
- if (connection->connection_timeout == daemon->connection_timeout)
+ if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
XDLL_remove (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
connection);
@@ -3995,13 +5038,28 @@
connection);
}
va_start (ap, option);
- connection->connection_timeout = va_arg (ap,
- unsigned int);
+ ui_val = va_arg (ap, unsigned int);
va_end (ap);
+#if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
+ if ((UINT64_MAX / 4000 - 1) < ui_val)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ _ ("The specified connection timeout (%u) is too " \
+ "large. Maximum allowed value (%" PRIu64 ") will be used " \
+ "instead.\n"),
+ ui_val,
+ (UINT64_MAX / 4000 - 1));
+#endif
+ ui_val = UINT64_MAX / 4000 - 1;
+ }
+ else
+#endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
+ connection->connection_timeout_ms = ui_val * 1000;
if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
(! connection->suspended) )
{
- if (connection->connection_timeout == daemon->connection_timeout)
+ if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
XDLL_insert (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
connection);
@@ -4024,12 +5082,18 @@
* Queue a response to be transmitted to the client (as soon as
* possible but after #MHD_AccessHandlerCallback returns).
*
+ * For any active connection this function must be called
+ * only by #MHD_AccessHandlerCallback callback.
+ * For suspended connection this function can be called at any moment. Response
+ * will be sent as soon as connection is resumed.
+ *
* @param connection the connection identifying the client
* @param status_code HTTP status code (i.e. #MHD_HTTP_OK)
* @param response response to transmit
* @return #MHD_NO on error (i.e. reply already sent),
* #MHD_YES on success or if message has been queued
* @ingroup response
+ * @sa #MHD_AccessHandlerCallback
*/
enum MHD_Result
MHD_queue_response (struct MHD_Connection *connection,
@@ -4038,17 +5102,10 @@
{
struct MHD_Daemon *daemon;
- if ( (NULL == connection) ||
- (NULL == response) ||
- (NULL != connection->response) ||
- ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
- (MHD_CONNECTION_FOOTERS_RECEIVED != connection->state) ) )
+ if ((NULL == connection) || (NULL == response))
return MHD_NO;
- daemon = connection->daemon;
- if (daemon->shutdown)
- return MHD_YES; /* If daemon was shut down in parallel,
- * response will be aborted now or on later stage. */
+ daemon = connection->daemon;
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
if ( (! connection->suspended) &&
@@ -4062,28 +5119,109 @@
return MHD_NO;
}
#endif
+
+ if (daemon->shutdown)
+ return MHD_YES; /* If daemon was shut down in parallel,
+ * response will be aborted now or on later stage. */
+
+ if ( (NULL != connection->response) ||
+ ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
+ (MHD_CONNECTION_FULL_REQ_RECEIVED != connection->state) ) )
+ return MHD_NO;
+
#ifdef UPGRADE_SUPPORT
- if ( (NULL != response->upgrade_handler) &&
- (0 == (daemon->options & MHD_ALLOW_UPGRADE)) )
+ if (NULL != response->upgrade_handler)
{
+ struct MHD_HTTP_Header *conn_header;
+ if (0 == (daemon->options & MHD_ALLOW_UPGRADE))
+ {
#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _ (
- "Attempted 'upgrade' connection on daemon without MHD_ALLOW_UPGRADE option!\n"));
+ MHD_DLOG (daemon,
+ _ ("Attempted 'upgrade' connection on daemon without" \
+ " MHD_ALLOW_UPGRADE option!\n"));
#endif
- return MHD_NO;
+ return MHD_NO;
+ }
+ if (MHD_HTTP_SWITCHING_PROTOCOLS != status_code)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _ ("Application used invalid status code for" \
+ " 'upgrade' response!\n"));
+#endif
+ return MHD_NO;
+ }
+ if (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _ ("Application used invalid response" \
+ " without \"Connection\" header!\n"));
+#endif
+ return MHD_NO;
+ }
+ conn_header = response->first_header;
+ mhd_assert (NULL != conn_header);
+ mhd_assert (MHD_str_equal_caseless_ (conn_header->header,
+ MHD_HTTP_HEADER_CONNECTION));
+ if (! MHD_str_has_s_token_caseless_ (conn_header->value,
+ "upgrade"))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _ ("Application used invalid response" \
+ " without \"upgrade\" token in" \
+ " \"Connection\" header!\n"));
+#endif
+ return MHD_NO;
+ }
+ if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->http_ver))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _ ("Connection \"Upgrade\" can be used " \
+ "with HTTP/1.1 connections!\n"));
+#endif
+ return MHD_NO;
+ }
}
- if ( (MHD_HTTP_SWITCHING_PROTOCOLS != status_code) &&
- (NULL != response->upgrade_handler) )
+#endif /* UPGRADE_SUPPORT */
+ if ( (100 > (status_code & (~MHD_ICY_FLAG))) ||
+ (999 < (status_code & (~MHD_ICY_FLAG))) )
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
- _ (
- "Application used invalid status code for 'upgrade' response!\n"));
+ _ ("Refused wrong status code (%u). " \
+ "HTTP requires three digits status code!\n"),
+ (status_code & (~MHD_ICY_FLAG)));
#endif
return MHD_NO;
}
-#endif /* UPGRADE_SUPPORT */
+ if (200 > (status_code & (~MHD_ICY_FLAG)))
+ {
+ if (MHD_HTTP_VER_1_0 == connection->http_ver)
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _ ("Wrong status code (%u) refused. " \
+ "HTTP/1.0 clients do not support 1xx status codes!\n"),
+ (status_code & (~MHD_ICY_FLAG)));
+#endif
+ return MHD_NO;
+ }
+ if (0 != (response->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
+ | MHD_RF_HTTP_1_0_SERVER)))
+ {
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _ ("Wrong status code (%u) refused. " \
+ "HTTP/1.0 reply mode does not support 1xx status codes!\n"),
+ (status_code & (~MHD_ICY_FLAG)));
+#endif
+ return MHD_NO;
+ }
+ }
+
MHD_increment_response_rc (response);
connection->response = response;
connection->responseCode = status_code;
@@ -4104,9 +5242,7 @@
/* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice()
to avoid two user-space copies... */
- if ( ( (NULL != connection->method) &&
- (MHD_str_equal_caseless_ (connection->method,
- MHD_HTTP_METHOD_HEAD)) ) ||
+ if ( (MHD_HTTP_MTHD_HEAD == connection->http_mthd) ||
(MHD_HTTP_OK > status_code) ||
(MHD_HTTP_NO_CONTENT == status_code) ||
(MHD_HTTP_NOT_MODIFIED == status_code) )
@@ -4114,14 +5250,16 @@
/* if this is a "HEAD" request, or a status code for
which a body is not allowed, pretend that we
have already sent the full message body. */
+ /* TODO: remove the next assignment, use 'rp_props.send_reply_body' in
+ * checks */
connection->response_write_position = response->total_size;
}
if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
{
/* response was queued "early", refuse to read body / footers or
further requests! */
- connection->read_closed = true;
- connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ connection->discard_request = true;
+ connection->state = MHD_CONNECTION_START_REPLY;
connection->remaining_upload_size = 0;
}
if (! connection->in_idle)
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/connection.h
^
|
@@ -89,13 +89,15 @@
/**
* This function handles a particular connection when it has been
* determined that there is data to be read off a socket. All
- * implementations (multithreaded, external select, internal select)
+ * implementations (multithreaded, external polling, internal polling)
* call this function to handle reads.
*
* @param connection connection to handle
+ * @param socket_error set to true if socket error was detected
*/
void
-MHD_connection_handle_read (struct MHD_Connection *connection);
+MHD_connection_handle_read (struct MHD_Connection *connection,
+ bool socket_error);
/**
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/connection_https.c
^
|
@@ -65,7 +65,8 @@
{
#ifdef EPOLL_SUPPORT
if (GNUTLS_E_AGAIN == res)
- connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
#endif
/* Any network errors means that buffer is empty. */
connection->tls_read_ready = false;
@@ -90,8 +91,13 @@
(GNUTLS_E_CRYPTODEV_IOCTL_ERROR == res) ||
(GNUTLS_E_CRYPTODEV_DEVICE_ERROR == res) )
return MHD_ERR_PIPE_;
+#if defined(GNUTLS_E_PREMATURE_TERMINATION)
if (GNUTLS_E_PREMATURE_TERMINATION == res)
return MHD_ERR_CONNRESET_;
+#elif defined(GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
+ if (GNUTLS_E_UNEXPECTED_PACKET_LENGTH == res)
+ return MHD_ERR_CONNRESET_;
+#endif /* GNUTLS_E_UNEXPECTED_PACKET_LENGTH */
if (GNUTLS_E_MEMORY_ERROR == res)
return MHD_ERR_NOMEM_;
/* Treat any other error as a hard error. */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/daemon.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+ Copyright (C) 2015-2021 Evgeny Grin (Karlson2k)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -124,7 +125,7 @@
* @param line line number with the problem
* @param reason error message with details
*/
-static void
+_MHD_NORETURN static void
mhd_panic_std (void *cls,
const char *file,
unsigned int line,
@@ -225,9 +226,9 @@
const char *fm,
va_list ap)
{
- vfprintf ((FILE*) cls, fm, ap);
+ vfprintf ((FILE *) cls, fm, ap);
#ifdef _DEBUG
- fflush ((FILE*) cls);
+ fflush ((FILE *) cls);
#endif /* _DEBUG */
}
@@ -256,7 +257,7 @@
* @param daemon handle to a daemon
* @return master daemon handle
*/
-static struct MHD_Daemon*
+static struct MHD_Daemon *
MHD_get_master (struct MHD_Daemon *daemon)
{
while (NULL != daemon->master)
@@ -371,7 +372,7 @@
/* IPv4 addresses */
if (sizeof (struct sockaddr_in) == addrlen)
{
- const struct sockaddr_in *addr4 = (const struct sockaddr_in*) addr;
+ const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr;
key->family = AF_INET;
memcpy (&key->addr.ipv4,
@@ -384,7 +385,7 @@
/* IPv6 addresses */
if (sizeof (struct sockaddr_in6) == addrlen)
{
- const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*) addr;
+ const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr;
key->family = AF_INET6;
memcpy (&key->addr.ipv6,
@@ -699,12 +700,14 @@
* before calling this function. FD_SETSIZE is assumed
* to be platform's default.
*
- * This function should only be called in when MHD is configured to
- * use external select with 'select()' or with 'epoll'.
+ * This function should be called only when MHD is configured to
+ * use "external" sockets polling with 'select()' or with 'epoll'.
* In the latter case, it will only add the single 'epoll' file
* descriptor used by MHD to the sets.
- * It's necessary to use #MHD_get_timeout() in combination with
- * this function.
+ * It's necessary to use #MHD_get_timeout() to get maximum timeout
+ * value for `select()`. Usage of `select()` with indefinite timeout
+ * (or timeout larger than returned by #MHD_get_timeout()) will
+ * violate MHD API and may results in pending unprocessed data.
*
* This function must be called only for daemon started
* without #MHD_USE_INTERNAL_POLLING_THREAD flag.
@@ -838,8 +841,10 @@
const MHD_socket mhd_sckt = urh->mhd.socket;
/* Reset read/write ready, preserve error state. */
- urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY);
- urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY);
+ urh->app.celi &= (~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY)
+ & ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY));
+ urh->mhd.celi &= (~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY)
+ & ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY));
if (MHD_INVALID_SOCKET != conn_sckt)
{
@@ -934,8 +939,10 @@
struct pollfd p[2])
{
/* Reset read/write ready, preserve error state. */
- urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY);
- urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY);
+ urh->app.celi &= (~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY)
+ & ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY));
+ urh->mhd.celi &= (~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY)
+ & ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY));
if (0 != (p[0].revents & POLLIN))
urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
@@ -1100,12 +1107,14 @@
* Passing custom FD_SETSIZE as @a fd_setsize allow usage of
* larger/smaller than platform's default fd_sets.
*
- * This function should only be called in when MHD is configured to
- * use external select with 'select()' or with 'epoll'.
+ * This function should be called only when MHD is configured to
+ * use "external" sockets polling with 'select()' or with 'epoll'.
* In the latter case, it will only add the single 'epoll' file
* descriptor used by MHD to the sets.
- * It's necessary to use #MHD_get_timeout() in combination with
- * this function.
+ * It's necessary to use #MHD_get_timeout() to get maximum timeout
+ * value for `select()`. Usage of `select()` with indefinite timeout
+ * (or timeout larger than returned by #MHD_get_timeout()) will
+ * violate MHD API and may results in pending unprocessed data.
*
* This function must be called only for daemon started
* without #MHD_USE_INTERNAL_POLLING_THREAD flag.
@@ -1204,15 +1213,18 @@
if (con->tls_read_ready)
read_ready = true;
#endif /* HTTPS_SUPPORT */
+ if ( (MHD_EVENT_LOOP_INFO_READ == con->event_loop_info) &&
+ (read_ready || (force_close && con->sk_nonblck)) )
+ {
+ MHD_connection_handle_read (con, force_close);
+ mhd_assert (! force_close || MHD_CONNECTION_CLOSED == con->state);
+ ret = MHD_connection_handle_idle (con);
+ if (force_close)
+ return ret;
+ states_info_processed = true;
+ }
if (! force_close)
{
- if ( (MHD_EVENT_LOOP_INFO_READ == con->event_loop_info) &&
- read_ready)
- {
- MHD_connection_handle_read (con);
- ret = MHD_connection_handle_idle (con);
- states_info_processed = true;
- }
/* No need to check value of 'ret' here as closed connection
* cannot be in MHD_EVENT_LOOP_INFO_WRITE state. */
if ( (MHD_EVENT_LOOP_INFO_WRITE == con->event_loop_info) &&
@@ -1373,9 +1385,9 @@
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
_ ("Failed to forward to application "
- MHD_UNSIGNED_LONG_LONG_PRINTF \
+ "%" PRIu64 \
" bytes of data received from remote side: application shut down socket.\n"),
- (MHD_UNSIGNED_LONG_LONG) urh->in_buffer_used);
+ (uint64_t) urh->in_buffer_used);
#endif
}
@@ -1389,10 +1401,10 @@
/* Discard any data received form remote. */
urh->in_buffer_used = 0;
/* Do not try to push data to application. */
- urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ urh->mhd.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
/* Reading from remote client is not required anymore. */
urh->in_buffer_size = 0;
- urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ urh->app.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
connection->tls_read_ready = false;
}
@@ -1430,7 +1442,7 @@
{
if (GNUTLS_E_INTERRUPTED != res)
{
- urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ urh->app.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
if (GNUTLS_E_AGAIN != res)
{
/* Unrecoverable error on socket was detected or
@@ -1481,7 +1493,7 @@
((! MHD_SCKT_ERR_IS_EINTR_ (err)) &&
(! MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err))))
{
- urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ urh->mhd.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
if ((0 == res) ||
(was_closed) ||
(0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) ||
@@ -1499,7 +1511,7 @@
{
urh->out_buffer_used += res;
if (buf_size > (size_t) res)
- urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ urh->mhd.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
}
if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
( (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) ||
@@ -1532,7 +1544,7 @@
{
if (GNUTLS_E_INTERRUPTED != res)
{
- urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ urh->app.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
if (GNUTLS_E_AGAIN != res)
{
/* TLS connection shut down or
@@ -1541,16 +1553,16 @@
MHD_DLOG (daemon,
_ (
"Failed to forward to remote client "
- MHD_UNSIGNED_LONG_LONG_PRINTF \
+ "%" PRIu64 \
" bytes of data received from application: %s\n"),
- (MHD_UNSIGNED_LONG_LONG) urh->out_buffer_used,
+ (uint64_t) urh->out_buffer_used,
gnutls_strerror (res));
#endif
/* Discard any data unsent to remote. */
urh->out_buffer_used = 0;
/* Do not try to pull more data from application. */
urh->out_buffer_size = 0;
- urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ urh->mhd.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
}
}
}
@@ -1563,7 +1575,7 @@
&urh->out_buffer[res],
next_out_buffer_used);
if (data_size > (size_t) res)
- urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ urh->app.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
}
urh->out_buffer_used = next_out_buffer_used;
}
@@ -1573,10 +1585,10 @@
/* Unrecoverable error on socket was detected and all
* pending data was sent to remote. */
/* Do not try to send to remote anymore. */
- urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ urh->app.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
/* Do not try to pull more data from application. */
urh->out_buffer_size = 0;
- urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ urh->mhd.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
}
}
@@ -1602,7 +1614,7 @@
if ( (! MHD_SCKT_ERR_IS_EINTR_ (err)) &&
(! MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err)) )
{
- urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ urh->mhd.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
if (! MHD_SCKT_ERR_IS_EAGAIN_ (err))
{
/* Socketpair connection shut down or
@@ -1611,16 +1623,16 @@
MHD_DLOG (daemon,
_ (
"Failed to forward to application "
- MHD_UNSIGNED_LONG_LONG_PRINTF \
+ "%" PRIu64 \
" bytes of data received from remote side: %s\n"),
- (MHD_UNSIGNED_LONG_LONG) urh->in_buffer_used,
+ (uint64_t) urh->in_buffer_used,
MHD_socket_strerr_ (err));
#endif
/* Discard any data received form remote. */
urh->in_buffer_used = 0;
/* Reading from remote client is not required anymore. */
urh->in_buffer_size = 0;
- urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ urh->app.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
connection->tls_read_ready = false;
}
}
@@ -1634,7 +1646,7 @@
&urh->in_buffer[res],
next_in_buffer_used);
if (data_size > (size_t) res)
- urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ urh->mhd.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
}
urh->in_buffer_used = next_in_buffer_used;
}
@@ -1642,10 +1654,10 @@
(0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) )
{
/* Do not try to push data to application. */
- urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ urh->mhd.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
/* Reading from remote client is not required anymore. */
urh->in_buffer_size = 0;
- urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ urh->app.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
connection->tls_read_ready = false;
}
}
@@ -1667,17 +1679,17 @@
MHD_DLOG (daemon,
_ (
"Failed to forward to remote client "
- MHD_UNSIGNED_LONG_LONG_PRINTF \
+ "%" PRIu64 \
" bytes of data received from application: daemon shut down.\n"),
- (MHD_UNSIGNED_LONG_LONG) urh->out_buffer_used);
+ (uint64_t) urh->out_buffer_used);
#endif
/* Discard any data unsent to remote. */
urh->out_buffer_used = 0;
/* Do not try to sent to remote anymore. */
- urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
+ urh->app.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
/* Do not try to pull more data from application. */
urh->out_buffer_size = 0;
- urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
+ urh->mhd.celi &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_READ_READY);
}
}
@@ -1743,7 +1755,7 @@
/* FIXME: does this check really needed? */
if (MHD_INVALID_SOCKET != max_fd)
{
- struct timeval*tvp;
+ struct timeval *tvp;
struct timeval tv;
if (((con->tls_read_ready) &&
(urh->in_buffer_used < urh->in_buffer_size)) ||
@@ -1846,6 +1858,54 @@
/**
+ * Get maximum wait period for the connection (the amount of time left before
+ * connection time out)
+ * @param c the connection to check
+ * @return the maximum wait period before the connection must be processed
+ * again.
+ */
+static uint64_t
+connection_get_wait (struct MHD_Connection *c)
+{
+ const uint64_t now = MHD_monotonic_msec_counter ();
+ const uint64_t since_actv = now - c->last_activity;
+ const uint64_t timeout = c->connection_timeout_ms;
+ uint64_t mseconds_left;
+
+ mhd_assert (0 != timeout);
+ /* Keep the next lines in sync with #connection_check_timedout() to avoid
+ * undesired side-effects like busy-waiting. */
+ if (timeout < since_actv)
+ {
+ if (UINT64_MAX / 2 < since_actv)
+ {
+ const uint64_t jump_back = c->last_activity - now;
+ /* Very unlikely that it is more than quarter-million years pause.
+ * More likely that system clock jumps back. */
+ if (5000 >= jump_back)
+ { /* Jump back is less than 5 seconds, try to recover. */
+ return 100; /* Set wait time to 0.1 seconds */
+ }
+ /* Too large jump back */
+ }
+ return 0; /* Connection has timed out */
+ }
+ else if (since_actv == timeout)
+ {
+ /* Exact match for timeout and time from last activity.
+ * Maybe this is just a precise match or this happens because the timer
+ * resolution is too low.
+ * Set wait time to 0.1 seconds to avoid busy-waiting with low
+ * timer resolution as connection is not timed-out yet. */
+ return 100;
+ }
+ mseconds_left = timeout - since_actv;
+
+ return mseconds_left;
+}
+
+
+/**
* Main function of the thread that handles an individual
* connection when #MHD_USE_THREAD_PER_CONNECTION is set.
*
@@ -1864,7 +1924,6 @@
MHD_socket maxsock;
struct timeval tv;
struct timeval *tvp;
- time_t now;
#if WINDOWS
#ifdef HAVE_POLL
int extra_slot;
@@ -1888,7 +1947,7 @@
while ( (! daemon->shutdown) &&
(MHD_CONNECTION_CLOSED != con->state) )
{
- const time_t timeout = daemon->connection_timeout;
+ uint64_t timeout = con->connection_timeout_ms;
#ifdef UPGRADE_SUPPORT
struct MHD_UpgradeResponseHandle *const urh = con->urh;
#else /* ! UPGRADE_SUPPORT */
@@ -1984,22 +2043,16 @@
if ( (NULL == tvp) &&
(timeout > 0) )
{
- now = MHD_monotonic_sec_counter ();
- if (now - con->last_activity > timeout)
- tv.tv_sec = 0;
+ const uint64_t mseconds_left = connection_get_wait (con);
+#if (SIZEOF_UINT64_T - 2) >= SIZEOF_STRUCT_TIMEVAL_TV_SEC
+ if (mseconds_left / 1000 > TIMEVAL_TV_SEC_MAX)
+ tv.tv_sec = TIMEVAL_TV_SEC_MAX;
else
- {
- const time_t seconds_left = timeout - (now - con->last_activity);
-#if ! defined(_WIN32) || defined(__CYGWIN__)
- tv.tv_sec = seconds_left;
-#else /* _WIN32 && !__CYGWIN__ */
- if (seconds_left > TIMEVAL_TV_SEC_MAX)
- tv.tv_sec = TIMEVAL_TV_SEC_MAX;
- else
- tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) seconds_left;
-#endif /* _WIN32 && ! __CYGWIN__ */
- }
- tv.tv_usec = 0;
+#endif /* (SIZEOF_UINT64_T - 2) >= SIZEOF_STRUCT_TIMEVAL_TV_SEC */
+ tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) mseconds_left / 1000;
+
+ tv.tv_usec = (mseconds_left % 1000) * 1000;
+
tvp = &tv;
}
if (! use_poll)
@@ -2133,7 +2186,7 @@
#else
1,
#endif
- (NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0)
+ (NULL == tvp) ? -1 : (tv.tv_sec * 1000)) < 0)
{
if (MHD_SCKT_LAST_ERR_IS_ (MHD_SCKT_EINTR_))
continue;
@@ -2155,8 +2208,7 @@
call_handlers (con,
(0 != (p[0].revents & POLLIN)),
(0 != (p[0].revents & POLLOUT)),
- (0 != (p[0].revents & (POLLERR
- | MHD_POLL_REVENTS_ERR_DISC))) ))
+ (0 != (p[0].revents & MHD_POLL_REVENTS_ERR_DISC)) ))
goto exit;
}
#endif
@@ -2294,8 +2346,10 @@
{
struct MHD_Connection *connection;
struct MHD_Daemon *daemon;
+#if GNUTLS_VERSION_MAJOR >= 3
void *app_psk;
size_t app_psk_size;
+#endif /* GNUTLS_VERSION_MAJOR >= 3 */
connection = gnutls_session_get_ptr (session);
if (NULL == connection)
@@ -2348,6 +2402,7 @@
free (app_psk);
return 0;
#else
+ (void) username; (void) key; /* Mute compiler warning */
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
_ ("PSK not supported by this server.\n"));
@@ -2469,7 +2524,6 @@
connection->sk_nodelay = _MHD_UNKNOWN;
}
- connection->connection_timeout = daemon->connection_timeout;
if (NULL == (connection->addr = malloc (addrlen)))
{
eno = errno;
@@ -2495,7 +2549,9 @@
connection->is_nonip = sk_is_nonip;
connection->sk_spipe_suppress = sk_spipe_supprs;
connection->daemon = daemon;
- connection->last_activity = MHD_monotonic_sec_counter ();
+ connection->connection_timeout_ms = daemon->connection_timeout_ms;
+ if (0 != connection->connection_timeout_ms)
+ connection->last_activity = MHD_monotonic_msec_counter ();
if (0 == (daemon->options & MHD_USE_TLS))
{
@@ -2557,9 +2613,9 @@
const char prt1[] = "http/1.1"; /* Registered code for HTTP/1.1 */
const char prt2[] = "http/1.0"; /* Registered code for HTTP/1.0 */
- prts[0].data = (void*) prt1;
+ prts[0].data = (void *) prt1;
prts[0].size = MHD_STATICSTR_LEN_ (prt1);
- prts[1].data = (void*) prt2;
+ prts[1].data = (void *) prt2;
prts[1].size = MHD_STATICSTR_LEN_ (prt2);
if (GNUTLS_E_SUCCESS !=
gnutls_alpn_set_protocols (connection->tls_session,
@@ -2619,7 +2675,7 @@
#else /* GnuTLS before 3.1.9 or Win x64 */
gnutls_transport_set_ptr (connection->tls_session,
(gnutls_transport_ptr_t) (intptr_t) (client_socket));
-#endif /* GnuTLS before 3.1.9 */
+#endif /* GnuTLS before 3.1.9 or Win x64 */
#ifdef MHD_TLSLIB_NEED_PUSH_FUNC
gnutls_transport_set_push_function (connection->tls_session,
MHD_tls_push_func_);
@@ -2712,158 +2768,171 @@
* NUMA and/or complex cache hierarchy. */
connection->pool = MHD_pool_create (daemon->pool_size);
if (NULL == connection->pool)
- {
+ { /* 'pool' creation failed */
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
_ ("Error allocating memory: %s\n"),
MHD_strerror_ (errno));
#endif
- MHD_socket_close_chk_ (connection->socket_fd);
- MHD_ip_limit_del (daemon,
- connection->addr,
- connection->addr_len);
- free (connection);
#if ENOMEM
- errno = ENOMEM;
+ eno = ENOMEM;
#endif
- return MHD_NO;
+ (void) 0; /* Mute possible compiler warning */
}
-
-#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
- MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
-#endif
- /* Firm check under lock. */
- if (daemon->connections >= daemon->connection_limit)
- {
-#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
- MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
-#endif
- /* above connection limit - reject */
+ else
+ { /* 'pool' creation succeed */
+ MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
+ /* Firm check under lock. */
+ if (daemon->connections >= daemon->connection_limit)
+ { /* Connections limit */
#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _ (
- "Server reached connection limit. Closing inbound connection.\n"));
+ MHD_DLOG (daemon,
+ _ ("Server reached connection limit. "
+ "Closing inbound connection.\n"));
#endif
#if ENFILE
- eno = ENFILE;
-#endif
- goto cleanup;
- }
- daemon->connections++;
- if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
- {
- XDLL_insert (daemon->normal_timeout_head,
- daemon->normal_timeout_tail,
- connection);
- }
- DLL_insert (daemon->connections_head,
- daemon->connections_tail,
- connection);
-#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
- MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
-#endif
- if (NULL != daemon->notify_connection)
- daemon->notify_connection (daemon->notify_connection_cls,
- connection,
- &connection->socket_context,
- MHD_CONNECTION_NOTIFY_STARTED);
-#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
- /* attempt to create handler thread */
- if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
- {
- if (! MHD_create_named_thread_ (&connection->pid,
- "MHD-connection",
- daemon->thread_stack_size,
- &thread_main_handle_connection,
- connection))
- {
- eno = errno;
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _ ("Failed to create a thread: %s\n"),
- MHD_strerror_ (eno));
+ eno = ENFILE;
#endif
- goto cleanup;
+ (void) 0; /* Mute possible compiler warning */
}
- }
- else
- connection->pid = daemon->pid;
-#endif
-#ifdef EPOLL_SUPPORT
- if (0 != (daemon->options & MHD_USE_EPOLL))
- {
- if (0 == (daemon->options & MHD_USE_TURBO))
- {
- struct epoll_event event;
-
- event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
- event.data.ptr = connection;
- if (0 != epoll_ctl (daemon->epoll_fd,
- EPOLL_CTL_ADD,
- connection->socket_fd,
- &event))
+ else
+ { /* Have space for new connection */
+ daemon->connections++;
+ DLL_insert (daemon->connections_head,
+ daemon->connections_tail,
+ connection);
+ if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
{
- eno = errno;
+ XDLL_insert (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ }
+ MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
+ if (NULL != daemon->notify_connection)
+ daemon->notify_connection (daemon->notify_connection_cls,
+ connection,
+ &connection->socket_context,
+ MHD_CONNECTION_NOTIFY_STARTED);
+#ifdef MHD_USE_THREADS
+ if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+ {
+ mhd_assert (0 == (daemon->options & MHD_USE_EPOLL));
+ if (! MHD_create_named_thread_ (&connection->pid,
+ "MHD-connection",
+ daemon->thread_stack_size,
+ &thread_main_handle_connection,
+ connection))
+ {
+ eno = errno;
#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _ ("Call to epoll_ctl failed: %s\n"),
- MHD_socket_last_strerr_ ());
-#endif
- goto cleanup;
+#ifdef EAGAIN
+ if (EAGAIN == eno)
+ MHD_DLOG (daemon,
+ _ ("Failed to create a new thread because it would "
+ "have exceeded the system limit on the number of "
+ "threads or no system resources available.\n"));
+ else
+#endif /* EAGAIN */
+ MHD_DLOG (daemon,
+ _ ("Failed to create a thread: %s\n"),
+ MHD_strerror_ (eno));
+#endif /* HAVE_MESSAGES */
+ }
+ else /* New thread has been created successfully */
+ return MHD_YES; /* *** Function success exit point *** */
}
- connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
- }
- else
- {
- connection->epoll_state |= MHD_EPOLL_STATE_READ_READY
- | MHD_EPOLL_STATE_WRITE_READY
- | MHD_EPOLL_STATE_IN_EREADY_EDLL;
- EDLL_insert (daemon->eready_head,
- daemon->eready_tail,
- connection);
- }
- }
-#endif
+ else
+#else /* ! MHD_USE_THREADS */
+ if (1)
+#endif /* ! MHD_USE_THREADS */
+ { /* No 'thread-per-connection' */
+#ifdef MHD_USE_THREADS
+ connection->pid = daemon->pid;
+#endif /* MHD_USE_THREADS */
+#ifdef EPOLL_SUPPORT
+ if (0 != (daemon->options & MHD_USE_EPOLL))
+ {
+ if (0 == (daemon->options & MHD_USE_TURBO))
+ {
+ struct epoll_event event;
- return MHD_YES;
+ event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
+ event.data.ptr = connection;
+ if (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_ADD,
+ connection->socket_fd,
+ &event))
+ {
+ eno = errno;
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _ ("Call to epoll_ctl failed: %s\n"),
+ MHD_socket_last_strerr_ ());
+#endif
+ }
+ else
+ { /* 'socket_fd' has been added to 'epool' */
+ connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
+
+ return MHD_YES; /* *** Function success exit point *** */
+ }
+ }
+ else
+ {
+ connection->epoll_state |= MHD_EPOLL_STATE_READ_READY
+ | MHD_EPOLL_STATE_WRITE_READY
+ | MHD_EPOLL_STATE_IN_EREADY_EDLL;
+ EDLL_insert (daemon->eready_head,
+ daemon->eready_tail,
+ connection);
+
+ return MHD_YES; /* *** Function success exit point *** */
+ }
+ }
+ else /* No 'epoll' */
+#endif /* EPOLL_SUPPORT */
+ return MHD_YES; /* *** Function success exit point *** */
+ }
-cleanup:
- if (NULL != daemon->notify_connection)
- daemon->notify_connection (daemon->notify_connection_cls,
- connection,
- &connection->socket_context,
- MHD_CONNECTION_NOTIFY_CLOSED);
+ /* ** Below is a cleanup path ** */
+ if (NULL != daemon->notify_connection)
+ daemon->notify_connection (daemon->notify_connection_cls,
+ connection,
+ &connection->socket_context,
+ MHD_CONNECTION_NOTIFY_CLOSED);
+ MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
+ if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+ {
+ XDLL_remove (daemon->normal_timeout_head,
+ daemon->normal_timeout_tail,
+ connection);
+ }
+ DLL_remove (daemon->connections_head,
+ daemon->connections_tail,
+ connection);
+ daemon->connections--;
+ MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
+ }
+ MHD_pool_destroy (connection->pool);
+ }
+ /* Free resources allocated before the call of this functions */
#ifdef HTTPS_SUPPORT
if (NULL != connection->tls_session)
gnutls_deinit (connection->tls_session);
#endif /* HTTPS_SUPPORT */
- MHD_socket_close_chk_ (connection->socket_fd);
MHD_ip_limit_del (daemon,
connection->addr,
connection->addr_len);
-#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
- MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
-#endif
- if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
- {
- XDLL_remove (daemon->normal_timeout_head,
- daemon->normal_timeout_tail,
- connection);
- }
- DLL_remove (daemon->connections_head,
- daemon->connections_tail,
- connection);
-#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
- MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
-#endif
- MHD_pool_destroy (connection->pool);
free (connection->addr);
+ MHD_socket_close_chk_ (connection->socket_fd);
free (connection);
if (0 != eno)
errno = eno;
+#ifdef EINVAL
else
- errno = EINVAL;
- return MHD_NO;
+ errno = EINVAL;
+#endif /* EINVAL */
+ return MHD_NO; /* *** Function failure exit point *** */
}
@@ -3056,7 +3125,7 @@
}
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
{
- if (connection->connection_timeout == daemon->connection_timeout)
+ if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
XDLL_remove (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
connection);
@@ -3081,7 +3150,8 @@
EDLL_remove (daemon->eready_head,
daemon->eready_tail,
connection);
- connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_IN_EREADY_EDLL);
}
if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET))
{
@@ -3090,7 +3160,8 @@
connection->socket_fd,
NULL))
MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
- connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_IN_EPOLL_SET);
}
connection->epoll_state |= MHD_EPOLL_STATE_SUSPENDED;
}
@@ -3102,14 +3173,13 @@
/**
- * Suspend handling of network data for a given connection. This can
- * be used to dequeue a connection from MHD's event loop (external
- * select, internal select or thread pool; not applicable to
- * thread-per-connection!) for a while.
+ * Suspend handling of network data for a given connection.
+ * This can be used to dequeue a connection from MHD's event loop
+ * (not applicable to thread-per-connection!) for a while.
*
- * If you use this API in conjunction with a internal select or a
- * thread pool, you must set the option #MHD_USE_ITC to
- * ensure that a resumed connection is immediately processed by MHD.
+ * If you use this API in conjunction with an "internal" socket polling,
+ * you must set the option #MHD_USE_ITC to ensure that a resumed
+ * connection is immediately processed by MHD.
*
* Suspended connections continue to count against the total number of
* connections allowed (per daemon, as well as per IP, if such limits
@@ -3118,8 +3188,8 @@
* connection is suspended, MHD will not detect disconnects by the
* client.
*
- * The only safe time to suspend a connection is from the
- * #MHD_AccessHandlerCallback.
+ * The only safe way to call this function is to call it from the
+ * #MHD_AccessHandlerCallback or #MHD_ContentReaderCallback.
*
* Finally, it is an API violation to call #MHD_stop_daemon while
* having suspended connections (this will at least create memory and
@@ -3131,6 +3201,8 @@
* daemon's select()/poll()/etc.
*
* @param connection the connection to suspend
+ *
+ * @sa #MHD_AccessHandlerCallback
*/
void
MHD_suspend_connection (struct MHD_Connection *connection)
@@ -3265,10 +3337,10 @@
if (! used_thr_p_c)
{
/* Reset timeout timer on resume. */
- if (0 != pos->connection_timeout)
- pos->last_activity = MHD_monotonic_sec_counter ();
+ if (0 != pos->connection_timeout_ms)
+ pos->last_activity = MHD_monotonic_msec_counter ();
- if (pos->connection_timeout == daemon->connection_timeout)
+ if (pos->connection_timeout_ms == daemon->connection_timeout_ms)
XDLL_insert (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
pos);
@@ -3290,7 +3362,7 @@
pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL \
| MHD_EPOLL_STATE_READ_READY
| MHD_EPOLL_STATE_WRITE_READY;
- pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED;
+ pos->epoll_state &= ~((enum MHD_EpollState) MHD_EPOLL_STATE_SUSPENDED);
}
#endif
}
@@ -3514,6 +3586,7 @@
MHD_socket fd;
bool sk_nonbl;
bool sk_spipe_supprs;
+ bool sk_cloexec;
#ifdef MHD_USE_THREADS
mhd_assert ( (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) || \
@@ -3539,16 +3612,22 @@
#else /* MHD_WINSOCK_SOCKETS */
sk_spipe_supprs = true; /* Nothing to suppress on W32 */
#endif /* MHD_WINSOCK_SOCKETS */
+ sk_cloexec = (SOCK_CLOEXEC_OR_ZERO != 0);
#else /* ! USE_ACCEPT4 */
s = accept (fd,
addr,
&addrlen);
+#ifdef MHD_ACCEPT_INHERIT_NONBLOCK
+ sk_nonbl = daemon->listen_nonblk;
+#else /* ! MHD_ACCEPT_INHERIT_NONBLOCK */
sk_nonbl = false;
+#endif /* ! MHD_ACCEPT_INHERIT_NONBLOCK */
#ifndef MHD_WINSOCK_SOCKETS
sk_spipe_supprs = false;
#else /* MHD_WINSOCK_SOCKETS */
sk_spipe_supprs = true; /* Nothing to suppress on W32 */
#endif /* MHD_WINSOCK_SOCKETS */
+ sk_cloexec = false;
#endif /* ! USE_ACCEPT4 */
if ( (MHD_INVALID_SOCKET == s) ||
(addrlen <= 0) )
@@ -3581,8 +3660,10 @@
would ever be cleared. Instead trying to produce
bit fat ugly warning. */
MHD_DLOG (daemon,
- _ (
- "Hit process or system resource limit at FIRST connection. This is really bad as there is no sane way to proceed. Will try busy waiting for system resources to become magically available.\n"));
+ _ ("Hit process or system resource limit at FIRST " \
+ "connection. This is really bad as there is no sane " \
+ "way to proceed. Will try busy waiting for system " \
+ "resources to become magically available.\n"));
#endif
}
else
@@ -3596,44 +3677,48 @@
#endif
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
- _ (
- "Hit process or system resource limit at %u connections, temporarily suspending accept(). Consider setting a lower MHD_OPTION_CONNECTION_LIMIT.\n"),
+ _ ("Hit process or system resource limit at %u " \
+ "connections, temporarily suspending accept(). " \
+ "Consider setting a lower MHD_OPTION_CONNECTION_LIMIT.\n"),
(unsigned int) daemon->connections);
#endif
}
}
return MHD_NO;
}
-#if ! defined(USE_ACCEPT4) || ! defined(HAVE_SOCK_NONBLOCK)
- if (! MHD_socket_nonblocking_ (s))
+
+ if (! sk_nonbl && ! MHD_socket_nonblocking_ (s))
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
- _ (
- "Failed to set nonblocking mode on incoming connection socket: %s\n"),
+ _ ("Failed to set nonblocking mode on incoming connection " \
+ "socket: %s\n"),
MHD_socket_last_strerr_ ());
-#endif
+#else /* ! HAVE_MESSAGES */
+ (void) 0; /* Mute compiler warning */
+#endif /* ! HAVE_MESSAGES */
}
else
sk_nonbl = true;
-#endif /* !USE_ACCEPT4 || !HAVE_SOCK_NONBLOCK */
-#if ! defined(USE_ACCEPT4) || ! defined(SOCK_CLOEXEC)
- if (! MHD_socket_noninheritable_ (s))
+
+ if (! sk_cloexec && ! MHD_socket_noninheritable_ (s))
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
- _ (
- "Failed to set noninheritable mode on incoming connection socket.\n"));
-#endif
+ _ ("Failed to set noninheritable mode on incoming connection " \
+ "socket.\n"));
+#else /* ! HAVE_MESSAGES */
+ (void) 0; /* Mute compiler warning */
+#endif /* ! HAVE_MESSAGES */
}
-#endif /* !USE_ACCEPT4 || !SOCK_CLOEXEC */
+
#if defined(MHD_socket_nosignal_)
if (! sk_spipe_supprs && ! MHD_socket_nosignal_ (s))
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
- _ (
- "Failed to suppress SIGPIPE on incoming connection socket: %s\n"),
+ _ ("Failed to suppress SIGPIPE on incoming connection " \
+ "socket: %s\n"),
MHD_socket_last_strerr_ ());
#else /* ! HAVE_MESSAGES */
(void) 0; /* Mute compiler warning */
@@ -3727,7 +3812,8 @@
EDLL_remove (daemon->eready_head,
daemon->eready_tail,
pos);
- pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
+ pos->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_IN_EREADY_EDLL);
}
if ( (-1 != daemon->epoll_fd) &&
(0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) )
@@ -3742,8 +3828,11 @@
EPOLL_CTL_DEL,
pos->socket_fd,
NULL))
- MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
- pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET;
+ MHD_PANIC (_ (
+ "Failed to remove FD from epoll set.\n"));
+ pos->epoll_state &=
+ ~((enum MHD_EpollState)
+ MHD_EPOLL_STATE_IN_EPOLL_SET);
}
}
#endif
@@ -3772,32 +3861,43 @@
/**
* Obtain timeout value for polling function for this daemon.
- * This function set value to amount of milliseconds for which polling
- * function (`select()` or `poll()`) should at most block, not the
+ *
+ * This function set value to the amount of milliseconds for which polling
+ * function (`select()`, `poll()` or epoll) should at most block, not the
* timeout value set for connections.
- * It is important to always use this function, even if connection
- * timeout is not set as in some cases MHD may already have more
- * data to process on next turn (data pending in TLS buffers,
- * connections are already ready with epoll etc.) and returned timeout
- * will be zero.
+ *
+ * Any "external" sockets polling function must be called with the timeout
+ * value provided by this function. Smaller timeout values can be used for
+ * polling function if it is required for any reason, but using larger
+ * timeout value or no timeout (indefinite timeout) when this function
+ * return #MHD_YES will break MHD processing logic and result in "hung"
+ * connections with data pending in network buffers and other problems.
+ *
+ * It is important to always use this function when "external" polling is
+ * used. If this function returns #MHD_YES then #MHD_run() (or
+ * #MHD_run_from_select()) must be called right after return from polling
+ * function, regardless of the states of MHD fds.
+ *
+ * In practice, if #MHD_YES is returned then #MHD_run() (or
+ * #MHD_run_from_select()) must be called not later than @a timeout
+ * millisecond even if not activity is detected on sockets by
+ * sockets polling function.
* @remark To be called only from thread that process
* daemon's select()/poll()/etc.
*
* @param daemon daemon to query for timeout
* @param timeout set to the timeout (in milliseconds)
* @return #MHD_YES on success, #MHD_NO if timeouts are
- * not used (or no connections exist that would
- * necessitate the use of a timeout right now).
+ * not used and no data processing is pending.
* @ingroup event
*/
enum MHD_Result
MHD_get_timeout (struct MHD_Daemon *daemon,
MHD_UNSIGNED_LONG_LONG *timeout)
{
- time_t earliest_deadline;
- time_t now;
+ uint64_t earliest_deadline;
struct MHD_Connection *pos;
- bool have_timeout;
+ struct MHD_Connection *earliest_tmot_conn; /**< the connection with earliest timeout */
#ifdef MHD_USE_THREADS
mhd_assert ( (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) || \
@@ -3832,44 +3932,43 @@
}
#endif /* EPOLL_SUPPORT */
- have_timeout = false;
- earliest_deadline = 0; /* avoid compiler warnings */
- for (pos = daemon->manual_timeout_tail; NULL != pos; pos = pos->prevX)
- {
- if (0 != pos->connection_timeout)
- {
- if ( (! have_timeout) ||
- (earliest_deadline - pos->last_activity > pos->connection_timeout) )
- earliest_deadline = pos->last_activity + pos->connection_timeout;
- have_timeout = true;
- }
- }
+ earliest_tmot_conn = NULL;
+ earliest_deadline = 0; /* mute compiler warning */
/* normal timeouts are sorted, so we only need to look at the 'tail' (oldest) */
pos = daemon->normal_timeout_tail;
if ( (NULL != pos) &&
- (0 != pos->connection_timeout) )
+ (0 != pos->connection_timeout_ms) )
{
- if ( (! have_timeout) ||
- (earliest_deadline - pos->connection_timeout > pos->last_activity) )
- earliest_deadline = pos->last_activity + pos->connection_timeout;
- have_timeout = true;
+ earliest_tmot_conn = pos;
+ earliest_deadline = pos->last_activity + pos->connection_timeout_ms;
}
- if (! have_timeout)
- return MHD_NO;
- now = MHD_monotonic_sec_counter ();
- if (earliest_deadline < now)
- *timeout = 0;
- else
+ for (pos = daemon->manual_timeout_tail; NULL != pos; pos = pos->prevX)
{
- const time_t second_left = earliest_deadline - now;
+ if (0 != pos->connection_timeout_ms)
+ {
+ if ( (NULL == earliest_tmot_conn) ||
+ (earliest_deadline - pos->last_activity >
+ pos->connection_timeout_ms) )
+ {
+ earliest_tmot_conn = pos;
+ earliest_deadline = pos->last_activity + pos->connection_timeout_ms;
+ }
+ }
+ }
- if (((unsigned long long) second_left) > ULLONG_MAX / 1000)
+ if (NULL != earliest_tmot_conn)
+ {
+ const uint64_t mssecond_left = connection_get_wait (earliest_tmot_conn);
+#if SIZEOF_UINT64_T > SIZEOF_UNSIGNED_LONG_LONG
+ if (mssecond_left > ULLONG_MAX)
*timeout = ULLONG_MAX;
else
- *timeout = 1000LLU * (unsigned long long) second_left;
+#endif /* UINT64 != ULLONG_MAX */
+ *timeout = (unsigned long long) mssecond_left;
+ return MHD_YES;
}
- return MHD_YES;
+ return MHD_NO;
}
@@ -4010,8 +4109,12 @@
* not have to call `select()` again to determine which operations are
* ready.
*
+ * If #MHD_get_timeout() returned #MHD_YES, than this function must be
+ * called right after `select()` returns regardless of detected activity
+ * on the daemon's FDs.
+ *
* This function cannot be used with daemon started with
- * MHD_USE_INTERNAL_POLLING_THREAD flag.
+ * #MHD_USE_INTERNAL_POLLING_THREAD flag.
*
* @param daemon daemon to run select loop for
* @param read_fd_set read set
@@ -4910,7 +5013,7 @@
#endif
return MHD_NO;
}
- for (i = 0; i<(unsigned int) num_events; i++)
+ for (i = 0; i < (unsigned int) num_events; i++)
{
/* First, check for the values of `ptr` that would indicate
that this event is not about a normal connection. */
@@ -5060,7 +5163,8 @@
EDLL_remove (daemon->eready_head,
daemon->eready_tail,
pos);
- pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
+ pos->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_IN_EREADY_EDLL);
}
}
}
@@ -5073,18 +5177,25 @@
/**
- * Run webserver operations (without blocking unless in client
- * callbacks). This method should be called by clients in combination
- * with #MHD_get_fdset if the client-controlled select method is used and
- * #MHD_get_timeout().
+ * Run webserver operations (without blocking unless in client callbacks).
+ *
+ * This method should be called by clients in combination with
+ * #MHD_get_fdset() (or #MHD_get_daemon_info() with MHD_DAEMON_INFO_EPOLL_FD
+ * if epoll is used) and #MHD_get_timeout() if the client-controlled
+ * connection polling method is used (i.e. daemon was started without
+ * #MHD_USE_INTERNAL_POLLING_THREAD flag).
*
* This function is a convenience method, which is useful if the
* fd_sets from #MHD_get_fdset were not directly passed to `select()`;
* with this function, MHD will internally do the appropriate `select()`
- * call itself again. While it is always safe to call #MHD_run (in
- * external select mode), you should call #MHD_run_from_select if
- * performance is important (as it saves an expensive call to
- * `select()`).
+ * call itself again. While it is acceptable to call #MHD_run (if
+ * #MHD_USE_INTERNAL_POLLING_THREAD is not set) at any moment, you should
+ * call #MHD_run_from_select() if performance is important (as it saves an
+ * expensive call to `select()`).
+ *
+ * If #MHD_get_timeout() returned #MHD_YES, than this function must be called
+ * right after polling function returns regardless of detected activity on
+ * the daemon's FDs.
*
* @param daemon daemon to run
* @return #MHD_YES on success, #MHD_NO if this
@@ -5106,25 +5217,35 @@
/**
* Run websever operation with possible blocking.
- * This function do the following: waits for any network event not more than
- * specified number of milliseconds, processes all incoming and outgoing
- * data, processes new connections, processes any timed-out connection, and
- * do other things required to run webserver.
+ *
+ * This function does the following: waits for any network event not more than
+ * specified number of milliseconds, processes all incoming and outgoing data,
+ * processes new connections, processes any timed-out connection, and does
+ * other things required to run webserver.
* Once all connections are processed, function returns.
- * This function is useful for quick and simple webserver implementation if
- * application needs to run a single thread only and does not have any other
+ *
+ * This function is useful for quick and simple (lazy) webserver implementation
+ * if application needs to run a single thread only and does not have any other
* network activity.
+ *
+ * This function calls MHD_get_timeout() internally and use returned value as
+ * maximum wait time if it less than value of @a millisec parameter.
+ *
+ * It is expected that the "external" socket polling function is not used in
+ * conjunction with this function unless the @a millisec is set to zero.
+ *
* @param daemon the daemon to run
* @param millisec the maximum time in milliseconds to wait for network and
* other events. Note: there is no guarantee that function
- * blocks for specified amount of time. The real processing
- * time can be shorter (if some data comes earlier) or
- * longer (if data processing requires more time, especially
- * in the user callbacks).
+ * blocks for the specified amount of time. The real processing
+ * time can be shorter (if some data or connection timeout
+ * comes earlier) or longer (if data processing requires more
+ * time, especially in user callbacks).
* If set to '0' then function does not block and processes
* only already available data (if any).
* If set to '-1' then function waits for events
- * indefinitely (blocks until next network activity).
+ * indefinitely (blocks until next network activity or
+ * connection timeout).
* @return #MHD_YES on success, #MHD_NO if this
* daemon was not started with the right
* options for this call or some serious
@@ -5200,7 +5321,7 @@
#endif
mhd_assert (! pos->suspended);
mhd_assert (! pos->resuming);
- if (pos->connection_timeout == daemon->connection_timeout)
+ if (pos->connection_timeout_ms == daemon->connection_timeout_ms)
XDLL_remove (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
pos);
@@ -5546,20 +5667,21 @@
case MHD_OPTION_CONNECTION_TIMEOUT:
uv = va_arg (ap,
unsigned int);
- daemon->connection_timeout = (time_t) uv;
- /* Next comparison could be always false on some platforms and whole branch will
- * be optimized out on those platforms. On others it will be compiled into real
- * check. */
- if ( ( (MHD_TYPE_IS_SIGNED_ (time_t)) &&
- (daemon->connection_timeout < 0) ) || /* Compiler may warn on some platforms, ignore warning. */
- (uv != (unsigned int) daemon->connection_timeout) )
+#if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
+ if ((UINT64_MAX / 4000 - 1) < uv)
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
- _ ("Warning: Too large timeout value, ignored.\n"));
+ _ ("The specified connection timeout (%u) is too large. " \
+ "Maximum allowed value (%" PRIu64 ") will be used " \
+ "instead.\n"),
+ uv,
+ (UINT64_MAX / 4000 - 1));
#endif
- daemon->connection_timeout = 0;
+ uv = UINT64_MAX / 4000 - 1;
}
+#endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
+ daemon->connection_timeout_ms = uv * 1000;
break;
case MHD_OPTION_NOTIFY_COMPLETED:
daemon->notify_completed = va_arg (ap,
@@ -5615,7 +5737,7 @@
#endif
daemon->worker_pool_size = 0;
}
-#if (0 == (UINT_MAX + 0)) || (UINT_MAX >= (SIZE_MAX / (64 * 1024)))
+#if SIZEOF_UNSIGNED_INT >= (SIZEOF_SIZE_T - 2)
/* Next comparison could be always false on some platforms and whole branch will
* be optimized out on these platforms. On others it will be compiled into real
* check. */
@@ -5629,7 +5751,7 @@
#endif
return MHD_NO;
}
-#endif /* (UINT_MAX >= (SIZE_MAX/(64*1024))) */
+#endif /* SIZEOF_UNSIGNED_INT >= (SIZEOF_SIZE_T - 2) */
else
{
if (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD))
@@ -5938,7 +6060,7 @@
break;
case MHD_OPTION_ARRAY:
daemon->num_opts--; /* Do not count MHD_OPTION_ARRAY */
- oa = va_arg (ap, struct MHD_OptionItem*);
+ oa = va_arg (ap, struct MHD_OptionItem *);
i = 0;
while (MHD_OPTION_END != (opt = oa[i].option))
{
@@ -6397,7 +6519,7 @@
daemon->pool_size = MHD_POOL_SIZE_DEFAULT;
daemon->pool_increment = MHD_BUF_INC_SIZE;
daemon->unescape_callback = &unescape_wrapper;
- daemon->connection_timeout = 0; /* no timeout */
+ daemon->connection_timeout_ms = 0; /* no timeout */
MHD_itc_set_invalid_ (daemon->itc);
#ifdef SOMAXCONN
daemon->listen_backlog_size = SOMAXCONN;
@@ -6422,7 +6544,7 @@
*pflags |= MHD_USE_INTERNAL_POLLING_THREAD;
}
if (0 == (*pflags & MHD_USE_INTERNAL_POLLING_THREAD))
- *pflags &= ~MHD_USE_ITC; /* useless if we are using 'external' select */
+ *pflags = (*pflags & ~((enum MHD_FLAG) MHD_USE_ITC)); /* useless if we are using 'external' select */
else
{
#ifdef HAVE_LISTEN_SHUTDOWN
@@ -6619,7 +6741,7 @@
if (0 > setsockopt (listen_fd,
SOL_SOCKET,
SO_REUSEADDR,
- (void*) &on, sizeof (on)))
+ (void *) &on, sizeof (on)))
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
@@ -6638,7 +6760,7 @@
if (0 > setsockopt (listen_fd,
SOL_SOCKET,
SO_REUSEADDR,
- (void*) &on, sizeof (on)))
+ (void *) &on, sizeof (on)))
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
@@ -6806,7 +6928,7 @@
if (0 != setsockopt (listen_fd,
IPPROTO_TCP,
TCP_FASTOPEN,
- (const void*) &daemon->fastopen_queue_size,
+ (const void *) &daemon->fastopen_queue_size,
sizeof (daemon->fastopen_queue_size)))
{
#ifdef HAVE_MESSAGES
@@ -6913,42 +7035,49 @@
}
}
#endif /* HAVE_GETSOCKNAME */
- if ( (MHD_INVALID_SOCKET != listen_fd) &&
- (! MHD_socket_nonblocking_ (listen_fd)) )
+
+ if (MHD_INVALID_SOCKET != listen_fd)
{
+ if (! MHD_socket_nonblocking_ (listen_fd))
+ {
#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _ ("Failed to set nonblocking mode on listening socket: %s\n"),
- MHD_socket_last_strerr_ ());
+ MHD_DLOG (daemon,
+ _ ("Failed to set nonblocking mode on listening socket: %s\n"),
+ MHD_socket_last_strerr_ ());
#endif
- if (0 != (*pflags & MHD_USE_EPOLL)
+ if (0 != (*pflags & MHD_USE_EPOLL)
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
- || (daemon->worker_pool_size > 0)
+ || (daemon->worker_pool_size > 0)
#endif
- )
+ )
+ {
+ /* Accept must be non-blocking. Multiple children may wake up
+ * to handle a new connection, but only one will win the race.
+ * The others must immediately return. */
+ MHD_socket_close_chk_ (listen_fd);
+ goto free_and_fail;
+ }
+ daemon->listen_nonblk = false;
+ }
+ else
+ daemon->listen_nonblk = true;
+ if ( (! MHD_SCKT_FD_FITS_FDSET_ (listen_fd,
+ NULL)) &&
+ (0 == (*pflags & (MHD_USE_POLL | MHD_USE_EPOLL)) ) )
{
- /* Accept must be non-blocking. Multiple children may wake up
- * to handle a new connection, but only one will win the race.
- * The others must immediately return. */
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ _ ("Listen socket descriptor (%d) is not " \
+ "less than FD_SETSIZE (%d).\n"),
+ (int) listen_fd,
+ (int) FD_SETSIZE);
+#endif
MHD_socket_close_chk_ (listen_fd);
goto free_and_fail;
}
}
- if ( (MHD_INVALID_SOCKET != listen_fd) &&
- (! MHD_SCKT_FD_FITS_FDSET_ (listen_fd,
- NULL)) &&
- (0 == (*pflags & (MHD_USE_POLL | MHD_USE_EPOLL)) ) )
- {
-#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _ ("Listen socket descriptor (%d) is not " \
- "less than FD_SETSIZE (%d).\n"),
- (int) listen_fd,
- (int) FD_SETSIZE);
-#endif
- MHD_socket_close_chk_ (listen_fd);
- goto free_and_fail;
- }
+ else
+ daemon->listen_nonblk = false; /* Actually listen socket does not exist */
#ifdef EPOLL_SUPPORT
if ( (0 != (*pflags & MHD_USE_EPOLL))
@@ -7054,10 +7183,18 @@
daemon) )
{
#ifdef HAVE_MESSAGES
+#ifdef EAGAIN
+ if (EAGAIN == errno)
+ MHD_DLOG (daemon,
+ _ ("Failed to create a new thread because it would have " \
+ "exceeded the system limit on the number of threads or " \
+ "no system resources available.\n"));
+ else
+#endif /* EAGAIN */
MHD_DLOG (daemon,
_ ("Failed to create listen thread: %s\n"),
MHD_strerror_ (errno));
-#endif
+#endif /* HAVE_MESSAGES */
MHD_mutex_destroy_chk_ (&daemon->new_connections_mutex);
MHD_mutex_destroy_chk_ (&daemon->cleanup_connection_mutex);
MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex);
@@ -7180,6 +7317,14 @@
d))
{
#ifdef HAVE_MESSAGES
+#ifdef EAGAIN
+ if (EAGAIN == errno)
+ MHD_DLOG (daemon,
+ _ ("Failed to create a new pool thread because it would " \
+ "have exceeded the system limit on the number of " \
+ "threads or no system resources available.\n"));
+ else
+#endif /* EAGAIN */
MHD_DLOG (daemon,
_ ("Failed to create pool thread: %s\n"),
MHD_strerror_ (errno));
@@ -7931,8 +8076,8 @@
return MHD_NO;
#endif
case MHD_FEATURE_AUTOSUPPRESS_SIGPIPE:
-#if defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
- defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED)
+#if defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE) || \
+ ! defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED)
return MHD_YES;
#else
return MHD_NO;
@@ -7968,7 +8113,7 @@
if (NULL == *ppmtx)
return ENOMEM;
- if (! MHD_mutex_init_ ((MHD_mutex_*) *ppmtx))
+ if (! MHD_mutex_init_ ((MHD_mutex_ *) *ppmtx))
{
free (*ppmtx);
*ppmtx = NULL;
@@ -7982,7 +8127,7 @@
static int
gcry_w32_mutex_destroy (void **ppmtx)
{
- int res = (MHD_mutex_destroy_ ((MHD_mutex_*) *ppmtx)) ? 0 : EINVAL;
+ int res = (MHD_mutex_destroy_ ((MHD_mutex_ *) *ppmtx)) ? 0 : EINVAL;
free (*ppmtx);
return res;
}
@@ -7991,14 +8136,14 @@
static int
gcry_w32_mutex_lock (void **ppmtx)
{
- return MHD_mutex_lock_ ((MHD_mutex_*) *ppmtx) ? 0 : EINVAL;
+ return MHD_mutex_lock_ ((MHD_mutex_ *) *ppmtx) ? 0 : EINVAL;
}
static int
gcry_w32_mutex_unlock (void **ppmtx)
{
- return MHD_mutex_unlock_ ((MHD_mutex_*) *ppmtx) ? 0 : EINVAL;
+ return MHD_mutex_unlock_ ((MHD_mutex_ *) *ppmtx) ? 0 : EINVAL;
}
@@ -8057,6 +8202,15 @@
MHD_monotonic_sec_counter_init ();
MHD_send_init_static_vars_ ();
MHD_init_mem_pools_ ();
+ /* Check whether sizes were correctly detected by configure */
+#ifdef _DEBUG
+ if (1)
+ {
+ struct timeval tv;
+ mhd_assert (sizeof(tv.tv_sec) == SIZEOF_STRUCT_TIMEVAL_TV_SEC);
+ }
+#endif /* _DEBUG */
+ mhd_assert (sizeof(uint64_t) == SIZEOF_UINT64_T);
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/digestauth.c
^
|
@@ -1212,9 +1212,10 @@
da.sessionkey = skey.sha256; \
da.init = &MHD_SHA256_init; \
da.update = &MHD_SHA256_update; \
- da.digest = &sha256_finish; \
+ da.digest = &MHD_SHA256_finish; \
break; \
default: \
+ da.digest_size = 0; \
mhd_assert (false); \
break; \
} \
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/internal.c
^
|
@@ -39,6 +39,8 @@
{
case MHD_CONNECTION_INIT:
return "connection init";
+ case MHD_CONNECTION_REQ_LINE_RECEIVING:
+ return "receiving request line";
case MHD_CONNECTION_URL_RECEIVED:
return "connection url received";
case MHD_CONNECTION_HEADER_PART_RECEIVED:
@@ -57,18 +59,22 @@
return "footer partially received";
case MHD_CONNECTION_FOOTERS_RECEIVED:
return "footers received";
+ case MHD_CONNECTION_FULL_REQ_RECEIVED:
+ return "full request received";
+ case MHD_CONNECTION_START_REPLY:
+ return "start sending reply";
case MHD_CONNECTION_HEADERS_SENDING:
return "headers sending";
case MHD_CONNECTION_HEADERS_SENT:
return "headers sent";
- case MHD_CONNECTION_NORMAL_BODY_READY:
- return "normal body ready";
case MHD_CONNECTION_NORMAL_BODY_UNREADY:
return "normal body unready";
- case MHD_CONNECTION_CHUNKED_BODY_READY:
- return "chunked body ready";
+ case MHD_CONNECTION_NORMAL_BODY_READY:
+ return "normal body ready";
case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
return "chunked body unready";
+ case MHD_CONNECTION_CHUNKED_BODY_READY:
+ return "chunked body ready";
case MHD_CONNECTION_BODY_SENT:
return "body sent";
case MHD_CONNECTION_FOOTERS_SENDING:
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/internal.h
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -19,9 +20,10 @@
/**
* @file microhttpd/internal.h
- * @brief internal shared structures
+ * @brief MHD internal shared structures
* @author Daniel Pittman
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#ifndef INTERNAL_H
@@ -43,6 +45,13 @@
#include <stdbool.h>
#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif /* HAVE_INTTYPES_H */
+
+#ifndef PRIu64
+#define PRIu64 "llu"
+#endif /* ! PRIu64 */
#ifdef MHD_PANIC
/* Override any defined MHD_PANIC macro with proper one */
@@ -149,9 +158,9 @@
*/
extern void *mhd_panic_cls;
-/* If we have Clang or gcc >= 4.5, use __buildin_unreachable() */
-#if defined(__clang__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= \
- 5)
+/* If we have Clang or gcc >= 4.5, use __builtin_unreachable() */
+#if defined(__clang__) || (__GNUC__ > 4) || \
+ (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
#define BUILTIN_NOT_REACHED __builtin_unreachable ()
#elif defined(_MSC_FULL_VER)
#define BUILTIN_NOT_REACHED __assume (0)
@@ -177,7 +186,7 @@
_MHD_NO = false, /**< State is "off" / "disabled" */
_MHD_ON = true, /**< State is "on" / "enabled" */
_MHD_YES = true /**< State is "on" / "enabled" */
-};
+} _MHD_FIXED_ENUM;
/**
@@ -223,7 +232,7 @@
* Is this connection in some error state?
*/
MHD_EPOLL_STATE_ERROR = 128
-};
+} _MHD_FIXED_FLAGS_ENUM;
/**
@@ -250,7 +259,7 @@
* We are finished and are awaiting cleanup.
*/
MHD_EVENT_LOOP_INFO_CLEANUP = 3
-};
+} _MHD_FIXED_ENUM;
/**
@@ -313,17 +322,22 @@
struct MHD_HTTP_Header
{
/**
- * Headers are kept in a linked list.
+ * Headers are kept in a double-linked list.
*/
struct MHD_HTTP_Header *next;
/**
+ * Headers are kept in a double-linked list.
+ */
+ struct MHD_HTTP_Header *prev;
+
+ /**
* The name of the header (key), without the colon.
*/
char *header;
/**
- * Number of bytes in @a header.
+ * The length of the @a header, not including the final zero termination.
*/
size_t header_size;
@@ -333,7 +347,7 @@
char *value;
/**
- * Number of bytes in @a value.
+ * The length of the @a value, not including the final zero termination.
*/
size_t value_size;
@@ -346,6 +360,19 @@
};
+/**
+ * Automatically assigned flags
+ */
+enum MHD_ResponseAutoFlags
+{
+ MHD_RAF_NO_FLAGS = 0, /**< No auto flags */
+ MHD_RAF_HAS_CONNECTION_HDR = 1 << 0, /**< Has "Connection" header */
+ MHD_RAF_HAS_CONNECTION_CLOSE = 1 << 1, /**< Has "Connection: close" */
+ MHD_RAF_HAS_TRANS_ENC_CHUNKED = 1 << 2, /**< Has "Transfer-Encoding: chunked */
+ MHD_RAF_HAS_DATE_HDR = 1 << 3 /**< Has "Date" header */
+} _MHD_FIXED_FLAGS_ENUM;
+
+
#if defined(MHD_WINSOCK_SOCKETS)
/**
* Internally used I/O vector type for use with winsock.
@@ -407,13 +434,16 @@
{
/**
- * Headers to send for the response. Initially
- * the linked list is created in inverse order;
- * the order should be inverted before sending!
+ * Head of double-linked list of headers to send for the response.
*/
struct MHD_HTTP_Header *first_header;
/**
+ * Tail of double-linked list of headers to send for the response.
+ */
+ struct MHD_HTTP_Header *last_header;
+
+ /**
* Buffer pointing to data that we are supposed
* to send as a response.
*/
@@ -460,6 +490,7 @@
#endif
/**
+ * The size of the response body.
* Set to #MHD_SIZE_UNKNOWN if size is not known.
*/
uint64_t total_size;
@@ -503,6 +534,11 @@
enum MHD_ResponseFlags flags;
/**
+ * Automatic flags set for the MHD response.
+ */
+ enum MHD_ResponseAutoFlags flags_auto;
+
+ /**
* If the @e fd is a pipe (no sendfile()).
*/
bool is_pipe;
@@ -543,101 +579,119 @@
MHD_CONNECTION_INIT = 0,
/**
- * 1: We got the URL (and request type and version). Wait for a header line.
+ * Part of the request line was received.
+ * Wait for complete line.
*/
- MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1,
+ MHD_CONNECTION_REQ_LINE_RECEIVING = MHD_CONNECTION_INIT + 1,
/**
- * 2: We got part of a multi-line request header. Wait for the rest.
+ * We got the URL (and request type and version). Wait for a header line.
+ */
+ MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_REQ_LINE_RECEIVING + 1,
+
+ /**
+ * We got part of a multi-line request header. Wait for the rest.
*/
MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1,
/**
- * 3: We got the request headers. Process them.
+ * We got the request headers. Process them.
*/
MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1,
/**
- * 4: We have processed the request headers. Send 100 continue.
+ * We have processed the request headers. Send 100 continue.
*/
MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1,
/**
- * 5: We have processed the headers and need to send 100 CONTINUE.
+ * We have processed the headers and need to send 100 CONTINUE.
*/
MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1,
/**
- * 6: We have sent 100 CONTINUE (or do not need to). Read the message body.
+ * We have sent 100 CONTINUE (or do not need to). Read the message body.
*/
MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1,
/**
- * 7: We got the request body. Wait for a line of the footer.
+ * We got the request body. Wait for a line of the footer.
*/
MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1,
/**
- * 8: We got part of a line of the footer. Wait for the
+ * We got part of a line of the footer. Wait for the
* rest.
*/
MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1,
/**
- * 9: We received the entire footer. Wait for a response to be queued
- * and prepare the response headers.
+ * We received the entire footer.
*/
MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1,
/**
- * 10: We have prepared the response headers in the writ buffer.
+ * We received the entire request.
+ * Wait for a response to be queued.
+ */
+ MHD_CONNECTION_FULL_REQ_RECEIVED = MHD_CONNECTION_FOOTERS_RECEIVED + 1,
+
+ /**
+ * Finished reading of the request and the response is ready.
+ * Switch internal logic from receiving to sending, prepare connection
+ * sending the reply and build the reply header.
+ */
+ MHD_CONNECTION_START_REPLY = MHD_CONNECTION_FULL_REQ_RECEIVED + 1,
+
+ /**
+ * We have prepared the response headers in the write buffer.
* Send the response headers.
*/
- MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1,
+ MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_START_REPLY + 1,
/**
- * 11: We have sent the response headers. Get ready to send the body.
+ * We have sent the response headers. Get ready to send the body.
*/
MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1,
/**
- * 12: We are ready to send a part of a non-chunked body. Send it.
+ * We are waiting for the client to provide more
+ * data of a non-chunked body.
*/
- MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1,
+ MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_HEADERS_SENT + 1,
/**
- * 13: We are waiting for the client to provide more
- * data of a non-chunked body.
+ * We are ready to send a part of a non-chunked body. Send it.
*/
- MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1,
+ MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1,
/**
- * 14: We are ready to send a chunk.
+ * We are waiting for the client to provide a chunk of the body.
*/
- MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1,
+ MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1,
/**
- * 15: We are waiting for the client to provide a chunk of the body.
+ * We are ready to send a chunk.
*/
- MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1,
+ MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1,
/**
- * 16: We have sent the response body. Prepare the footers.
+ * We have sent the response body. Prepare the footers.
*/
- MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1,
+ MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_READY + 1,
/**
- * 17: We have prepared the response footer. Send it.
+ * We have prepared the response footer. Send it.
*/
MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1,
/**
- * 18: We have sent the response footer. Shutdown or restart.
+ * We have sent the response footer. Shutdown or restart.
*/
MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1,
/**
- * 19: This connection is to be closed.
+ * This connection is to be closed.
*/
MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1,
@@ -649,7 +703,7 @@
MHD_CONNECTION_UPGRADE
#endif /* UPGRADE_SUPPORT */
-};
+} _MHD_FIXED_ENUM;
/**
@@ -667,7 +721,7 @@
MHD_TLS_CONN_TLS_CLOSED, /**< TLS session is terminated. */
MHD_TLS_CONN_TLS_FAILED, /**< TLS session failed. */
MHD_TLS_CONN_INVALID_STATE/**< Sentinel. Not a valid value. */
-};
+} _MHD_FIXED_ENUM;
/**
* Should all state transitions be printed to stderr?
@@ -729,9 +783,127 @@
/**
* Connection can be used for serving next request
*/
- MHD_CONN_USE_KEEPALIVE = 1
-};
+ MHD_CONN_USE_KEEPALIVE = 1,
+
+ /**
+ * Connection will be upgraded
+ */
+ MHD_CONN_MUST_UPGRADE = 2
+} _MHD_FIXED_ENUM;
+
+enum MHD_HTTP_Version
+{
+ /**
+ * Not a HTTP protocol or HTTP version is invalid.
+ */
+ MHD_HTTP_VER_INVALID = -1,
+
+ /**
+ * HTTP version is not yet received from the client.
+ */
+ MHD_HTTP_VER_UNKNOWN = 0,
+
+ /**
+ * HTTP version before 1.0, unsupported.
+ */
+ MHD_HTTP_VER_TOO_OLD = 1,
+
+ /**
+ * HTTP version 1.0
+ */
+ MHD_HTTP_VER_1_0 = 2,
+
+ /**
+ * HTTP version 1.1
+ */
+ MHD_HTTP_VER_1_1 = 3,
+
+ /**
+ * HTTP version 1.2-1.9, must be used as 1.1
+ */
+ MHD_HTTP_VER_1_2__1_9 = 4,
+
+ /**
+ * HTTP future version. Unsupported.
+ */
+ MHD_HTTP_VER_FUTURE = 100
+} _MHD_FIXED_ENUM;
+
+/**
+ * Returns boolean 'true' if HTTP version is supported by MHD
+ */
+#define MHD_IS_HTTP_VER_SUPPORTED(ver) (MHD_HTTP_VER_1_0 <= (ver) && \
+ MHD_HTTP_VER_1_2__1_9 >= (ver))
+/**
+ * Protocol should be used as HTTP/1.1 protocol.
+ *
+ * See the last paragraph of
+ * https://datatracker.ietf.org/doc/html/rfc7230#section-2.6
+ */
+#define MHD_IS_HTTP_VER_1_1_COMPAT(ver) (MHD_HTTP_VER_1_1 == (ver) || \
+ MHD_HTTP_VER_1_2__1_9 == (ver))
+
+/**
+ * The HTTP method.
+ *
+ * Only primary methods (specified in RFC7231) are defined here.
+ */
+enum MHD_HTTP_Method
+{
+ /**
+ * No request string has been received yet
+ */
+ MHD_HTTP_MTHD_NO_METHOD = 0,
+ /**
+ * HTTP method GET
+ */
+ MHD_HTTP_MTHD_GET = 1,
+ /**
+ * HTTP method HEAD
+ */
+ MHD_HTTP_MTHD_HEAD = 2,
+ /**
+ * HTTP method POST
+ */
+ MHD_HTTP_MTHD_POST = 3,
+ /**
+ * HTTP method PUT
+ */
+ MHD_HTTP_MTHD_PUT = 4,
+ /**
+ * HTTP method DELETE
+ */
+ MHD_HTTP_MTHD_DELETE = 5,
+ /**
+ * HTTP method CONNECT
+ */
+ MHD_HTTP_MTHD_CONNECT = 6,
+ /**
+ * HTTP method OPTIONS
+ */
+ MHD_HTTP_MTHD_OPTIONS = 7,
+ /**
+ * HTTP method TRACE
+ */
+ MHD_HTTP_MTHD_TRACE = 8,
+ /**
+ * Other HTTP method. Check the string value.
+ */
+ MHD_HTTP_MTHD_OTHER = 1000
+} _MHD_FIXED_ENUM;
+
+
+/**
+ * Reply-specific properties.
+ */
+struct MHD_Reply_Properties
+{
+ bool set; /**< Indicates that other members are set and valid */
+ bool use_reply_body_headers; /**< Use reply body-specific headers */
+ bool send_reply_body; /**< Send reply body (can be zero-sized) */
+ bool chunked; /**< Use chunked encoding for reply */
+};
/**
* State kept for each HTTP request.
@@ -828,6 +1000,11 @@
char *method;
/**
+ * The request method as enum.
+ */
+ enum MHD_HTTP_Method http_mthd;
+
+ /**
* Requested URL (everything after "GET" only). Allocated
* in pool.
*/
@@ -840,6 +1017,11 @@
char *version;
/**
+ * HTTP protocol version as enum.
+ */
+ enum MHD_HTTP_Version http_ver;
+
+ /**
* Close connection after sending response?
* Functions may change value from "Unknown" or "KeepAlive" to "Must close",
* but no functions reset value "Must Close" to any other value.
@@ -891,16 +1073,15 @@
#endif
/**
- * Size of @e read_buffer (in bytes). This value indicates
- * how many bytes we're willing to read into the buffer;
- * the real buffer is one byte longer to allow for
- * adding zero-termination (when needed).
+ * Size of @e read_buffer (in bytes).
+ * This value indicates how many bytes we're willing to read
+ * into the buffer.
*/
size_t read_buffer_size;
/**
- * Position where we currently append data in
- * @e read_buffer (last valid position).
+ * Position where we currently append data in @e read_buffer (the
+ * next char after the last valid position).
*/
size_t read_buffer_offset;
@@ -971,13 +1152,14 @@
* Last time this connection had any activity
* (reading or writing).
*/
- time_t last_activity;
+ uint64_t last_activity;
/**
- * After how many seconds of inactivity should
- * this connection time out? Zero for no timeout.
+ * After how many milliseconds of inactivity should
+ * this connection time out?
+ * Zero for no timeout.
*/
- time_t connection_timeout;
+ uint64_t connection_timeout_ms;
/**
* Special member to be returned by #MHD_get_connection_info()
@@ -1032,6 +1214,24 @@
*/
bool read_closed;
+ /**
+ * Some error happens during processing the connection therefore this
+ * connection must be closed.
+ * The error may come from the client side (like wrong request format),
+ * from the application side (like data callback returned error), or from
+ * the OS side (like out-of-memory).
+ */
+ bool stop_with_error;
+
+ /**
+ * Response queued early, before the request is fully processed,
+ * the client upload is rejected.
+ * The connection cannot be reused for additional requests as the current
+ * request is incompletely read and it is unclear where is the initial
+ * byte of the next request.
+ */
+ bool discard_request;
+
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
/**
* Set to `true` if the thread has been joined.
@@ -1046,8 +1246,7 @@
bool in_idle;
/**
- * Are we currently inside the "idle" handler (to avoid recursively
- * invoking it).
+ * Connection is in the cleanup DL-linked list.
*/
bool in_cleanup;
@@ -1075,19 +1274,28 @@
unsigned int responseCode;
/**
- * Are we receiving with chunked encoding? This will be set to
- * #MHD_YES after we parse the headers and are processing the body
- * with chunks. After we are done with the body and we are
- * processing the footers; once the footers are also done, this will
- * be set to #MHD_NO again (before the final call to the handler).
+ * Reply-specific properties
+ */
+ struct MHD_Reply_Properties rp_props;
+
+ /**
+ * Are we receiving with chunked encoding?
+ * This will be set to #MHD_YES after we parse the headers and
+ * are processing the body with chunks.
+ * After we are done with the body and we are processing the footers;
+ * once the footers are also done, this will be set to #MHD_NO again
+ * (before the final call to the handler).
+ * It is used only for requests, chunked encoding for response is
+ * indicated by @a rp_props.
*/
bool have_chunked_upload;
/**
* If we are receiving with chunked encoding, where are we right
- * now? Set to 0 if we are waiting to receive the chunk size;
- * otherwise, this is the size of the current chunk. A value of
- * zero is also used when we're at the end of the chunks.
+ * now?
+ * Set to 0 if we are waiting to receive the chunk size;
+ * otherwise, this is the size of the current chunk.
+ * A value of zero is also used when we're at the end of the chunks.
*/
uint64_t current_chunk_size;
@@ -1516,7 +1724,7 @@
* moved back to the tail of the list.
*
* All connections by default start in this list; if a custom
- * timeout that does not match @e connection_timeout is set, they
+ * timeout that does not match @e connection_timeout_ms is set, they
* are moved to the @e manual_timeout_head-XDLL.
* Not used in MHD_USE_THREAD_PER_CONNECTION mode as each thread
* needs only one connection-specific timeout.
@@ -1636,6 +1844,11 @@
*/
MHD_socket listen_fd;
+ /**
+ * Listen socket is non-blocking.
+ */
+ bool listen_nonblk;
+
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
/**
* Worker daemons (one per thread)
@@ -1772,10 +1985,11 @@
unsigned int connection_limit;
/**
- * After how many seconds of inactivity should
- * connections time out? Zero for no timeout.
+ * After how many milliseconds of inactivity should
+ * this connection time out?
+ * Zero for no timeout.
*/
- time_t connection_timeout;
+ uint64_t connection_timeout_ms;
/**
* Maximum number of connections per IP, or 0 for
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/md5.c
^
|
@@ -26,6 +26,12 @@
#include "mhd_bithelpers.h"
#include "mhd_assert.h"
+/**
+ * Number of bytes in single MD5 word
+ * used to process data
+ */
+#define MD5_BYTES_IN_WORD (32 / 8)
+
/**
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
@@ -40,10 +46,10 @@
mhd_assert (ctx != NULL);
ctx->count = 0;
- ctx->state[0] = 0x67452301;
- ctx->state[1] = 0xefcdab89;
- ctx->state[2] = 0x98badcfe;
- ctx->state[3] = 0x10325476;
+ ctx->state[0] = UINT32_C (0x67452301);
+ ctx->state[1] = UINT32_C (0xefcdab89);
+ ctx->state[2] = UINT32_C (0x98badcfe);
+ ctx->state[3] = UINT32_C (0x10325476);
}
@@ -87,26 +93,37 @@
/* Put number of bits */
count_bits = ctx->count << 3;
- _MHD_PUT_64BIT_LE (ctx->buffer + 56, count_bits);
+ _MHD_PUT_64BIT_LE_SAFE (ctx->buffer + 56, count_bits);
MD5Transform (ctx->state, ctx->buffer);
/* Put digest in LE mode */
- _MHD_PUT_32BIT_LE (digest, ctx->state[0]);
- _MHD_PUT_32BIT_LE (digest + 4, ctx->state[1]);
- _MHD_PUT_32BIT_LE (digest + 8, ctx->state[2]);
- _MHD_PUT_32BIT_LE (digest + 12, ctx->state[3]);
+#ifndef _MHD_PUT_32BIT_LE_UNALIGNED
+ if (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN)
+ {
+ uint32_t alig_dgst[MD5_DIGEST_SIZE / MD5_BYTES_IN_WORD];
+ _MHD_PUT_32BIT_LE (alig_dgst + 0, ctx->state[0]);
+ _MHD_PUT_32BIT_LE (alig_dgst + 1, ctx->state[1]);
+ _MHD_PUT_32BIT_LE (alig_dgst + 2, ctx->state[2]);
+ _MHD_PUT_32BIT_LE (alig_dgst + 3, ctx->state[3]);
+ /* Copy result to unaligned destination address */
+ memcpy (digest, alig_dgst, MD5_DIGEST_SIZE);
+ }
+ else
+#else /* _MHD_PUT_32BIT_LE_UNALIGNED */
+ if (1)
+#endif /* _MHD_PUT_32BIT_LE_UNALIGNED */
+ {
+ _MHD_PUT_32BIT_LE (digest, ctx->state[0]);
+ _MHD_PUT_32BIT_LE (digest + 4, ctx->state[1]);
+ _MHD_PUT_32BIT_LE (digest + 8, ctx->state[2]);
+ _MHD_PUT_32BIT_LE (digest + 12, ctx->state[3]);
+ }
/* Erase buffer */
memset (ctx, 0, sizeof(*ctx));
}
-/**
- * Number of bytes in single SHA-256 word
- * used to process data
- */
-#define MD5_BYTES_IN_WORD (32 / 8)
-
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
@@ -117,7 +134,7 @@
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
- (w += f (x, y, z) + data, w = w << s | w >> (32 - s), w += x)
+ (w += f (x, y, z) + data, w = _MHD_ROTL32(w, s), w += x)
/**
* The core of the MD5 algorithm, this alters an existing MD5 hash to
@@ -129,91 +146,113 @@
const uint8_t block[MD5_BLOCK_SIZE])
{
uint32_t a, b, c, d;
+ uint32_t data_buf[MD5_BLOCK_SIZE / MD5_BYTES_IN_WORD];
+ const uint32_t *in;
-#if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
- const uint32_t *in = (const uint32_t *) block;
-#else
- uint32_t in[MD5_BLOCK_SIZE / MD5_BYTES_IN_WORD];
- int i;
-
- for (i = 0; i < MD5_BLOCK_SIZE / MD5_BYTES_IN_WORD; i++)
+#if (_MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN) || \
+ ! defined (_MHD_GET_32BIT_LE_UNALIGNED)
+ if (0 != (((uintptr_t) block) % _MHD_UINT32_ALIGN))
{
- in[i] = _MHD_GET_32BIT_LE (block + i * MD5_BYTES_IN_WORD);
+ /* Copy data to the aligned buffer */
+ memcpy (data_buf, block, MD5_BLOCK_SIZE);
+ in = data_buf;
}
-#endif
+ else
+ in = (const uint32_t *) block;
+#endif /* _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN) || \
+ ! _MHD_GET_32BIT_LE_UNALIGNED */
+#if _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN
+ data_buf[0] = _MHD_GET_32BIT_LE (in + 0);
+ data_buf[1] = _MHD_GET_32BIT_LE (in + 1);
+ data_buf[2] = _MHD_GET_32BIT_LE (in + 2);
+ data_buf[3] = _MHD_GET_32BIT_LE (in + 3);
+ data_buf[4] = _MHD_GET_32BIT_LE (in + 4);
+ data_buf[5] = _MHD_GET_32BIT_LE (in + 5);
+ data_buf[6] = _MHD_GET_32BIT_LE (in + 6);
+ data_buf[7] = _MHD_GET_32BIT_LE (in + 7);
+ data_buf[8] = _MHD_GET_32BIT_LE (in + 8);
+ data_buf[9] = _MHD_GET_32BIT_LE (in + 9);
+ data_buf[10] = _MHD_GET_32BIT_LE (in + 10);
+ data_buf[11] = _MHD_GET_32BIT_LE (in + 11);
+ data_buf[12] = _MHD_GET_32BIT_LE (in + 12);
+ data_buf[13] = _MHD_GET_32BIT_LE (in + 13);
+ data_buf[14] = _MHD_GET_32BIT_LE (in + 14);
+ data_buf[15] = _MHD_GET_32BIT_LE (in + 15);
+ in = data_buf;
+#endif /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
- MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7);
- MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
- MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17);
- MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
- MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
- MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12);
- MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17);
- MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22);
- MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7);
- MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
- MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
- MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22);
- MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7);
- MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12);
- MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17);
- MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
- MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5);
- MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9);
- MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14);
- MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
- MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5);
- MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9);
- MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
- MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
- MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
- MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9);
- MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
- MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20);
- MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
- MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
- MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14);
- MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
- MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4);
- MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11);
- MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
- MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23);
- MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4);
- MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
- MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
- MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
- MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
- MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
- MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
- MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23);
- MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
- MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
- MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
- MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
- MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6);
- MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10);
- MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15);
- MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21);
- MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6);
- MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
- MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15);
- MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21);
- MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
- MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
- MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15);
- MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
- MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6);
- MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10);
- MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
- MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+ MD5STEP (F1, a, b, c, d, in[0] + UINT32_C (0xd76aa478), 7);
+ MD5STEP (F1, d, a, b, c, in[1] + UINT32_C (0xe8c7b756), 12);
+ MD5STEP (F1, c, d, a, b, in[2] + UINT32_C (0x242070db), 17);
+ MD5STEP (F1, b, c, d, a, in[3] + UINT32_C (0xc1bdceee), 22);
+ MD5STEP (F1, a, b, c, d, in[4] + UINT32_C (0xf57c0faf), 7);
+ MD5STEP (F1, d, a, b, c, in[5] + UINT32_C (0x4787c62a), 12);
+ MD5STEP (F1, c, d, a, b, in[6] + UINT32_C (0xa8304613), 17);
+ MD5STEP (F1, b, c, d, a, in[7] + UINT32_C (0xfd469501), 22);
+ MD5STEP (F1, a, b, c, d, in[8] + UINT32_C (0x698098d8), 7);
+ MD5STEP (F1, d, a, b, c, in[9] + UINT32_C (0x8b44f7af), 12);
+ MD5STEP (F1, c, d, a, b, in[10] + UINT32_C (0xffff5bb1), 17);
+ MD5STEP (F1, b, c, d, a, in[11] + UINT32_C (0x895cd7be), 22);
+ MD5STEP (F1, a, b, c, d, in[12] + UINT32_C (0x6b901122), 7);
+ MD5STEP (F1, d, a, b, c, in[13] + UINT32_C (0xfd987193), 12);
+ MD5STEP (F1, c, d, a, b, in[14] + UINT32_C (0xa679438e), 17);
+ MD5STEP (F1, b, c, d, a, in[15] + UINT32_C (0x49b40821), 22);
+
+ MD5STEP (F2, a, b, c, d, in[1] + UINT32_C (0xf61e2562), 5);
+ MD5STEP (F2, d, a, b, c, in[6] + UINT32_C (0xc040b340), 9);
+ MD5STEP (F2, c, d, a, b, in[11] + UINT32_C (0x265e5a51), 14);
+ MD5STEP (F2, b, c, d, a, in[0] + UINT32_C (0xe9b6c7aa), 20);
+ MD5STEP (F2, a, b, c, d, in[5] + UINT32_C (0xd62f105d), 5);
+ MD5STEP (F2, d, a, b, c, in[10] + UINT32_C (0x02441453), 9);
+ MD5STEP (F2, c, d, a, b, in[15] + UINT32_C (0xd8a1e681), 14);
+ MD5STEP (F2, b, c, d, a, in[4] + UINT32_C (0xe7d3fbc8), 20);
+ MD5STEP (F2, a, b, c, d, in[9] + UINT32_C (0x21e1cde6), 5);
+ MD5STEP (F2, d, a, b, c, in[14] + UINT32_C (0xc33707d6), 9);
+ MD5STEP (F2, c, d, a, b, in[3] + UINT32_C (0xf4d50d87), 14);
+ MD5STEP (F2, b, c, d, a, in[8] + UINT32_C (0x455a14ed), 20);
+ MD5STEP (F2, a, b, c, d, in[13] + UINT32_C (0xa9e3e905), 5);
+ MD5STEP (F2, d, a, b, c, in[2] + UINT32_C (0xfcefa3f8), 9);
+ MD5STEP (F2, c, d, a, b, in[7] + UINT32_C (0x676f02d9), 14);
+ MD5STEP (F2, b, c, d, a, in[12] + UINT32_C (0x8d2a4c8a), 20);
+
+ MD5STEP (F3, a, b, c, d, in[5] + UINT32_C (0xfffa3942), 4);
+ MD5STEP (F3, d, a, b, c, in[8] + UINT32_C (0x8771f681), 11);
+ MD5STEP (F3, c, d, a, b, in[11] + UINT32_C (0x6d9d6122), 16);
+ MD5STEP (F3, b, c, d, a, in[14] + UINT32_C (0xfde5380c), 23);
+ MD5STEP (F3, a, b, c, d, in[1] + UINT32_C (0xa4beea44), 4);
+ MD5STEP (F3, d, a, b, c, in[4] + UINT32_C (0x4bdecfa9), 11);
+ MD5STEP (F3, c, d, a, b, in[7] + UINT32_C (0xf6bb4b60), 16);
+ MD5STEP (F3, b, c, d, a, in[10] + UINT32_C (0xbebfbc70), 23);
+ MD5STEP (F3, a, b, c, d, in[13] + UINT32_C (0x289b7ec6), 4);
+ MD5STEP (F3, d, a, b, c, in[0] + UINT32_C (0xeaa127fa), 11);
+ MD5STEP (F3, c, d, a, b, in[3] + UINT32_C (0xd4ef3085), 16);
+ MD5STEP (F3, b, c, d, a, in[6] + UINT32_C (0x04881d05), 23);
+ MD5STEP (F3, a, b, c, d, in[9] + UINT32_C (0xd9d4d039), 4);
+ MD5STEP (F3, d, a, b, c, in[12] + UINT32_C (0xe6db99e5), 11);
+ MD5STEP (F3, c, d, a, b, in[15] + UINT32_C (0x1fa27cf8), 16);
+ MD5STEP (F3, b, c, d, a, in[2] + UINT32_C (0xc4ac5665), 23);
+
+ MD5STEP (F4, a, b, c, d, in[0] + UINT32_C (0xf4292244), 6);
+ MD5STEP (F4, d, a, b, c, in[7] + UINT32_C (0x432aff97), 10);
+ MD5STEP (F4, c, d, a, b, in[14] + UINT32_C (0xab9423a7), 15);
+ MD5STEP (F4, b, c, d, a, in[5] + UINT32_C (0xfc93a039), 21);
+ MD5STEP (F4, a, b, c, d, in[12] + UINT32_C (0x655b59c3), 6);
+ MD5STEP (F4, d, a, b, c, in[3] + UINT32_C (0x8f0ccc92), 10);
+ MD5STEP (F4, c, d, a, b, in[10] + UINT32_C (0xffeff47d), 15);
+ MD5STEP (F4, b, c, d, a, in[1] + UINT32_C (0x85845dd1), 21);
+ MD5STEP (F4, a, b, c, d, in[8] + UINT32_C (0x6fa87e4f), 6);
+ MD5STEP (F4, d, a, b, c, in[15] + UINT32_C (0xfe2ce6e0), 10);
+ MD5STEP (F4, c, d, a, b, in[6] + UINT32_C (0xa3014314), 15);
+ MD5STEP (F4, b, c, d, a, in[13] + UINT32_C (0x4e0811a1), 21);
+ MD5STEP (F4, a, b, c, d, in[4] + UINT32_C (0xf7537e82), 6);
+ MD5STEP (F4, d, a, b, c, in[11] + UINT32_C (0xbd3af235), 10);
+ MD5STEP (F4, c, d, a, b, in[2] + UINT32_C (0x2ad7d2bb), 15);
+ MD5STEP (F4, b, c, d, a, in[9] + UINT32_C (0xeb86d391), 21);
state[0] += a;
state[1] += b;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/md5.h
^
|
@@ -20,7 +20,9 @@
#include "mhd_options.h"
#include <stdint.h>
+#ifdef HAVE_STDDEF_H
#include <stddef.h>
+#endif /* HAVE_STDDEF_H */
#define MD5_BLOCK_SIZE 64
#define MD5_DIGEST_SIZE 16
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/memorypool.c
^
|
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2007--2019 Daniel Pittman, Christian Grothoff and
+ Copyright (C) 2007--2021 Daniel Pittman, Christian Grothoff, and
Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
@@ -25,7 +25,9 @@
* @author Karlson2k (Evgeny Grin)
*/
#include "memorypool.h"
+#ifdef HAVE_STDLIB_H
#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
#include <string.h>
#include <stdint.h>
#include "mhd_assert.h"
@@ -43,6 +45,34 @@
#define MHD_SC_PAGESIZE _SC_PAGESIZE
#endif /* _SC_PAGESIZE */
#endif /* HAVE_SYSCONF */
+#include "mhd_limits.h" /* for SIZE_MAX, PAGESIZE / PAGE_SIZE */
+
+#if defined(MHD_USE_PAGESIZE_MACRO) || defined (MHD_USE_PAGE_SIZE_MACRO)
+#ifndef HAVE_SYSCONF /* Avoid duplicate include */
+#include <unistd.h>
+#endif /* HAVE_SYSCONF */
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+#endif /* MHD_USE_PAGESIZE_MACRO || MHD_USE_PAGE_SIZE_MACRO */
+
+/**
+ * Fallback value of page size
+ */
+#define _MHD_FALLBACK_PAGE_SIZE (4096)
+
+#if defined(MHD_USE_PAGESIZE_MACRO)
+#define MHD_DEF_PAGE_SIZE_ PAGESIZE
+#elif defined(MHD_USE_PAGE_SIZE_MACRO)
+#define MHD_DEF_PAGE_SIZE_ PAGE_SIZE
+#else /* ! PAGESIZE */
+#define MHD_DEF_PAGE_SIZE_ _MHD_FALLBACK_PAGE_SIZE
+#endif /* ! PAGESIZE */
+
+
+#ifdef MHD_ASAN_POISON_ACTIVE
+#include <sanitizer/asan_interface.h>
+#endif /* MHD_ASAN_POISON_ACTIVE */
/* define MAP_ANONYMOUS for Mac OS X */
#if defined(MAP_ANON) && ! defined(MAP_ANONYMOUS)
@@ -65,18 +95,39 @@
#define ROUND_TO_ALIGN(n) (((n) + (ALIGN_SIZE - 1)) \
/ (ALIGN_SIZE) *(ALIGN_SIZE))
-#if defined(PAGE_SIZE) && (0 < (PAGE_SIZE + 0))
-#define MHD_DEF_PAGE_SIZE_ PAGE_SIZE
-#elif defined(PAGESIZE) && (0 < (PAGESIZE + 0))
-#define MHD_DEF_PAGE_SIZE_ PAGESIZE
-#else /* ! PAGESIZE */
-#define MHD_DEF_PAGE_SIZE_ (4096)
-#endif /* ! PAGESIZE */
+
+#ifndef MHD_ASAN_POISON_ACTIVE
+#define _MHD_NOSANITIZE_PTRS /**/
+#define _MHD_RED_ZONE_SIZE (0)
+#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) ROUND_TO_ALIGN(n)
+#define _MHD_POISON_MEMORY(pointer, size) (void)0
+#define _MHD_UNPOISON_MEMORY(pointer, size) (void)0
+#else /* MHD_ASAN_POISON_ACTIVE */
+#if defined(FUNC_ATTR_PTRCOMPARE_WOKRS)
+#define _MHD_NOSANITIZE_PTRS \
+ __attribute__((no_sanitize("pointer-compare","pointer-subtract")))
+#elif defined(FUNC_ATTR_NOSANITIZE_WORKS)
+#define _MHD_NOSANITIZE_PTRS __attribute__((no_sanitize("address")))
+#endif
+#define _MHD_RED_ZONE_SIZE (ALIGN_SIZE)
+#define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) (ROUND_TO_ALIGN(n) + _MHD_RED_ZONE_SIZE)
+#define _MHD_POISON_MEMORY(pointer, size) \
+ ASAN_POISON_MEMORY_REGION ((pointer), (size))
+#define _MHD_UNPOISON_MEMORY(pointer, size) \
+ ASAN_UNPOISON_MEMORY_REGION ((pointer), (size))
+#endif /* MHD_ASAN_POISON_ACTIVE */
/**
* Size of memory page
*/
-static size_t MHD_sys_page_size_ = MHD_DEF_PAGE_SIZE_; /* Default fallback value */
+static size_t MHD_sys_page_size_ =
+#if defined(MHD_USE_PAGESIZE_MACRO_STATIC)
+ PAGESIZE;
+#elif defined(MHD_USE_PAGE_SIZE_MACRO_STATIC)
+ PAGE_SIZE;
+#else /* ! MHD_USE_PAGE_SIZE_MACRO_STATIC */
+ _MHD_FALLBACK_PAGE_SIZE; /* Default fallback value */
+#endif /* ! MHD_USE_PAGE_SIZE_MACRO_STATIC */
/**
* Initialise values for memory pools
@@ -203,6 +254,7 @@
pool->end = alloc_size;
pool->size = alloc_size;
mhd_assert (0 < alloc_size);
+ _MHD_POISON_MEMORY (pool->memory, pool->size);
return pool;
}
@@ -220,6 +272,7 @@
mhd_assert (pool->end >= pool->pos);
mhd_assert (pool->size >= pool->end - pool->pos);
+ _MHD_POISON_MEMORY (pool->memory, pool->size);
if (! pool->is_mmap)
free (pool->memory);
else
@@ -248,7 +301,11 @@
{
mhd_assert (pool->end >= pool->pos);
mhd_assert (pool->size >= pool->end - pool->pos);
- return (pool->end - pool->pos);
+#ifdef MHD_ASAN_POISON_ACTIVE
+ if ((pool->end - pool->pos) <= _MHD_RED_ZONE_SIZE)
+ return 0;
+#endif /* MHD_ASAN_POISON_ACTIVE */
+ return (pool->end - pool->pos) - _MHD_RED_ZONE_SIZE;
}
@@ -273,11 +330,10 @@
mhd_assert (pool->end >= pool->pos);
mhd_assert (pool->size >= pool->end - pool->pos);
- asize = ROUND_TO_ALIGN (size);
+ asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
if ( (0 == asize) && (0 != size) )
return NULL; /* size too close to SIZE_MAX */
- if ( (pool->pos + asize > pool->end) ||
- (pool->pos + asize < pool->pos))
+ if (asize > pool->end - pool->pos)
return NULL;
if (from_end)
{
@@ -289,6 +345,59 @@
ret = &pool->memory[pool->pos];
pool->pos += asize;
}
+ _MHD_UNPOISON_MEMORY (ret, size);
+ return ret;
+}
+
+
+/**
+ * Try to allocate @a size bytes memory area from the @a pool.
+ *
+ * If allocation fails, @a required_bytes is updated with size required to be
+ * freed in the @a pool from rellocatable area to allocate requested number
+ * of bytes.
+ * Allocated memory area is always not rellocatable ("from end").
+ *
+ * @param pool memory pool to use for the operation
+ * @param size the size of memory in bytes to allocate
+ * @param[out] required_bytes the pointer to variable to be updated with
+ * the size of the required additional free
+ * memory area, not updated if function succeed.
+ * Cannot be NULL.
+ * @return the pointer to allocated memory area if succeed,
+ * NULL if the pool doesn't have enough space, required_bytes is updated
+ * with amount of space needed to be freed in rellocatable area or
+ * set to SIZE_MAX if requested size is too large for the pool.
+ */
+void *
+MHD_pool_try_alloc (struct MemoryPool *pool,
+ size_t size,
+ size_t *required_bytes)
+{
+ void *ret;
+ size_t asize;
+
+ mhd_assert (pool->end >= pool->pos);
+ mhd_assert (pool->size >= pool->end - pool->pos);
+ asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
+ if ( (0 == asize) && (0 != size) )
+ { /* size is too close to SIZE_MAX, very unlikely */
+ *required_bytes = SIZE_MAX;
+ return NULL;
+ }
+ if (asize > pool->end - pool->pos)
+ {
+ mhd_assert ((pool->end - pool->pos) == \
+ ROUND_TO_ALIGN (pool->end - pool->pos));
+ if (asize <= pool->end)
+ *required_bytes = asize - (pool->end - pool->pos);
+ else
+ *required_bytes = SIZE_MAX;
+ return NULL;
+ }
+ ret = &pool->memory[pool->end - asize];
+ pool->end -= asize;
+ _MHD_UNPOISON_MEMORY (ret, size);
return ret;
}
@@ -299,7 +408,7 @@
* shrinking the block that was last (re)allocated.
* If the given block is not the most recently
* (re)allocated block, the memory of the previous
- * allocation may be leaked until the pool is
+ * allocation may be not released until the pool is
* destroyed or reset.
*
* @param pool memory pool to use for the operation
@@ -310,7 +419,7 @@
* NULL if the pool cannot support @a new_size
* bytes (old continues to be valid for @a old_size)
*/
-void *
+_MHD_NOSANITIZE_PTRS void *
MHD_pool_reallocate (struct MemoryPool *pool,
void *old,
size_t old_size,
@@ -322,25 +431,35 @@
mhd_assert (pool->end >= pool->pos);
mhd_assert (pool->size >= pool->end - pool->pos);
mhd_assert (old != NULL || old_size == 0);
- mhd_assert (old == NULL || pool->memory <= (uint8_t*) old);
- mhd_assert (old == NULL || pool->memory + pool->size >= (uint8_t*) old
- + old_size);
+ mhd_assert (pool->size >= old_size);
+ mhd_assert (old == NULL || pool->memory <= (uint8_t *) old);
+ /* (old == NULL || pool->memory + pool->size >= (uint8_t*) old + old_size) */
+ mhd_assert (old == NULL || \
+ (pool->size - _MHD_RED_ZONE_SIZE) >= \
+ (((size_t) (((uint8_t *) old) - pool->memory)) + old_size));
/* Blocks "from the end" must not be reallocated */
+ /* (old == NULL || old_size == 0 || pool->memory + pool->pos > (uint8_t*) old) */
+ mhd_assert (old == NULL || old_size == 0 || \
+ pool->pos > (size_t) ((uint8_t *) old - pool->memory));
mhd_assert (old == NULL || old_size == 0 || \
- pool->memory + pool->pos > (uint8_t*) old);
+ (size_t) (((uint8_t *) old) - pool->memory) + old_size <= \
+ pool->end - _MHD_RED_ZONE_SIZE);
- if (0 != old_size)
- { /* Need to save some data */
- const size_t old_offset = (uint8_t*) old - pool->memory;
+ if (NULL != old)
+ { /* Have previously allocated data */
+ const size_t old_offset = (uint8_t *) old - pool->memory;
const bool shrinking = (old_size > new_size);
/* Try resizing in-place */
if (shrinking)
{ /* Shrinking in-place, zero-out freed part */
- memset ((uint8_t*) old + new_size, 0, old_size - new_size);
+ memset ((uint8_t *) old + new_size, 0, old_size - new_size);
+ _MHD_POISON_MEMORY ((uint8_t *) old + new_size, old_size - new_size);
}
- if (pool->pos == ROUND_TO_ALIGN (old_offset + old_size))
+ if (pool->pos ==
+ ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + old_size))
{ /* "old" block is the last allocated block */
- const size_t new_apos = ROUND_TO_ALIGN (old_offset + new_size);
+ const size_t new_apos =
+ ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + new_size);
if (! shrinking)
{ /* Grow in-place, check for enough space. */
if ( (new_apos > pool->end) ||
@@ -349,13 +468,14 @@
}
/* Resized in-place */
pool->pos = new_apos;
+ _MHD_UNPOISON_MEMORY (old, new_size);
return old;
}
if (shrinking)
return old; /* Resized in-place, freed part remains allocated */
}
/* Need to allocate new block */
- asize = ROUND_TO_ALIGN (new_size);
+ asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
if ( ( (0 == asize) &&
(0 != new_size) ) || /* Value wrap, too large new_size. */
(asize > pool->end - pool->pos) ) /* Not enough space */
@@ -364,12 +484,14 @@
new_blc = pool->memory + pool->pos;
pool->pos += asize;
+ _MHD_UNPOISON_MEMORY (new_blc, new_size);
if (0 != old_size)
{
/* Move data to new block, old block remains allocated */
memcpy (new_blc, old, old_size);
/* Zero-out old block */
memset (old, 0, old_size);
+ _MHD_POISON_MEMORY (old, old_size);
}
return new_blc;
}
@@ -388,7 +510,7 @@
* (should be larger or equal to @a copy_bytes)
* @return addr new address of @a keep (if it had to change)
*/
-void *
+_MHD_NOSANITIZE_PTRS void *
MHD_pool_reset (struct MemoryPool *pool,
void *keep,
size_t copy_bytes,
@@ -396,11 +518,15 @@
{
mhd_assert (pool->end >= pool->pos);
mhd_assert (pool->size >= pool->end - pool->pos);
- mhd_assert (copy_bytes < new_size);
+ mhd_assert (copy_bytes <= new_size);
+ mhd_assert (copy_bytes <= pool->size);
mhd_assert (keep != NULL || copy_bytes == 0);
- mhd_assert (keep == NULL || pool->memory <= (uint8_t*) keep);
- mhd_assert (keep == NULL || pool->memory + pool->size >= (uint8_t*) keep
- + copy_bytes);
+ mhd_assert (keep == NULL || pool->memory <= (uint8_t *) keep);
+ /* (keep == NULL || pool->memory + pool->size >= (uint8_t*) keep + copy_bytes) */
+ mhd_assert (keep == NULL || \
+ pool->size >= \
+ ((size_t) ((uint8_t *) keep - pool->memory)) + copy_bytes);
+ _MHD_UNPOISON_MEMORY (pool->memory, new_size);
if ( (NULL != keep) &&
(keep != pool->memory) )
{
@@ -415,6 +541,7 @@
size_t to_zero; /** Size of area to zero-out */
to_zero = pool->size - copy_bytes;
+ _MHD_UNPOISON_MEMORY (pool->memory + copy_bytes, to_zero);
#ifdef _WIN32
if (pool->is_mmap)
{
@@ -444,8 +571,10 @@
0,
to_zero);
}
- pool->pos = ROUND_TO_ALIGN (new_size);
+ pool->pos = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
pool->end = pool->size;
+ _MHD_POISON_MEMORY (((uint8_t *) pool->memory) + new_size, \
+ pool->size - new_size);
return pool->memory;
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/memorypool.h
^
|
@@ -31,7 +31,9 @@
#define MEMORYPOOL_H
#include "mhd_options.h"
+#ifdef HAVE_STDDEF_H
#include <stddef.h>
+#endif /* HAVE_STDDEF_H */
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#endif
@@ -87,12 +89,37 @@
/**
+ * Try to allocate @a size bytes memory area from the @a pool.
+ *
+ * If allocation fails, @a required_bytes is updated with size required to be
+ * freed in the @a pool from relocatable area to allocate requested number
+ * of bytes.
+ * Allocated memory area is always not rellocatable ("from end").
+ *
+ * @param pool memory pool to use for the operation
+ * @param size the size of memory in bytes to allocate
+ * @param[out] required_bytes the pointer to variable to be updated with
+ * the size of the required additional free
+ * memory area, not updated if function succeed.
+ * Cannot be NULL.
+ * @return the pointer to allocated memory area if succeed,
+ * NULL if the pool doesn't have enough space, required_bytes is updated
+ * with amount of space needed to be freed in relocatable area or
+ * set to SIZE_MAX if requested size is too large for the pool.
+ */
+void *
+MHD_pool_try_alloc (struct MemoryPool *pool,
+ size_t size,
+ size_t *required_bytes);
+
+
+/**
* Reallocate a block of memory obtained from the pool.
* This is particularly efficient when growing or
* shrinking the block that was last (re)allocated.
* If the given block is not the most recently
* (re)allocated block, the memory of the previous
- * allocation may be leaked until the pool is
+ * allocation may be not released until the pool is
* destroyed or reset.
*
* @param pool memory pool to use for the operation
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_align.h
^
|
@@ -0,0 +1,97 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2021 Karlson2k (Evgeny Grin)
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file microhttpd/mhd_align.h
+ * @brief types alignment macros
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_ALIGN_H
+#define MHD_ALIGN_H 1
+
+#include "mhd_options.h"
+#include <stdint.h>
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_C_ALIGNOF
+#ifdef HAVE_STDALIGN_H
+#include <stdalign.h>
+#endif /* HAVE_STDALIGN_H */
+#define _MHD_ALIGNOF(type) alignof(type)
+#endif /* HAVE_C_ALIGNOF */
+
+#ifndef _MHD_ALIGNOF
+#if defined(_MSC_VER) && ! defined(__clang__) && _MSC_VER >= 1700
+#define _MHD_ALIGNOF(type) __alignof(type)
+#endif /* _MSC_VER >= 1700 */
+#endif /* !_MHD_ALIGNOF */
+
+#ifdef _MHD_ALIGNOF
+#if (defined (__GNUC__) && __GNUC__ < 4 && __GNUC_MINOR__ < 9 && \
+ ! defined(__clang__)) || \
+ (defined (__clang__) && __clang_major__ < 8) || \
+ (defined (__clang__) && __clang_major__ < 11 && \
+ defined(__apple_build_version__))
+/* GCC before 4.9 and clang before 8.0 have incorrect implementation of 'alignof()'
+ which returns preferred alignment instead of minimal required alignment */
+#define _MHD_ALIGNOF_UNRELIABLE 1
+#endif
+
+#if defined(_MSC_VER) && ! defined(__clang__) && _MSC_VER < 1900
+/* MSVC has the same problem as old GCC versions:
+ '__alignof()' may return "preferred" alignment instead of "required". */
+#define _MHD_ALIGNOF_UNRELIABLE 1
+#endif /* _MSC_VER < 1900 */
+#endif /* _MHD_ALIGNOF */
+
+
+#ifdef offsetof
+#define _MHD_OFFSETOF(strct, membr) offsetof(strct, membr)
+#else /* ! offsetof */
+#define _MHD_OFFSETOF(strct, membr) (size_t)(((char*)&(((strct*)0)->membr)) - \
+ ((char*)((strct*)0)))
+#endif /* ! offsetof */
+
+/* Provide a limited set of alignment macros */
+/* The set could be extended as needed */
+#if defined(_MHD_ALIGNOF) && ! defined(_MHD_ALIGNOF_UNRELIABLE)
+#define _MHD_UINT32_ALIGN _MHD_ALIGNOF(uint32_t)
+#define _MHD_UINT64_ALIGN _MHD_ALIGNOF(uint64_t)
+#else /* ! _MHD_ALIGNOF */
+struct _mhd_dummy_uint32_offset_test
+{
+ char dummy;
+ uint32_t ui32;
+};
+#define _MHD_UINT32_ALIGN \
+ _MHD_OFFSETOF(struct _mhd_dummy_uint32_offset_test, ui32)
+
+struct _mhd_dummy_uint64_offset_test
+{
+ char dummy;
+ uint64_t ui64;
+};
+#define _MHD_UINT64_ALIGN \
+ _MHD_OFFSETOF(struct _mhd_dummy_uint64_offset_test, ui64)
+#endif /* ! _MHD_ALIGNOF */
+
+#endif /* ! MHD_ALIGN_H */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_assert.h
^
|
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2017 Karlson2k (Evgeny Grin)
+ Copyright (C) 2017-2021 Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -23,10 +23,20 @@
* @author Karlson2k (Evgeny Grin)
*/
+/* Unlike POSIX version of 'assert.h', MHD version of 'assert' header
+ * does not allow multiple redefinition of 'mhd_assert' macro within single
+ * source file. */
#ifndef MHD_ASSERT_H
#define MHD_ASSERT_H 1
#include "mhd_options.h"
+
+#if ! defined(_DEBUG) && ! defined(NDEBUG)
+#define NDEBUG 1 /* Use NDEBUG by default */
+#endif /* !_DEBUG && !NDEBUG */
+#if defined(_DEBUG) && defined(NDEBUG)
+#error Both _DEBUG and NDEBUG are defined
+#endif /* _DEBUG && NDEBUG */
#ifdef NDEBUG
# define mhd_assert(ignore) ((void) 0)
#else /* _DEBUG */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_bithelpers.h
^
|
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2019 Karlson2k (Evgeny Grin)
+ Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -26,13 +26,18 @@
#ifndef MHD_BITHELPERS_H
#define MHD_BITHELPERS_H 1
-#include "mhd_byteorder.h"
+#include "mhd_options.h"
#include <stdint.h>
#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
defined(__OPTIMIZE__)))
/* Declarations for VC & Clang/C2 built-ins */
#include <intrin.h>
#endif /* _MSC_FULL_VER */
+#include "mhd_byteorder.h"
+#if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN || _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
+#include "mhd_align.h"
+#endif /* _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN ||
+ _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN */
#ifndef __has_builtin
/* Avoid precompiler errors with non-clang */
@@ -55,8 +60,8 @@
#define _MHD_BYTES_SWAP32(value32) \
((uint32_t) __builtin_bswap32 ((uint32_t) value32))
#else /* ! __has_builtin(__builtin_bswap32) */
-#define _MHD_BYTES_SWAP32(value32) \
- ( (((uint32_t) (value32)) << 24) \
+#define _MHD_BYTES_SWAP32(value32) \
+ ( (((uint32_t) (value32)) << 24) \
| ((((uint32_t) (value32)) & ((uint32_t) 0x0000FF00)) << 8) \
| ((((uint32_t) (value32)) & ((uint32_t) 0x00FF0000)) >> 8) \
| (((uint32_t) (value32)) >> 24) )
@@ -77,8 +82,8 @@
#define _MHD_BYTES_SWAP64(value64) \
((uint64_t) __builtin_bswap64 ((uint64_t) value64))
#else /* ! __has_builtin(__builtin_bswap64) */
-#define _MHD_BYTES_SWAP64(value64) \
- ( (((uint64_t) (value64)) << 56) \
+#define _MHD_BYTES_SWAP64(value64) \
+ ( (((uint64_t) (value64)) << 56) \
| ((((uint64_t) (value64)) & ((uint64_t) 0x000000000000FF00)) << 40) \
| ((((uint64_t) (value64)) & ((uint64_t) 0x0000000000FF0000)) << 24) \
| ((((uint64_t) (value64)) & ((uint64_t) 0x00000000FF000000)) << 8) \
@@ -93,6 +98,17 @@
* put native-endian 64-bit value64 to addr
* in little-endian mode.
*/
+/* Slow version that works with unaligned addr and with any bytes order */
+#define _MHD_PUT_64BIT_LE_SLOW(addr, value64) do { \
+ ((uint8_t*) (addr))[0] = (uint8_t) ((uint64_t) (value64)); \
+ ((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 8); \
+ ((uint8_t*) (addr))[2] = (uint8_t) (((uint64_t) (value64)) >> 16); \
+ ((uint8_t*) (addr))[3] = (uint8_t) (((uint64_t) (value64)) >> 24); \
+ ((uint8_t*) (addr))[4] = (uint8_t) (((uint64_t) (value64)) >> 32); \
+ ((uint8_t*) (addr))[5] = (uint8_t) (((uint64_t) (value64)) >> 40); \
+ ((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 48); \
+ ((uint8_t*) (addr))[7] = (uint8_t) (((uint64_t) (value64)) >> 56); \
+} while (0)
#if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
#define _MHD_PUT_64BIT_LE(addr, value64) \
((*(uint64_t*) (addr)) = (uint64_t) (value64))
@@ -101,7 +117,7 @@
((*(uint64_t*) (addr)) = _MHD_BYTES_SWAP64 (value64))
#else /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_PUT_64BIT_LE(addr, value64) do { \
+#define _MHD_PUT_64BIT_LE(addr, value64) do { \
((uint8_t*) (addr))[0] = (uint8_t) ((uint64_t) (value64)); \
((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 8); \
((uint8_t*) (addr))[2] = (uint8_t) (((uint64_t) (value64)) >> 16); \
@@ -111,8 +127,23 @@
((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 48); \
((uint8_t*) (addr))[7] = (uint8_t) (((uint64_t) (value64)) >> 56); \
} while (0)
+/* Indicate that _MHD_PUT_64BIT_LE does not need aligned pointer */
+#define _MHD_PUT_64BIT_LE_UNALIGNED 1
#endif /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
+/* Put result safely to unaligned address */
+_MHD_static_inline void
+_MHD_PUT_64BIT_LE_SAFE (void *dst, uint64_t value)
+{
+#ifndef _MHD_PUT_64BIT_LE_UNALIGNED
+ if (0 != ((uintptr_t) dst) % (_MHD_UINT64_ALIGN))
+ _MHD_PUT_64BIT_LE_SLOW (dst, value);
+ else
+#endif /* ! _MHD_PUT_64BIT_BE_UNALIGNED */
+ _MHD_PUT_64BIT_LE (dst, value);
+}
+
+
/* _MHD_PUT_32BIT_LE (addr, value32)
* put native-endian 32-bit value32 to addr
* in little-endian mode.
@@ -125,12 +156,14 @@
((*(uint32_t*) (addr)) = _MHD_BYTES_SWAP32 (value32))
#else /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_PUT_32BIT_LE(addr, value32) do { \
+#define _MHD_PUT_32BIT_LE(addr, value32) do { \
((uint8_t*) (addr))[0] = (uint8_t) ((uint32_t) (value32)); \
((uint8_t*) (addr))[1] = (uint8_t) (((uint32_t) (value32)) >> 8); \
((uint8_t*) (addr))[2] = (uint8_t) (((uint32_t) (value32)) >> 16); \
((uint8_t*) (addr))[3] = (uint8_t) (((uint32_t) (value32)) >> 24); \
} while (0)
+/* Indicate that _MHD_PUT_32BIT_LE does not need aligned pointer */
+#define _MHD_PUT_32BIT_LE_UNALIGNED 1
#endif /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
/* _MHD_GET_32BIT_LE (addr)
@@ -145,11 +178,13 @@
_MHD_BYTES_SWAP32 (*(const uint32_t*) (addr))
#else /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_GET_32BIT_LE(addr) \
- ( ( (uint32_t) (((const uint8_t*) addr)[0])) \
+#define _MHD_GET_32BIT_LE(addr) \
+ ( ( (uint32_t) (((const uint8_t*) addr)[0])) \
| (((uint32_t) (((const uint8_t*) addr)[1])) << 8) \
| (((uint32_t) (((const uint8_t*) addr)[2])) << 16) \
| (((uint32_t) (((const uint8_t*) addr)[3])) << 24) )
+/* Indicate that _MHD_GET_32BIT_LE does not need aligned pointer */
+#define _MHD_GET_32BIT_LE_UNALIGNED 1
#endif /* _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN */
@@ -157,15 +192,8 @@
* put native-endian 64-bit value64 to addr
* in big-endian mode.
*/
-#if _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
-#define _MHD_PUT_64BIT_BE(addr, value64) \
- ((*(uint64_t*) (addr)) = (uint64_t) (value64))
-#elif _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
-#define _MHD_PUT_64BIT_BE(addr, value64) \
- ((*(uint64_t*) (addr)) = _MHD_BYTES_SWAP64 (value64))
-#else /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
-/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_PUT_64BIT_BE(addr, value64) do { \
+/* Slow version that works with unaligned addr and with any bytes order */
+#define _MHD_PUT_64BIT_BE_SLOW(addr, value64) do { \
((uint8_t*) (addr))[7] = (uint8_t) ((uint64_t) (value64)); \
((uint8_t*) (addr))[6] = (uint8_t) (((uint64_t) (value64)) >> 8); \
((uint8_t*) (addr))[5] = (uint8_t) (((uint64_t) (value64)) >> 16); \
@@ -175,8 +203,32 @@
((uint8_t*) (addr))[1] = (uint8_t) (((uint64_t) (value64)) >> 48); \
((uint8_t*) (addr))[0] = (uint8_t) (((uint64_t) (value64)) >> 56); \
} while (0)
+#if _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN
+#define _MHD_PUT_64BIT_BE(addr, value64) \
+ ((*(uint64_t*) (addr)) = (uint64_t) (value64))
+#elif _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN
+#define _MHD_PUT_64BIT_BE(addr, value64) \
+ ((*(uint64_t*) (addr)) = _MHD_BYTES_SWAP64 (value64))
+#else /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
+/* Endianness was not detected or non-standard like PDP-endian */
+#define _MHD_PUT_64BIT_BE(addr, value64) _MHD_PUT_64BIT_BE_SLOW(addr, value64)
+/* Indicate that _MHD_PUT_64BIT_BE does not need aligned pointer */
+#define _MHD_PUT_64BIT_BE_UNALIGNED 1
#endif /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
+/* Put result safely to unaligned address */
+_MHD_static_inline void
+_MHD_PUT_64BIT_BE_SAFE (void *dst, uint64_t value)
+{
+#ifndef _MHD_PUT_64BIT_BE_UNALIGNED
+ if (0 != ((uintptr_t) dst) % (_MHD_UINT64_ALIGN))
+ _MHD_PUT_64BIT_BE_SLOW (dst, value);
+ else
+#endif /* ! _MHD_PUT_64BIT_BE_UNALIGNED */
+ _MHD_PUT_64BIT_BE (dst, value);
+}
+
+
/* _MHD_PUT_32BIT_BE (addr, value32)
* put native-endian 32-bit value32 to addr
* in big-endian mode.
@@ -189,12 +241,14 @@
((*(uint32_t*) (addr)) = _MHD_BYTES_SWAP32 (value32))
#else /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_PUT_32BIT_BE(addr, value32) do { \
+#define _MHD_PUT_32BIT_BE(addr, value32) do { \
((uint8_t*) (addr))[3] = (uint8_t) ((uint32_t) (value32)); \
((uint8_t*) (addr))[2] = (uint8_t) (((uint32_t) (value32)) >> 8); \
((uint8_t*) (addr))[1] = (uint8_t) (((uint32_t) (value32)) >> 16); \
((uint8_t*) (addr))[0] = (uint8_t) (((uint32_t) (value32)) >> 24); \
} while (0)
+/* Indicate that _MHD_PUT_32BIT_BE does not need aligned pointer */
+#define _MHD_PUT_32BIT_BE_UNALIGNED 1
#endif /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
/* _MHD_GET_32BIT_BE (addr)
@@ -209,11 +263,13 @@
_MHD_BYTES_SWAP32 (*(const uint32_t*) (addr))
#else /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
/* Endianness was not detected or non-standard like PDP-endian */
-#define _MHD_GET_32BIT_BE(addr) \
- ( (((uint32_t) (((const uint8_t*) addr)[0])) << 24) \
+#define _MHD_GET_32BIT_BE(addr) \
+ ( (((uint32_t) (((const uint8_t*) addr)[0])) << 24) \
| (((uint32_t) (((const uint8_t*) addr)[1])) << 16) \
| (((uint32_t) (((const uint8_t*) addr)[2])) << 8) \
| ((uint32_t) (((const uint8_t*) addr)[3])) )
+/* Indicate that _MHD_GET_32BIT_BE does not need aligned pointer */
+#define _MHD_GET_32BIT_BE_UNALIGNED 1
#endif /* _MHD_BYTE_ORDER != _MHD_LITTLE_ENDIAN */
@@ -229,11 +285,48 @@
#endif /* ! __clang__ */
#define _MHD_ROTR32(value32, bits) \
((uint32_t) _rotr ((uint32_t) (value32),(bits)))
-#else /* ! _MSC_FULL_VER */
-/* Defined in form which modern compiler could optimize. */
+#elif __has_builtin (__builtin_rotateright32)
#define _MHD_ROTR32(value32, bits) \
- (((uint32_t) (value32)) >> (bits) | ((uint32_t) (value32)) << (32 - bits))
-#endif /* ! _MSC_FULL_VER */
+ ((uint32_t) __builtin_rotateright32 ((value32), (bits)))
+#else /* ! __builtin_rotateright32 */
+_MHD_static_inline uint32_t
+_MHD_ROTR32 (uint32_t value32, int bits)
+{
+ bits %= 32;
+ /* Defined in form which modern compiler could optimize. */
+ return (value32 >> bits) | (value32 << (32 - bits));
+}
+
+
+#endif /* ! __builtin_rotateright32 */
+
+
+/**
+ * Rotate left 32-bit value by number of bits.
+ * bits parameter must be more than zero and must be less than 32.
+ */
+#if defined(_MSC_FULL_VER) && (! defined(__clang__) || (defined(__c2__) && \
+ defined(__OPTIMIZE__)))
+/* Clang/C2 do not inline this function if optimizations are turned off. */
+#ifndef __clang__
+#pragma intrinsic(_rotl)
+#endif /* ! __clang__ */
+#define _MHD_ROTL32(value32, bits) \
+ ((uint32_t) _rotl ((uint32_t) (value32),(bits)))
+#elif __has_builtin (__builtin_rotateleft32)
+#define _MHD_ROTL32(value32, bits) \
+ ((uint32_t) __builtin_rotateleft32 ((value32), (bits)))
+#else /* ! __builtin_rotateleft32 */
+_MHD_static_inline uint32_t
+_MHD_ROTL32 (uint32_t value32, int bits)
+{
+ bits %= 32;
+ /* Defined in form which modern compiler could optimize. */
+ return (value32 << bits) | (value32 >> (32 - bits));
+}
+
+
+#endif /* ! __builtin_rotateleft32 */
#endif /* ! MHD_BITHELPERS_H */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_compat.h
^
|
@@ -35,7 +35,9 @@
#define MHD_COMPAT_H 1
#include "mhd_options.h"
+#ifdef HAVE_STDLIB_H
#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
#ifdef HAVE_STRING_H /* for strerror() */
#include <string.h>
#endif /* HAVE_STRING_H */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_itc.h
^
|
@@ -38,7 +38,9 @@
#ifndef MHD_PANIC
# include <stdio.h>
-# include <stdlib.h>
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif /* HAVE_STDLIB_H */
/* Simple implementation of MHD_PANIC, to be used outside lib */
# define MHD_PANIC(msg) do { fprintf (stderr, \
"Abnormal termination at %d line in file %s: %s\n", \
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_limits.h
^
|
@@ -52,10 +52,14 @@
#else /* ! __LONG_MAX__ */
#define LONG_MAX MHD_SIGNED_TYPE_MAX (long)
#endif /* ! __LONG_MAX__ */
-#endif /* !OFF_T_MAX */
+#endif /* !LONG_MAX */
#ifndef ULLONG_MAX
+#ifdef ULONGLONG_MAX
+#define ULLONG_MAX ULONGLONG_MAX
+#else /* ! ULONGLONG_MAX */
#define ULLONG_MAX MHD_UNSIGNED_TYPE_MAX_ (MHD_UNSIGNED_LONG_LONG)
+#endif /* ! ULONGLONG_MAX */
#endif /* !ULLONG_MAX */
#ifndef INT32_MAX
@@ -103,8 +107,6 @@
#ifndef SSIZE_MAX
#ifdef __SSIZE_MAX__
#define SSIZE_MAX __SSIZE_MAX__
-#elif defined(PTRDIFF_MAX)
-#define SSIZE_MAX PTRDIFF_MAX
#elif defined(INTPTR_MAX)
#define SSIZE_MAX INTPTR_MAX
#else
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_locks.h
^
|
@@ -58,7 +58,9 @@
#ifndef MHD_PANIC
# include <stdio.h>
-# include <stdlib.h>
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif /* HAVE_STDLIB_H */
/* Simple implementation of MHD_PANIC, to be used outside lib */
# define MHD_PANIC(msg) do { fprintf (stderr, \
"Abnormal termination at %d line in file %s: %s\n", \
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_mono_clock.c
^
|
@@ -25,25 +25,22 @@
#include "mhd_mono_clock.h"
-#if defined(_WIN32) && ! defined(__CYGWIN__) && defined(HAVE_CLOCK_GETTIME)
+#if defined(_WIN32) && ! defined(__CYGWIN__)
/* Prefer native clock source over wrappers */
-#undef HAVE_CLOCK_GETTIME
-#endif /* _WIN32 && ! __CYGWIN__ && HAVE_CLOCK_GETTIME */
-
#ifdef HAVE_CLOCK_GETTIME
-#include <time.h>
+#undef HAVE_CLOCK_GETTIME
#endif /* HAVE_CLOCK_GETTIME */
+#ifdef HAVE_GETTIMEOFDAY
+#undef HAVE_GETTIMEOFDAY
+#endif /* HAVE_GETTIMEOFDAY */
+#endif /* _WIN32 && ! __CYGWIN__ */
-#ifdef HAVE_GETHRTIME
-#ifdef HAVE_SYS_TIME_H
-/* Solaris defines gethrtime() in sys/time.h */
-#include <sys/time.h>
-#endif /* HAVE_SYS_TIME_H */
#ifdef HAVE_TIME_H
-/* HP-UX defines gethrtime() in time.h */
#include <time.h>
#endif /* HAVE_TIME_H */
-#endif /* HAVE_GETHRTIME */
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
#ifdef HAVE_CLOCK_GET_TIME
#include <mach/mach.h>
@@ -65,6 +62,10 @@
#include <stdint.h>
#endif /* _WIN32 */
+#ifndef NULL
+#define NULL ((void*)0)
+#endif /* ! NULL */
+
#ifdef HAVE_CLOCK_GETTIME
#ifdef CLOCK_REALTIME
#define _MHD_UNWANTED_CLOCK CLOCK_REALTIME
@@ -80,6 +81,10 @@
defined(HAVE_GETHRTIME)
static time_t mono_clock_start;
#endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */
+#if defined(HAVE_TIMESPEC_GET) || defined(HAVE_GETTIMEOFDAY)
+/* The start value shared for timespec_get() and gettimeofday () */
+static time_t gettime_start;
+#endif /* HAVE_TIMESPEC_GET || HAVE_GETTIMEOFDAY */
static time_t sys_clock_start;
#ifdef HAVE_GETHRTIME
static hrtime_t hrtime_start;
@@ -88,8 +93,8 @@
#if _WIN32_WINNT >= 0x0600
static uint64_t tick_start;
#else /* _WIN32_WINNT < 0x0600 */
-static int64_t perf_freq;
-static int64_t perf_start;
+static uint64_t perf_freq;
+static uint64_t perf_start;
#endif /* _WIN32_WINNT < 0x0600 */
#endif /* _WIN32 */
@@ -132,7 +137,7 @@
/**
- * Initialise monotonic seconds counter.
+ * Initialise monotonic seconds and milliseconds counters.
*/
void
MHD_monotonic_sec_counter_init (void)
@@ -154,6 +159,7 @@
various following ifdef's to work out nicely */
if (0)
{
+ (void) 0; /* Mute possible compiler warning */
}
else
#ifdef HAVE_CLOCK_GETTIME
@@ -182,9 +188,23 @@
}
else
#endif /* CLOCK_MONOTONIC_COARSE */
+#ifdef CLOCK_MONOTONIC_RAW_APPROX
+ /* Darwin-specific clock */
+ /* Not affected by frequency adjustment, returns clock value cached at
+ * context switch. Can be "milliseconds old", but it's fast. */
+ if (0 == clock_gettime (CLOCK_MONOTONIC_RAW_APPROX,
+ &ts))
+ {
+ mono_clock_id = CLOCK_MONOTONIC_RAW_APPROX;
+ mono_clock_start = ts.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GETTIME;
+ }
+ else
+#endif /* CLOCK_MONOTONIC_RAW */
#ifdef CLOCK_MONOTONIC_RAW
- /* Linux-specific clock */
- /* Not affected by frequency adjustment, but don't count time in suspend */
+ /* Linux and Darwin clock */
+ /* Not affected by frequency adjustment,
+ * on Linux don't count time in suspend */
if (0 == clock_gettime (CLOCK_MONOTONIC_RAW,
&ts))
{
@@ -195,8 +215,7 @@
else
#endif /* CLOCK_MONOTONIC_RAW */
#ifdef CLOCK_BOOTTIME
- /* Linux-specific clock */
- /* Count time in suspend so it's real monotonic on Linux, */
+ /* Count time in suspend on Linux so it's real monotonic, */
/* but can be slower value-getting than other clocks */
if (0 == clock_gettime (CLOCK_BOOTTIME,
&ts))
@@ -219,6 +238,18 @@
mono_clock_source = _MHD_CLOCK_GETTIME;
}
else
+#endif /* CLOCK_MONOTONIC */
+#ifdef CLOCK_UPTIME
+ /* non-Linux clock */
+ /* Doesn't count time in suspend */
+ if (0 == clock_gettime (CLOCK_UPTIME,
+ &ts))
+ {
+ mono_clock_id = CLOCK_UPTIME;
+ mono_clock_start = ts.tv_sec;
+ mono_clock_source = _MHD_CLOCK_GETTIME;
+ }
+ else
#endif /* CLOCK_BOOTTIME */
#endif /* HAVE_CLOCK_GETTIME */
#ifdef HAVE_CLOCK_GET_TIME
@@ -256,8 +287,8 @@
QueryPerformanceFrequency (&freq); /* never fail on XP and later */
QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */
- perf_freq = freq.QuadPart;
- perf_start = perf_counter.QuadPart;
+ perf_freq = (uint64_t) freq.QuadPart;
+ perf_start = (uint64_t) perf_counter.QuadPart;
mono_clock_source = _MHD_CLOCK_PERFCOUNTER;
}
else
@@ -305,12 +336,32 @@
(void) mono_clock_source; /* avoid compiler warning */
#endif /* HAVE_CLOCK_GET_TIME */
+#ifdef HAVE_TIMESPEC_GET
+ if (1)
+ {
+ struct timespec tsg;
+ if (TIME_UTC == timespec_get (&tsg, TIME_UTC))
+ gettime_start = tsg.tv_sec;
+ else
+ gettime_start = 0;
+ }
+#elif defined (HAVE_GETTIMEOFDAY)
+ if (1)
+ {
+ struct timeval tv;
+ if (0 == gettimeofday (&tv, NULL))
+ gettime_start = tv.tv_sec;
+ else
+ gettime_start = 0;
+ }
+#endif /* HAVE_GETTIMEOFDAY */
sys_clock_start = time (NULL);
}
/**
- * Deinitialise monotonic seconds counter by freeing any allocated resources
+ * Deinitialise monotonic seconds and milliseconds counters by freeing
+ * any allocated resources
*/
void
MHD_monotonic_sec_counter_finish (void)
@@ -327,7 +378,7 @@
/**
- * Monotonic seconds counter, useful for timeout calculation.
+ * Monotonic seconds counter.
* Tries to be not affected by manually setting the system real time
* clock or adjustments by NTP synchronization.
*
@@ -364,7 +415,7 @@
LARGE_INTEGER perf_counter;
QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */
- return (time_t) (((uint64_t) (perf_counter.QuadPart - perf_start))
+ return (time_t) (((uint64_t) perf_counter.QuadPart - perf_start)
/ perf_freq);
}
#endif /* _WIN32_WINNT < 0x0600 */
@@ -376,3 +427,77 @@
return time (NULL) - sys_clock_start;
}
+
+
+/**
+ * Monotonic milliseconds counter, useful for timeout calculation.
+ * Tries to be not affected by manually setting the system real time
+ * clock or adjustments by NTP synchronization.
+ *
+ * @return number of microseconds from some fixed moment
+ */
+uint64_t
+MHD_monotonic_msec_counter (void)
+{
+#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_TIMESPEC_GET)
+ struct timespec ts;
+#endif /* HAVE_CLOCK_GETTIME || HAVE_TIMESPEC_GET */
+
+#ifdef HAVE_CLOCK_GETTIME
+ if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) &&
+ (0 == clock_gettime (mono_clock_id,
+ &ts)) )
+ return (uint64_t) (((uint64_t) (ts.tv_sec - mono_clock_start)) * 1000
+ + (ts.tv_nsec / 1000000));
+#endif /* HAVE_CLOCK_GETTIME */
+#ifdef HAVE_CLOCK_GET_TIME
+ if (_MHD_INVALID_CLOCK_SERV != mono_clock_service)
+ {
+ mach_timespec_t cur_time;
+
+ if (KERN_SUCCESS == clock_get_time (mono_clock_service,
+ &cur_time))
+ return (uint64_t) (((uint64_t) (cur_time.tv_sec - mono_clock_start))
+ * 1000 + (cur_time.tv_nsec / 1000000));
+ }
+#endif /* HAVE_CLOCK_GET_TIME */
+#if defined(_WIN32)
+#if _WIN32_WINNT >= 0x0600
+ if (1)
+ return (uint64_t) (GetTickCount64 () - tick_start);
+#else /* _WIN32_WINNT < 0x0600 */
+ if (0 != perf_freq)
+ {
+ LARGE_INTEGER perf_counter;
+ uint64_t num_ticks;
+
+ QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */
+ num_ticks = (uint64_t) (perf_counter.QuadPart - perf_start);
+ return ((num_ticks / perf_freq) * 1000)
+ + ((num_ticks % perf_freq) / (perf_freq / 1000));
+ }
+#endif /* _WIN32_WINNT < 0x0600 */
+#endif /* _WIN32 */
+#ifdef HAVE_GETHRTIME
+ if (1)
+ return ((uint64_t) (gethrtime () - hrtime_start)) / 1000000;
+#endif /* HAVE_GETHRTIME */
+
+ /* Fallbacks, affected by system time change */
+#ifdef HAVE_TIMESPEC_GET
+ if (TIME_UTC == timespec_get (&ts, TIME_UTC))
+ return (uint64_t) (((uint64_t) (ts.tv_sec - gettime_start)) * 1000
+ + (ts.tv_nsec / 1000000));
+#elif defined (HAVE_GETTIMEOFDAY)
+ if (1)
+ {
+ struct timeval tv;
+ if (0 == gettimeofday (&tv, NULL))
+ return (uint64_t) (((uint64_t) (tv.tv_sec - gettime_start)) * 1000
+ + (tv.tv_usec / 1000));
+ }
+#endif /* HAVE_GETTIMEOFDAY */
+
+ /* The last resort fallback with very low resolution */
+ return (uint64_t) (time (NULL) - sys_clock_start) * 1000;
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_mono_clock.h
^
|
@@ -32,23 +32,25 @@
#elif defined(HAVE_SYS_TYPES_H)
#include <sys/types.h>
#endif
+#include <stdint.h>
/**
- * Initialise monotonic seconds counter.
+ * Initialise monotonic seconds and milliseconds counters.
*/
void
MHD_monotonic_sec_counter_init (void);
/**
- * Deinitialise monotonic seconds counter by freeing any allocated resources
+ * Deinitialise monotonic seconds and milliseconds counters by freeing
+ * any allocated resources
*/
void
MHD_monotonic_sec_counter_finish (void);
/**
- * Monotonic seconds counter, useful for timeout calculation.
+ * Monotonic seconds counter.
* Tries to be not affected by manually setting the system real time
* clock or adjustments by NTP synchronization.
*
@@ -57,4 +59,15 @@
time_t
MHD_monotonic_sec_counter (void);
+
+/**
+ * Monotonic milliseconds counter, useful for timeout calculation.
+ * Tries to be not affected by manually setting the system real time
+ * clock or adjustments by NTP synchronization.
+ *
+ * @return number of microseconds from some fixed moment
+ */
+uint64_t
+MHD_monotonic_msec_counter (void);
+
#endif /* MHD_MONO_CLOCK_H */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_send.c
^
|
@@ -781,7 +781,8 @@
if (GNUTLS_E_AGAIN == ret)
{
#ifdef EPOLL_SUPPORT
- connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
#endif
return MHD_ERR_AGAIN_;
}
@@ -798,8 +799,13 @@
(GNUTLS_E_CRYPTODEV_IOCTL_ERROR == ret) ||
(GNUTLS_E_CRYPTODEV_DEVICE_ERROR == ret) )
return MHD_ERR_PIPE_;
+#if defined(GNUTLS_E_PREMATURE_TERMINATION)
if (GNUTLS_E_PREMATURE_TERMINATION == ret)
return MHD_ERR_CONNRESET_;
+#elif defined(GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
+ if (GNUTLS_E_UNEXPECTED_PACKET_LENGTH == ret)
+ return MHD_ERR_CONNRESET_;
+#endif /* GNUTLS_E_UNEXPECTED_PACKET_LENGTH */
if (GNUTLS_E_MEMORY_ERROR == ret)
return MHD_ERR_NOMEM_;
if (ret < 0)
@@ -845,7 +851,8 @@
{
#if EPOLL_SUPPORT
/* EAGAIN, no longer write-ready */
- connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
#endif /* EPOLL_SUPPORT */
return MHD_ERR_AGAIN_;
}
@@ -870,7 +877,8 @@
}
#if EPOLL_SUPPORT
else if (buffer_size > (size_t) ret)
- connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
#endif /* EPOLL_SUPPORT */
}
@@ -1089,7 +1097,8 @@
{
#if EPOLL_SUPPORT
/* EAGAIN, no longer write-ready */
- connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
#endif /* EPOLL_SUPPORT */
return MHD_ERR_AGAIN_;
}
@@ -1114,7 +1123,8 @@
}
#if EPOLL_SUPPORT
else if ((header_size + body_size) > (size_t) ret)
- connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
#endif /* EPOLL_SUPPORT */
/* If there is a need to push the data from network buffers
@@ -1243,7 +1253,8 @@
{
#ifdef EPOLL_SUPPORT
/* EAGAIN --- no longer write-ready */
- connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
#endif /* EPOLL_SUPPORT */
return MHD_ERR_AGAIN_;
}
@@ -1278,7 +1289,8 @@
}
#ifdef EPOLL_SUPPORT
else if (send_size > (size_t) ret)
- connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
#endif /* EPOLL_SUPPORT */
#elif defined(HAVE_FREEBSD_SENDFILE)
#ifdef SF_FLAGS
@@ -1463,7 +1475,8 @@
{
#ifdef EPOLL_SUPPORT
/* EAGAIN --- no longer write-ready */
- connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
#endif /* EPOLL_SUPPORT */
return MHD_ERR_AGAIN_;
}
@@ -1503,14 +1516,15 @@
else
{
#ifdef EPOLL_SUPPORT
- connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+ connection->epoll_state &=
+ ~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
#endif /* EPOLL_SUPPORT */
if (0 != res)
{
mhd_assert (r_iov->cnt > r_iov->sent);
/* The last iov element has been partially sent */
r_iov->iov[r_iov->sent].iov_base =
- (void*) ((uint8_t*) r_iov->iov[r_iov->sent].iov_base + (size_t) res);
+ (void *) ((uint8_t *) r_iov->iov[r_iov->sent].iov_base + (size_t) res);
r_iov->iov[r_iov->sent].iov_len -= (MHD_iov_size_) res;
}
}
@@ -1578,7 +1592,7 @@
/* Incomplete buffer has been sent.
* Adjust buffer of the last element. */
r_iov->iov[r_iov->sent].iov_base =
- (void*) ((uint8_t*) r_iov->iov[r_iov->sent].iov_base + (size_t) res);
+ (void *) ((uint8_t *) r_iov->iov[r_iov->sent].iov_base + (size_t) res);
r_iov->iov[r_iov->sent].iov_len -= res;
return total_sent;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_sockets.c
^
|
@@ -31,7 +31,7 @@
* @param err the WinSock error code.
* @return pointer to string description of specified WinSock error.
*/
-const char*
+const char *
MHD_W32_strerror_winsock_ (int err)
{
switch (err)
@@ -271,12 +271,12 @@
listen_addr.sin_port = 0; /* same as htons(0) */
listen_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
if ( ((0 == bind (listen_s,
- (struct sockaddr*) &listen_addr,
+ (struct sockaddr *) &listen_addr,
c_addinlen)) &&
(0 == listen (listen_s,
1) ) &&
(0 == getsockname (listen_s,
- (struct sockaddr*) &listen_addr,
+ (struct sockaddr *) &listen_addr,
&addr_len))) )
{
SOCKET client_s = socket (AF_INET,
@@ -294,10 +294,10 @@
}
if ( (0 != ioctlsocket (client_s,
- FIONBIO,
+ (int) FIONBIO,
&on_val)) ||
( (0 != connect (client_s,
- (struct sockaddr*) &listen_addr,
+ (struct sockaddr *) &listen_addr,
c_addinlen)) &&
(WSAGetLastError () != WSAEWOULDBLOCK)) )
{
@@ -309,7 +309,7 @@
addr_len = c_addinlen;
server_s = accept (listen_s,
- (struct sockaddr*) &accepted_from_addr,
+ (struct sockaddr *) &accepted_from_addr,
&addr_len);
if (INVALID_SOCKET == server_s)
{
@@ -321,19 +321,29 @@
addr_len = c_addinlen;
if ( (0 == getsockname (client_s,
- (struct sockaddr*) &client_addr,
+ (struct sockaddr *) &client_addr,
&addr_len)) &&
- (accepted_from_addr.sin_family == client_addr.sin_family) &&
(accepted_from_addr.sin_port == client_addr.sin_port) &&
(accepted_from_addr.sin_addr.s_addr ==
client_addr.sin_addr.s_addr) &&
+ (accepted_from_addr.sin_family == client_addr.sin_family) &&
( (0 != non_blk) ?
(0 == ioctlsocket (server_s,
- FIONBIO,
+ (int) FIONBIO,
&on_val)) :
(0 == ioctlsocket (client_s,
- FIONBIO,
- &off_val)) ) )
+ (int) FIONBIO,
+ &off_val)) ) &&
+ (0 == setsockopt (server_s,
+ IPPROTO_TCP,
+ TCP_NODELAY,
+ (const void *) (&on_val),
+ sizeof (on_val))) &&
+ (0 == setsockopt (client_s,
+ IPPROTO_TCP,
+ TCP_NODELAY,
+ (const void *) (&on_val),
+ sizeof (on_val))) )
{
closesocket (listen_s);
sockets_pair[0] = server_s;
@@ -417,7 +427,7 @@
unsigned long flags = 1;
if (0 != ioctlsocket (sock,
- FIONBIO,
+ (int) FIONBIO,
&flags))
return 0;
#endif /* MHD_WINSOCK_SOCKETS */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_sockets.h
^
|
@@ -35,11 +35,21 @@
#include "mhd_options.h"
#include <errno.h>
+#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
+#endif /* HAVE_STDBOOL_H */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <fcntl.h>
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif /* HAVE_STDDEF_H */
+#if defined(_MSC_FULL_VER) && ! defined (_SSIZE_T_DEFINED)
+# include <stdint.h>
+# define _SSIZE_T_DEFINED
+typedef intptr_t ssize_t;
+#endif /* !_SSIZE_T_DEFINED */
#if ! defined(MHD_POSIX_SOCKETS) && ! defined(MHD_WINSOCK_SOCKETS)
# if ! defined(_WIN32) || defined(__CYGWIN__)
@@ -60,17 +70,26 @@
# ifdef HAVE_SYS_TYPES_H
# include <sys/types.h> /* required on old platforms */
# endif
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# endif
+# ifdef HAVE_TIME_H
+# include <time.h>
+# endif
+# ifdef HAVE_STRING_H
+# include <string.h> /* for strerror() */
+# endif
# ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
# endif
# if defined(__VXWORKS__) || defined(__vxworks) || defined(OS_VXWORKS)
+# include <strings.h> /* required for FD_SET (bzero() function) */
# ifdef HAVE_SOCKLIB_H
# include <sockLib.h>
# endif /* HAVE_SOCKLIB_H */
# ifdef HAVE_INETLIB_H
# include <inetLib.h>
# endif /* HAVE_INETLIB_H */
-# include <strings.h> /* required for FD_SET (bzero() function) */
# endif /* __VXWORKS__ || __vxworks || OS_VXWORKS */
# ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
@@ -81,12 +100,6 @@
# ifdef HAVE_NET_IF_H
# include <net/if.h>
# endif
-# ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-# endif
-# ifdef HAVE_TIME_H
-# include <time.h>
-# endif
# ifdef HAVE_NETDB_H
# include <netdb.h>
# endif
@@ -100,9 +113,6 @@
/* for TCP_FASTOPEN and TCP_CORK */
# include <netinet/tcp.h>
# endif
-# ifdef HAVE_STRING_H
-# include <string.h> /* for strerror() */
-# endif
#elif defined(MHD_WINSOCK_SOCKETS)
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN 1
@@ -115,12 +125,6 @@
# include <poll.h>
#endif
-#include <stddef.h>
-#if defined(_MSC_FULL_VER) && ! defined (_SSIZE_T_DEFINED)
-# include <stdint.h>
-# define _SSIZE_T_DEFINED
-typedef intptr_t ssize_t;
-#endif /* !_SSIZE_T_DEFINED */
#ifdef __FreeBSD__
#include <sys/param.h> /* For __FreeBSD_version */
@@ -139,9 +143,10 @@
# include <stdio.h>
# include <stdlib.h>
/* Simple implementation of MHD_PANIC, to be used outside lib */
-# define MHD_PANIC(msg) do { fprintf (stderr, \
- "Abnormal termination at %d line in file %s: %s\n", \
- (int) __LINE__, __FILE__, msg); abort (); \
+# define MHD_PANIC(msg) \
+ do { fprintf (stderr, \
+ "Abnormal termination at %d line in file %s: %s\n", \
+ (int) __LINE__, __FILE__, msg); abort (); \
} while (0)
#endif /* ! MHD_PANIC */
@@ -199,6 +204,13 @@
# define USE_ACCEPT4 1
#endif
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
+ defined(MHD_WINSOCK_SOCKETS) || defined(__MACH__) || defined(__sun) || \
+ defined(SOMEBSD)
+/* Most of OSes inherit nonblocking setting from the listen socket */
+#define MHD_ACCEPT_INHERIT_NONBLOCK 1
+#endif
+
#if defined(HAVE_EPOLL_CREATE1) && defined(EPOLL_CLOEXEC)
# define USE_EPOLL_CREATE1 1
#endif /* HAVE_EPOLL_CREATE1 && EPOLL_CLOEXEC */
@@ -378,18 +390,13 @@
* boolean false otherwise.
*/
#if defined(MHD_POSIX_SOCKETS)
-# define MHD_SCKT_FD_FITS_FDSET_SETSIZE_(fd,pset,setsize) ((fd) < \
- ((MHD_socket) \
- setsize))
+# define MHD_SCKT_FD_FITS_FDSET_SETSIZE_(fd,pset,setsize) \
+ ((fd) < ((MHD_socket) setsize))
#elif defined(MHD_WINSOCK_SOCKETS)
-# define MHD_SCKT_FD_FITS_FDSET_SETSIZE_(fd,pset,setsize) ( ((void*) (pset)== \
- (void*) 0) || \
- (((fd_set*) (pset)) \
- ->fd_count < \
- ((unsigned) \
- setsize)) || \
- (FD_ISSET ((fd), \
- (pset))) )
+# define MHD_SCKT_FD_FITS_FDSET_SETSIZE_(fd,pset,setsize) \
+ ( ((void*) (pset)== (void*) 0) || \
+ (((fd_set*) (pset))->fd_count < ((unsigned) setsize)) || \
+ (FD_ISSET ((fd), (pset))) )
#endif
/**
@@ -400,9 +407,8 @@
* @return boolean true if FD can be added to fd_set,
* boolean false otherwise.
*/
-#define MHD_SCKT_FD_FITS_FDSET_(fd,pset) MHD_SCKT_FD_FITS_FDSET_SETSIZE_ ((fd), \
- (pset), \
- FD_SETSIZE)
+#define MHD_SCKT_FD_FITS_FDSET_(fd,pset) \
+ MHD_SCKT_FD_FITS_FDSET_SETSIZE_ ((fd), (pset), FD_SETSIZE)
/**
* Add FD to fd_set with specified FD_SETSIZE.
@@ -413,13 +419,13 @@
* system definition of FD_SET() is not used.
*/
#if defined(MHD_POSIX_SOCKETS)
-# define MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd,pset,setsize) FD_SET ((fd), \
- (pset))
+# define MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd,pset,setsize) \
+ FD_SET ((fd), (pset))
#elif defined(MHD_WINSOCK_SOCKETS)
-# define MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd,pset,setsize) \
- do { \
- u_int _i_ = 0; \
- fd_set*const _s_ = (fd_set*) (pset); \
+# define MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd,pset,setsize) \
+ do { \
+ u_int _i_ = 0; \
+ fd_set*const _s_ = (fd_set*) (pset); \
while ((_i_ < _s_->fd_count) && ((fd) != _s_->fd_array [_i_])) {++_i_;} \
if ((_i_ == _s_->fd_count)) {_s_->fd_array [_s_->fd_count ++] = (fd);} \
} while (0)
@@ -433,9 +439,9 @@
( ( (((void*) (r) == (void*) 0) || ((fd_set*) (r))->fd_count == 0) && \
(((void*) (w) == (void*) 0) || ((fd_set*) (w))->fd_count == 0) && \
(((void*) (e) == (void*) 0) || ((fd_set*) (e))->fd_count == 0) ) ? \
- ( ((void*) (t) == (void*) 0) ? 0 : \
- (Sleep (((struct timeval*) (t))->tv_sec * 1000 \
- + ((struct timeval*) (t))->tv_usec / 1000), 0) ) : \
+ ( ((void*) (t) == (void*) 0) ? 0 : \
+ (Sleep (((struct timeval*) (t))->tv_sec * 1000 \
+ + ((struct timeval*) (t))->tv_usec / 1000), 0) ) : \
(select ((int) 0,(r),(w),(e),(t))) )
#endif
@@ -470,8 +476,8 @@
# elif defined(__linux__)
# define MHD_POLL_EVENTS_ERR_DISC POLLPRI
# else /* ! __linux__ */
-# define MHD_POLL_EVENTS_ERR_DISC (MHD_POLLPRI_OR_ZERO \
- | MHD_POLLRDBAND_OR_ZERO)
+# define MHD_POLL_EVENTS_ERR_DISC \
+ (MHD_POLLPRI_OR_ZERO | MHD_POLLRDBAND_OR_ZERO)
# endif /* ! __linux__ */
/* MHD_POLL_REVENTS_ERR_DISC is 'revents' mask for errors and disconnect.
* Note: Out-of-band data is treated as error. */
@@ -597,6 +603,21 @@
# else /* ! ENETDOWN */
# define MHD_SCKT_ENETDOWN_ MHD_SCKT_MISSING_ERR_CODE_
# endif /* ! ENETDOWN */
+# ifdef EALREADY
+# define MHD_SCKT_EALREADY_ EALREADY
+# else /* ! EALREADY */
+# define MHD_SCKT_EALREADY_ MHD_SCKT_MISSING_ERR_CODE_
+# endif /* ! EALREADY */
+# ifdef EINPROGRESS
+# define MHD_SCKT_EINPROGRESS_ EINPROGRESS
+# else /* ! EINPROGRESS */
+# define MHD_SCKT_EINPROGRESS_ MHD_SCKT_MISSING_ERR_CODE_
+# endif /* ! EINPROGRESS */
+# ifdef EISCONN
+# define MHD_SCKT_EISCONN_ EISCONN
+# else /* ! EISCONN */
+# define MHD_SCKT_EISCONN_ MHD_SCKT_MISSING_ERR_CODE_
+# endif /* ! EISCONN */
#elif defined(MHD_WINSOCK_SOCKETS)
# define MHD_SCKT_EAGAIN_ WSAEWOULDBLOCK
# define MHD_SCKT_EWOULDBLOCK_ WSAEWOULDBLOCK
@@ -619,6 +640,9 @@
# define MHD_SCKT_EOPNOTSUPP_ WSAEOPNOTSUPP
# define MHD_SCKT_EACCESS_ WSAEACCES
# define MHD_SCKT_ENETDOWN_ WSAENETDOWN
+# define MHD_SCKT_EALREADY_ WSAEALREADY
+# define MHD_SCKT_EINPROGRESS_ WSAEACCES
+# define MHD_SCKT_EISCONN_ WSAEISCONN
#endif
/**
@@ -639,7 +663,7 @@
* @param err the WinSock error code.
* @return pointer to string description of specified WinSock error.
*/
-const char*MHD_W32_strerror_winsock_ (int err);
+const char *MHD_W32_strerror_winsock_ (int err);
#endif /* MHD_WINSOCK_SOCKETS */
@@ -671,9 +695,9 @@
* zero if specified @a err code is not defined on system
* and error was not set.
*/
-#define MHD_socket_try_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ != (err)) ? \
- (MHD_socket_fset_error_ ((err)), ! 0) : \
- 0)
+#define MHD_socket_try_set_error_(err) \
+ ( (MHD_SCKT_MISSING_ERR_CODE_ != (err)) ? \
+ (MHD_socket_fset_error_ ((err)), ! 0) : 0)
/**
* MHD_socket_set_error_() set socket system native error code to
@@ -714,8 +738,8 @@
* @a err equals to MHD_SCKT_E*_ @a code;
* boolean false otherwise
*/
-#define MHD_SCKT_ERR_IS_(err,code) ( (MHD_SCKT_MISSING_ERR_CODE_ != (code)) && \
- ((code) == (err)) )
+#define MHD_SCKT_ERR_IS_(err,code) \
+ ( (MHD_SCKT_MISSING_ERR_CODE_ != (code)) && ((code) == (err)) )
/**
* Check whether last socket error is equal to specified system
@@ -726,8 +750,8 @@
* last socket error equals to MHD_SCKT_E*_ @a code;
* boolean false otherwise
*/
-#define MHD_SCKT_LAST_ERR_IS_(code) MHD_SCKT_ERR_IS_ (MHD_socket_get_error_ (), \
- (code))
+#define MHD_SCKT_LAST_ERR_IS_(code) \
+ MHD_SCKT_ERR_IS_ (MHD_socket_get_error_ (), (code))
/* Specific error code checks */
@@ -748,10 +772,9 @@
#if MHD_SCKT_EAGAIN_ == MHD_SCKT_EWOULDBLOCK_
# define MHD_SCKT_ERR_IS_EAGAIN_(err) MHD_SCKT_ERR_IS_ ((err),MHD_SCKT_EAGAIN_)
#else /* MHD_SCKT_EAGAIN_ != MHD_SCKT_EWOULDBLOCK_ */
-# define MHD_SCKT_ERR_IS_EAGAIN_(err) (MHD_SCKT_ERR_IS_ ((err), \
- MHD_SCKT_EAGAIN_) || \
- MHD_SCKT_ERR_IS_ ((err), \
- MHD_SCKT_EWOULDBLOCK_) )
+# define MHD_SCKT_ERR_IS_EAGAIN_(err) \
+ ( MHD_SCKT_ERR_IS_ ((err), MHD_SCKT_EAGAIN_) || \
+ MHD_SCKT_ERR_IS_ ((err), MHD_SCKT_EWOULDBLOCK_) )
#endif /* MHD_SCKT_EAGAIN_ != MHD_SCKT_EWOULDBLOCK_ */
/**
@@ -759,17 +782,11 @@
* @return boolean true if @a err is any kind of "low resource" error,
* boolean false otherwise.
*/
-#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err) (MHD_SCKT_ERR_IS_ ((err), \
- MHD_SCKT_EMFILE_) \
- || \
- MHD_SCKT_ERR_IS_ ((err), \
- MHD_SCKT_ENFILE_) \
- || \
- MHD_SCKT_ERR_IS_ ((err), \
- MHD_SCKT_ENOMEM_) \
- || \
- MHD_SCKT_ERR_IS_ ((err), \
- MHD_SCKT_ENOBUFS_) )
+#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err) \
+ ( MHD_SCKT_ERR_IS_ ((err), MHD_SCKT_EMFILE_) || \
+ MHD_SCKT_ERR_IS_ ((err), MHD_SCKT_ENFILE_) || \
+ MHD_SCKT_ERR_IS_ ((err), MHD_SCKT_ENOMEM_) || \
+ MHD_SCKT_ERR_IS_ ((err), MHD_SCKT_ENOBUFS_) )
/**
* Check whether is given socket error is type of "incoming connection
@@ -778,11 +795,11 @@
* boolean false otherwise.
*/
#if defined(MHD_POSIX_SOCKETS)
-# define MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_(err) MHD_SCKT_ERR_IS_ ((err), \
- MHD_SCKT_ECONNABORTED_)
+# define MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_(err) \
+ MHD_SCKT_ERR_IS_ ((err), MHD_SCKT_ECONNABORTED_)
#elif defined(MHD_WINSOCK_SOCKETS)
-# define MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_(err) MHD_SCKT_ERR_IS_ ((err), \
- MHD_SCKT_ECONNRESET_)
+# define MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_(err) \
+ MHD_SCKT_ERR_IS_ ((err), MHD_SCKT_ECONNRESET_)
#endif
/**
@@ -791,11 +808,9 @@
* @return boolean true is @a err match described socket error code,
* boolean false otherwise.
*/
-#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err) (MHD_SCKT_ERR_IS_ ((err), \
- MHD_SCKT_ECONNRESET_) \
- || \
- MHD_SCKT_ERR_IS_ ((err), \
- MHD_SCKT_ECONNABORTED_))
+#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err) \
+ ( MHD_SCKT_ERR_IS_ ((err), MHD_SCKT_ECONNRESET_) || \
+ MHD_SCKT_ERR_IS_ ((err), MHD_SCKT_ECONNABORTED_) )
/* Specific error code set */
@@ -804,11 +819,11 @@
* available on platform.
*/
#if MHD_SCKT_MISSING_ERR_CODE_ != MHD_SCKT_ENOMEM_
-# define MHD_socket_set_error_to_ENOMEM() MHD_socket_set_error_ ( \
- MHD_SCKT_ENOMEM_)
+# define MHD_socket_set_error_to_ENOMEM() \
+ MHD_socket_set_error_ (MHD_SCKT_ENOMEM_)
#elif MHD_SCKT_MISSING_ERR_CODE_ != MHD_SCKT_ENOBUFS_
-# define MHD_socket_set_error_to_ENOMEM() MHD_socket_set_error_ ( \
- MHD_SCKT_ENOBUFS_)
+# define MHD_socket_set_error_to_ENOMEM() \
+ MHD_socket_set_error_ (MHD_SCKT_ENOBUFS_)
#else
# warning \
No suitable replacement for ENOMEM error codes is found. Edit this file and add replacement code which is defined on system.
@@ -825,13 +840,11 @@
#endif /* AF_UNIX */
#if defined(MHD_POSIX_SOCKETS) && defined(MHD_SCKT_LOCAL)
-# define MHD_socket_pair_(fdarr) (! socketpair (MHD_SCKT_LOCAL, SOCK_STREAM, 0, \
- (fdarr)))
+# define MHD_socket_pair_(fdarr) \
+ (! socketpair (MHD_SCKT_LOCAL, SOCK_STREAM, 0, (fdarr)))
# if defined(HAVE_SOCK_NONBLOCK)
-# define MHD_socket_pair_nblk_(fdarr) (! socketpair (MHD_SCKT_LOCAL, \
- SOCK_STREAM \
- | SOCK_NONBLOCK, 0, \
- (fdarr)))
+# define MHD_socket_pair_nblk_(fdarr) \
+ (! socketpair (MHD_SCKT_LOCAL, SOCK_STREAM | SOCK_NONBLOCK, 0, (fdarr)))
# endif /* HAVE_SOCK_NONBLOCK*/
#elif defined(MHD_WINSOCK_SOCKETS)
/**
@@ -916,8 +929,8 @@
/**
* Indicate that SIGPIPE can be suppressed by MHD for normal send() by flags
* or socket options.
- * If this macro is undefined, MHD cannot suppress SIGPIPE for normal
- * processing so sendfile() or writev() calls is not avoided.
+ * If this macro is undefined, MHD cannot suppress SIGPIPE for socket functions
+ * so sendfile() or writev() calls are avoided in application threads.
*/
#define MHD_SEND_SPIPE_SUPPRESS_POSSIBLE 1
#endif /* MHD_WINSOCK_SOCKETS || MHD_socket_nosignal_ || MSG_NOSIGNAL */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_str.c
^
|
@@ -27,9 +27,12 @@
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
-#endif
+#endif /* HAVE_STDBOOL_H */
+#include <string.h>
+#include "mhd_assert.h"
#include "mhd_limits.h"
+#include "mhd_assert.h"
#ifdef MHD_FAVOR_SMALL_CODE
#ifdef _MHD_static_inline
@@ -143,6 +146,7 @@
#endif /* Disable unused functions. */
+#if 0 /* Disable unused functions. */
/**
* Convert US-ASCII character to lower case.
* If character is upper case letter in US-ASCII than it's converted to lower
@@ -159,7 +163,6 @@
}
-#if 0 /* Disable unused functions. */
/**
* Convert US-ASCII character to upper case.
* If character is lower case letter in US-ASCII than it's converted to upper
@@ -219,6 +222,23 @@
}
+/**
+ * Caseless compare two characters.
+ *
+ * @param c1 the first char to compare
+ * @param c1 the second char to compare
+ * @return boolean 'true' if chars are caseless equal, false otherwise
+ */
+_MHD_static_inline bool
+charsequalcaseless (const char c1, const char c2)
+{
+ return ( (c1 == c2) ||
+ (isasciiupper (c1) ?
+ ((c1 - 'A' + 'a') == c2) :
+ ((c1 == (c2 - 'A' + 'a')) && isasciiupper (c2))) );
+}
+
+
#else /* !INLINE_FUNC */
@@ -331,12 +351,26 @@
( (((char) (c)) >= 'a' && ((char) (c)) <= 'f') ? \
(int) (((unsigned char) (c)) - 'a' + 10) : \
(int) (-1) )))
+
+/**
+ * Caseless compare two characters.
+ *
+ * @param c1 the first char to compare
+ * @param c1 the second char to compare
+ * @return boolean 'true' if chars are caseless equal, false otherwise
+ */
+#define charsequalcaseless(c1, c2) \
+ ( ((c1) == (c2)) || \
+ (isasciiupper (c1) ? \
+ (((c1) - 'A' + 'a') == (c2)) : \
+ (((c1) == ((c2) - 'A' + 'a')) && isasciiupper (c2))) )
+
#endif /* !INLINE_FUNC */
#ifndef MHD_FAVOR_SMALL_CODE
/**
- * Check two string for equality, ignoring case of US-ASCII letters.
+ * Check two strings for equality, ignoring case of US-ASCII letters.
*
* @param str1 first string to compare
* @param str2 second string to compare
@@ -350,11 +384,13 @@
{
const char c1 = *str1;
const char c2 = *str2;
- if ( (c1 != c2) &&
- (toasciilower (c1) != toasciilower (c2)) )
+ if (charsequalcaseless (c1, c2))
+ {
+ str1++;
+ str2++;
+ }
+ else
return 0;
- str1++;
- str2++;
}
return 0 == (*str2);
}
@@ -387,8 +423,9 @@
const char c2 = str2[i];
if (0 == c2)
return 0 == c1;
- if ( (c1 != c2) &&
- (toasciilower (c1) != toasciilower (c2)) )
+ if (charsequalcaseless (c1, c2))
+ continue;
+ else
return 0;
}
return ! 0;
@@ -415,8 +452,9 @@
{
const char c1 = str1[i];
const char c2 = str2[i];
- if ( (c1 != c2) &&
- (toasciilower (c1) != toasciilower (c2)) )
+ if (charsequalcaseless (c1, c2))
+ continue;
+ else
return 0;
}
return ! 0;
@@ -428,7 +466,7 @@
* Token could be surrounded by spaces and tabs and delimited by comma.
* Match succeed if substring between start, end (of string) or comma
* contains only case-insensitive token and optional spaces and tabs.
- * @warning token must not contain null-charters except optional
+ * @warning token must not contain null-characters except optional
* terminating null-character.
* @param str the string to check
* @param token the token to find
@@ -460,8 +498,7 @@
if (0 == sc)
return false;
- if ( (sc != tc) &&
- (toasciilower (sc) != toasciilower (tc)) )
+ if (! charsequalcaseless (sc, tc))
break;
if (i >= token_len)
{
@@ -484,6 +521,351 @@
}
+/**
+ * Remove case-insensitive @a token from the @a str and put result
+ * to the output @a buf.
+ *
+ * Tokens in @a str could be surrounded by spaces and tabs and delimited by
+ * comma. The token match succeed if substring between start, end (of string)
+ * or comma contains only case-insensitive token and optional spaces and tabs.
+ * The quoted strings and comments are not supported by this function.
+ *
+ * The output string is normalised: empty tokens and repeated whitespaces
+ * are removed, no whitespaces before commas, exactly one space is used after
+ * each comma.
+ *
+ * @param str the string to process
+ * @param str_len the length of the @a str, not including optional
+ * terminating null-character.
+ * @param token the token to find
+ * @param token_len the length of @a token, not including optional
+ * terminating null-character.
+ * @param[out] buf the output buffer, not null-terminated.
+ * @param[in,out] buf_size pointer to the size variable, at input it
+ * is the size of allocated buffer, at output
+ * it is the size of the resulting string (can
+ * be up to 50% larger than input) or negative value
+ * if there is not enough space for the result
+ * @return 'true' if token has been removed,
+ * 'false' otherwise.
+ */
+bool
+MHD_str_remove_token_caseless_ (const char *str,
+ size_t str_len,
+ const char *const token,
+ const size_t token_len,
+ char *buf,
+ ssize_t *buf_size)
+{
+ const char *s1; /**< the "input" string / character */
+ char *s2; /**< the "output" string / character */
+ size_t t_pos; /**< position of matched character in the token */
+ bool token_removed;
+
+ mhd_assert (NULL == memchr (token, 0, token_len));
+ mhd_assert (NULL == memchr (token, ' ', token_len));
+ mhd_assert (NULL == memchr (token, '\t', token_len));
+ mhd_assert (NULL == memchr (token, ',', token_len));
+ mhd_assert (0 <= *buf_size);
+
+ s1 = str;
+ s2 = buf;
+ token_removed = false;
+
+ while ((size_t) (s1 - str) < str_len)
+ {
+ const char *cur_token; /**< the first char of current token */
+ size_t copy_size;
+
+ /* Skip any initial whitespaces and empty tokens */
+ while ( ((size_t) (s1 - str) < str_len) &&
+ ((' ' == *s1) || ('\t' == *s1) || (',' == *s1)) )
+ s1++;
+
+ /* 's1' points to the first char of token in the input string or
+ * points just beyond the end of the input string */
+
+ if ((size_t) (s1 - str) >= str_len)
+ break; /* Nothing to copy, end of the input string */
+
+ /* 's1' points to the first char of token in the input string */
+
+ cur_token = s1; /* the first char of input token */
+
+ /* Check the token with case-insensetive match */
+ t_pos = 0;
+ while ( ((size_t) (s1 - str) < str_len) && (token_len > t_pos) &&
+ (charsequalcaseless (*s1, token[t_pos])) )
+ {
+ s1++;
+ t_pos++;
+ }
+ /* s1 may point just beyond the end of the input string */
+ if ( (token_len == t_pos) && (0 != token_len) )
+ {
+ /* 'token' matched, check that current input token does not have
+ * any suffixes */
+ while ( ((size_t) (s1 - str) < str_len) &&
+ ((' ' == *s1) || ('\t' == *s1)) )
+ s1++;
+ /* 's1' points to the first non-whitespace char after the token matched
+ * requested token or points just beyond the end of the input string after
+ * the requested token */
+ if (((size_t) (s1 - str) == str_len) || (',' == *s1))
+ {/* full token match, do not copy current token to the output */
+ token_removed = true;
+ continue;
+ }
+ }
+
+ /* 's1' points to first non-whitespace char, to some char after
+ * first non-whitespace char in the token in the input string, to
+ * the ',', or just beyond the end of the input string */
+ /* The current token in the input string does not match the token
+ * to exclude, it must be copied to the output string */
+ /* the current token size excluding leading whitespaces and current char */
+ copy_size = (size_t) (s1 - cur_token);
+ if (buf == s2)
+ { /* The first token to copy to the output */
+ if (buf + *buf_size < s2 + copy_size)
+ { /* Not enough space in the output buffer */
+ *buf_size = (ssize_t) -1;
+ return false;
+ }
+ }
+ else
+ { /* Some token was already copied to the output buffer */
+ if (buf + *buf_size < s2 + copy_size + 2)
+ { /* Not enough space in the output buffer */
+ *buf_size = (ssize_t) -1;
+ return false;
+ }
+ *(s2++) = ',';
+ *(s2++) = ' ';
+ }
+ /* Copy non-matched token to the output */
+ if (0 != copy_size)
+ {
+ memcpy (s2, cur_token, copy_size);
+ s2 += copy_size;
+ }
+
+ while ( ((size_t) (s1 - str) < str_len) && (',' != *s1))
+ {
+ /* 's1' points to first non-whitespace char, to some char after
+ * first non-whitespace char in the token in the input string */
+ /* Copy all non-whitespace chars from the current token in
+ * the input string */
+ while ( ((size_t) (s1 - str) < str_len) &&
+ (',' != *s1) && (' ' != *s1) && ('\t' != *s1) )
+ {
+ if (buf + *buf_size <= s2) /* '<= s2' equals '< s2 + 1' */
+ { /* Not enough space in the output buffer */
+ *buf_size = (ssize_t) -1;
+ return false;
+ }
+ *(s2++) = *(s1++);
+ }
+ /* 's1' points to some whitespace char in the token in the input
+ * string, to the ',', or just beyond the end of the input string */
+ /* Skip all whitespaces */
+ while ( ((size_t) (s1 - str) < str_len) &&
+ ((' ' == *s1) || ('\t' == *s1)) )
+ s1++;
+
+ /* 's1' points to the first non-whitespace char in the input string
+ * after whitespace chars, to the ',', or just beyond the end of
+ * the input string */
+ if (((size_t) (s1 - str) < str_len) && (',' != *s1))
+ { /* Not the end of the current token */
+ if (buf + *buf_size <= s2) /* '<= s2' equals '< s2 + 1' */
+ { /* Not enough space in the output buffer */
+ *buf_size = (ssize_t) -1;
+ return false;
+ }
+ *(s2++) = ' ';
+ }
+ }
+ }
+ mhd_assert (((ssize_t) (s2 - buf)) <= *buf_size);
+ *buf_size = (ssize_t) (s2 - buf);
+ return token_removed;
+}
+
+
+/**
+ * Perform in-place case-insensitive removal of @a tokens from the @a str.
+ *
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * The token match succeed if substring between start, end (of the string), or
+ * comma contains only case-insensitive token and optional spaces and tabs.
+ * The quoted strings and comments are not supported by this function.
+ *
+ * The input string must be normalised: empty tokens and repeated whitespaces
+ * are removed, no whitespaces before commas, exactly one space is used after
+ * each comma. The string is updated in-place.
+ *
+ * Behavior is undefined is the input string in not normalised.
+ *
+ * @param[in,out] str the string to update
+ * @param[in,out] str_len the length of the @a str, not including optional
+ * terminating null-character, not null-terminated
+ * @param tokens the token to find
+ * @param tokens_len the length of @a tokens, not including optional
+ * terminating null-character.
+ * @return 'true' if any token has been removed,
+ * 'false' otherwise.
+ */
+bool
+MHD_str_remove_tokens_caseless_ (char *str,
+ size_t *str_len,
+ const char *const tokens,
+ const size_t tokens_len)
+{
+ const char *const t = tokens; /**< a short alias for @a tokens */
+ size_t pt; /**< position in @a tokens */
+ bool token_removed;
+
+ mhd_assert (NULL == memchr (tokens, 0, tokens_len));
+
+ token_removed = false;
+ pt = 0;
+
+ while (pt < tokens_len && *str_len != 0)
+ {
+ const char *tkn; /**< the current token */
+ size_t tkn_len;
+
+ /* Skip any initial whitespaces and empty tokens in 'tokens' */
+ while ( (pt < tokens_len) &&
+ ((' ' == t[pt]) || ('\t' == t[pt]) || (',' == t[pt])) )
+ pt++;
+
+ if (pt >= tokens_len)
+ break; /* No more tokens, nothing to remove */
+
+ /* Found non-whitespace char which is not a comma */
+ tkn = t + pt;
+ do
+ {
+ do
+ {
+ pt++;
+ } while (pt < tokens_len &&
+ (' ' != t[pt] && '\t' != t[pt] && ',' != t[pt]));
+ /* Found end of the token string, space, tab, or comma */
+ tkn_len = pt - (size_t) (tkn - t);
+
+ /* Skip all spaces and tabs */
+ while (pt < tokens_len && (' ' == t[pt] || '\t' == t[pt]))
+ pt++;
+ /* Found end of the token string or non-whitespace char */
+ } while(pt < tokens_len && ',' != t[pt]);
+
+ /* 'tkn' is the input token with 'tkn_len' chars */
+ mhd_assert (0 != tkn_len);
+
+ if (*str_len == tkn_len)
+ {
+ if (MHD_str_equal_caseless_bin_n_ (str, tkn, tkn_len))
+ {
+ *str_len = 0;
+ token_removed = true;
+ }
+ continue;
+ }
+ /* 'tkn' cannot match part of 'str' if length of 'tkn' is larger
+ * than length of 'str'.
+ * It's know that 'tkn' is not equal to the 'str' (was checked previously).
+ * As 'str' is normalized when 'tkn' is not equal to the 'str'
+ * it is required that 'str' to be at least 3 chars larger then 'tkn'
+ * (the comma, the space and at least one additional character for the next
+ * token) to remove 'tkn' from the 'str'. */
+ if (*str_len > tkn_len + 2)
+ { /* Remove 'tkn' from the input string */
+ size_t pr; /**< the 'read' position in the @a str */
+ size_t pw; /**< the 'write' position in the @a str */
+
+ pr = 0;
+ pw = 0;
+
+ do
+ {
+ mhd_assert (pr >= pw);
+ mhd_assert ((*str_len) >= (pr + tkn_len));
+ if ( ( ((*str_len) == (pr + tkn_len)) || (',' == str[pr + tkn_len]) ) &&
+ MHD_str_equal_caseless_bin_n_ (str + pr, tkn, tkn_len) )
+ {
+ /* current token in the input string matches the 'tkn', skip it */
+ mhd_assert ((*str_len == pr + tkn_len) || \
+ (' ' == str[pr + tkn_len + 1])); /* 'str' must be normalized */
+ token_removed = true;
+ /* Advance to the next token in the input string or beyond
+ * the end of the input string. */
+ pr += tkn_len + 2;
+ }
+ else
+ {
+ /* current token in the input string does not match the 'tkn',
+ * copy to the output */
+ if (0 != pw)
+ { /* not the first output token, add ", " to separate */
+ if (pr != pw + 2)
+ {
+ str[pw++] = ',';
+ str[pw++] = ' ';
+ }
+ else
+ pw += 2; /* 'str' is not yet modified in this round */
+ }
+ do
+ {
+ if (pr != pw)
+ str[pw] = str[pr];
+ pr++;
+ pw++;
+ } while (pr < *str_len && ',' != str[pr]);
+ /* Advance to the next token in the input string or beyond
+ * the end of the input string. */
+ pr += 2;
+ }
+ /* 'pr' should point to the next token in the input string or beyond
+ * the end of the input string */
+ if ((*str_len) < (pr + tkn_len))
+ { /* The rest of the 'str + pr' is too small to match 'tkn' */
+ if ((*str_len) > pr)
+ { /* Copy the rest of the string */
+ size_t copy_size;
+ copy_size = *str_len - pr;
+ if (0 != pw)
+ { /* not the first output token, add ", " to separate */
+ if (pr != pw + 2)
+ {
+ str[pw++] = ',';
+ str[pw++] = ' ';
+ }
+ else
+ pw += 2; /* 'str' is not yet modified in this round */
+ }
+ if (pr != pw)
+ memmove (str + pw, str + pr, copy_size);
+ pw += copy_size;
+ }
+ *str_len = pw;
+ break;
+ }
+ mhd_assert ((' ' != str[0]) && ('\t' != str[0]));
+ mhd_assert ((0 == pr) || (3 <= pr));
+ mhd_assert ((0 == pr) || (' ' == str[pr - 1]));
+ mhd_assert ((0 == pr) || (',' == str[pr - 2]));
+ } while (1);
+ }
+ }
+
+ return token_removed;
+}
+
+
#ifndef MHD_FAVOR_SMALL_CODE
/* Use individual function for each case */
@@ -803,9 +1185,9 @@
if (i)
{
if (8 == val_size)
- *(uint64_t*) out_val = res;
+ *(uint64_t *) out_val = res;
else if (4 == val_size)
- *(uint32_t*) out_val = (uint32_t) res;
+ *(uint32_t *) out_val = (uint32_t) res;
else
return 0;
}
@@ -814,3 +1196,161 @@
#endif /* MHD_FAVOR_SMALL_CODE */
+
+
+size_t
+MHD_uint32_to_strx (uint32_t val,
+ char *buf,
+ size_t buf_size)
+{
+ size_t o_pos = 0; /**< position of the output character */
+ int digit_pos = 8; /** zero-based, digit position in @a 'val' */
+ int digit;
+
+ /* Skip leading zeros */
+ do
+ {
+ digit_pos--;
+ digit = (int) (val >> 28);
+ val <<= 4;
+ } while ((0 == digit) && (0 != digit_pos));
+
+ while (o_pos < buf_size)
+ {
+ buf[o_pos++] = (digit <= 9) ? ('0' + (char) digit) :
+ ('A' + (char) digit - 10);
+ if (0 == digit_pos)
+ return o_pos;
+ digit_pos--;
+ digit = (int) (val >> 28);
+ val <<= 4;
+ }
+ return 0; /* The buffer is too small */
+}
+
+
+#ifndef MHD_FAVOR_SMALL_CODE
+size_t
+MHD_uint16_to_str (uint16_t val,
+ char *buf,
+ size_t buf_size)
+{
+ char *chr; /**< pointer to the current printed digit */
+ /* The biggest printable number is 65535 */
+ uint16_t divisor = UINT16_C (10000);
+ int digit;
+
+ chr = buf;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+
+ /* Do not print leading zeros */
+ while ((0 == digit) && (1 < divisor))
+ {
+ divisor /= 10;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+ }
+
+ while (0 != buf_size)
+ {
+ *chr = (char) digit + '0';
+ chr++;
+ buf_size--;
+ if (1 == divisor)
+ return (size_t) (chr - buf);
+ val %= divisor;
+ divisor /= 10;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+ }
+ return 0; /* The buffer is too small */
+}
+
+
+#endif /* !MHD_FAVOR_SMALL_CODE */
+
+
+size_t
+MHD_uint64_to_str (uint64_t val,
+ char *buf,
+ size_t buf_size)
+{
+ char *chr; /**< pointer to the current printed digit */
+ /* The biggest printable number is 18446744073709551615 */
+ uint64_t divisor = UINT64_C (10000000000000000000);
+ int digit;
+
+ chr = buf;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+
+ /* Do not print leading zeros */
+ while ((0 == digit) && (1 < divisor))
+ {
+ divisor /= 10;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+ }
+
+ while (0 != buf_size)
+ {
+ *chr = (char) digit + '0';
+ chr++;
+ buf_size--;
+ if (1 == divisor)
+ return (size_t) (chr - buf);
+ val %= divisor;
+ divisor /= 10;
+ digit = (int) (val / divisor);
+ mhd_assert (digit < 10);
+ }
+ return 0; /* The buffer is too small */
+}
+
+
+size_t
+MHD_uint8_to_str_pad (uint8_t val,
+ uint8_t min_digits,
+ char *buf,
+ size_t buf_size)
+{
+ size_t pos; /**< the position of the current printed digit */
+ int digit;
+ mhd_assert (3 >= min_digits);
+ if (0 == buf_size)
+ return 0;
+
+ pos = 0;
+ digit = val / 100;
+ if (0 == digit)
+ {
+ if (3 <= min_digits)
+ buf[pos++] = '0';
+ }
+ else
+ {
+ buf[pos++] = '0' + digit;
+ val %= 100;
+ min_digits = 2;
+ }
+
+ if (buf_size <= pos)
+ return 0;
+ digit = val / 10;
+ if (0 == digit)
+ {
+ if (2 <= min_digits)
+ buf[pos++] = '0';
+ }
+ else
+ {
+ buf[pos++] = '0' + digit;
+ val %= 10;
+ }
+
+ if (buf_size <= pos)
+ return 0;
+ buf[pos++] = '0' + val;
+ return pos;
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_str.h
^
|
@@ -27,13 +27,22 @@
#define MHD_STR_H 1
#include "mhd_options.h"
-
#include <stdint.h>
+#ifdef HAVE_STDDEF_H
#include <stddef.h>
+#endif /* HAVE_STDDEF_H */
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#endif /* HAVE_STDBOOL_H */
+#if defined(_MSC_FULL_VER) && ! defined (_SSIZE_T_DEFINED)
+#define _SSIZE_T_DEFINED
+typedef intptr_t ssize_t;
+#endif /* !_SSIZE_T_DEFINED */
+
#ifdef MHD_FAVOR_SMALL_CODE
#include "mhd_limits.h"
#endif /* MHD_FAVOR_SMALL_CODE */
@@ -45,6 +54,18 @@
#define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
#endif /* ! MHD_STATICSTR_LEN_ */
+struct _MHD_str_w_len
+{
+ const char *str;
+ const size_t len;
+};
+
+/**
+ * Static string initialiser for struct _MHD_str_w_len
+ */
+#define _MHD_S_STR_W_LEN(str) { str, MHD_STATICSTR_LEN_(str) }
+
+
/*
* Block of functions/macros that use US-ASCII charset as required by HTTP
* standards. Not affected by current locale settings.
@@ -52,7 +73,7 @@
#ifndef MHD_FAVOR_SMALL_CODE
/**
- * Check two string for equality, ignoring case of US-ASCII letters.
+ * Check two strings for equality, ignoring case of US-ASCII letters.
* @param str1 first string to compare
* @param str2 second string to compare
* @return non-zero if two strings are equal, zero otherwise.
@@ -104,7 +125,7 @@
* Token could be surrounded by spaces and tabs and delimited by comma.
* Match succeed if substring between start, end of string or comma
* contains only case-insensitive token and optional spaces and tabs.
- * @warning token must not contain null-charters except optional
+ * @warning token must not contain null-characters except optional
* terminating null-character.
* @param str the string to check
* @param token the token to find
@@ -130,6 +151,74 @@
#define MHD_str_has_s_token_caseless_(str,tkn) \
MHD_str_has_token_caseless_ ((str),(tkn),MHD_STATICSTR_LEN_ (tkn))
+
+/**
+ * Remove case-insensitive @a token from the @a str and put result
+ * to the output @a buf.
+ *
+ * Tokens in @a str could be surrounded by spaces and tabs and delimited by
+ * comma. The token match succeed if substring between start, end (of string)
+ * or comma contains only case-insensitive token and optional spaces and tabs.
+ * The quoted strings and comments are not supported by this function.
+ *
+ * The output string is normalised: empty tokens and repeated whitespaces
+ * are removed, no whitespaces before commas, exactly one space is used after
+ * each comma.
+ *
+ * @param str the string to process
+ * @param str_len the length of the @a str, not including optional
+ * terminating null-character.
+ * @param token the token to find
+ * @param token_len the length of @a token, not including optional
+ * terminating null-character.
+ * @param[out] buf the output buffer, not null-terminated.
+ * @param[in,out] buf_size pointer to the size variable, at input it
+ * is the size of allocated buffer, at output
+ * it is the size of the resulting string (can
+ * be up to 50% larger than input) or negative value
+ * if there is not enough space for the result
+ * @return 'true' if token has been removed,
+ * 'false' otherwise.
+ */
+bool
+MHD_str_remove_token_caseless_ (const char *str,
+ size_t str_len,
+ const char *const token,
+ const size_t token_len,
+ char *buf,
+ ssize_t *buf_size);
+
+
+/**
+ * Perform in-place case-insensitive removal of @a tokens from the @a str.
+ *
+ * Token could be surrounded by spaces and tabs and delimited by comma.
+ * The token match succeed if substring between start, end (of the string), or
+ * comma contains only case-insensitive token and optional spaces and tabs.
+ * The quoted strings and comments are not supported by this function.
+ *
+ * The input string must be normalised: empty tokens and repeated whitespaces
+ * are removed, no whitespaces before commas, exactly one space is used after
+ * each comma. The string is updated in-place.
+ *
+ * Behavior is undefined is the input string in not normalised.
+ *
+ * @param[in,out] str the string to update
+ * @param[in,out] str_len the length of the @a str, not including optional
+ * terminating null-character, not null-terminated
+ * @param tokens the token to find
+ * @param tokens_len the length of @a tokens, not including optional
+ * terminating null-character.
+ * @return 'true' if any token has been removed,
+ * 'false' otherwise.
+ */
+bool
+MHD_str_remove_tokens_caseless_ (char *str,
+ size_t *str_len,
+ const char *const tokens,
+ const size_t tokens_len);
+
+
#ifndef MHD_FAVOR_SMALL_CODE
/* Use individual function for each case to improve speed */
@@ -288,4 +377,77 @@
#endif /* MHD_FAVOR_SMALL_CODE */
+
+/**
+ * Convert uint32_t value to hexdecimal US-ASCII string.
+ * @note: result is NOT zero-terminated.
+ * @param val the value to convert
+ * @param buf the buffer to result to
+ * @param buf_size size of the @a buffer
+ * @return number of characters has been put to the @a buf,
+ * zero if buffer is too small (buffer may be modified).
+ */
+size_t
+MHD_uint32_to_strx (uint32_t val,
+ char *buf,
+ size_t buf_size);
+
+
+#ifndef MHD_FAVOR_SMALL_CODE
+/**
+ * Convert uint16_t value to decimal US-ASCII string.
+ * @note: result is NOT zero-terminated.
+ * @param val the value to convert
+ * @param buf the buffer to result to
+ * @param buf_size size of the @a buffer
+ * @return number of characters has been put to the @a buf,
+ * zero if buffer is too small (buffer may be modified).
+ */
+size_t
+MHD_uint16_to_str (uint16_t val,
+ char *buf,
+ size_t buf_size);
+
+#else /* MHD_FAVOR_SMALL_CODE */
+#define MHD_uint16_to_str(v,b,s) MHD_uint64_to_str(v,b,s)
+#endif /* MHD_FAVOR_SMALL_CODE */
+
+
+/**
+ * Convert uint64_t value to decimal US-ASCII string.
+ * @note: result is NOT zero-terminated.
+ * @param val the value to convert
+ * @param buf the buffer to result to
+ * @param buf_size size of the @a buffer
+ * @return number of characters has been put to the @a buf,
+ * zero if buffer is too small (buffer may be modified).
+ */
+size_t
+MHD_uint64_to_str (uint64_t val,
+ char *buf,
+ size_t buf_size);
+
+
+/**
+ * Convert uint16_t value to decimal US-ASCII string padded with
+ * zeros on the left side.
+ *
+ * @note: result is NOT zero-terminated.
+ * @param val the value to convert
+ * @param min_digits the minimal number of digits to print,
+ * output padded with zeros on the left side,
+ * 'zero' value is interpreted as 'one',
+ * valid values are 3, 2, 1, 0
+ * @param buf the buffer to result to
+ * @param buf_size size of the @a buffer
+ * @return number of characters has been put to the @a buf,
+ * zero if buffer is too small (buffer may be modified).
+ */
+size_t
+MHD_uint8_to_str_pad (uint8_t val,
+ uint8_t min_digits,
+ char *buf,
+ size_t buf_size);
+
+
#endif /* MHD_STR_H */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_threads.c
^
|
@@ -30,7 +30,9 @@
#include <process.h>
#endif
#ifdef MHD_USE_THREAD_NAME_
+#ifdef HAVE_STDLIB_H
#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>
#endif /* HAVE_PTHREAD_NP_H */
@@ -49,8 +51,8 @@
#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \
defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
# define MHD_USE_THREAD_ATTR_SETNAME 1
-#endif \
- /* HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD || HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI */
+#endif /* HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD || \
+ HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI */
#if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \
defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) \
@@ -73,12 +75,12 @@
#if defined(HAVE_PTHREAD_SETNAME_NP_GNU)
return ! pthread_setname_np (thread_id, thread_name);
#elif defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD)
- /* FreeBSD and OpenBSD use different name and void return type */
+ /* FreeBSD and OpenBSD use different function name and void return type */
pthread_set_name_np (thread_id, thread_name);
return ! 0;
#elif defined(HAVE_PTHREAD_SETNAME_NP_NETBSD)
/* NetBSD use 3 arguments: second argument is string in printf-like format,
- * third argument is single argument for printf;
+ * third argument is a single argument for printf();
* OSF1 use 3 arguments too, but last one always must be zero (NULL).
* MHD doesn't use '%' in thread names, so both form are used in same way.
*/
@@ -213,13 +215,13 @@
return ! res;
#elif defined(MHD_USE_W32_THREADS)
-#if SIZE_MAX != UINT_MAX
+#if SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT
if (stack_size > UINT_MAX)
{
errno = EINVAL;
return 0;
}
-#endif /* SIZE_MAX != UINT_MAX */
+#endif /* SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT */
thread->handle = (MHD_thread_handle_)
_beginthreadex (NULL,
@@ -295,7 +297,7 @@
*/
int
MHD_create_named_thread_ (MHD_thread_handle_ID_ *thread,
- const char*thread_name,
+ const char *thread_name,
size_t stack_size,
MHD_THREAD_START_ROUTINE_ start_routine,
void *arg)
@@ -359,7 +361,7 @@
if (! MHD_create_thread_ (thread,
stack_size,
&named_thread_starter,
- (void*) param))
+ (void *) param))
{
free (param);
return 0;
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/mhd_threads.h
^
|
@@ -39,9 +39,11 @@
#include "mhd_options.h"
#ifdef HAVE_STDDEF_H
# include <stddef.h> /* for size_t */
-#else /* ! HAVE_STDDEF_H */
+#elif defined(HAVE_STDLIB_H)
# include <stdlib.h> /* for size_t */
-#endif /* ! HAVE_STDDEF_H */
+#else /* ! HAVE_STDLIB_H */
+# include <stdio.h> /* for size_t */
+#endif /* ! HAVE_STDLIB_H */
#if defined(MHD_USE_POSIX_THREADS)
# undef HAVE_CONFIG_H
@@ -148,10 +150,9 @@
* @param thread handle to watch
* @return nonzero on success, zero otherwise
*/
-#define MHD_join_thread_(thread) (WAIT_OBJECT_0 == WaitForSingleObject ( \
- (thread), INFINITE) ? (CloseHandle ( \
- (thread)), ! 0) : \
- 0)
+#define MHD_join_thread_(thread) \
+ ( (WAIT_OBJECT_0 == WaitForSingleObject ( (thread), INFINITE)) ? \
+ (CloseHandle ( (thread)), ! 0) : 0 )
#endif
#if defined(MHD_USE_POSIX_THREADS)
@@ -237,7 +238,7 @@
*/
int
MHD_create_named_thread_ (MHD_thread_handle_ID_ *thread,
- const char*thread_name,
+ const char *thread_name,
size_t stack_size,
MHD_THREAD_START_ROUTINE_ start_routine,
void *arg);
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/microhttpd_dll_res.rc.in
^
|
@@ -19,11 +19,11 @@
VALUE "ProductName", "GNU libmicrohttpd\0"
VALUE "ProductVersion", "@PACKAGE_VERSION@\0"
VALUE "FileVersion", "@PACKAGE_VERSION@\0"
- VALUE "FileDescription", "GNU libmicrohttpd DLL for Windows (MinGW build)\0"
+ VALUE "FileDescription", "GNU libmicrohttpd DLL for Windows (MinGW build, @W32CRT@ run-time lib)\0"
VALUE "InternalName", "libmicrohttpd\0"
VALUE "OriginalFilename", "libmicrohttpd-@MHD_W32_DLL_SUFF@.dll\0"
VALUE "CompanyName", "Free Software Foundation\0"
- VALUE "LegalCopyright", "Copyright (C) 2007-2020 Christian Grothoff, Evgeny Grin and project contributors\0"
+ VALUE "LegalCopyright", "Copyright (C) 2007-2021 Christian Grothoff, Evgeny Grin, and project contributors\0"
VALUE "Comments", "http://www.gnu.org/software/libmicrohttpd/\0"
END
END
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/postprocessor.c
^
|
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2007-2021 Daniel Pittman and Christian Grothoff
+ Copyright (C) 2007-2021 Daniel Pittman, Christian Grothoff, and Evgeny Grin
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -21,6 +21,7 @@
* @file postprocessor.c
* @brief Methods for parsing POST data
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "internal.h"
@@ -47,9 +48,9 @@
PP_NextBoundary,
/* url encoding-states */
+ PP_ProcessKey,
PP_ProcessValue,
PP_Callback,
- PP_ExpectNewLine,
/* post encoding-states */
PP_ProcessEntryHeaders,
@@ -355,10 +356,15 @@
size_t xoff;
mhd_assert (pp->xbuf_pos < sizeof (xbuf));
+ /* 'value_start' and 'value_end' must be either both non-NULL or both NULL */
+ mhd_assert ( (NULL == value_start) || (NULL != value_end) );
+ mhd_assert ( (NULL != value_start) || (NULL == value_end) );
+ mhd_assert ( (NULL == last_escape) || (NULL != value_start) );
/* move remaining input from previous round into processing buffer */
- memcpy (xbuf,
- pp->xbuf,
- pp->xbuf_pos);
+ if (0 != pp->xbuf_pos)
+ memcpy (xbuf,
+ pp->xbuf,
+ pp->xbuf_pos);
xoff = pp->xbuf_pos;
pp->xbuf_pos = 0;
if ( (NULL != last_escape) &&
@@ -381,11 +387,14 @@
if (delta > XBUF_SIZE - xoff)
delta = XBUF_SIZE - xoff;
/* move (additional) input into processing buffer */
- memcpy (&xbuf[xoff],
- value_start,
- delta);
- xoff += delta;
- value_start += delta;
+ if (0 != delta)
+ {
+ memcpy (&xbuf[xoff],
+ value_start,
+ delta);
+ xoff += delta;
+ value_start += delta;
+ }
/* find if escape sequence is at the end of the processing buffer;
if so, exclude those from processing (reduce delta to point at
end of processed region) */
@@ -430,8 +439,11 @@
mhd_assert (xoff < sizeof (xbuf));
/* unescape */
xbuf[xoff] = '\0'; /* 0-terminate in preparation */
- MHD_unescape_plus (xbuf);
- xoff = MHD_http_unescape (xbuf);
+ if (0 != xoff)
+ {
+ MHD_unescape_plus (xbuf);
+ xoff = MHD_http_unescape (xbuf);
+ }
/* finally: call application! */
if (pp->must_ikvi || (0 != xoff) )
{
@@ -453,10 +465,13 @@
pp->value_offset += xoff;
if (cut)
break;
- xbuf[delta] = '%'; /* undo 0-termination */
- memmove (xbuf,
- &xbuf[delta],
- clen);
+ if (0 != clen)
+ {
+ xbuf[delta] = '%'; /* undo 0-termination */
+ memmove (xbuf,
+ &xbuf[delta],
+ clen);
+ }
xoff = clen;
}
}
@@ -495,41 +510,80 @@
case PP_Error:
/* clearly impossible as per while loop invariant */
abort ();
- break;
+ break; /* Unreachable */
case PP_Init:
- /* key phase */
- if (NULL == start_key)
+ /* initial phase */
+ mhd_assert (NULL == start_key);
+ mhd_assert (NULL == end_key);
+ mhd_assert (NULL == start_value);
+ mhd_assert (NULL == end_value);
+ switch (post_data[poff])
+ {
+ case '=':
+ /* Case: (no key)'=' */
+ /* Empty key with value */
+ pp->state = PP_Error;
+ continue;
+ case '&':
+ /* Case: (no key)'&' */
+ /* Empty key without value */
+ poff++;
+ continue;
+ case '\n':
+ case '\r':
+ /* Case: (no key)'\n' or (no key)'\r' */
+ pp->state = PP_Done;
+ poff++;
+ break;
+ default:
+ /* normal character, key start, advance! */
+ pp->state = PP_ProcessKey;
start_key = &post_data[poff];
- pp->must_ikvi = true;
+ pp->must_ikvi = true;
+ poff++;
+ continue;
+ }
+ break; /* end PP_Init */
+ case PP_ProcessKey:
+ /* key phase */
+ mhd_assert (NULL == start_value);
+ mhd_assert (NULL == end_value);
+ mhd_assert (NULL != start_key || 0 == poff);
+ mhd_assert (0 != poff || NULL == start_key);
+ mhd_assert (NULL == end_key);
switch (post_data[poff])
{
case '=':
/* Case: 'key=' */
- end_key = &post_data[poff];
+ if (0 != poff)
+ end_key = &post_data[poff];
poff++;
pp->state = PP_ProcessValue;
break;
case '&':
/* Case: 'key&' */
- end_key = &post_data[poff];
- mhd_assert (NULL == start_value);
- mhd_assert (NULL == end_value);
+ if (0 != poff)
+ end_key = &post_data[poff];
poff++;
pp->state = PP_Callback;
break;
case '\n':
case '\r':
/* Case: 'key\n' or 'key\r' */
- end_key = &post_data[poff];
- poff++;
- pp->state = PP_Done;
+ if (0 != poff)
+ end_key = &post_data[poff];
+ /* No advance here, 'PP_Done' will be selected by next 'PP_Init' phase */
+ pp->state = PP_Callback;
break;
default:
/* normal character, advance! */
+ if (0 == poff)
+ start_key = post_data;
poff++;
- continue;
+ break;
}
- break; /* end PP_Init */
+ mhd_assert (NULL == end_key || NULL != start_key);
+ break; /* end PP_ProcessKey */
case PP_ProcessValue:
if (NULL == start_value)
start_value = &post_data[poff];
@@ -561,11 +615,14 @@
case '\r':
/* Case: 'value\n' or 'value\r' */
end_value = &post_data[poff];
- poff++;
- if (pp->must_ikvi)
- pp->state = PP_Callback;
+ if (pp->must_ikvi ||
+ (start_value != end_value) )
+ pp->state = PP_Callback; /* No poff advance here to set PP_Done in the next iteration */
else
+ {
+ poff++;
pp->state = PP_Done;
+ }
break;
case '%':
last_escape = &post_data[poff];
@@ -603,26 +660,33 @@
pp->state = PP_Error;
break;
case PP_Callback:
- if ( (pp->buffer_pos + (end_key - start_key) >=
- pp->buffer_size) ||
- (pp->buffer_pos + (end_key - start_key) <
- pp->buffer_pos) )
+ mhd_assert ((NULL != end_key) || (NULL == start_key));
+ if (1)
{
- /* key too long, cannot parse! */
- pp->state = PP_Error;
- continue;
- }
- /* compute key, if we have not already */
- if (NULL != start_key)
- {
- memcpy (&kbuf[pp->buffer_pos],
- start_key,
- end_key - start_key);
- pp->buffer_pos += end_key - start_key;
- start_key = NULL;
- end_key = NULL;
- pp->must_unescape_key = true;
+ const size_t key_len = end_key - start_key;
+ if (0 != key_len)
+ {
+ if ( (pp->buffer_pos + key_len >= pp->buffer_size) ||
+ (pp->buffer_pos + key_len < pp->buffer_pos) )
+ {
+ /* key too long, cannot parse! */
+ pp->state = PP_Error;
+ continue;
+ }
+ /* compute key, if we have not already */
+ memcpy (&kbuf[pp->buffer_pos],
+ start_key,
+ key_len);
+ pp->buffer_pos += key_len;
+ start_key = NULL;
+ end_key = NULL;
+ pp->must_unescape_key = true;
+ }
}
+#ifdef _DEBUG
+ else
+ mhd_assert (0 != pp->buffer_pos);
+#endif /* _DEBUG */
if (pp->must_unescape_key)
{
kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
@@ -648,22 +712,36 @@
__LINE__,
NULL); /* should never happen! */
}
+ mhd_assert ((end_key == NULL) || (start_key != NULL));
+ mhd_assert ((end_value == NULL) || (start_value != NULL));
+ }
+
+ mhd_assert (PP_Callback != pp->state);
+
+ if (PP_Error == pp->state)
+ {
+ /* State in error, returning failure */
+ return MHD_NO;
}
/* save remaining data for next iteration */
if (NULL != start_key)
{
+ size_t key_len;
+ mhd_assert ((PP_ProcessKey == pp->state) || (NULL != end_key));
if (NULL == end_key)
end_key = &post_data[poff];
- if (pp->buffer_pos + (end_key - start_key) >= pp->buffer_size)
+ key_len = end_key - start_key;
+ mhd_assert (0 != key_len); /* it must be always non-zero here */
+ if (pp->buffer_pos + key_len >= pp->buffer_size)
{
pp->state = PP_Error;
return MHD_NO;
}
memcpy (&kbuf[pp->buffer_pos],
start_key,
- end_key - start_key);
- pp->buffer_pos += end_key - start_key;
+ key_len);
+ pp->buffer_pos += key_len;
pp->must_unescape_key = true;
start_key = NULL;
end_key = NULL;
@@ -681,6 +759,9 @@
}
if (NULL == end_value)
end_value = &post_data[poff];
+ if ( (NULL != last_escape) &&
+ (2 < (end_value - last_escape)) )
+ last_escape = NULL;
process_value (pp,
start_value,
end_value,
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/reason_phrase.c
^
|
@@ -26,142 +26,143 @@
*/
#include "platform.h"
#include "microhttpd.h"
+#include "mhd_str.h"
#ifndef NULL
#define NULL ((void*) 0)
#endif
-static const char *const invalid_hundred[] = {
- NULL
+static const struct _MHD_str_w_len invalid_hundred[] = {
+ { NULL, 0 }
};
-static const char *const one_hundred[] = {
- /* 100 */ "Continue", /* RFC7231, Section 6.2.1 */
- /* 101 */ "Switching Protocols", /* RFC7231, Section 6.2.2 */
- /* 102 */ "Processing", /* RFC2518 */
- /* 103 */ "Early Hints" /* RFC8297 */
-};
-
-static const char *const two_hundred[] = {
- /* 200 */ "OK", /* RFC7231, Section 6.3.1 */
- /* 201 */ "Created", /* RFC7231, Section 6.3.2 */
- /* 202 */ "Accepted", /* RFC7231, Section 6.3.3 */
- /* 203 */ "Non-Authoritative Information", /* RFC7231, Section 6.3.4 */
- /* 204 */ "No Content", /* RFC7231, Section 6.3.5 */
- /* 205 */ "Reset Content", /* RFC7231, Section 6.3.6 */
- /* 206 */ "Partial Content", /* RFC7233, Section 4.1 */
- /* 207 */ "Multi-Status", /* RFC4918 */
- /* 208 */ "Already Reported", /* RFC5842 */
- /* 209 */ "Unknown", /* Not used */
- /* 210 */ "Unknown", /* Not used */
- /* 211 */ "Unknown", /* Not used */
- /* 212 */ "Unknown", /* Not used */
- /* 213 */ "Unknown", /* Not used */
- /* 214 */ "Unknown", /* Not used */
- /* 215 */ "Unknown", /* Not used */
- /* 216 */ "Unknown", /* Not used */
- /* 217 */ "Unknown", /* Not used */
- /* 218 */ "Unknown", /* Not used */
- /* 219 */ "Unknown", /* Not used */
- /* 220 */ "Unknown", /* Not used */
- /* 221 */ "Unknown", /* Not used */
- /* 222 */ "Unknown", /* Not used */
- /* 223 */ "Unknown", /* Not used */
- /* 224 */ "Unknown", /* Not used */
- /* 225 */ "Unknown", /* Not used */
- /* 226 */ "IM Used" /* RFC3229 */
-};
-
-static const char *const three_hundred[] = {
- /* 300 */ "Multiple Choices", /* RFC7231, Section 6.4.1 */
- /* 301 */ "Moved Permanently", /* RFC7231, Section 6.4.2 */
- /* 302 */ "Found", /* RFC7231, Section 6.4.3 */
- /* 303 */ "See Other", /* RFC7231, Section 6.4.4 */
- /* 304 */ "Not Modified", /* RFC7232, Section 4.1 */
- /* 305 */ "Use Proxy", /* RFC7231, Section 6.4.5 */
- /* 306 */ "Switch Proxy", /* Not used! RFC7231, Section 6.4.6 */
- /* 307 */ "Temporary Redirect", /* RFC7231, Section 6.4.7 */
- /* 308 */ "Permanent Redirect" /* RFC7538 */
-};
-
-static const char *const four_hundred[] = {
- /* 400 */ "Bad Request", /* RFC7231, Section 6.5.1 */
- /* 401 */ "Unauthorized", /* RFC7235, Section 3.1 */
- /* 402 */ "Payment Required", /* RFC7231, Section 6.5.2 */
- /* 403 */ "Forbidden", /* RFC7231, Section 6.5.3 */
- /* 404 */ "Not Found", /* RFC7231, Section 6.5.4 */
- /* 405 */ "Method Not Allowed", /* RFC7231, Section 6.5.5 */
- /* 406 */ "Not Acceptable", /* RFC7231, Section 6.5.6 */
- /* 407 */ "Proxy Authentication Required", /* RFC7235, Section 3.2 */
- /* 408 */ "Request Timeout", /* RFC7231, Section 6.5.7 */
- /* 409 */ "Conflict", /* RFC7231, Section 6.5.8 */
- /* 410 */ "Gone", /* RFC7231, Section 6.5.9 */
- /* 411 */ "Length Required", /* RFC7231, Section 6.5.10 */
- /* 412 */ "Precondition Failed", /* RFC7232, Section 4.2; RFC8144, Section 3.2 */
- /* 413 */ "Payload Too Large", /* RFC7231, Section 6.5.11 */
- /* 414 */ "URI Too Long", /* RFC7231, Section 6.5.12 */
- /* 415 */ "Unsupported Media Type", /* RFC7231, Section 6.5.13; RFC7694, Section 3 */
- /* 416 */ "Range Not Satisfiable", /* RFC7233, Section 4.4 */
- /* 417 */ "Expectation Failed", /* RFC7231, Section 6.5.14 */
- /* 418 */ "Unknown", /* Not used */
- /* 419 */ "Unknown", /* Not used */
- /* 420 */ "Unknown", /* Not used */
- /* 421 */ "Misdirected Request", /* RFC7540, Section 9.1.2 */
- /* 422 */ "Unprocessable Entity", /* RFC4918 */
- /* 423 */ "Locked", /* RFC4918 */
- /* 424 */ "Failed Dependency", /* RFC4918 */
- /* 425 */ "Too Early", /* RFC8470 */
- /* 426 */ "Upgrade Required", /* RFC7231, Section 6.5.15 */
- /* 427 */ "Unknown", /* Not used */
- /* 428 */ "Precondition Required", /* RFC6585 */
- /* 429 */ "Too Many Requests", /* RFC6585 */
- /* 430 */ "Unknown", /* Not used */
- /* 431 */ "Request Header Fields Too Large", /* RFC6585 */
- /* 432 */ "Unknown", /* Not used */
- /* 433 */ "Unknown", /* Not used */
- /* 434 */ "Unknown", /* Not used */
- /* 435 */ "Unknown", /* Not used */
- /* 436 */ "Unknown", /* Not used */
- /* 437 */ "Unknown", /* Not used */
- /* 438 */ "Unknown", /* Not used */
- /* 439 */ "Unknown", /* Not used */
- /* 440 */ "Unknown", /* Not used */
- /* 441 */ "Unknown", /* Not used */
- /* 442 */ "Unknown", /* Not used */
- /* 443 */ "Unknown", /* Not used */
- /* 444 */ "Unknown", /* Not used */
- /* 445 */ "Unknown", /* Not used */
- /* 446 */ "Unknown", /* Not used */
- /* 447 */ "Unknown", /* Not used */
- /* 448 */ "Unknown", /* Not used */
- /* 449 */ "Reply With", /* MS IIS extension */
- /* 450 */ "Blocked by Windows Parental Controls", /* MS extension */
- /* 451 */ "Unavailable For Legal Reasons" /* RFC7725 */
-};
-
-static const char *const five_hundred[] = {
- /* 500 */ "Internal Server Error", /* RFC7231, Section 6.6.1 */
- /* 501 */ "Not Implemented", /* RFC7231, Section 6.6.2 */
- /* 502 */ "Bad Gateway", /* RFC7231, Section 6.6.3 */
- /* 503 */ "Service Unavailable", /* RFC7231, Section 6.6.4 */
- /* 504 */ "Gateway Timeout", /* RFC7231, Section 6.6.5 */
- /* 505 */ "HTTP Version Not Supported", /* RFC7231, Section 6.6.6 */
- /* 506 */ "Variant Also Negotiates", /* RFC2295 */
- /* 507 */ "Insufficient Storage", /* RFC4918 */
- /* 508 */ "Loop Detected", /* RFC5842 */
- /* 509 */ "Bandwidth Limit Exceeded", /* Apache extension */
- /* 510 */ "Not Extended", /* RFC2774 */
- /* 511 */ "Network Authentication Required" /* RFC6585 */
+static const struct _MHD_str_w_len one_hundred[] = {
+ /* 100 */ _MHD_S_STR_W_LEN ("Continue"), /* RFC-ietf-httpbis-semantics, Section 15.2.1 */
+ /* 101 */ _MHD_S_STR_W_LEN ("Switching Protocols"), /* RFC-ietf-httpbis-semantics, Section 15.2.2 */
+ /* 102 */ _MHD_S_STR_W_LEN ("Processing"), /* RFC2518 */
+ /* 103 */ _MHD_S_STR_W_LEN ("Early Hints") /* RFC8297 */
+};
+
+static const struct _MHD_str_w_len two_hundred[] = {
+ /* 200 */ _MHD_S_STR_W_LEN ("OK"), /* RFC-ietf-httpbis-semantics, Section 15.3.1 */
+ /* 201 */ _MHD_S_STR_W_LEN ("Created"), /* RFC-ietf-httpbis-semantics, Section 15.3.2 */
+ /* 202 */ _MHD_S_STR_W_LEN ("Accepted"), /* RFC-ietf-httpbis-semantics, Section 15.3.3 */
+ /* 203 */ _MHD_S_STR_W_LEN ("Non-Authoritative Information"), /* RFC-ietf-httpbis-semantics, Section 15.3.4 */
+ /* 204 */ _MHD_S_STR_W_LEN ("No Content"), /* RFC-ietf-httpbis-semantics, Section 15.3.5 */
+ /* 205 */ _MHD_S_STR_W_LEN ("Reset Content"), /* RFC-ietf-httpbis-semantics, Section 15.3.6 */
+ /* 206 */ _MHD_S_STR_W_LEN ("Partial Content"), /* RFC-ietf-httpbis-semantics, Section 15.3.7 */
+ /* 207 */ _MHD_S_STR_W_LEN ("Multi-Status"), /* RFC4918 */
+ /* 208 */ _MHD_S_STR_W_LEN ("Already Reported"), /* RFC5842 */
+ /* 209 */ {"Unknown", 0}, /* Not used */
+ /* 210 */ {"Unknown", 0}, /* Not used */
+ /* 211 */ {"Unknown", 0}, /* Not used */
+ /* 212 */ {"Unknown", 0}, /* Not used */
+ /* 213 */ {"Unknown", 0}, /* Not used */
+ /* 214 */ {"Unknown", 0}, /* Not used */
+ /* 215 */ {"Unknown", 0}, /* Not used */
+ /* 216 */ {"Unknown", 0}, /* Not used */
+ /* 217 */ {"Unknown", 0}, /* Not used */
+ /* 218 */ {"Unknown", 0}, /* Not used */
+ /* 219 */ {"Unknown", 0}, /* Not used */
+ /* 220 */ {"Unknown", 0}, /* Not used */
+ /* 221 */ {"Unknown", 0}, /* Not used */
+ /* 222 */ {"Unknown", 0}, /* Not used */
+ /* 223 */ {"Unknown", 0}, /* Not used */
+ /* 224 */ {"Unknown", 0}, /* Not used */
+ /* 225 */ {"Unknown", 0}, /* Not used */
+ /* 226 */ _MHD_S_STR_W_LEN ("IM Used") /* RFC3229 */
+};
+
+static const struct _MHD_str_w_len three_hundred[] = {
+ /* 300 */ _MHD_S_STR_W_LEN ("Multiple Choices"), /* RFC-ietf-httpbis-semantics, Section 15.4.1 */
+ /* 301 */ _MHD_S_STR_W_LEN ("Moved Permanently"), /* RFC-ietf-httpbis-semantics, Section 15.4.2 */
+ /* 302 */ _MHD_S_STR_W_LEN ("Found"), /* RFC-ietf-httpbis-semantics, Section 15.4.3 */
+ /* 303 */ _MHD_S_STR_W_LEN ("See Other"), /* RFC-ietf-httpbis-semantics, Section 15.4.4 */
+ /* 304 */ _MHD_S_STR_W_LEN ("Not Modified"), /* RFC-ietf-httpbis-semantics, Section 15.4.5 */
+ /* 305 */ _MHD_S_STR_W_LEN ("Use Proxy"), /* RFC-ietf-httpbis-semantics, Section 15.4.6 */
+ /* 306 */ _MHD_S_STR_W_LEN ("Switch Proxy"), /* Not used! RFC-ietf-httpbis-semantics, Section 15.4.7 */
+ /* 307 */ _MHD_S_STR_W_LEN ("Temporary Redirect"), /* RFC-ietf-httpbis-semantics, Section 15.4.8 */
+ /* 308 */ _MHD_S_STR_W_LEN ("Permanent Redirect") /* RFC-ietf-httpbis-semantics, Section 15.4.9 */
+};
+
+static const struct _MHD_str_w_len four_hundred[] = {
+ /* 400 */ _MHD_S_STR_W_LEN ("Bad Request"), /* RFC-ietf-httpbis-semantics, Section 15.5.1 */
+ /* 401 */ _MHD_S_STR_W_LEN ("Unauthorized"), /* RFC-ietf-httpbis-semantics, Section 15.5.2 */
+ /* 402 */ _MHD_S_STR_W_LEN ("Payment Required"), /* RFC-ietf-httpbis-semantics, Section 15.5.3 */
+ /* 403 */ _MHD_S_STR_W_LEN ("Forbidden"), /* RFC-ietf-httpbis-semantics, Section 15.5.4 */
+ /* 404 */ _MHD_S_STR_W_LEN ("Not Found"), /* RFC-ietf-httpbis-semantics, Section 15.5.5 */
+ /* 405 */ _MHD_S_STR_W_LEN ("Method Not Allowed"), /* RFC-ietf-httpbis-semantics, Section 15.5.6 */
+ /* 406 */ _MHD_S_STR_W_LEN ("Not Acceptable"), /* RFC-ietf-httpbis-semantics, Section 15.5.7 */
+ /* 407 */ _MHD_S_STR_W_LEN ("Proxy Authentication Required"), /* RFC-ietf-httpbis-semantics, Section 15.5.8 */
+ /* 408 */ _MHD_S_STR_W_LEN ("Request Timeout"), /* RFC-ietf-httpbis-semantics, Section 15.5.9 */
+ /* 409 */ _MHD_S_STR_W_LEN ("Conflict"), /* RFC-ietf-httpbis-semantics, Section 15.5.10 */
+ /* 410 */ _MHD_S_STR_W_LEN ("Gone"), /* RFC-ietf-httpbis-semantics, Section 15.5.11 */
+ /* 411 */ _MHD_S_STR_W_LEN ("Length Required"), /* RFC-ietf-httpbis-semantics, Section 15.5.12 */
+ /* 412 */ _MHD_S_STR_W_LEN ("Precondition Failed"), /* RFC-ietf-httpbis-semantics, Section 15.5.13 */
+ /* 413 */ _MHD_S_STR_W_LEN ("Content Too Large"), /* RFC-ietf-httpbis-semantics, Section 15.5.14 */
+ /* 414 */ _MHD_S_STR_W_LEN ("URI Too Long"), /* RFC-ietf-httpbis-semantics, Section 15.5.15 */
+ /* 415 */ _MHD_S_STR_W_LEN ("Unsupported Media Type"), /* RFC-ietf-httpbis-semantics, Section 15.5.16 */
+ /* 416 */ _MHD_S_STR_W_LEN ("Range Not Satisfiable"), /* RFC-ietf-httpbis-semantics, Section 15.5.17 */
+ /* 417 */ _MHD_S_STR_W_LEN ("Expectation Failed"), /* RFC-ietf-httpbis-semantics, Section 15.5.18 */
+ /* 418 */ {"Unknown", 0}, /* Not used */
+ /* 419 */ {"Unknown", 0}, /* Not used */
+ /* 420 */ {"Unknown", 0}, /* Not used */
+ /* 421 */ _MHD_S_STR_W_LEN ("Misdirected Request"), /* RFC-ietf-httpbis-semantics, Section 15.5.20 */
+ /* 422 */ _MHD_S_STR_W_LEN ("Unprocessable Content"), /* RFC-ietf-httpbis-semantics, Section 15.5.21 */
+ /* 423 */ _MHD_S_STR_W_LEN ("Locked"), /* RFC4918 */
+ /* 424 */ _MHD_S_STR_W_LEN ("Failed Dependency"), /* RFC4918 */
+ /* 425 */ _MHD_S_STR_W_LEN ("Too Early"), /* RFC8470 */
+ /* 426 */ _MHD_S_STR_W_LEN ("Upgrade Required"), /* RFC-ietf-httpbis-semantics, Section 15.5.22 */
+ /* 427 */ {"Unknown", 0}, /* Not used */
+ /* 428 */ _MHD_S_STR_W_LEN ("Precondition Required"), /* RFC6585 */
+ /* 429 */ _MHD_S_STR_W_LEN ("Too Many Requests"), /* RFC6585 */
+ /* 430 */ {"Unknown", 0}, /* Not used */
+ /* 431 */ _MHD_S_STR_W_LEN ("Request Header Fields Too Large"), /* RFC6585 */
+ /* 432 */ {"Unknown", 0}, /* Not used */
+ /* 433 */ {"Unknown", 0}, /* Not used */
+ /* 434 */ {"Unknown", 0}, /* Not used */
+ /* 435 */ {"Unknown", 0}, /* Not used */
+ /* 436 */ {"Unknown", 0}, /* Not used */
+ /* 437 */ {"Unknown", 0}, /* Not used */
+ /* 438 */ {"Unknown", 0}, /* Not used */
+ /* 439 */ {"Unknown", 0}, /* Not used */
+ /* 440 */ {"Unknown", 0}, /* Not used */
+ /* 441 */ {"Unknown", 0}, /* Not used */
+ /* 442 */ {"Unknown", 0}, /* Not used */
+ /* 443 */ {"Unknown", 0}, /* Not used */
+ /* 444 */ {"Unknown", 0}, /* Not used */
+ /* 445 */ {"Unknown", 0}, /* Not used */
+ /* 446 */ {"Unknown", 0}, /* Not used */
+ /* 447 */ {"Unknown", 0}, /* Not used */
+ /* 448 */ {"Unknown", 0}, /* Not used */
+ /* 449 */ _MHD_S_STR_W_LEN ("Reply With"), /* MS IIS extension */
+ /* 450 */ _MHD_S_STR_W_LEN ("Blocked by Windows Parental Controls"), /* MS extension */
+ /* 451 */ _MHD_S_STR_W_LEN ("Unavailable For Legal Reasons") /* RFC7725 */
+};
+
+static const struct _MHD_str_w_len five_hundred[] = {
+ /* 500 */ _MHD_S_STR_W_LEN ("Internal Server Error"), /* RFC-ietf-httpbis-semantics, Section 15.6.1 */
+ /* 501 */ _MHD_S_STR_W_LEN ("Not Implemented"), /* RFC-ietf-httpbis-semantics, Section 15.6.2 */
+ /* 502 */ _MHD_S_STR_W_LEN ("Bad Gateway"), /* RFC-ietf-httpbis-semantics, Section 15.6.3 */
+ /* 503 */ _MHD_S_STR_W_LEN ("Service Unavailable"), /* RFC-ietf-httpbis-semantics, Section 15.6.4 */
+ /* 504 */ _MHD_S_STR_W_LEN ("Gateway Timeout"), /* RFC-ietf-httpbis-semantics, Section 15.6.5 */
+ /* 505 */ _MHD_S_STR_W_LEN ("HTTP Version Not Supported"), /* RFC-ietf-httpbis-semantics, Section 15.6.6 */
+ /* 506 */ _MHD_S_STR_W_LEN ("Variant Also Negotiates"), /* RFC2295 */
+ /* 507 */ _MHD_S_STR_W_LEN ("Insufficient Storage"), /* RFC4918 */
+ /* 508 */ _MHD_S_STR_W_LEN ("Loop Detected"), /* RFC5842 */
+ /* 509 */ _MHD_S_STR_W_LEN ("Bandwidth Limit Exceeded"), /* Apache extension */
+ /* 510 */ _MHD_S_STR_W_LEN ("Not Extended"), /* RFC2774 */
+ /* 511 */ _MHD_S_STR_W_LEN ("Network Authentication Required") /* RFC6585 */
};
struct MHD_Reason_Block
{
size_t max;
- const char *const*data;
+ const struct _MHD_str_w_len *const data;
};
-#define BLOCK(m) { (sizeof(m) / sizeof(char*)), m }
+#define BLOCK(m) { (sizeof(m) / sizeof(m[0])), m }
static const struct MHD_Reason_Block reasons[] = {
BLOCK (invalid_hundred),
@@ -179,6 +180,17 @@
if ( (code >= 100) &&
(code < 600) &&
(reasons[code / 100].max > (code % 100)) )
- return reasons[code / 100].data[code % 100];
+ return reasons[code / 100].data[code % 100].str;
return "Unknown";
}
+
+
+size_t
+MHD_get_reason_phrase_len_for (unsigned int code)
+{
+ if ( (code >= 100) &&
+ (code < 600) &&
+ (reasons[code / 100].max > (code % 100)) )
+ return reasons[code / 100].data[code % 100].len;
+ return 0;
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/response.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007-2021 Daniel Pittman and Christian Grothoff
+ Copyright (C) 2015-2021 Evgeny Grin (Karlson2k)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -44,6 +45,7 @@
#include "memorypool.h"
#include "mhd_send.h"
#include "mhd_compat.h"
+#include "mhd_assert.h"
#if defined(MHD_W32_MUTEX_)
@@ -69,6 +71,78 @@
#endif /* _WIN32 */
#endif /* !MHD_FD_BLOCK_SIZE */
+/**
+ * Insert a new header at the first position of the response
+ */
+#define _MHD_insert_header_first(presponse, phdr) do { \
+ mhd_assert (NULL == phdr->next); \
+ mhd_assert (NULL == phdr->prev); \
+ if (NULL == presponse->first_header) \
+ { \
+ mhd_assert (NULL == presponse->last_header); \
+ presponse->first_header = phdr; \
+ presponse->last_header = phdr; \
+ } \
+ else \
+ { \
+ mhd_assert (NULL != presponse->last_header); \
+ presponse->first_header->prev = phdr; \
+ phdr->next = presponse->first_header; \
+ presponse->first_header = phdr; \
+ } \
+} while (0)
+
+/**
+ * Insert a new header at the last position of the response
+ */
+#define _MHD_insert_header_last(presponse, phdr) do { \
+ mhd_assert (NULL == phdr->next); \
+ mhd_assert (NULL == phdr->prev); \
+ if (NULL == presponse->last_header) \
+ { \
+ mhd_assert (NULL == presponse->first_header); \
+ presponse->last_header = phdr; \
+ presponse->first_header = phdr; \
+ } \
+ else \
+ { \
+ mhd_assert (NULL != presponse->first_header); \
+ presponse->last_header->next = phdr; \
+ phdr->prev = presponse->last_header; \
+ presponse->last_header = phdr; \
+ } \
+} while (0)
+
+
+/**
+ * Remove a header from the response
+ */
+#define _MHD_remove_header(presponse, phdr) do { \
+ mhd_assert (NULL != presponse->first_header); \
+ mhd_assert (NULL != presponse->last_header); \
+ if (NULL == phdr->prev) \
+ { \
+ mhd_assert (phdr == presponse->first_header); \
+ presponse->first_header = phdr->next; \
+ } \
+ else \
+ { \
+ mhd_assert (phdr != presponse->first_header); \
+ mhd_assert (phdr == phdr->prev->next); \
+ phdr->prev->next = phdr->next; \
+ } \
+ if (NULL == phdr->next) \
+ { \
+ mhd_assert (phdr == presponse->last_header); \
+ presponse->last_header = phdr->prev; \
+ } \
+ else \
+ { \
+ mhd_assert (phdr != presponse->last_header); \
+ mhd_assert (phdr == phdr->next->prev); \
+ phdr->next->prev = phdr->prev; \
+ } \
+} while (0)
/**
* Add a header or footer line to the response.
@@ -93,13 +167,13 @@
(0 == header[0]) ||
(0 == content[0]) ||
(NULL != strchr (header, '\t')) ||
+ (NULL != strchr (header, ' ')) ||
(NULL != strchr (header, '\r')) ||
(NULL != strchr (header, '\n')) ||
- (NULL != strchr (content, '\t')) ||
(NULL != strchr (content, '\r')) ||
(NULL != strchr (content, '\n')) )
return MHD_NO;
- if (NULL == (hdr = malloc (sizeof (struct MHD_HTTP_Header))))
+ if (NULL == (hdr = MHD_calloc_ (1, sizeof (struct MHD_HTTP_Header))))
return MHD_NO;
if (NULL == (hdr->header = strdup (header)))
{
@@ -115,8 +189,253 @@
}
hdr->value_size = strlen (content);
hdr->kind = kind;
- hdr->next = response->first_header;
- response->first_header = hdr;
+ _MHD_insert_header_last (response, hdr);
+ return MHD_YES;
+}
+
+
+/**
+ * Add "Connection:" header to the response with special processing.
+ *
+ * "Connection:" header value will be combined with any existing "Connection:"
+ * header, "close" token (if any) will be de-duplicated and moved to the first
+ * position.
+ *
+ * @param response the response to add a header to
+ * @param value the value to add
+ * @return #MHD_NO on error (no memory).
+ */
+static enum MHD_Result
+add_response_header_connection (struct MHD_Response *response,
+ const char *value)
+{
+ static const char *key = MHD_HTTP_HEADER_CONNECTION;
+ /** the length of the "Connection" key */
+ static const size_t key_len =
+ MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONNECTION);
+ size_t value_len; /**< the length of the @a value */
+ size_t old_value_len; /**< the length of the existing "Connection" value */
+ size_t buf_size; /**< the size of the buffer */
+ ssize_t norm_len; /**< the length of the normalised value */
+ char *buf; /**< the temporal buffer */
+ struct MHD_HTTP_Header *hdr; /**< existing "Connection" header */
+ bool value_has_close; /**< the @a value has "close" token */
+ bool already_has_close; /**< existing "Connection" header has "close" token */
+ size_t pos = 0; /**< position of addition in the @a buf */
+
+ if ( (NULL != strchr (value, '\r')) ||
+ (NULL != strchr (value, '\n')) )
+ return MHD_NO;
+
+ if (0 != (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
+ {
+ hdr = MHD_get_response_element_n_ (response, MHD_HEADER_KIND,
+ key, key_len);
+ already_has_close =
+ (0 != (response->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE));
+ mhd_assert (already_has_close == (0 == memcmp (hdr->value, "close", 5)));
+ mhd_assert (NULL != hdr);
+ }
+ else
+ {
+ hdr = NULL;
+ already_has_close = false;
+ mhd_assert (NULL == MHD_get_response_element_n_ (response,
+ MHD_HEADER_KIND,
+ key, key_len));
+ mhd_assert (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE));
+ }
+ if (NULL != hdr)
+ old_value_len = hdr->value_size + 2; /* additional size for ", " */
+ else
+ old_value_len = 0;
+
+ value_len = strlen (value);
+ if (value_len >= SSIZE_MAX)
+ return MHD_NO;
+ /* Additional space for normalisation and zero-termination*/
+ norm_len = (ssize_t) (value_len + value_len / 2 + 1);
+ buf_size = old_value_len + (size_t) norm_len;
+
+ buf = malloc (buf_size);
+ if (NULL == buf)
+ return MHD_NO;
+ /* Remove "close" token (if any), it will be moved to the front */
+ value_has_close = MHD_str_remove_token_caseless_ (value, value_len, "close",
+ MHD_STATICSTR_LEN_ ( \
+ "close"),
+ buf + old_value_len,
+ &norm_len);
+#ifdef UPGRADE_SUPPORT
+ if ( (NULL != response->upgrade_handler) && value_has_close)
+ { /* The "close" token cannot be used with connection "upgrade" */
+ free (buf);
+ return MHD_NO;
+ }
+#endif /* UPGRADE_SUPPORT */
+ mhd_assert (0 <= norm_len);
+ if (0 > norm_len)
+ norm_len = 0; /* Must never happen */
+ if (0 != norm_len)
+ {
+ size_t len = norm_len;
+ MHD_str_remove_tokens_caseless_ (buf + old_value_len, &len,
+ "keep-alive",
+ MHD_STATICSTR_LEN_ ("keep-alive"));
+ norm_len = (ssize_t) len;
+ }
+ if (0 == norm_len)
+ { /* New value is empty after normalisation */
+ if (! value_has_close)
+ { /* The new value had no tokens */
+ free (buf);
+ return MHD_NO;
+ }
+ if (already_has_close)
+ { /* The "close" token is already present, nothing to modify */
+ free (buf);
+ return MHD_YES;
+ }
+ }
+ /* Add "close" token if required */
+ if (value_has_close && ! already_has_close)
+ {
+ /* Need to insert "close" token at the first position */
+ mhd_assert (buf_size >= old_value_len + (size_t) norm_len \
+ + MHD_STATICSTR_LEN_ ("close, ") + 1);
+ if (0 != norm_len)
+ memmove (buf + MHD_STATICSTR_LEN_ ("close, ") + old_value_len,
+ buf + old_value_len, norm_len + 1);
+ memcpy (buf, "close", MHD_STATICSTR_LEN_ ("close"));
+ pos += MHD_STATICSTR_LEN_ ("close");
+ }
+ /* Add old value tokens (if any) */
+ if (0 != old_value_len)
+ {
+ if (0 != pos)
+ {
+ buf[pos++] = ',';
+ buf[pos++] = ' ';
+ }
+ memcpy (buf + pos, hdr->value,
+ hdr->value_size);
+ pos += hdr->value_size;
+ }
+ /* Add new value token (if any) */
+ if (0 != norm_len)
+ {
+ if (0 != pos)
+ {
+ buf[pos++] = ',';
+ buf[pos++] = ' ';
+ }
+ /* The new value tokens must be already at the correct position */
+ mhd_assert ((value_has_close && ! already_has_close) ? \
+ (MHD_STATICSTR_LEN_ ("close, ") + old_value_len == pos) : \
+ (old_value_len == pos));
+ pos += (size_t) norm_len;
+ }
+ mhd_assert (buf_size > pos);
+ buf[pos] = 0; /* Null terminate the result */
+
+ if (NULL == hdr)
+ {
+ struct MHD_HTTP_Header *new_hdr; /**< new "Connection" header */
+ /* Create new response header entry */
+ new_hdr = MHD_calloc_ (1, sizeof (struct MHD_HTTP_Header));
+ if (NULL != new_hdr)
+ {
+ new_hdr->header = malloc (key_len + 1);
+ if (NULL != new_hdr->header)
+ {
+ memcpy (new_hdr->header, key, key_len + 1);
+ new_hdr->header_size = key_len;
+ new_hdr->value = buf;
+ new_hdr->value_size = pos;
+ new_hdr->kind = MHD_HEADER_KIND;
+ if (value_has_close)
+ response->flags_auto = (MHD_RAF_HAS_CONNECTION_HDR
+ | MHD_RAF_HAS_CONNECTION_CLOSE);
+ else
+ response->flags_auto = MHD_RAF_HAS_CONNECTION_HDR;
+ _MHD_insert_header_first (response, new_hdr);
+ return MHD_YES;
+ }
+ free (new_hdr);
+ }
+ free (buf);
+ return MHD_NO;
+ }
+
+ /* Update existing header entry */
+ free (hdr->value);
+ hdr->value = buf;
+ hdr->value_size = pos;
+ if (value_has_close && ! already_has_close)
+ response->flags_auto |= MHD_RAF_HAS_CONNECTION_CLOSE;
+ return MHD_YES;
+}
+
+
+/**
+ * Remove tokens from "Connection:" header of the response.
+ *
+ * Provided tokens will be removed from "Connection:" header value.
+ *
+ * @param response the response to manipulate "Connection:" header
+ * @param value the tokens to remove
+ * @return #MHD_NO on error (no headers or tokens found).
+ */
+static enum MHD_Result
+del_response_header_connection (struct MHD_Response *response,
+ const char *value)
+{
+ struct MHD_HTTP_Header *hdr; /**< existing "Connection" header */
+
+ hdr = MHD_get_response_element_n_ (response, MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONNECTION,
+ MHD_STATICSTR_LEN_ ( \
+ MHD_HTTP_HEADER_CONNECTION));
+ if (NULL == hdr)
+ return MHD_NO;
+
+ if (! MHD_str_remove_tokens_caseless_ (hdr->value, &hdr->value_size, value,
+ strlen (value)))
+ return MHD_NO;
+ if (0 == hdr->value_size)
+ {
+ _MHD_remove_header (response, hdr);
+ free (hdr->value);
+ free (hdr->header);
+ free (hdr);
+ response->flags_auto &=
+ ~((enum MHD_ResponseAutoFlags) MHD_RAF_HAS_CONNECTION_HDR
+ | (enum MHD_ResponseAutoFlags) MHD_RAF_HAS_CONNECTION_CLOSE);
+ }
+ else
+ {
+ hdr->value[hdr->value_size] = 0; /* Null-terminate the result */
+ if (0 != (response->flags_auto
+ & ~((enum MHD_ResponseAutoFlags) MHD_RAF_HAS_CONNECTION_CLOSE)))
+ {
+ if (MHD_STATICSTR_LEN_ ("close") == hdr->value_size)
+ {
+ if (0 != memcmp (hdr->value, "close", MHD_STATICSTR_LEN_ ("close")))
+ response->flags_auto &=
+ ~((enum MHD_ResponseAutoFlags) MHD_RAF_HAS_CONNECTION_CLOSE);
+ }
+ else if (MHD_STATICSTR_LEN_ ("close, ") < hdr->value_size)
+ {
+ if (0 != memcmp (hdr->value, "close, ",
+ MHD_STATICSTR_LEN_ ("close, ")))
+ response->flags_auto &=
+ ~((enum MHD_ResponseAutoFlags) MHD_RAF_HAS_CONNECTION_CLOSE);
+ }
+ else
+ response->flags_auto &=
+ ~((enum MHD_ResponseAutoFlags) MHD_RAF_HAS_CONNECTION_CLOSE);
+ }
+ }
return MHD_YES;
}
@@ -124,10 +443,50 @@
/**
* Add a header line to the response.
*
- * @param response response to add a header to
- * @param header the header to add
- * @param content value to add
- * @return #MHD_NO on error (i.e. invalid header or content format).
+ * When reply is generated with queued response, some headers are generated
+ * automatically. Automatically generated headers are only sent to the client,
+ * but not added back to the response object.
+ *
+ * The list of automatic headers:
+ * + "Date" header is added automatically unless already set by
+ * this function
+ * @see #MHD_USE_SUPPRESS_DATE_NO_CLOCK
+ * + "Content-Length" is added automatically when required, attempt to set
+ * it manually by this function is ignored.
+ * @see #MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
+ * + "Transfer-Encoding" with value "chunked" is added automatically,
+ * when chunked transfer encoding is used automatically. Same header with
+ * the same value can be set manually by this function to enforce chunked
+ * encoding, however for HTTP/1.0 clients chunked encoding will not be used
+ * and manually set "Transfer-Encoding" header is automatically removed
+ * for HTTP/1.0 clients
+ * + "Connection" may be added automatically with value "Keep-Alive" (only
+ * for HTTP/1.0 clients) or "Close". The header "Connection" with value
+ * "Close" could be set by this function to enforce closure of
+ * the connection after sending this response. "Keep-Alive" cannot be
+ * enforced and will be removed automatically.
+ * @see #MHD_RF_SEND_KEEP_ALIVE_HEADER
+ *
+ * Some headers are pre-processed by this function:
+ * * "Connection" headers are combined into single header entry, value is
+ * normilised, "Keep-Alive" tokens are removed.
+ * * "Transfer-Encoding" header: the only one header is allowed, the only
+ * allowed value is "chunked".
+ * * "Date" header: the only one header is allowed, the second added header
+ * replaces the first one.
+ * * "Content-Length" application-defined header is not allowed.
+ * @see #MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
+ *
+ * Headers are used in order as they were added.
+ *
+ * @param response the response to add a header to
+ * @param header the header name to add, no need to be static, an internal copy
+ * will be created automatically
+ * @param content the header value to add, no need to be static, an internal
+ * copy will be created automatically
+ * @return #MHD_YES on success,
+ * #MHD_NO on error (i.e. invalid header or content format),
+ * or out of memory
* @ingroup response
*/
enum MHD_Result
@@ -135,17 +494,51 @@
const char *header,
const char *content)
{
- if ( (MHD_str_equal_caseless_ (header,
- MHD_HTTP_HEADER_TRANSFER_ENCODING)) &&
- (! MHD_str_equal_caseless_ (content,
- "identity")) &&
- (! MHD_str_equal_caseless_ (content,
- "chunked")) )
- {
- /* Setting transfer encodings other than "identity" or
- "chunked" is not allowed. Note that MHD will set the
- correct transfer encoding if required automatically. */
- /* NOTE: for compressed bodies, use the "Content-encoding" header */
+ if (MHD_str_equal_caseless_ (header, MHD_HTTP_HEADER_CONNECTION))
+ return add_response_header_connection (response, content);
+
+ if (MHD_str_equal_caseless_ (header,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING))
+ {
+ if (! MHD_str_equal_caseless_ (content, "chunked"))
+ return MHD_NO;
+ if (0 != (response->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED))
+ return MHD_YES;
+ if (MHD_NO != add_response_entry (response,
+ MHD_HEADER_KIND,
+ header,
+ content))
+ {
+ response->flags_auto |= MHD_RAF_HAS_TRANS_ENC_CHUNKED;
+ return MHD_YES;
+ }
+ return MHD_NO;
+ }
+ if (MHD_str_equal_caseless_ (header,
+ MHD_HTTP_HEADER_DATE))
+ {
+ if (0 != (response->flags_auto & MHD_RAF_HAS_DATE_HDR))
+ {
+ struct MHD_HTTP_Header *hdr;
+ hdr = MHD_get_response_element_n_ (response, MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_DATE,
+ MHD_STATICSTR_LEN_ ( \
+ MHD_HTTP_HEADER_DATE));
+ mhd_assert (NULL != hdr);
+ _MHD_remove_header (response, hdr);
+ if (NULL != hdr->value)
+ free (hdr->value);
+ free (hdr->header);
+ free (hdr);
+ }
+ if (MHD_NO != add_response_entry (response,
+ MHD_HEADER_KIND,
+ header,
+ content))
+ {
+ response->flags_auto |= MHD_RAF_HAS_DATE_HDR;
+ return MHD_YES;
+ }
return MHD_NO;
}
if ( (0 == (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
@@ -189,6 +582,11 @@
/**
* Delete a header (or footer) line from the response.
*
+ * For "Connection" headers this function remove all tokens from existing
+ * value. Successful result means that at least one token has been removed.
+ * If all tokens are removed from "Connection" header, the empty "Connection"
+ * header removed.
+ *
* @param response response to remove a header from
* @param header the header to delete
* @param content value to delete
@@ -201,7 +599,6 @@
const char *content)
{
struct MHD_HTTP_Header *pos;
- struct MHD_HTTP_Header *prev;
size_t header_len;
size_t content_len;
@@ -209,8 +606,14 @@
(NULL == content) )
return MHD_NO;
header_len = strlen (header);
+
+ if ((0 != (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR)) &&
+ (MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONNECTION) == header_len) &&
+ MHD_str_equal_caseless_bin_n_ (header, MHD_HTTP_HEADER_CONNECTION,
+ header_len))
+ return del_response_header_connection (response, content);
+
content_len = strlen (content);
- prev = NULL;
pos = response->first_header;
while (NULL != pos)
{
@@ -223,16 +626,26 @@
pos->value,
content_len)))
{
+ _MHD_remove_header (response, pos);
free (pos->header);
free (pos->value);
- if (NULL == prev)
- response->first_header = pos->next;
- else
- prev->next = pos->next;
free (pos);
+ if ( (MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING) ==
+ header_len) &&
+ MHD_str_equal_caseless_bin_n_ (header,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ header_len) )
+ response->flags_auto &=
+ ~((enum MHD_ResponseAutoFlags) MHD_RAF_HAS_TRANS_ENC_CHUNKED);
+ else if ( (MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_DATE) ==
+ header_len) &&
+ MHD_str_equal_caseless_bin_n_ (header,
+ MHD_HTTP_HEADER_DATE,
+ header_len) )
+ response->flags_auto &=
+ ~((enum MHD_ResponseAutoFlags) MHD_RAF_HAS_DATE_HDR);
return MHD_YES;
}
- prev = pos;
pos = pos->next;
}
return MHD_NO;
@@ -305,6 +718,42 @@
/**
+ * Get a particular header (or footer) element from the response.
+ *
+ * Function returns the first found element.
+ * @param response response to query
+ * @param kind the kind of element: header or footer
+ * @param key the key which header to get
+ * @param key_len the length of the @a key
+ * @return NULL if header element does not exist
+ * @ingroup response
+ */
+struct MHD_HTTP_Header *
+MHD_get_response_element_n_ (struct MHD_Response *response,
+ enum MHD_ValueKind kind,
+ const char *key,
+ size_t key_len)
+{
+ struct MHD_HTTP_Header *pos;
+
+ mhd_assert (NULL != key);
+ mhd_assert (0 != key[0]);
+ mhd_assert (0 != key_len);
+
+ for (pos = response->first_header;
+ NULL != pos;
+ pos = pos->next)
+ {
+ if ((pos->header_size == key_len) &&
+ (kind == pos->kind) &&
+ (MHD_str_equal_caseless_bin_n_ (pos->header, key, pos->header_size)))
+ return pos;
+ }
+ return NULL;
+}
+
+
+/**
* Check whether response header contains particular token.
*
* Token could be surrounded by spaces and tabs and delimited by comma.
@@ -585,8 +1034,15 @@
#undef MHD_create_response_from_fd_at_offset
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided file with
+ * specified offset used as the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response
* @param fd file descriptor referring to a file on disk with the
@@ -612,8 +1068,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided file with
+ * specified offset used as the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response;
* sizes larger than 2 GiB may be not supported by OS or
@@ -662,8 +1125,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used ONLY ONCE.
+ * Create a response object with the response body created by reading
+ * the provided pipe.
+ *
+ * The response object can be extended with header information and
+ * then be used ONLY ONCE.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param fd file descriptor referring to a read-end of a pipe with the
* data; will be closed when response is destroyed;
@@ -691,8 +1161,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided file used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response
* @param fd file descriptor referring to a file on disk with the data
@@ -710,8 +1187,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided file used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response;
* sizes larger than 2 GiB may be not supported by OS or
@@ -733,8 +1217,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided buffer used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the @a data portion of the response
* @param data the data itself
@@ -797,8 +1288,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided buffer used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response
* @param buffer size bytes containing the response's data portion
@@ -819,8 +1317,15 @@
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * Create a response object with the content of provided buffer used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param size size of the data portion of the response
* @param buffer size bytes containing the response's data portion
@@ -848,9 +1353,53 @@
/**
- * Create a response object from an array of memory buffers.
- * The response object can be extended with header information and then be used
- * any number of times.
+ * Create a response object with the content of provided buffer used as
+ * the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
+ *
+ * @param size size of the data portion of the response
+ * @param buffer size bytes containing the response's data portion
+ * @param crfc function to call to cleanup, if set to NULL then callback
+ * is not called
+ * @param crfc_cls an argument for @a crfc
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @note Available since #MHD_VERSION 0x00097302
+ * @ingroup response
+ */
+_MHD_EXTERN struct MHD_Response *
+MHD_create_response_from_buffer_with_free_callback_cls (size_t size,
+ void *buffer,
+ MHD_ContentReaderFreeCallback
+ crfc,
+ void *crfc_cls)
+{
+ struct MHD_Response *r;
+
+ r = MHD_create_response_from_buffer_with_free_callback (size,
+ buffer,
+ crfc);
+ if (NULL != r)
+ r->crc_cls = crfc_cls;
+ return r;
+}
+
+
+/**
+ * Create a response object with an array of memory buffers
+ * used as the response body.
+ *
+ * The response object can be extended with header information and then
+ * be used any number of times.
+ *
+ * If response object is used to answer HEAD request then the body
+ * of the response is not used, while all headers (including automatic
+ * headers) are used.
*
* @param iov the array for response data buffers, an internal copy of this
* will be made
@@ -1087,13 +1636,15 @@
return MHD_NO;
if (NULL ==
- MHD_get_response_header (response,
- MHD_HTTP_HEADER_UPGRADE))
+ MHD_get_response_element_n_ (response, MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_UPGRADE,
+ MHD_STATICSTR_LEN_ ( \
+ MHD_HTTP_HEADER_UPGRADE)))
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
- _ (
- "Invalid response for upgrade: application failed to set the 'Upgrade' header!\n"));
+ _ ("Invalid response for upgrade: " \
+ "application failed to set the 'Upgrade' header!\n"));
#endif
return MHD_NO;
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/response.h
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Daniel Pittman and Christian Grothoff
+ Copyright (C) 2015-2021 Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -22,6 +23,7 @@
* @brief Methods for managing response objects
* @author Daniel Pittman
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#ifndef RESPONSE_H
@@ -54,4 +56,21 @@
struct MHD_Connection *connection);
+/**
+ * Get a particular header (or footer) element from the response.
+ *
+ * Function returns the first found element.
+ * @param response response to query
+ * @param kind the kind of element: header or footer
+ * @param key the key which header to get
+ * @param key_len the length of the @a key
+ * @return NULL if header element does not exist
+ * @ingroup response
+ */
+struct MHD_HTTP_Header *
+MHD_get_response_element_n_ (struct MHD_Response *response,
+ enum MHD_ValueKind kind,
+ const char *key,
+ size_t key_len);
+
#endif
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/sha1.c
^
|
@@ -0,0 +1,378 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
+
+ libmicrohttpd 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file microhttpd/sha1.c
+ * @brief Calculation of SHA-1 digest as defined in FIPS PUB 180-4 (2015)
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "sha1.h"
+
+#include <string.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif /* HAVE_MEMORY_H */
+#include "mhd_bithelpers.h"
+#include "mhd_assert.h"
+
+/**
+ * Initialise structure for SHA-1 calculation.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ */
+void
+MHD_SHA1_init (void *ctx_)
+{
+ struct sha1_ctx *const ctx = ctx_;
+ /* Initial hash values, see FIPS PUB 180-4 paragraph 5.3.1 */
+ /* Just some "magic" numbers defined by standard */
+ ctx->H[0] = UINT32_C (0x67452301);
+ ctx->H[1] = UINT32_C (0xefcdab89);
+ ctx->H[2] = UINT32_C (0x98badcfe);
+ ctx->H[3] = UINT32_C (0x10325476);
+ ctx->H[4] = UINT32_C (0xc3d2e1f0);
+
+ /* Initialise number of bytes. */
+ ctx->count = 0;
+}
+
+
+/**
+ * Base of SHA-1 transformation.
+ * Gets full 512 bits / 64 bytes block of data and updates hash values;
+ * @param H hash values
+ * @param data data, must be exactly 64 bytes long
+ */
+static void
+sha1_transform (uint32_t H[_SHA1_DIGEST_LENGTH],
+ const uint8_t data[SHA1_BLOCK_SIZE])
+{
+ /* Working variables,
+ see FIPS PUB 180-4 paragraph 6.1.3 */
+ uint32_t a = H[0];
+ uint32_t b = H[1];
+ uint32_t c = H[2];
+ uint32_t d = H[3];
+ uint32_t e = H[4];
+
+ /* Data buffer, used as cyclic buffer.
+ See FIPS PUB 180-4 paragraphs 5.2.1, 6.1.3 */
+ uint32_t W[16];
+
+ /* 'Ch' and 'Maj' macro functions are defined with
+ widely-used optimization.
+ See FIPS PUB 180-4 formulae 4.1. */
+#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) )
+#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
+ /* Unoptimized (original) versions: */
+/* #define Ch(x,y,z) ( ( (x) & (y) ) ^ ( ~(x) & (z) ) ) */
+/* #define Maj(x,y,z) ( ((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)) ) */
+#define Par(x,y,z) ( (x) ^ (y) ^ (z) )
+
+ /* Single step of SHA-1 computation,
+ see FIPS PUB 180-4 paragraph 6.1.3 step 3.
+ * Note: instead of reassigning all working variables on each step,
+ variables are rotated for each step:
+ SHA1STEP32 (a, b, c, d, e, func, K00, W[0]);
+ SHA1STEP32 (e, a, b, c, d, func, K00, W[1]);
+ so current 'vC' will be used as 'vD' on the next step,
+ current 'vE' will be used as 'vA' on the next step.
+ * Note: 'wt' must be used exactly one time in this macro as it change other data as well
+ every time when used. */
+
+#define SHA1STEP32(vA,vB,vC,vD,vE,ft,kt,wt) do { \
+ (vE) += _MHD_ROTL32 ((vA), 5) + ft ((vB), (vC), (vD)) + (kt) + (wt); \
+ (vB) = _MHD_ROTL32 ((vB), 30); } while (0)
+
+ /* Get value of W(t) from input data buffer,
+ See FIPS PUB 180-4 paragraph 6.1.3.
+ Input data must be read in big-endian bytes order,
+ see FIPS PUB 180-4 paragraph 3.1.2. */
+#define GET_W_FROM_DATA(buf,t) \
+ _MHD_GET_32BIT_BE (((const uint8_t*) (buf)) + (t) * SHA1_BYTES_IN_WORD)
+
+#ifndef _MHD_GET_32BIT_BE_UNALIGNED
+ if (0 != (((uintptr_t) data) % _MHD_UINT32_ALIGN))
+ {
+ /* Copy the unaligned input data to the aligned buffer */
+ memcpy (W, data, SHA1_BLOCK_SIZE);
+ /* The W[] buffer itself will be used as the source of the data,
+ * but data will be reloaded in correct bytes order during
+ * the next steps */
+ data = (uint8_t*) W;
+ }
+#endif /* _MHD_GET_32BIT_BE_UNALIGNED */
+
+/* SHA-1 values of Kt for t=0..19, see FIPS PUB 180-4 paragraph 4.2.1. */
+#define K00 UINT32_C(0x5a827999)
+/* SHA-1 values of Kt for t=20..39, see FIPS PUB 180-4 paragraph 4.2.1.*/
+#define K20 UINT32_C(0x6ed9eba1)
+/* SHA-1 values of Kt for t=40..59, see FIPS PUB 180-4 paragraph 4.2.1.*/
+#define K40 UINT32_C(0x8f1bbcdc)
+/* SHA-1 values of Kt for t=60..79, see FIPS PUB 180-4 paragraph 4.2.1.*/
+#define K60 UINT32_C(0xca62c1d6)
+
+ /* During first 16 steps, before making any calculations on each step,
+ the W element is read from input data buffer as big-endian value and
+ stored in array of W elements. */
+ /* Note: instead of using K constants as array, all K values are specified
+ individually for each step. */
+ SHA1STEP32 (a, b, c, d, e, Ch, K00, W[0] = GET_W_FROM_DATA (data, 0));
+ SHA1STEP32 (e, a, b, c, d, Ch, K00, W[1] = GET_W_FROM_DATA (data, 1));
+ SHA1STEP32 (d, e, a, b, c, Ch, K00, W[2] = GET_W_FROM_DATA (data, 2));
+ SHA1STEP32 (c, d, e, a, b, Ch, K00, W[3] = GET_W_FROM_DATA (data, 3));
+ SHA1STEP32 (b, c, d, e, a, Ch, K00, W[4] = GET_W_FROM_DATA (data, 4));
+ SHA1STEP32 (a, b, c, d, e, Ch, K00, W[5] = GET_W_FROM_DATA (data, 5));
+ SHA1STEP32 (e, a, b, c, d, Ch, K00, W[6] = GET_W_FROM_DATA (data, 6));
+ SHA1STEP32 (d, e, a, b, c, Ch, K00, W[7] = GET_W_FROM_DATA (data, 7));
+ SHA1STEP32 (c, d, e, a, b, Ch, K00, W[8] = GET_W_FROM_DATA (data, 8));
+ SHA1STEP32 (b, c, d, e, a, Ch, K00, W[9] = GET_W_FROM_DATA (data, 9));
+ SHA1STEP32 (a, b, c, d, e, Ch, K00, W[10] = GET_W_FROM_DATA (data, 10));
+ SHA1STEP32 (e, a, b, c, d, Ch, K00, W[11] = GET_W_FROM_DATA (data, 11));
+ SHA1STEP32 (d, e, a, b, c, Ch, K00, W[12] = GET_W_FROM_DATA (data, 12));
+ SHA1STEP32 (c, d, e, a, b, Ch, K00, W[13] = GET_W_FROM_DATA (data, 13));
+ SHA1STEP32 (b, c, d, e, a, Ch, K00, W[14] = GET_W_FROM_DATA (data, 14));
+ SHA1STEP32 (a, b, c, d, e, Ch, K00, W[15] = GET_W_FROM_DATA (data, 15));
+
+ /* 'W' generation and assignment for 16 <= t <= 79.
+ See FIPS PUB 180-4 paragraph 6.1.3.
+ As only last 16 'W' are used in calculations, it is possible to
+ use 16 elements array of W as cyclic buffer. */
+#define Wgen(w,t) _MHD_ROTL32((w)[(t + 13) & 0xf] ^ (w)[(t + 8) & 0xf] \
+ ^ (w)[(t + 2) & 0xf] ^ (w)[t & 0xf], 1)
+
+ /* During last 60 steps, before making any calculations on each step,
+ W element is generated from W elements of cyclic buffer and generated value
+ stored back in cyclic buffer. */
+ /* Note: instead of using K constants as array, all K values are specified
+ individually for each step, see FIPS PUB 180-4 paragraph 4.2.1. */
+ SHA1STEP32 (e, a, b, c, d, Ch, K00, W[16 & 0xf] = Wgen (W, 16));
+ SHA1STEP32 (d, e, a, b, c, Ch, K00, W[17 & 0xf] = Wgen (W, 17));
+ SHA1STEP32 (c, d, e, a, b, Ch, K00, W[18 & 0xf] = Wgen (W, 18));
+ SHA1STEP32 (b, c, d, e, a, Ch, K00, W[19 & 0xf] = Wgen (W, 19));
+ SHA1STEP32 (a, b, c, d, e, Par, K20, W[20 & 0xf] = Wgen (W, 20));
+ SHA1STEP32 (e, a, b, c, d, Par, K20, W[21 & 0xf] = Wgen (W, 21));
+ SHA1STEP32 (d, e, a, b, c, Par, K20, W[22 & 0xf] = Wgen (W, 22));
+ SHA1STEP32 (c, d, e, a, b, Par, K20, W[23 & 0xf] = Wgen (W, 23));
+ SHA1STEP32 (b, c, d, e, a, Par, K20, W[24 & 0xf] = Wgen (W, 24));
+ SHA1STEP32 (a, b, c, d, e, Par, K20, W[25 & 0xf] = Wgen (W, 25));
+ SHA1STEP32 (e, a, b, c, d, Par, K20, W[26 & 0xf] = Wgen (W, 26));
+ SHA1STEP32 (d, e, a, b, c, Par, K20, W[27 & 0xf] = Wgen (W, 27));
+ SHA1STEP32 (c, d, e, a, b, Par, K20, W[28 & 0xf] = Wgen (W, 28));
+ SHA1STEP32 (b, c, d, e, a, Par, K20, W[29 & 0xf] = Wgen (W, 29));
+ SHA1STEP32 (a, b, c, d, e, Par, K20, W[30 & 0xf] = Wgen (W, 30));
+ SHA1STEP32 (e, a, b, c, d, Par, K20, W[31 & 0xf] = Wgen (W, 31));
+ SHA1STEP32 (d, e, a, b, c, Par, K20, W[32 & 0xf] = Wgen (W, 32));
+ SHA1STEP32 (c, d, e, a, b, Par, K20, W[33 & 0xf] = Wgen (W, 33));
+ SHA1STEP32 (b, c, d, e, a, Par, K20, W[34 & 0xf] = Wgen (W, 34));
+ SHA1STEP32 (a, b, c, d, e, Par, K20, W[35 & 0xf] = Wgen (W, 35));
+ SHA1STEP32 (e, a, b, c, d, Par, K20, W[36 & 0xf] = Wgen (W, 36));
+ SHA1STEP32 (d, e, a, b, c, Par, K20, W[37 & 0xf] = Wgen (W, 37));
+ SHA1STEP32 (c, d, e, a, b, Par, K20, W[38 & 0xf] = Wgen (W, 38));
+ SHA1STEP32 (b, c, d, e, a, Par, K20, W[39 & 0xf] = Wgen (W, 39));
+ SHA1STEP32 (a, b, c, d, e, Maj, K40, W[40 & 0xf] = Wgen (W, 40));
+ SHA1STEP32 (e, a, b, c, d, Maj, K40, W[41 & 0xf] = Wgen (W, 41));
+ SHA1STEP32 (d, e, a, b, c, Maj, K40, W[42 & 0xf] = Wgen (W, 42));
+ SHA1STEP32 (c, d, e, a, b, Maj, K40, W[43 & 0xf] = Wgen (W, 43));
+ SHA1STEP32 (b, c, d, e, a, Maj, K40, W[44 & 0xf] = Wgen (W, 44));
+ SHA1STEP32 (a, b, c, d, e, Maj, K40, W[45 & 0xf] = Wgen (W, 45));
+ SHA1STEP32 (e, a, b, c, d, Maj, K40, W[46 & 0xf] = Wgen (W, 46));
+ SHA1STEP32 (d, e, a, b, c, Maj, K40, W[47 & 0xf] = Wgen (W, 47));
+ SHA1STEP32 (c, d, e, a, b, Maj, K40, W[48 & 0xf] = Wgen (W, 48));
+ SHA1STEP32 (b, c, d, e, a, Maj, K40, W[49 & 0xf] = Wgen (W, 49));
+ SHA1STEP32 (a, b, c, d, e, Maj, K40, W[50 & 0xf] = Wgen (W, 50));
+ SHA1STEP32 (e, a, b, c, d, Maj, K40, W[51 & 0xf] = Wgen (W, 51));
+ SHA1STEP32 (d, e, a, b, c, Maj, K40, W[52 & 0xf] = Wgen (W, 52));
+ SHA1STEP32 (c, d, e, a, b, Maj, K40, W[53 & 0xf] = Wgen (W, 53));
+ SHA1STEP32 (b, c, d, e, a, Maj, K40, W[54 & 0xf] = Wgen (W, 54));
+ SHA1STEP32 (a, b, c, d, e, Maj, K40, W[55 & 0xf] = Wgen (W, 55));
+ SHA1STEP32 (e, a, b, c, d, Maj, K40, W[56 & 0xf] = Wgen (W, 56));
+ SHA1STEP32 (d, e, a, b, c, Maj, K40, W[57 & 0xf] = Wgen (W, 57));
+ SHA1STEP32 (c, d, e, a, b, Maj, K40, W[58 & 0xf] = Wgen (W, 58));
+ SHA1STEP32 (b, c, d, e, a, Maj, K40, W[59 & 0xf] = Wgen (W, 59));
+ SHA1STEP32 (a, b, c, d, e, Par, K60, W[60 & 0xf] = Wgen (W, 60));
+ SHA1STEP32 (e, a, b, c, d, Par, K60, W[61 & 0xf] = Wgen (W, 61));
+ SHA1STEP32 (d, e, a, b, c, Par, K60, W[62 & 0xf] = Wgen (W, 62));
+ SHA1STEP32 (c, d, e, a, b, Par, K60, W[63 & 0xf] = Wgen (W, 63));
+ SHA1STEP32 (b, c, d, e, a, Par, K60, W[64 & 0xf] = Wgen (W, 64));
+ SHA1STEP32 (a, b, c, d, e, Par, K60, W[65 & 0xf] = Wgen (W, 65));
+ SHA1STEP32 (e, a, b, c, d, Par, K60, W[66 & 0xf] = Wgen (W, 66));
+ SHA1STEP32 (d, e, a, b, c, Par, K60, W[67 & 0xf] = Wgen (W, 67));
+ SHA1STEP32 (c, d, e, a, b, Par, K60, W[68 & 0xf] = Wgen (W, 68));
+ SHA1STEP32 (b, c, d, e, a, Par, K60, W[69 & 0xf] = Wgen (W, 69));
+ SHA1STEP32 (a, b, c, d, e, Par, K60, W[70 & 0xf] = Wgen (W, 70));
+ SHA1STEP32 (e, a, b, c, d, Par, K60, W[71 & 0xf] = Wgen (W, 71));
+ SHA1STEP32 (d, e, a, b, c, Par, K60, W[72 & 0xf] = Wgen (W, 72));
+ SHA1STEP32 (c, d, e, a, b, Par, K60, W[73 & 0xf] = Wgen (W, 73));
+ SHA1STEP32 (b, c, d, e, a, Par, K60, W[74 & 0xf] = Wgen (W, 74));
+ SHA1STEP32 (a, b, c, d, e, Par, K60, W[75 & 0xf] = Wgen (W, 75));
+ SHA1STEP32 (e, a, b, c, d, Par, K60, W[76 & 0xf] = Wgen (W, 76));
+ SHA1STEP32 (d, e, a, b, c, Par, K60, W[77 & 0xf] = Wgen (W, 77));
+ SHA1STEP32 (c, d, e, a, b, Par, K60, W[78 & 0xf] = Wgen (W, 78));
+ SHA1STEP32 (b, c, d, e, a, Par, K60, W[79 & 0xf] = Wgen (W, 79));
+
+ /* Compute intermediate hash.
+ See FIPS PUB 180-4 paragraph 6.1.3 step 4. */
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+}
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param data bytes to add to hash
+ * @param length number of bytes in @a data
+ */
+void
+MHD_SHA1_update (void *ctx_,
+ const uint8_t *data,
+ size_t length)
+{
+ struct sha1_ctx *const ctx = ctx_;
+ unsigned bytes_have; /**< Number of bytes in buffer */
+
+ mhd_assert ((data != NULL) || (length == 0));
+
+ if (0 == length)
+ return; /* Do nothing */
+
+ /* Note: (count & (SHA1_BLOCK_SIZE-1))
+ equal (count % SHA1_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned) (ctx->count & (SHA1_BLOCK_SIZE - 1));
+ ctx->count += length;
+
+ if (0 != bytes_have)
+ {
+ unsigned bytes_left = SHA1_BLOCK_SIZE - bytes_have;
+ if (length >= bytes_left)
+ { /* Combine new data with the data in the buffer and
+ process the full block. */
+ memcpy (ctx->buffer + bytes_have,
+ data,
+ bytes_left);
+ data += bytes_left;
+ length -= bytes_left;
+ sha1_transform (ctx->H, ctx->buffer);
+ bytes_have = 0;
+ }
+ }
+
+ while (SHA1_BLOCK_SIZE <= length)
+ { /* Process any full blocks of new data directly,
+ without copying to the buffer. */
+ sha1_transform (ctx->H, data);
+ data += SHA1_BLOCK_SIZE;
+ length -= SHA1_BLOCK_SIZE;
+ }
+
+ if (0 != length)
+ { /* Copy incomplete block of new data (if any)
+ to the buffer. */
+ memcpy (ctx->buffer + bytes_have, data, length);
+ }
+}
+
+
+/**
+ * Size of "length" padding addition in bytes.
+ * See FIPS PUB 180-4 paragraph 5.1.1.
+ */
+#define SHA1_SIZE_OF_LEN_ADD (64 / 8)
+
+/**
+ * Finalise SHA-1 calculation, return digest.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param[out] digest set to the hash, must be #SHA1_DIGEST_SIZE bytes
+ */
+void
+MHD_SHA1_finish (void *ctx_,
+ uint8_t digest[SHA1_DIGEST_SIZE])
+{
+ struct sha1_ctx *const ctx = ctx_;
+ uint64_t num_bits; /**< Number of processed bits */
+ unsigned bytes_have; /**< Number of bytes in buffer */
+
+ num_bits = ctx->count << 3;
+ /* Note: (count & (SHA1_BLOCK_SIZE-1))
+ equals (count % SHA1_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned) (ctx->count & (SHA1_BLOCK_SIZE - 1));
+
+ /* Input data must be padded with bit "1" and with length of data in bits.
+ See FIPS PUB 180-4 paragraph 5.1.1. */
+ /* Data is always processed in form of bytes (not by individual bits),
+ therefore position of first padding bit in byte is always predefined (0x80). */
+ /* Buffer always have space at least for one byte (as full buffers are
+ processed immediately). */
+ ctx->buffer[bytes_have++] = 0x80;
+
+ if (SHA1_BLOCK_SIZE - bytes_have < SHA1_SIZE_OF_LEN_ADD)
+ { /* No space in current block to put total length of message.
+ Pad current block with zeros and process it. */
+ if (SHA1_BLOCK_SIZE > bytes_have)
+ memset (ctx->buffer + bytes_have, 0, SHA1_BLOCK_SIZE - bytes_have);
+ /* Process full block. */
+ sha1_transform (ctx->H, ctx->buffer);
+ /* Start new block. */
+ bytes_have = 0;
+ }
+
+ /* Pad the rest of the buffer with zeros. */
+ memset (ctx->buffer + bytes_have, 0,
+ SHA1_BLOCK_SIZE - SHA1_SIZE_OF_LEN_ADD - bytes_have);
+ /* Put the number of bits in the processed message as a big-endian value. */
+ _MHD_PUT_64BIT_BE_SAFE (ctx->buffer + SHA1_BLOCK_SIZE - SHA1_SIZE_OF_LEN_ADD,
+ num_bits);
+ /* Process the full final block. */
+ sha1_transform (ctx->H, ctx->buffer);
+
+ /* Put final hash/digest in BE mode */
+#ifndef _MHD_PUT_32BIT_BE_UNALIGNED
+ if (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN)
+ {
+ uint32_t alig_dgst[_SHA1_DIGEST_LENGTH];
+ _MHD_PUT_32BIT_BE (alig_dgst + 0, ctx->H[0]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 1, ctx->H[1]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 2, ctx->H[2]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 3, ctx->H[3]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 4, ctx->H[4]);
+ /* Copy result to unaligned destination address */
+ memcpy (digest, alig_dgst, SHA1_DIGEST_SIZE);
+ }
+ else
+#else /* _MHD_PUT_32BIT_BE_UNALIGNED */
+ if (1)
+#endif /* _MHD_PUT_32BIT_BE_UNALIGNED */
+ {
+ _MHD_PUT_32BIT_BE (digest + 0 * SHA1_BYTES_IN_WORD, ctx->H[0]);
+ _MHD_PUT_32BIT_BE (digest + 1 * SHA1_BYTES_IN_WORD, ctx->H[1]);
+ _MHD_PUT_32BIT_BE (digest + 2 * SHA1_BYTES_IN_WORD, ctx->H[2]);
+ _MHD_PUT_32BIT_BE (digest + 3 * SHA1_BYTES_IN_WORD, ctx->H[3]);
+ _MHD_PUT_32BIT_BE (digest + 4 * SHA1_BYTES_IN_WORD, ctx->H[4]);
+ }
+
+ /* Erase potentially sensitive data. */
+ memset (ctx, 0, sizeof(struct sha1_ctx));
+}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/sha1.h
^
|
@@ -0,0 +1,110 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file microhttpd/sha1.h
+ * @brief Calculation of SHA-1 digest
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA1_H
+#define MHD_SHA1_H 1
+
+#include "mhd_options.h"
+#include <stdint.h>
+#ifdef HAVE_STDDEF_H
+#include <stddef.h> /* for size_t */
+#endif /* HAVE_STDDEF_H */
+
+/**
+ * SHA-1 digest is kept internally as 5 32-bit words.
+ */
+#define _SHA1_DIGEST_LENGTH 5
+
+/**
+ * Number of bits in single SHA-1 word
+ */
+#define SHA1_WORD_SIZE_BITS 32
+
+/**
+ * Number of bytes in single SHA-1 word
+ */
+#define SHA1_BYTES_IN_WORD (SHA1_WORD_SIZE_BITS / 8)
+
+/**
+ * Size of SHA-1 digest in bytes
+ */
+#define SHA1_DIGEST_SIZE (_SHA1_DIGEST_LENGTH * SHA1_BYTES_IN_WORD)
+
+/**
+ * Size of SHA-1 digest string in chars including termination NUL
+ */
+#define SHA1_DIGEST_STRING_SIZE ((SHA1_DIGEST_SIZE) * 2 + 1)
+
+/**
+ * Size of single processing block in bits
+ */
+#define SHA1_BLOCK_SIZE_BITS 512
+
+/**
+ * Size of single processing block in bytes
+ */
+#define SHA1_BLOCK_SIZE (SHA1_BLOCK_SIZE_BITS / 8)
+
+
+struct sha1_ctx
+{
+ uint32_t H[_SHA1_DIGEST_LENGTH]; /**< Intermediate hash value / digest at end of calculation */
+ uint8_t buffer[SHA1_BLOCK_SIZE]; /**< SHA256 input data buffer */
+ uint64_t count; /**< number of bytes, mod 2^64 */
+};
+
+/**
+ * Initialise structure for SHA-1 calculation.
+ *
+ * @param ctx must be a `struct sha1_ctx *`
+ */
+void
+MHD_SHA1_init (void *ctx_);
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param data bytes to add to hash
+ * @param length number of bytes in @a data
+ */
+void
+MHD_SHA1_update (void *ctx_,
+ const uint8_t *data,
+ size_t length);
+
+
+/**
+ * Finalise SHA-1 calculation, return digest.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param[out] digest set to the hash, must be #SHA1_DIGEST_SIZE bytes
+ */
+void
+MHD_SHA1_finish (void *ctx_,
+ uint8_t digest[SHA1_DIGEST_SIZE]);
+
+#endif /* MHD_SHA1_H */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/sha256.c
^
|
@@ -1,8 +1,6 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2019 Karlson2k (Evgeny Grin)
- Some ideas are based on Libgcrypt implementation.
- Copyright (C) 2003, 2006, 2008, 2009 Free Software Foundation, Inc.
+ Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -25,8 +23,6 @@
* @author Karlson2k (Evgeny Grin)
*/
-/* Some tricks are based on Libgcrypt implementation. */
-
#include "sha256.h"
#include <string.h>
@@ -49,14 +45,14 @@
/* First thirty-two bits of the fractional parts of the square
* roots of the first eight prime numbers: 2, 3, 5, 7, 11, 13,
* 17, 19." */
- ctx->H[0] = 0x6a09e667UL;
- ctx->H[1] = 0xbb67ae85UL;
- ctx->H[2] = 0x3c6ef372UL;
- ctx->H[3] = 0xa54ff53aUL;
- ctx->H[4] = 0x510e527fUL;
- ctx->H[5] = 0x9b05688cUL;
- ctx->H[6] = 0x1f83d9abUL;
- ctx->H[7] = 0x5be0cd19UL;
+ ctx->H[0] = UINT32_C (0x6a09e667);
+ ctx->H[1] = UINT32_C (0xbb67ae85);
+ ctx->H[2] = UINT32_C (0x3c6ef372);
+ ctx->H[3] = UINT32_C (0xa54ff53a);
+ ctx->H[4] = UINT32_C (0x510e527f);
+ ctx->H[5] = UINT32_C (0x9b05688c);
+ ctx->H[6] = UINT32_C (0x1f83d9ab);
+ ctx->H[7] = UINT32_C (0x5be0cd19);
/* Initialise number of bytes. */
ctx->count = 0;
@@ -64,12 +60,6 @@
/**
- * Number of bytes in single SHA-256 word
- * used to process data
- */
-#define SHA256_BYTES_IN_WORD 4
-
-/**
* Base of SHA-256 transformation.
* Gets full 64 bytes block of data and updates hash values;
* @param H hash values
@@ -100,17 +90,19 @@
#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) )
#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
/* Unoptimized (original) versions: */
-/* #define Ch(x,y,z) ( ( (x) & (y) ) | ( ~(x) & (z) ) ) */
+/* #define Ch(x,y,z) ( ( (x) & (y) ) ^ ( ~(x) & (z) ) ) */
/* #define Maj(x,y,z) ( ((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)) ) */
/* Four 'Sigma' macro functions.
See FIPS PUB 180-4 formulae 4.4, 4.5, 4.6, 4.7. */
-#define SIG0(x) (_MHD_ROTR32 ((x),2) ^ _MHD_ROTR32 ((x),13) ^ _MHD_ROTR32 ((x), \
- 22) )
-#define SIG1(x) (_MHD_ROTR32 ((x),6) ^ _MHD_ROTR32 ((x),11) ^ _MHD_ROTR32 ((x), \
- 25) )
-#define sig0(x) (_MHD_ROTR32 ((x),7) ^ _MHD_ROTR32 ((x),18) ^ ((x) >> 3) )
-#define sig1(x) (_MHD_ROTR32 ((x),17) ^ _MHD_ROTR32 ((x),19) ^ ((x) >> 10) )
+#define SIG0(x) (_MHD_ROTR32 ((x), 2) ^ _MHD_ROTR32 ((x), 13) ^ \
+ _MHD_ROTR32 ((x), 22) )
+#define SIG1(x) (_MHD_ROTR32 ((x), 6) ^ _MHD_ROTR32 ((x), 11) ^ \
+ _MHD_ROTR32 ((x), 25) )
+#define sig0(x) (_MHD_ROTR32 ((x), 7) ^ _MHD_ROTR32 ((x), 18) ^ \
+ ((x) >> 3) )
+#define sig1(x) (_MHD_ROTR32 ((x), 17) ^ _MHD_ROTR32 ((x),19) ^ \
+ ((x) >> 10) )
/* Single step of SHA-256 computation,
see FIPS PUB 180-4 paragraph 6.2.2 step 3.
@@ -124,10 +116,22 @@
second (vH += SIG0(vA) + Maj(vE,vF,vC) equals T1 + T2 in FIPS PUB 180-4 paragraph 6.2.2 step 3.
* Note: 'wt' must be used exactly one time in this macro as it change other data as well
every time when used. */
-#define SHA2STEP32(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
+#define SHA2STEP32(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
(vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt)); \
(vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0)
+#ifndef _MHD_GET_32BIT_BE_UNALIGNED
+ if (0 != (((uintptr_t) data) % _MHD_UINT32_ALIGN))
+ {
+ /* Copy the unaligned input data to the aligned buffer */
+ memcpy (W, data, SHA256_BLOCK_SIZE);
+ /* The W[] buffer itself will be used as the source of the data,
+ * but data will be reloaded in correct bytes order during
+ * the next steps */
+ data = (uint8_t*) W;
+ }
+#endif /* _MHD_GET_32BIT_BE_UNALIGNED */
+
/* Get value of W(t) from input data buffer,
See FIPS PUB 180-4 paragraph 6.2.
Input data must be read in big-endian bytes order,
@@ -140,38 +144,38 @@
stored in array of W elements. */
/* Note: instead of using K constants as array, all K values are specified
individually for each step, see FIPS PUB 180-4 paragraph 4.2.2 for K values. */
- SHA2STEP32 (a, b, c, d, e, f, g, h, 0x428a2f98UL, W[0] = GET_W_FROM_DATA (
- data,0));
- SHA2STEP32 (h, a, b, c, d, e, f, g, 0x71374491UL, W[1] = GET_W_FROM_DATA (
- data,1));
- SHA2STEP32 (g, h, a, b, c, d, e, f, 0xb5c0fbcfUL, W[2] = GET_W_FROM_DATA (
- data,2));
- SHA2STEP32 (f, g, h, a, b, c, d, e, 0xe9b5dba5UL, W[3] = GET_W_FROM_DATA (
- data,3));
- SHA2STEP32 (e, f, g, h, a, b, c, d, 0x3956c25bUL, W[4] = GET_W_FROM_DATA (
- data,4));
- SHA2STEP32 (d, e, f, g, h, a, b, c, 0x59f111f1UL, W[5] = GET_W_FROM_DATA (
- data,5));
- SHA2STEP32 (c, d, e, f, g, h, a, b, 0x923f82a4UL, W[6] = GET_W_FROM_DATA (
- data,6));
- SHA2STEP32 (b, c, d, e, f, g, h, a, 0xab1c5ed5UL, W[7] = GET_W_FROM_DATA (
- data,7));
- SHA2STEP32 (a, b, c, d, e, f, g, h, 0xd807aa98UL, W[8] = GET_W_FROM_DATA (
- data,8));
- SHA2STEP32 (h, a, b, c, d, e, f, g, 0x12835b01UL, W[9] = GET_W_FROM_DATA (
- data,9));
- SHA2STEP32 (g, h, a, b, c, d, e, f, 0x243185beUL, W[10] = GET_W_FROM_DATA (
- data,10));
- SHA2STEP32 (f, g, h, a, b, c, d, e, 0x550c7dc3UL, W[11] = GET_W_FROM_DATA (
- data,11));
- SHA2STEP32 (e, f, g, h, a, b, c, d, 0x72be5d74UL, W[12] = GET_W_FROM_DATA (
- data,12));
- SHA2STEP32 (d, e, f, g, h, a, b, c, 0x80deb1feUL, W[13] = GET_W_FROM_DATA (
- data,13));
- SHA2STEP32 (c, d, e, f, g, h, a, b, 0x9bdc06a7UL, W[14] = GET_W_FROM_DATA (
- data,14));
- SHA2STEP32 (b, c, d, e, f, g, h, a, 0xc19bf174UL, W[15] = GET_W_FROM_DATA (
- data,15));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x428a2f98), W[0] = \
+ GET_W_FROM_DATA (data, 0));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x71374491), W[1] = \
+ GET_W_FROM_DATA (data, 1));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0xb5c0fbcf), W[2] = \
+ GET_W_FROM_DATA (data, 2));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0xe9b5dba5), W[3] = \
+ GET_W_FROM_DATA (data, 3));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x3956c25b), W[4] = \
+ GET_W_FROM_DATA (data, 4));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x59f111f1), W[5] = \
+ GET_W_FROM_DATA (data, 5));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x923f82a4), W[6] = \
+ GET_W_FROM_DATA (data, 6));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0xab1c5ed5), W[7] = \
+ GET_W_FROM_DATA (data, 7));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0xd807aa98), W[8] = \
+ GET_W_FROM_DATA (data, 8));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x12835b01), W[9] = \
+ GET_W_FROM_DATA (data, 9));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x243185be), W[10] = \
+ GET_W_FROM_DATA (data, 10));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x550c7dc3), W[11] = \
+ GET_W_FROM_DATA (data, 11));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x72be5d74), W[12] = \
+ GET_W_FROM_DATA (data, 12));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x80deb1fe), W[13] = \
+ GET_W_FROM_DATA (data, 13));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x9bdc06a7), W[14] = \
+ GET_W_FROM_DATA (data, 14));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0xc19bf174), W[15] = \
+ GET_W_FROM_DATA (data, 15));
/* 'W' generation and assignment for 16 <= t <= 63.
See FIPS PUB 180-4 paragraph 6.2.2.
@@ -186,57 +190,105 @@
stored back in cyclic buffer. */
/* Note: instead of using K constants as array, all K values are specified
individually for each step, see FIPS PUB 180-4 paragraph 4.2.2 for K values. */
- SHA2STEP32 (a, b, c, d, e, f, g, h, 0xe49b69c1UL, W[16 & 0xf] = Wgen (W,16));
- SHA2STEP32 (h, a, b, c, d, e, f, g, 0xefbe4786UL, W[17 & 0xf] = Wgen (W,17));
- SHA2STEP32 (g, h, a, b, c, d, e, f, 0x0fc19dc6UL, W[18 & 0xf] = Wgen (W,18));
- SHA2STEP32 (f, g, h, a, b, c, d, e, 0x240ca1ccUL, W[19 & 0xf] = Wgen (W,19));
- SHA2STEP32 (e, f, g, h, a, b, c, d, 0x2de92c6fUL, W[20 & 0xf] = Wgen (W,20));
- SHA2STEP32 (d, e, f, g, h, a, b, c, 0x4a7484aaUL, W[21 & 0xf] = Wgen (W,21));
- SHA2STEP32 (c, d, e, f, g, h, a, b, 0x5cb0a9dcUL, W[22 & 0xf] = Wgen (W,22));
- SHA2STEP32 (b, c, d, e, f, g, h, a, 0x76f988daUL, W[23 & 0xf] = Wgen (W,23));
- SHA2STEP32 (a, b, c, d, e, f, g, h, 0x983e5152UL, W[24 & 0xf] = Wgen (W,24));
- SHA2STEP32 (h, a, b, c, d, e, f, g, 0xa831c66dUL, W[25 & 0xf] = Wgen (W,25));
- SHA2STEP32 (g, h, a, b, c, d, e, f, 0xb00327c8UL, W[26 & 0xf] = Wgen (W,26));
- SHA2STEP32 (f, g, h, a, b, c, d, e, 0xbf597fc7UL, W[27 & 0xf] = Wgen (W,27));
- SHA2STEP32 (e, f, g, h, a, b, c, d, 0xc6e00bf3UL, W[28 & 0xf] = Wgen (W,28));
- SHA2STEP32 (d, e, f, g, h, a, b, c, 0xd5a79147UL, W[29 & 0xf] = Wgen (W,29));
- SHA2STEP32 (c, d, e, f, g, h, a, b, 0x06ca6351UL, W[30 & 0xf] = Wgen (W,30));
- SHA2STEP32 (b, c, d, e, f, g, h, a, 0x14292967UL, W[31 & 0xf] = Wgen (W,31));
- SHA2STEP32 (a, b, c, d, e, f, g, h, 0x27b70a85UL, W[32 & 0xf] = Wgen (W,32));
- SHA2STEP32 (h, a, b, c, d, e, f, g, 0x2e1b2138UL, W[33 & 0xf] = Wgen (W,33));
- SHA2STEP32 (g, h, a, b, c, d, e, f, 0x4d2c6dfcUL, W[34 & 0xf] = Wgen (W,34));
- SHA2STEP32 (f, g, h, a, b, c, d, e, 0x53380d13UL, W[35 & 0xf] = Wgen (W,35));
- SHA2STEP32 (e, f, g, h, a, b, c, d, 0x650a7354UL, W[36 & 0xf] = Wgen (W,36));
- SHA2STEP32 (d, e, f, g, h, a, b, c, 0x766a0abbUL, W[37 & 0xf] = Wgen (W,37));
- SHA2STEP32 (c, d, e, f, g, h, a, b, 0x81c2c92eUL, W[38 & 0xf] = Wgen (W,38));
- SHA2STEP32 (b, c, d, e, f, g, h, a, 0x92722c85UL, W[39 & 0xf] = Wgen (W,39));
- SHA2STEP32 (a, b, c, d, e, f, g, h, 0xa2bfe8a1UL, W[40 & 0xf] = Wgen (W,40));
- SHA2STEP32 (h, a, b, c, d, e, f, g, 0xa81a664bUL, W[41 & 0xf] = Wgen (W,41));
- SHA2STEP32 (g, h, a, b, c, d, e, f, 0xc24b8b70UL, W[42 & 0xf] = Wgen (W,42));
- SHA2STEP32 (f, g, h, a, b, c, d, e, 0xc76c51a3UL, W[43 & 0xf] = Wgen (W,43));
- SHA2STEP32 (e, f, g, h, a, b, c, d, 0xd192e819UL, W[44 & 0xf] = Wgen (W,44));
- SHA2STEP32 (d, e, f, g, h, a, b, c, 0xd6990624UL, W[45 & 0xf] = Wgen (W,45));
- SHA2STEP32 (c, d, e, f, g, h, a, b, 0xf40e3585UL, W[46 & 0xf] = Wgen (W,46));
- SHA2STEP32 (b, c, d, e, f, g, h, a, 0x106aa070UL, W[47 & 0xf] = Wgen (W,47));
- SHA2STEP32 (a, b, c, d, e, f, g, h, 0x19a4c116UL, W[48 & 0xf] = Wgen (W,48));
- SHA2STEP32 (h, a, b, c, d, e, f, g, 0x1e376c08UL, W[49 & 0xf] = Wgen (W,49));
- SHA2STEP32 (g, h, a, b, c, d, e, f, 0x2748774cUL, W[50 & 0xf] = Wgen (W,50));
- SHA2STEP32 (f, g, h, a, b, c, d, e, 0x34b0bcb5UL, W[51 & 0xf] = Wgen (W,51));
- SHA2STEP32 (e, f, g, h, a, b, c, d, 0x391c0cb3UL, W[52 & 0xf] = Wgen (W,52));
- SHA2STEP32 (d, e, f, g, h, a, b, c, 0x4ed8aa4aUL, W[53 & 0xf] = Wgen (W,53));
- SHA2STEP32 (c, d, e, f, g, h, a, b, 0x5b9cca4fUL, W[54 & 0xf] = Wgen (W,54));
- SHA2STEP32 (b, c, d, e, f, g, h, a, 0x682e6ff3UL, W[55 & 0xf] = Wgen (W,55));
- SHA2STEP32 (a, b, c, d, e, f, g, h, 0x748f82eeUL, W[56 & 0xf] = Wgen (W,56));
- SHA2STEP32 (h, a, b, c, d, e, f, g, 0x78a5636fUL, W[57 & 0xf] = Wgen (W,57));
- SHA2STEP32 (g, h, a, b, c, d, e, f, 0x84c87814UL, W[58 & 0xf] = Wgen (W,58));
- SHA2STEP32 (f, g, h, a, b, c, d, e, 0x8cc70208UL, W[59 & 0xf] = Wgen (W,59));
- SHA2STEP32 (e, f, g, h, a, b, c, d, 0x90befffaUL, W[60 & 0xf] = Wgen (W,60));
- SHA2STEP32 (d, e, f, g, h, a, b, c, 0xa4506cebUL, W[61 & 0xf] = Wgen (W,61));
- SHA2STEP32 (c, d, e, f, g, h, a, b, 0xbef9a3f7UL, W[62 & 0xf] = Wgen (W,62));
- SHA2STEP32 (b, c, d, e, f, g, h, a, 0xc67178f2UL, W[63 & 0xf] = Wgen (W,63));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0xe49b69c1), W[16 & 0xf] = \
+ Wgen (W,16));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0xefbe4786), W[17 & 0xf] = \
+ Wgen (W,17));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x0fc19dc6), W[18 & 0xf] = \
+ Wgen (W,18));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x240ca1cc), W[19 & 0xf] = \
+ Wgen (W,19));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x2de92c6f), W[20 & 0xf] = \
+ Wgen (W,20));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x4a7484aa), W[21 & 0xf] = \
+ Wgen (W,21));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x5cb0a9dc), W[22 & 0xf] = \
+ Wgen (W,22));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0x76f988da), W[23 & 0xf] = \
+ Wgen (W,23));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x983e5152), W[24 & 0xf] = \
+ Wgen (W,24));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0xa831c66d), W[25 & 0xf] = \
+ Wgen (W,25));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0xb00327c8), W[26 & 0xf] = \
+ Wgen (W,26));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0xbf597fc7), W[27 & 0xf] = \
+ Wgen (W,27));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0xc6e00bf3), W[28 & 0xf] = \
+ Wgen (W,28));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0xd5a79147), W[29 & 0xf] = \
+ Wgen (W,29));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x06ca6351), W[30 & 0xf] = \
+ Wgen (W,30));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0x14292967), W[31 & 0xf] = \
+ Wgen (W,31));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x27b70a85), W[32 & 0xf] = \
+ Wgen (W,32));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x2e1b2138), W[33 & 0xf] = \
+ Wgen (W,33));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x4d2c6dfc), W[34 & 0xf] = \
+ Wgen (W,34));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x53380d13), W[35 & 0xf] = \
+ Wgen (W,35));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x650a7354), W[36 & 0xf] = \
+ Wgen (W,36));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x766a0abb), W[37 & 0xf] = \
+ Wgen (W,37));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x81c2c92e), W[38 & 0xf] = \
+ Wgen (W,38));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0x92722c85), W[39 & 0xf] = \
+ Wgen (W,39));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0xa2bfe8a1), W[40 & 0xf] = \
+ Wgen (W,40));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0xa81a664b), W[41 & 0xf] = \
+ Wgen (W,41));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0xc24b8b70), W[42 & 0xf] = \
+ Wgen (W,42));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0xc76c51a3), W[43 & 0xf] = \
+ Wgen (W,43));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0xd192e819), W[44 & 0xf] = \
+ Wgen (W,44));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0xd6990624), W[45 & 0xf] = \
+ Wgen (W,45));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0xf40e3585), W[46 & 0xf] = \
+ Wgen (W,46));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0x106aa070), W[47 & 0xf] = \
+ Wgen (W,47));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x19a4c116), W[48 & 0xf] = \
+ Wgen (W,48));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x1e376c08), W[49 & 0xf] = \
+ Wgen (W,49));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x2748774c), W[50 & 0xf] = \
+ Wgen (W,50));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x34b0bcb5), W[51 & 0xf] = \
+ Wgen (W,51));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x391c0cb3), W[52 & 0xf] = \
+ Wgen (W,52));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0x4ed8aa4a), W[53 & 0xf] = \
+ Wgen (W,53));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0x5b9cca4f), W[54 & 0xf] = \
+ Wgen (W,54));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0x682e6ff3), W[55 & 0xf] = \
+ Wgen (W,55));
+ SHA2STEP32 (a, b, c, d, e, f, g, h, UINT32_C (0x748f82ee), W[56 & 0xf] = \
+ Wgen (W,56));
+ SHA2STEP32 (h, a, b, c, d, e, f, g, UINT32_C (0x78a5636f), W[57 & 0xf] = \
+ Wgen (W,57));
+ SHA2STEP32 (g, h, a, b, c, d, e, f, UINT32_C (0x84c87814), W[58 & 0xf] = \
+ Wgen (W,58));
+ SHA2STEP32 (f, g, h, a, b, c, d, e, UINT32_C (0x8cc70208), W[59 & 0xf] = \
+ Wgen (W,59));
+ SHA2STEP32 (e, f, g, h, a, b, c, d, UINT32_C (0x90befffa), W[60 & 0xf] = \
+ Wgen (W,60));
+ SHA2STEP32 (d, e, f, g, h, a, b, c, UINT32_C (0xa4506ceb), W[61 & 0xf] = \
+ Wgen (W,61));
+ SHA2STEP32 (c, d, e, f, g, h, a, b, UINT32_C (0xbef9a3f7), W[62 & 0xf] = \
+ Wgen (W,62));
+ SHA2STEP32 (b, c, d, e, f, g, h, a, UINT32_C (0xc67178f2), W[63 & 0xf] = \
+ Wgen (W,63));
/* Compute intermediate hash.
- See FIPS PUB 180-4 paragraph 4.2.2 step 4. */
+ See FIPS PUB 180-4 paragraph 6.2.2 step 4. */
H[0] += a;
H[1] += b;
H[2] += c;
@@ -269,7 +321,7 @@
return; /* Do nothing */
/* Note: (count & (SHA256_BLOCK_SIZE-1))
- equal (count % SHA256_BLOCK_SIZE) for this block size. */
+ equals (count % SHA256_BLOCK_SIZE) for this block size. */
bytes_have = (unsigned) (ctx->count & (SHA256_BLOCK_SIZE - 1));
ctx->count += length;
@@ -277,7 +329,7 @@
{
unsigned bytes_left = SHA256_BLOCK_SIZE - bytes_have;
if (length >= bytes_left)
- { /* Combine new data with data in buffer and
+ { /* Combine new data with data in the buffer and
process full block. */
memcpy (ctx->buffer + bytes_have,
data,
@@ -291,7 +343,7 @@
while (SHA256_BLOCK_SIZE <= length)
{ /* Process any full blocks of new data directly,
- without copying to buffer. */
+ without copying to the buffer. */
sha256_transform (ctx->H, data);
data += SHA256_BLOCK_SIZE;
length -= SHA256_BLOCK_SIZE;
@@ -299,7 +351,7 @@
if (0 != length)
{ /* Copy incomplete block of new data (if any)
- to buffer. */
+ to the buffer. */
memcpy (ctx->buffer + bytes_have, data, length);
}
}
@@ -318,8 +370,8 @@
* @param[out] digest set to the hash, must be #SHA256_DIGEST_SIZE bytes
*/
void
-sha256_finish (void *ctx_,
- uint8_t digest[SHA256_DIGEST_SIZE])
+MHD_SHA256_finish (void *ctx_,
+ uint8_t digest[SHA256_DIGEST_SIZE])
{
struct sha256_ctx *const ctx = ctx_;
uint64_t num_bits; /**< Number of processed bits */
@@ -341,8 +393,8 @@
if (SHA256_BLOCK_SIZE - bytes_have < SHA256_SIZE_OF_LEN_ADD)
{ /* No space in current block to put total length of message.
Pad current block with zeros and process it. */
- while (bytes_have < SHA256_BLOCK_SIZE)
- ctx->buffer[bytes_have++] = 0;
+ if (bytes_have < SHA256_BLOCK_SIZE)
+ memset (ctx->buffer + bytes_have, 0, SHA256_BLOCK_SIZE - bytes_have);
/* Process full block. */
sha256_transform (ctx->H, ctx->buffer);
/* Start new block. */
@@ -353,20 +405,42 @@
memset (ctx->buffer + bytes_have, 0,
SHA256_BLOCK_SIZE - SHA256_SIZE_OF_LEN_ADD - bytes_have);
/* Put number of bits in processed message as big-endian value. */
- _MHD_PUT_64BIT_BE (ctx->buffer + SHA256_BLOCK_SIZE - SHA256_SIZE_OF_LEN_ADD,
- num_bits);
+ _MHD_PUT_64BIT_BE_SAFE (ctx->buffer + SHA256_BLOCK_SIZE
+ - SHA256_SIZE_OF_LEN_ADD,
+ num_bits);
/* Process full final block. */
sha256_transform (ctx->H, ctx->buffer);
/* Put final hash/digest in BE mode */
- _MHD_PUT_32BIT_BE (digest + 0 * SHA256_BYTES_IN_WORD, ctx->H[0]);
- _MHD_PUT_32BIT_BE (digest + 1 * SHA256_BYTES_IN_WORD, ctx->H[1]);
- _MHD_PUT_32BIT_BE (digest + 2 * SHA256_BYTES_IN_WORD, ctx->H[2]);
- _MHD_PUT_32BIT_BE (digest + 3 * SHA256_BYTES_IN_WORD, ctx->H[3]);
- _MHD_PUT_32BIT_BE (digest + 4 * SHA256_BYTES_IN_WORD, ctx->H[4]);
- _MHD_PUT_32BIT_BE (digest + 5 * SHA256_BYTES_IN_WORD, ctx->H[5]);
- _MHD_PUT_32BIT_BE (digest + 6 * SHA256_BYTES_IN_WORD, ctx->H[6]);
- _MHD_PUT_32BIT_BE (digest + 7 * SHA256_BYTES_IN_WORD, ctx->H[7]);
+#ifndef _MHD_PUT_32BIT_BE_UNALIGNED
+ if (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN)
+ {
+ uint32_t alig_dgst[_SHA256_DIGEST_LENGTH];
+ _MHD_PUT_32BIT_BE (alig_dgst + 0, ctx->H[0]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 1, ctx->H[1]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 2, ctx->H[2]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 3, ctx->H[3]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 4, ctx->H[4]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 5, ctx->H[5]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 6, ctx->H[6]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 7, ctx->H[7]);
+ /* Copy result to unaligned destination address */
+ memcpy (digest, alig_dgst, SHA256_DIGEST_SIZE);
+ }
+ else
+#else /* _MHD_PUT_32BIT_BE_UNALIGNED */
+ if (1)
+#endif /* _MHD_PUT_32BIT_BE_UNALIGNED */
+ {
+ _MHD_PUT_32BIT_BE (digest + 0 * SHA256_BYTES_IN_WORD, ctx->H[0]);
+ _MHD_PUT_32BIT_BE (digest + 1 * SHA256_BYTES_IN_WORD, ctx->H[1]);
+ _MHD_PUT_32BIT_BE (digest + 2 * SHA256_BYTES_IN_WORD, ctx->H[2]);
+ _MHD_PUT_32BIT_BE (digest + 3 * SHA256_BYTES_IN_WORD, ctx->H[3]);
+ _MHD_PUT_32BIT_BE (digest + 4 * SHA256_BYTES_IN_WORD, ctx->H[4]);
+ _MHD_PUT_32BIT_BE (digest + 5 * SHA256_BYTES_IN_WORD, ctx->H[5]);
+ _MHD_PUT_32BIT_BE (digest + 6 * SHA256_BYTES_IN_WORD, ctx->H[6]);
+ _MHD_PUT_32BIT_BE (digest + 7 * SHA256_BYTES_IN_WORD, ctx->H[7]);
+ }
/* Erase potentially sensitive data. */
memset (ctx, 0, sizeof(struct sha256_ctx));
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/sha256.h
^
|
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2019 Karlson2k (Evgeny Grin)
+ Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -28,7 +28,10 @@
#include "mhd_options.h"
#include <stdint.h>
-#include <stddef.h>
+#ifdef HAVE_STDDEF_H
+#include <stddef.h> /* for size_t */
+#endif /* HAVE_STDDEF_H */
+
/**
* Digest is kept internally as 8 32-bit words.
@@ -36,26 +39,42 @@
#define _SHA256_DIGEST_LENGTH 8
/**
+ * Number of bits in single SHA-256 word
+ */
+#define SHA256_WORD_SIZE_BITS 32
+
+/**
+ * Number of bytes in single SHA-256 word
+ * used to process data
+ */
+#define SHA256_BYTES_IN_WORD (SHA256_WORD_SIZE_BITS / 8)
+
+/**
* Size of SHA-256 digest in bytes
*/
-#define SHA256_DIGEST_SIZE (_SHA256_DIGEST_LENGTH * 4)
+#define SHA256_DIGEST_SIZE (_SHA256_DIGEST_LENGTH * SHA256_BYTES_IN_WORD)
/**
- * Size of SHA-256 digest string in chars
+ * Size of SHA-256 digest string in chars including termination NUL
*/
#define SHA256_DIGEST_STRING_SIZE ((SHA256_DIGEST_SIZE) * 2 + 1)
/**
+ * Size of single processing block in bits
+ */
+#define SHA256_BLOCK_SIZE_BITS 512
+
+/**
* Size of single processing block in bytes
*/
-#define SHA256_BLOCK_SIZE 64
+#define SHA256_BLOCK_SIZE (SHA256_BLOCK_SIZE_BITS / 8)
struct sha256_ctx
{
uint32_t H[_SHA256_DIGEST_LENGTH]; /**< Intermediate hash value / digest at end of calculation */
- uint64_t count; /**< number of bytes, mod 2^64 */
uint8_t buffer[SHA256_BLOCK_SIZE]; /**< SHA256 input data buffer */
+ uint64_t count; /**< number of bytes, mod 2^64 */
};
/**
@@ -87,7 +106,7 @@
* @param[out] digest set to the hash, must be #SHA256_DIGEST_SIZE bytes
*/
void
-sha256_finish (void *ctx_,
- uint8_t digest[SHA256_DIGEST_SIZE]);
+MHD_SHA256_finish (void *ctx_,
+ uint8_t digest[SHA256_DIGEST_SIZE]);
#endif /* MHD_SHA256_H */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/sysfdsetsize.c
^
|
@@ -33,7 +33,9 @@
#undef FD_SETSIZE
#endif /* FD_SETSIZE */
+#ifdef HAVE_STDLIB_H
#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
#if defined(__VXWORKS__) || defined(__vxworks) || defined(OS_VXWORKS)
#include <sockLib.h>
#endif /* OS_VXWORKS */
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_client_put_stop.c
^
|
@@ -0,0 +1,2081 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2021 Evgeny Grin (Karlson2k)
+
+ 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 2, 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_client_put_stop.c
+ * @brief Testcase for handling of clients aborts
+ * @author Karlson2k (Evgeny Grin)
+ * @author Christian Grothoff
+ */
+#include "MHD_config.h"
+#include "platform.h"
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif /* HAVE_STRINGS_H */
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#ifndef WINDOWS
+#include <unistd.h>
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif /* HAVE_SIGNAL_H */
+
+#ifdef HAVE_SYSCTL
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif /* HAVE_SYS_SYSCTL_H */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif /* HAVE_NETINET_IP_H */
+#ifdef HAVE_NETINET_IP_ICMP_H
+#include <netinet/ip_icmp.h>
+#endif /* HAVE_NETINET_IP_ICMP_H */
+#ifdef HAVE_NETINET_ICMP_VAR_H
+#include <netinet/icmp_var.h>
+#endif /* HAVE_NETINET_ICMP_VAR_H */
+#endif /* HAVE_SYSCTL */
+
+#include <stdio.h>
+
+#include "mhd_sockets.h" /* only macros used */
+#include "test_helpers.h"
+#include "mhd_assert.h"
+
+#if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
+#undef MHD_CPU_COUNT
+#endif
+#if ! defined(MHD_CPU_COUNT)
+#define MHD_CPU_COUNT 2
+#endif
+#if MHD_CPU_COUNT > 32
+#undef MHD_CPU_COUNT
+/* Limit to reasonable value */
+#define MHD_CPU_COUNT 32
+#endif /* MHD_CPU_COUNT > 32 */
+
+#ifndef MHD_STATICSTR_LEN_
+/**
+ * Determine length of static string / macro strings at compile time.
+ */
+#define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
+#endif /* ! MHD_STATICSTR_LEN_ */
+
+#ifndef _MHD_INSTRMACRO
+/* Quoted macro parameter */
+#define _MHD_INSTRMACRO(a) #a
+#endif /* ! _MHD_INSTRMACRO */
+#ifndef _MHD_STRMACRO
+/* Quoted expanded macro parameter */
+#define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
+#endif /* ! _MHD_STRMACRO */
+
+
+/* Could be increased to facilitate debugging */
+#define TIMEOUTS_VAL 5
+
+/* Time in ms to wait for final packets to be delivered */
+#define FINAL_PACKETS_MS 20
+
+#define EXPECTED_URI_BASE_PATH "/a"
+
+#define REQ_HOST "localhost"
+
+#define REQ_METHOD "PUT"
+
+#define REQ_BODY "Some content data."
+
+#define REQ_LINE_END "\r\n"
+
+/* Mandatory request headers */
+#define REQ_HEADER_HOST_NAME "Host"
+#define REQ_HEADER_HOST_VALUE REQ_HOST
+#define REQ_HEADER_HOST \
+ REQ_HEADER_HOST_NAME ": " REQ_HEADER_HOST_VALUE REQ_LINE_END
+#define REQ_HEADER_UA_NAME "User-Agent"
+#define REQ_HEADER_UA_VALUE "dummyclient/0.9"
+#define REQ_HEADER_UA REQ_HEADER_UA_NAME ": " REQ_HEADER_UA_VALUE REQ_LINE_END
+
+/* Optional request headers */
+#define REQ_HEADER_CT_NAME "Content-Type"
+#define REQ_HEADER_CT_VALUE "text/plain"
+#define REQ_HEADER_CT REQ_HEADER_CT_NAME ": " REQ_HEADER_CT_VALUE REQ_LINE_END
+
+
+#if defined(HAVE___FUNC__)
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func(NULL, __func__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, __func__, __LINE__)
+#define mhdErrorExit(ignore) \
+ _mhdErrorExit_func(NULL, __func__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+ _mhdErrorExit_func(errDesc, __func__, __LINE__)
+#elif defined(HAVE___FUNCTION__)
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define mhdErrorExit(ignore) \
+ _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+ _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#else
+#define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, NULL, __LINE__)
+#define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
+#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
+#endif
+
+
+_MHD_NORETURN static void
+_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "System or external library call failed");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+ fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+ fflush (stderr);
+ exit (99);
+}
+
+
+_MHD_NORETURN static void
+_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "MHD unexpected error");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+
+ fflush (stderr);
+ exit (8);
+}
+
+
+/**
+ * Pause execution for specified number of milliseconds.
+ * @param ms the number of milliseconds to sleep
+ */
+void
+_MHD_sleep (uint32_t ms)
+{
+#if defined(_WIN32)
+ Sleep (ms);
+#elif defined(HAVE_NANOSLEEP)
+ struct timespec slp = {ms / 1000, (ms % 1000) * 1000000};
+ struct timespec rmn;
+ int num_retries = 0;
+ while (0 != nanosleep (&slp, &rmn))
+ {
+ if (EINTR != errno)
+ externalErrorExit ();
+ if (num_retries++ > 8)
+ break;
+ slp = rmn;
+ }
+#elif defined(HAVE_USLEEP)
+ uint64_t us = ms * 1000;
+ do
+ {
+ uint64_t this_sleep;
+ if (999999 < us)
+ this_sleep = 999999;
+ else
+ this_sleep = us;
+ /* Ignore return value as it could be void */
+ usleep (this_sleep);
+ us -= this_sleep;
+ } while (us > 0);
+#else
+ externalErrorExitDesc ("No sleep function available on this system");
+#endif
+}
+
+
+/* Global parameters */
+static int verbose; /**< Be verbose */
+static int oneone; /**< If false use HTTP/1.0 for requests*/
+static int global_port; /**< MHD daemons listen port number */
+
+static int use_shutdown; /**< Use shutdown at client side */
+static int use_close; /**< Use socket close at client side */
+static int use_hard_close; /**< Use socket close with RST at client side */
+static int by_step; /**< Send request byte-by-byte */
+static int upl_chunked; /**< Use chunked encoding for request body */
+
+static unsigned int rate_limiter; /**< Maximum number of checks per second */
+
+static void
+test_global_init (void)
+{
+ rate_limiter = 0;
+#ifdef HAVE_SYSCTLBYNAME
+ if (use_hard_close)
+ {
+ int blck_hl;
+ size_t blck_hl_size = sizeof (blck_hl);
+ if (0 == sysctlbyname ("net.inet.tcp.blackhole", &blck_hl, &blck_hl_size,
+ NULL, 0))
+ {
+ if (2 <= blck_hl)
+ {
+ fprintf (stderr, "'sysctl net.inet.tcp.blackhole = %d', test is "
+ "unreliable with this system setting, skipping.\n", blck_hl);
+ exit (77);
+ }
+ }
+ else
+ {
+ if (ENOENT != errno)
+ externalErrorExitDesc ("Cannot get 'net.inet.tcp.blackhole' value");
+ }
+ }
+#endif
+#if defined(HAVE_SYSCTL) && defined(CTL_NET) && defined(PF_INET) && \
+ defined(IPPROTO_ICMP) && defined(ICMPCTL_ICMPLIM)
+ if (use_hard_close)
+ {
+ int mib[4];
+ int limit;
+ size_t limit_size = sizeof(limit);
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET;
+ mib[2] = IPPROTO_ICMP;
+ mib[3] = ICMPCTL_ICMPLIM;
+ if ((0 != sysctl (mib, 4, &limit, &limit_size, NULL, 0)) ||
+ (sizeof(limit) != limit_size) )
+ externalErrorExitDesc ("Cannot get RST rate limit value");
+ if (limit > 0)
+ {
+#ifndef _MHD_HEAVY_TESTS
+ fprintf (stderr, "This system has limits on number of RST packet"
+ " per second (%d).\nThis test will be used only if configured "
+ "with '--enable-heavy-test'.\n", limit);
+ exit (77);
+#else /* _MHD_HEAVY_TESTS */
+ int test_limit; /**< Maximum number of checks per second */
+ test_limit = limit - limit / 10; /* Add some space to not hit the limiter */
+ test_limit /= 4; /* Assume that all four tests with 'hard_close' run in parallel */
+ test_limit -= 5; /* Add some more space to not hit the limiter */
+ test_limit /= 3; /* Use only one third of available limit */
+ if (test_limit <= 0)
+ {
+ fprintf (stderr, "System limit for 'net.inet.icmp.icmplim' is "
+ "too strict for this test (value: %d).\n", limit);
+ exit (77);
+ }
+ if (verbose)
+ {
+ printf ("Limiting number of checks to %d checks/second.\n", test_limit);
+ fflush (stdout);
+ }
+ rate_limiter = (unsigned int) test_limit;
+#if ! defined(HAVE_USLEEP) && ! defined(HAVE_NANOSLEEP) && ! defined(_WIN32)
+ fprintf (stderr, "Sleep function is required for this test, "
+ "but not available on this system.\n");
+ exit (77);
+#endif
+#endif /* _MHD_HEAVY_TESTS */
+ }
+ }
+#endif /* HAVE_SYSCTL && CTL_NET && PF_INET &&
+ IPPROTO_ICMP && ICMPCTL_ICMPLIM */
+ if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE))
+ {
+#if defined(HAVE_SIGNAL_H) && defined(SIGPIPE)
+ if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
+ externalErrorExitDesc ("Error suppressing SIGPIPE signal");
+#else /* ! HAVE_SIGNAL_H || ! SIGPIPE */
+ fprintf (stderr, "Cannot suppress SIGPIPE signal.\n");
+ /* exit (77); */
+#endif
+ }
+}
+
+
+static void
+test_global_cleanup (void)
+{
+}
+
+
+/**
+ * Change socket to blocking.
+ *
+ * @param fd the socket to manipulate
+ */
+static void
+make_blocking (MHD_socket fd)
+{
+#if defined(MHD_POSIX_SOCKETS)
+ int flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (-1 == flags)
+ externalErrorExitDesc ("Cannot make socket non-blocking");
+ if ((flags & ~O_NONBLOCK) != flags)
+ {
+ if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+ externalErrorExitDesc ("Cannot make socket non-blocking");
+ }
+#elif defined(MHD_WINSOCK_SOCKETS)
+ unsigned long flags = 0;
+
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ externalErrorExitDesc ("Cannot make socket non-blocking");
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+
+/**
+ * Change socket to non-blocking.
+ *
+ * @param fd the socket to manipulate
+ */
+static void
+make_nonblocking (MHD_socket fd)
+{
+#if defined(MHD_POSIX_SOCKETS)
+ int flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (-1 == flags)
+ externalErrorExitDesc ("Cannot make socket non-blocking");
+ if ((flags | O_NONBLOCK) != flags)
+ {
+ if (-1 == fcntl (fd, F_SETFL, flags | O_NONBLOCK))
+ externalErrorExitDesc ("Cannot make socket non-blocking");
+ }
+#elif defined(MHD_WINSOCK_SOCKETS)
+ unsigned long flags = 1;
+
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ externalErrorExitDesc ("Cannot make socket non-blocking");
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+
+enum _MHD_clientStage
+{
+ DUMB_CLIENT_INIT = 0,
+ DUMB_CLIENT_CONNECTING,
+ DUMB_CLIENT_CONNECTED,
+ DUMB_CLIENT_REQ_SENDING,
+ DUMB_CLIENT_REQ_SENT,
+ DUMB_CLIENT_HEADER_RECVEIVING,
+ DUMB_CLIENT_HEADER_RECVEIVED,
+ DUMB_CLIENT_BODY_RECVEIVING,
+ DUMB_CLIENT_BODY_RECVEIVED,
+ DUMB_CLIENT_FINISHING,
+ DUMB_CLIENT_FINISHED
+};
+
+struct _MHD_dumbClient
+{
+ MHD_socket sckt; /**< the socket to communicate */
+
+ int sckt_nonblock; /**< non-zero if socket is non-blocking */
+
+ unsigned int port; /**< the port to connect to */
+
+ const char *send_buf; /**< the buffer for the request, malloced */
+
+ size_t req_size; /**< the size of the request, including header */
+
+ size_t send_off; /**< the number of bytes already sent */
+
+ enum _MHD_clientStage stage;
+
+ /* the test-specific variables */
+ size_t single_send_size; /**< the maximum number of bytes to be sent by
+ single send() */
+ size_t send_size_limit; /**< the total number of send bytes limit */
+};
+
+struct _MHD_dumbClient *
+_MHD_dumbClient_create (unsigned int port, const char *method, const char *url,
+ const char *add_headers,
+ const uint8_t *req_body, size_t req_body_size,
+ int chunked)
+{
+ struct _MHD_dumbClient *clnt;
+ size_t method_size;
+ size_t url_size;
+ size_t add_hdrs_size;
+ size_t buf_alloc_size;
+ char *send_buf;
+ mhd_assert (0 != port);
+ mhd_assert (NULL != req_body || 0 == req_body_size);
+ mhd_assert (0 == req_body_size || NULL != req_body);
+
+ clnt = (struct _MHD_dumbClient *) malloc (sizeof(struct _MHD_dumbClient));
+ if (NULL == clnt)
+ externalErrorExit ();
+ memset (clnt, 0, sizeof(struct _MHD_dumbClient));
+ clnt->sckt = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (MHD_INVALID_SOCKET == clnt->sckt)
+ externalErrorExitDesc ("Cannot create the client socket");
+
+#ifdef MHD_socket_nosignal_
+ if (! MHD_socket_nosignal_ (clnt->sckt))
+ externalErrorExitDesc ("Cannot suppress SIGPIPE on the client socket");
+#endif /* MHD_socket_nosignal_ */
+
+ clnt->sckt_nonblock = 0;
+ if (clnt->sckt_nonblock)
+ make_nonblocking (clnt->sckt);
+ else
+ make_blocking (clnt->sckt);
+
+ if (1)
+ { /* Always set TCP NODELAY */
+ const MHD_SCKT_OPT_BOOL_ on_val = 1;
+
+ if (0 != setsockopt (clnt->sckt, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &on_val, sizeof (on_val)))
+ externalErrorExitDesc ("Cannot set TCP_NODELAY option");
+ }
+
+ clnt->port = port;
+
+ if (NULL != method)
+ method_size = strlen (method);
+ else
+ {
+ method = MHD_HTTP_METHOD_GET;
+ method_size = MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET);
+ }
+ mhd_assert (0 != method_size);
+ if (NULL != url)
+ url_size = strlen (url);
+ else
+ {
+ url = "/";
+ url_size = 1;
+ }
+ mhd_assert (0 != url_size);
+ add_hdrs_size = (NULL == add_headers) ? 0 : strlen (add_headers);
+ buf_alloc_size = 1024 + method_size + url_size
+ + add_hdrs_size + req_body_size;
+ send_buf = (char *) malloc (buf_alloc_size);
+ if (NULL == send_buf)
+ externalErrorExit ();
+
+ clnt->req_size = 0;
+ /* Form the request line */
+ memcpy (send_buf + clnt->req_size, method, method_size);
+ clnt->req_size += method_size;
+ send_buf[clnt->req_size++] = ' ';
+ memcpy (send_buf + clnt->req_size, url, url_size);
+ clnt->req_size += url_size;
+ send_buf[clnt->req_size++] = ' ';
+ memcpy (send_buf + clnt->req_size, MHD_HTTP_VERSION_1_1,
+ MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1));
+ clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1);
+ send_buf[clnt->req_size++] = '\r';
+ send_buf[clnt->req_size++] = '\n';
+ /* Form the header */
+ memcpy (send_buf + clnt->req_size, REQ_HEADER_HOST,
+ MHD_STATICSTR_LEN_ (REQ_HEADER_HOST));
+ clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_HOST);
+ memcpy (send_buf + clnt->req_size, REQ_HEADER_UA,
+ MHD_STATICSTR_LEN_ (REQ_HEADER_UA));
+ clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_UA);
+ if ((NULL != req_body) || chunked)
+ {
+ if (! chunked)
+ {
+ int prn_size;
+ memcpy (send_buf + clnt->req_size, MHD_HTTP_HEADER_CONTENT_LENGTH ": ",
+ MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": "));
+ clnt->req_size += MHD_STATICSTR_LEN_ (
+ MHD_HTTP_HEADER_CONTENT_LENGTH ": ");
+ prn_size = snprintf (send_buf + clnt->req_size,
+ (buf_alloc_size - clnt->req_size),
+ "%u", (unsigned int) req_body_size);
+ if (0 >= prn_size)
+ externalErrorExit ();
+ if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size)
+ externalErrorExit ();
+ clnt->req_size += (unsigned int) prn_size;
+ send_buf[clnt->req_size++] = '\r';
+ send_buf[clnt->req_size++] = '\n';
+ }
+ else
+ {
+ memcpy (send_buf + clnt->req_size,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING ": chunked\r\n",
+ MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \
+ ": chunked\r\n"));
+ clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \
+ ": chunked\r\n");
+ }
+ }
+ if (0 != add_hdrs_size)
+ {
+ memcpy (send_buf + clnt->req_size, add_headers, add_hdrs_size);
+ clnt->req_size += add_hdrs_size;
+ }
+ /* Terminate header */
+ send_buf[clnt->req_size++] = '\r';
+ send_buf[clnt->req_size++] = '\n';
+
+ /* Add body (if any) */
+ if (! chunked)
+ {
+ if (0 != req_body_size)
+ {
+ memcpy (send_buf + clnt->req_size, req_body, req_body_size);
+ clnt->req_size += req_body_size;
+ }
+ }
+ else
+ {
+ if (0 != req_body_size)
+ {
+ int prn_size;
+ prn_size = snprintf (send_buf + clnt->req_size,
+ (buf_alloc_size - clnt->req_size),
+ "%x", (unsigned int) req_body_size);
+ if (0 >= prn_size)
+ externalErrorExit ();
+ if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size)
+ externalErrorExit ();
+ clnt->req_size += (unsigned int) prn_size;
+ send_buf[clnt->req_size++] = '\r';
+ send_buf[clnt->req_size++] = '\n';
+ memcpy (send_buf + clnt->req_size, req_body, req_body_size);
+ clnt->req_size += req_body_size;
+ send_buf[clnt->req_size++] = '\r';
+ send_buf[clnt->req_size++] = '\n';
+ }
+ send_buf[clnt->req_size++] = '0';
+ send_buf[clnt->req_size++] = '\r';
+ send_buf[clnt->req_size++] = '\n';
+ send_buf[clnt->req_size++] = '\r';
+ send_buf[clnt->req_size++] = '\n';
+ }
+ mhd_assert (clnt->req_size < buf_alloc_size);
+ clnt->send_buf = send_buf;
+
+ return clnt;
+}
+
+
+void
+_MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt,
+ size_t step_size, size_t max_total_send)
+{
+ clnt->single_send_size = step_size;
+ clnt->send_size_limit = max_total_send;
+}
+
+
+/* internal */
+static void
+_MHD_dumbClient_connect_init (struct _MHD_dumbClient *clnt)
+{
+ struct sockaddr_in sa;
+ mhd_assert (DUMB_CLIENT_INIT == clnt->stage);
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons ((uint16_t) clnt->port);
+ sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ if (0 != connect (clnt->sckt, (struct sockaddr *) &sa, sizeof(sa)))
+ {
+ const int err = MHD_socket_get_error_ ();
+ if ( (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINPROGRESS_)) ||
+ (MHD_SCKT_ERR_IS_EAGAIN_ (err)))
+ clnt->stage = DUMB_CLIENT_CONNECTING;
+ else
+ externalErrorExitDesc ("Cannot 'connect()' the client socket");
+ }
+ else
+ clnt->stage = DUMB_CLIENT_CONNECTED;
+}
+
+
+void
+_MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt)
+{
+ mhd_assert (DUMB_CLIENT_INIT == clnt->stage);
+ _MHD_dumbClient_connect_init (clnt);
+}
+
+
+/* internal */
+static void
+_MHD_dumbClient_connect_finish (struct _MHD_dumbClient *clnt)
+{
+ int err = 0;
+ socklen_t err_size = sizeof(err);
+ mhd_assert (DUMB_CLIENT_CONNECTING == clnt->stage);
+ if (0 != getsockopt (clnt->sckt, SOL_SOCKET, SO_ERROR,
+ (void *) &err, &err_size))
+ externalErrorExitDesc ("'getsockopt()' call failed");
+ if (0 != err)
+ externalErrorExitDesc ("Socket connect() failed");
+ clnt->stage = DUMB_CLIENT_CONNECTED;
+}
+
+
+/* internal */
+static void
+_MHD_dumbClient_send_req (struct _MHD_dumbClient *clnt)
+{
+ size_t send_size;
+ ssize_t res;
+ mhd_assert (DUMB_CLIENT_CONNECTED <= clnt->stage);
+ mhd_assert (DUMB_CLIENT_REQ_SENT > clnt->stage);
+ mhd_assert (clnt->req_size > clnt->send_off);
+
+ send_size = (((0 != clnt->send_size_limit) &&
+ (clnt->req_size > clnt->send_size_limit)) ?
+ clnt->send_size_limit : clnt->req_size) - clnt->send_off;
+ mhd_assert (0 != send_size);
+ if ((0 != clnt->single_send_size) &&
+ (clnt->single_send_size < send_size))
+ send_size = clnt->single_send_size;
+
+ res = MHD_send_ (clnt->sckt, clnt->send_buf + clnt->send_off, send_size);
+
+ if (res < 0)
+ {
+ const int err = MHD_socket_get_error_ ();
+ if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
+ return;
+ if (MHD_SCKT_ERR_IS_EINTR_ (err))
+ return;
+ if (MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
+ mhdErrorExitDesc ("The connection was aborted by MHD");
+ if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_))
+ mhdErrorExitDesc ("The connection was shut down on MHD side");
+ externalErrorExitDesc ("Unexpected network error");
+ }
+ clnt->send_off += (size_t) res;
+ mhd_assert (clnt->send_off <= clnt->req_size);
+ mhd_assert (clnt->send_off <= clnt->send_size_limit || \
+ 0 == clnt->send_size_limit);
+ if (clnt->req_size == clnt->send_off)
+ clnt->stage = DUMB_CLIENT_REQ_SENT;
+ if ((0 != clnt->send_size_limit) &&
+ (clnt->send_size_limit == clnt->send_off))
+ clnt->stage = DUMB_CLIENT_FINISHING;
+}
+
+
+/* internal */
+static void
+_MHD_dumbClient_recv_reply (struct _MHD_dumbClient *clnt)
+{
+ (void) clnt;
+ externalErrorExitDesc ("Not implemented for this test");
+}
+
+
+int
+_MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt)
+{
+ return DUMB_CLIENT_REQ_SENT <= clnt->stage;
+}
+
+
+/* internal */
+static void
+_MHD_dumbClient_socket_close (struct _MHD_dumbClient *clnt)
+{
+ if (MHD_INVALID_SOCKET != clnt->sckt)
+ {
+ if (use_hard_close)
+ {
+#ifdef SO_LINGER
+ static const struct linger hard_close = {1, 0};
+ mhd_assert (0 == hard_close.l_linger);
+ if (0 != setsockopt (clnt->sckt, SOL_SOCKET, SO_LINGER,
+ (const void *) &hard_close, sizeof (hard_close)))
+#endif /* SO_LINGER */
+ externalErrorExitDesc ("Failed to set SO_LINGER option");
+ }
+ if (! MHD_socket_close_ (clnt->sckt))
+ externalErrorExitDesc ("Unexpected error while closing " \
+ "the client socket");
+ clnt->sckt = MHD_INVALID_SOCKET;
+ }
+}
+
+
+/* internal */
+static void
+_MHD_dumbClient_finalize (struct _MHD_dumbClient *clnt)
+{
+ if (MHD_INVALID_SOCKET != clnt->sckt)
+ {
+ if (use_shutdown)
+ {
+ if (0 != shutdown (clnt->sckt, SHUT_WR))
+ {
+ const int err = MHD_socket_get_error_ ();
+ if (! MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_) &&
+ ! MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
+ mhdErrorExitDesc ("Unexpected error when shutting down " \
+ "the client socket");
+ }
+ }
+ else if (use_close)
+ {
+ _MHD_dumbClient_socket_close (clnt);
+ }
+ else
+ mhd_assert (0);
+ }
+ clnt->stage = DUMB_CLIENT_FINISHED;
+}
+
+
+/* internal */
+static int
+_MHD_dumbClient_needs_send (const struct _MHD_dumbClient *clnt)
+{
+ return ((DUMB_CLIENT_CONNECTING <= clnt->stage) &&
+ (DUMB_CLIENT_REQ_SENT > clnt->stage)) ||
+ (DUMB_CLIENT_FINISHING == clnt->stage);
+}
+
+
+/* internal */
+static int
+_MHD_dumbClient_needs_recv (const struct _MHD_dumbClient *clnt)
+{
+ return (DUMB_CLIENT_HEADER_RECVEIVING <= clnt->stage) &&
+ (DUMB_CLIENT_BODY_RECVEIVED > clnt->stage);
+}
+
+
+/* internal */
+/**
+ * Check whether the client needs unconditionally process the data.
+ * @param clnt the client to check
+ * @return non-zero if client needs unconditionally process the data,
+ * zero otherwise.
+ */
+static int
+_MHD_dumbClient_needs_process (const struct _MHD_dumbClient *clnt)
+{
+ switch (clnt->stage)
+ {
+ case DUMB_CLIENT_INIT:
+ case DUMB_CLIENT_REQ_SENT:
+ case DUMB_CLIENT_HEADER_RECVEIVED:
+ case DUMB_CLIENT_BODY_RECVEIVED:
+ return ! 0;
+ default:
+ return 0;
+ }
+ return 0; /* Should be unreachable */
+}
+
+
+/**
+ * Process the client data with send()/recv() as needed.
+ * @param clnt the client to process
+ * @return non-zero if client finished processing the request,
+ * zero otherwise.
+ */
+int
+_MHD_dumbClient_process (struct _MHD_dumbClient *clnt)
+{
+ do
+ {
+ switch (clnt->stage)
+ {
+ case DUMB_CLIENT_INIT:
+ _MHD_dumbClient_connect_init (clnt);
+ break;
+ case DUMB_CLIENT_CONNECTING:
+ _MHD_dumbClient_connect_finish (clnt);
+ break;
+ case DUMB_CLIENT_CONNECTED:
+ case DUMB_CLIENT_REQ_SENDING:
+ _MHD_dumbClient_send_req (clnt);
+ break;
+ case DUMB_CLIENT_REQ_SENT:
+ mhd_assert (0);
+ clnt->stage = DUMB_CLIENT_HEADER_RECVEIVING;
+ break;
+ case DUMB_CLIENT_HEADER_RECVEIVING:
+ _MHD_dumbClient_recv_reply (clnt);
+ break;
+ case DUMB_CLIENT_HEADER_RECVEIVED:
+ clnt->stage = DUMB_CLIENT_BODY_RECVEIVING;
+ break;
+ case DUMB_CLIENT_BODY_RECVEIVING:
+ _MHD_dumbClient_recv_reply (clnt);
+ break;
+ case DUMB_CLIENT_BODY_RECVEIVED:
+ clnt->stage = DUMB_CLIENT_FINISHING;
+ break;
+ case DUMB_CLIENT_FINISHING:
+ _MHD_dumbClient_finalize (clnt);
+ break;
+ default:
+ mhd_assert (0);
+ mhdErrorExit ();
+ }
+ } while (_MHD_dumbClient_needs_process (clnt));
+ return DUMB_CLIENT_FINISHED == clnt->stage;
+}
+
+
+void
+_MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt,
+ MHD_socket *maxsckt,
+ fd_set *rs, fd_set *ws, fd_set *es)
+{
+ mhd_assert (NULL != rs);
+ mhd_assert (NULL != ws);
+ mhd_assert (NULL != es);
+ if (DUMB_CLIENT_FINISHED > clnt->stage)
+ {
+ if (MHD_INVALID_SOCKET != clnt->sckt)
+ {
+ if ( (MHD_INVALID_SOCKET == *maxsckt) ||
+ (clnt->sckt > *maxsckt) )
+ *maxsckt = clnt->sckt;
+ if (_MHD_dumbClient_needs_recv (clnt))
+ FD_SET (clnt->sckt, rs);
+ if (_MHD_dumbClient_needs_send (clnt))
+ FD_SET (clnt->sckt, ws);
+ FD_SET (clnt->sckt, es);
+ }
+ }
+}
+
+
+/**
+ * Process the client data with send()/recv() as needed based on
+ * information in fd_sets.
+ * @param clnt the client to process
+ * @return non-zero if client finished processing the request,
+ * zero otherwise.
+ */
+int
+_MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt,
+ fd_set *rs, fd_set *ws, fd_set *es)
+{
+ if (_MHD_dumbClient_needs_process (clnt))
+ return _MHD_dumbClient_process (clnt);
+ else if (MHD_INVALID_SOCKET != clnt->sckt)
+ {
+ if (_MHD_dumbClient_needs_recv (clnt) && FD_ISSET (clnt->sckt, rs))
+ return _MHD_dumbClient_process (clnt);
+ else if (_MHD_dumbClient_needs_send (clnt) && FD_ISSET (clnt->sckt, ws))
+ return _MHD_dumbClient_process (clnt);
+ else if (FD_ISSET (clnt->sckt, es))
+ return _MHD_dumbClient_process (clnt);
+ }
+ return DUMB_CLIENT_FINISHED == clnt->stage;
+}
+
+
+/**
+ * Perform full request.
+ * @param clnt the client to run
+ * @return zero if client finished processing the request,
+ * non-zero if timeout is reached.
+ */
+int
+_MHD_dumbClient_perform (struct _MHD_dumbClient *clnt)
+{
+ time_t start;
+ time_t now;
+ start = time (NULL);
+ now = start;
+ do
+ {
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ MHD_socket maxMhdSk;
+ struct timeval tv;
+
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+
+ if (! _MHD_dumbClient_needs_process (clnt))
+ {
+ maxMhdSk = MHD_INVALID_SOCKET;
+ _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es);
+ mhd_assert (now >= start);
+ tv.tv_sec = TIMEOUTS_VAL * 2 - (now - start) + 1;
+ tv.tv_usec = 250 * 1000;
+ if (-1 == select (maxMhdSk + 1, &rs, &ws, &es, &tv))
+ {
+#ifdef MHD_POSIX_SOCKETS
+ if (EINTR != errno)
+ externalErrorExitDesc ("Unexpected select() error");
+#else /* ! MHD_POSIX_SOCKETS */
+ mhd_assert ((0 != rs.fd_count) || (0 != ws.fd_count) || \
+ (0 != es.fd_count));
+ externalErrorExitDesc ("Unexpected select() error");
+ Sleep (tv.tv_sec * 1000 + tv.tv_usec / 1000);
+#endif /* ! MHD_POSIX_SOCKETS */
+ continue;
+ }
+ if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es))
+ return 0;
+ }
+ /* Use double timeout value here as MHD must catch timeout situations
+ * in this test. Timeout in client as a last resort. */
+ } while ((now = time (NULL)) - start <= (TIMEOUTS_VAL * 2));
+ return 1;
+}
+
+
+/**
+ * Close the client and free internally allocated resources.
+ * @param clnt the client to close
+ */
+void
+_MHD_dumbClient_close (struct _MHD_dumbClient *clnt)
+{
+ if (DUMB_CLIENT_FINISHED != clnt->stage)
+ _MHD_dumbClient_finalize (clnt);
+ _MHD_dumbClient_socket_close (clnt);
+ if (NULL != clnt->send_buf)
+ {
+ free ((void *) clnt->send_buf);
+ clnt->send_buf = NULL;
+ }
+ free (clnt);
+}
+
+
+struct sckt_notif_cb_param
+{
+ volatile unsigned int num_started;
+ volatile unsigned int num_finished;
+};
+
+void
+socket_cb (void *cls,
+ struct MHD_Connection *c,
+ void **socket_context,
+ enum MHD_ConnectionNotificationCode toe)
+{
+ struct sckt_notif_cb_param *param = (struct sckt_notif_cb_param *) cls;
+ if (NULL == socket_context)
+ mhdErrorExitDesc ("'socket_context' pointer is NULL");
+ if (NULL == c)
+ mhdErrorExitDesc ("'connection' pointer is NULL");
+ if (NULL == param)
+ mhdErrorExitDesc ("'cls' pointer is NULL");
+
+ if (MHD_CONNECTION_NOTIFY_STARTED == toe)
+ param->num_started++;
+ else if (MHD_CONNECTION_NOTIFY_CLOSED == toe)
+ param->num_finished++;
+ else
+ mhdErrorExitDesc ("Unknown 'toe' value");
+}
+
+
+struct term_notif_cb_param
+{
+ volatile int term_reason;
+ volatile int num_called;
+};
+
+
+static void
+term_cb (void *cls,
+ struct MHD_Connection *c,
+ void **con_cls,
+ enum MHD_RequestTerminationCode term_code)
+{
+ struct term_notif_cb_param *param = (struct term_notif_cb_param *) cls;
+ if (NULL == con_cls)
+ mhdErrorExitDesc ("'con_cls' pointer is NULL");
+ if (NULL == c)
+ mhdErrorExitDesc ("'connection' pointer is NULL");
+ if (NULL == param)
+ mhdErrorExitDesc ("'cls' pointer is NULL");
+ param->term_reason = (int) term_code;
+ param->num_called++;
+}
+
+
+const char *
+term_reason_str (enum MHD_RequestTerminationCode term_code)
+{
+ switch ((int) term_code)
+ {
+ case MHD_REQUEST_TERMINATED_COMPLETED_OK:
+ return "COMPLETED_OK";
+ case MHD_REQUEST_TERMINATED_WITH_ERROR:
+ return "TERMINATED_WITH_ERROR";
+ case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED:
+ return "TIMEOUT_REACHED";
+ case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN:
+ return "DAEMON_SHUTDOWN";
+ case MHD_REQUEST_TERMINATED_READ_ERROR:
+ return "READ_ERROR";
+ case MHD_REQUEST_TERMINATED_CLIENT_ABORT:
+ return "CLIENT_ABORT";
+ case -1:
+ return "(not called)";
+ default:
+ return "(unknown code)";
+ }
+ return "(problem)"; /* unreachable */
+}
+
+
+struct check_uri_cls
+{
+ const char *volatile uri;
+ volatile unsigned int cb_called;
+};
+
+static void *
+check_uri_cb (void *cls,
+ const char *uri,
+ struct MHD_Connection *con)
+{
+ struct check_uri_cls *param = (struct check_uri_cls *) cls;
+
+ if (NULL == con)
+ mhdErrorExitDesc ("The 'con' pointer is NULL");
+
+ param->cb_called++;
+
+ if (0 != strcmp (param->uri,
+ uri))
+ {
+ fprintf (stderr, "Wrong URI: '%s'\n", uri);
+ mhdErrorExit ();
+ }
+ return NULL;
+}
+
+
+struct mhd_header_checker_param
+{
+ int found_header_host; /**< the number of 'Host' headers */
+ int found_header_ua; /**< the number of 'User-Agent' headers */
+ int found_header_ct; /**< the number of 'Content-Type' headers */
+ int found_header_cl; /**< the number of 'Content-Length' headers */
+ int found_header_te; /**< the number of 'Transfer-Encoding' headers */
+};
+
+enum MHD_Result
+headerCheckerInterator (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ size_t key_size,
+ const char *value,
+ size_t value_size)
+{
+ struct mhd_header_checker_param *const param =
+ (struct mhd_header_checker_param *) cls;
+
+ if (NULL == param)
+ mhdErrorExitDesc ("cls parameter is NULL");
+
+ if (MHD_HEADER_KIND != kind)
+ return MHD_YES; /* Continue iteration */
+
+ if (0 == key_size)
+ mhdErrorExitDesc ("Zero key length");
+
+ if ((strlen (REQ_HEADER_HOST_NAME) == key_size) &&
+ (0 == memcmp (key, REQ_HEADER_HOST_NAME, key_size)))
+ {
+ if ((strlen (REQ_HEADER_HOST_VALUE) == value_size) &&
+ (0 == memcmp (value, REQ_HEADER_HOST_VALUE, value_size)))
+ param->found_header_host++;
+ else
+ fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
+ (int) value_size, value, REQ_HEADER_HOST_VALUE);
+ }
+ else if ((strlen (REQ_HEADER_UA_NAME) == key_size) &&
+ (0 == memcmp (key, REQ_HEADER_UA_NAME, key_size)))
+ {
+ if ((strlen (REQ_HEADER_UA_VALUE) == value_size) &&
+ (0 == memcmp (value, REQ_HEADER_UA_VALUE, value_size)))
+ param->found_header_ua++;
+ else
+ fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
+ (int) value_size, value, REQ_HEADER_UA_VALUE);
+ }
+ else if ((strlen (REQ_HEADER_CT_NAME) == key_size) &&
+ (0 == memcmp (key, REQ_HEADER_CT_NAME, key_size)))
+ {
+ if ((strlen (REQ_HEADER_CT_VALUE) == value_size) &&
+ (0 == memcmp (value, REQ_HEADER_CT_VALUE, value_size)))
+ param->found_header_ct++;
+ else
+ fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
+ (int) value_size, value, REQ_HEADER_CT_VALUE);
+ }
+ else if ((strlen (MHD_HTTP_HEADER_CONTENT_LENGTH) == key_size) &&
+ (0 == memcmp (key, MHD_HTTP_HEADER_CONTENT_LENGTH, key_size)))
+ {
+ /* do not check value of the header here for simplicity */
+ param->found_header_cl++;
+ }
+ else if ((strlen (MHD_HTTP_HEADER_TRANSFER_ENCODING) == key_size) &&
+ (0 == memcmp (key, MHD_HTTP_HEADER_TRANSFER_ENCODING, key_size)))
+ {
+ if ((strlen ("chunked") == value_size) &&
+ (0 == memcmp (value, "chunked", value_size)))
+ param->found_header_te++;
+ else
+ fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
+ (int) value_size, value, "chunked");
+ }
+ return MHD_YES;
+}
+
+
+struct ahc_cls_type
+{
+ const char *volatile rp_data;
+ volatile size_t rp_data_size;
+ const char *volatile rq_method;
+ const char *volatile rq_url;
+ const char *volatile req_body;
+ volatile unsigned int cb_called; /* Non-zero indicates that callback was called at least one time */
+ size_t req_body_size; /**< The number of bytes in @a req_body */
+ size_t req_body_uploaded; /* Updated by callback */
+};
+
+
+static enum MHD_Result
+ahcCheck (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data, size_t *upload_data_size,
+ void **con_cls)
+{
+ static int marker;
+ enum MHD_Result ret;
+ struct mhd_header_checker_param header_check_param;
+ struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
+
+ if (NULL == param)
+ mhdErrorExitDesc ("cls parameter is NULL");
+ param->cb_called++;
+
+ if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
+ mhdErrorExitDesc ("Unexpected HTTP version");
+
+ if (0 != strcmp (url, param->rq_url))
+ mhdErrorExitDesc ("Unexpected URI");
+
+ if (0 != strcmp (param->rq_method, method))
+ mhdErrorExitDesc ("Unexpected request method");
+
+ if (NULL == upload_data_size)
+ mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
+
+ if (0 != *upload_data_size)
+ {
+ const char *const upload_body = param->req_body;
+ if (NULL == upload_data)
+ mhdErrorExitDesc ("'upload_data' is NULL while " \
+ "'*upload_data_size' value is not zero");
+ if (NULL == upload_body)
+ mhdErrorExitDesc ("'*upload_data_size' value is not zero " \
+ "while no request body is expected");
+ if (param->req_body_uploaded + *upload_data_size > param->req_body_size)
+ {
+ fprintf (stderr, "Too large upload body received. Got %u, expected %u",
+ (unsigned int) (param->req_body_uploaded + *upload_data_size),
+ (unsigned int) param->req_body_size);
+ mhdErrorExit ();
+ }
+ if (0 != memcmp (upload_data, upload_body + param->req_body_uploaded,
+ *upload_data_size))
+ {
+ fprintf (stderr, "Unexpected request body at offset %u: " \
+ "'%.*s', expected: '%.*s'\n",
+ (unsigned int) param->req_body_uploaded,
+ (int) *upload_data_size, upload_data,
+ (int) *upload_data_size, upload_body + param->req_body_uploaded);
+ mhdErrorExit ();
+ }
+ param->req_body_uploaded += *upload_data_size;
+ *upload_data_size = 0;
+ }
+
+ if (&marker != *con_cls)
+ {
+ /* The first call of the callback for this connection */
+ mhd_assert (NULL == upload_data);
+ param->req_body_uploaded = 0;
+
+ *con_cls = ▮
+ return MHD_YES;
+ }
+
+ memset (&header_check_param, 0, sizeof(header_check_param));
+ if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND,
+ &headerCheckerInterator,
+ &header_check_param))
+ mhdErrorExitDesc ("Wrong number of headers in the request");
+ if (1 != header_check_param.found_header_host)
+ mhdErrorExitDesc ("'Host' header has not been detected in request");
+ if (1 != header_check_param.found_header_ua)
+ mhdErrorExitDesc ("'User-Agent' header has not been detected in request");
+ if (1 != header_check_param.found_header_ct)
+ mhdErrorExitDesc ("'Content-Type' header has not been detected in request");
+ if (! upl_chunked && (1 != header_check_param.found_header_cl))
+ mhdErrorExitDesc ("'Content-Length' header has not been detected "
+ "in request");
+ if (upl_chunked && (1 != header_check_param.found_header_te))
+ mhdErrorExitDesc ("'Transfer-Encoding' header has not been detected "
+ "in request");
+
+ if (NULL != upload_data)
+ return MHD_YES; /* Full request has not been received so far */
+
+#if 0 /* Code unused in this test */
+ struct MHD_Response *response;
+ response = MHD_create_response_from_buffer (param->rp_data_size,
+ (void *) param->rp_data,
+ MHD_RESPMEM_MUST_COPY);
+ if (NULL == response)
+ mhdErrorExitDesc ("Failed to create response");
+
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ if (MHD_YES != ret)
+ mhdErrorExitDesc ("Failed to queue response");
+#else
+ if (NULL == upload_data)
+ mhdErrorExitDesc ("Full request received, " \
+ "while incomplete request expected");
+ ret = MHD_NO;
+#endif
+
+ return ret;
+}
+
+
+struct simpleQueryParams
+{
+ /* Destination path for HTTP query */
+ const char *queryPath;
+
+ /* Custom query method, NULL for default */
+ const char *method;
+
+ /* Destination port for HTTP query */
+ int queryPort;
+
+ /* Additional request headers, static */
+ const char *headers;
+
+ /* NULL for request without body */
+ uint8_t *req_body;
+ size_t req_body_size;
+
+ /* Non-zero to use chunked encoding for request body */
+ int chunked;
+
+ /* Max size of data for single 'send()' call */
+ size_t step_size;
+
+ /* Limit for total amount of sent data */
+ size_t total_send_max;
+
+ /* HTTP query result error flag */
+ volatile int queryError;
+
+ /* Response HTTP code, zero if no response */
+ volatile int responseCode;
+};
+
+
+/* returns non-zero if timed-out */
+static int
+performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt)
+{
+ time_t start;
+ struct timeval tv;
+ int ret;
+ const union MHD_DaemonInfo *di;
+ MHD_socket lstn_sk;
+ int client_accepted;
+ int full_req_recieved;
+ int full_req_sent;
+ int some_data_recieved;
+
+ di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
+ if (NULL == di)
+ mhdErrorExitDesc ("Cannot get lister socket");
+ lstn_sk = di->listen_fd;
+
+ ret = 1; /* will be replaced with real result */
+ client_accepted = 0;
+
+ _MHD_dumbClient_start_connect (clnt);
+
+ full_req_recieved = 0;
+ some_data_recieved = 0;
+ start = time (NULL);
+ do
+ {
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ MHD_socket maxMhdSk;
+ int num_ready;
+ int do_client; /**< Process data in client */
+
+ maxMhdSk = MHD_INVALID_SOCKET;
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ if (NULL == clnt)
+ {
+ /* client has finished, check whether MHD is still
+ * processing any connections */
+ unsigned long long to;
+ full_req_sent = 1;
+ do_client = 0;
+ if (client_accepted && (MHD_YES != MHD_get_timeout (d, &to)))
+ {
+ ret = 0;
+ break; /* MHD finished as well */
+ }
+ }
+ else
+ {
+ full_req_sent = _MHD_dumbClient_is_req_sent (clnt);
+ if (! full_req_sent)
+ do_client = 1; /* Request hasn't been sent yet, send the data */
+ else
+ {
+ /* All request data has been sent.
+ * Client will close the socket as the next step. */
+ if (full_req_recieved)
+ do_client = 1; /* All data has been received by the MHD */
+ else if ((0 == rate_limiter) && some_data_recieved)
+ {
+ /* No RST rate limiter, no need to avoid extra RST
+ * and at least something was received by the MHD */
+ do_client = 1;
+ }
+ else
+ {
+ /* When rate limiter is enabled, all sent packets must be received
+ * before client close connection to avoid RST for every ACK.
+ * When rate limiter is not enabled, the MHD must receive at
+ * least something before closing the connection. */
+ do_client = 0;
+ }
+ }
+
+ if (do_client)
+ _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es);
+ }
+ if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
+ mhdErrorExitDesc ("MHD_get_fdset() failed");
+ if (do_client)
+ {
+ tv.tv_sec = 1;
+ tv.tv_usec = 250 * 1000;
+ }
+ else
+ { /* Request completely sent but not yet fully received */
+ tv.tv_sec = 0;
+ tv.tv_usec = FINAL_PACKETS_MS * 1000;
+ }
+ num_ready = select (maxMhdSk + 1, &rs, &ws, &es, &tv);
+ if (-1 == num_ready)
+ {
+#ifdef MHD_POSIX_SOCKETS
+ if (EINTR != errno)
+ externalErrorExitDesc ("Unexpected select() error");
+#else
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ externalErrorExitDesc ("Unexpected select() error");
+ Sleep (tv.tv_sec * 1000 + tv.tv_usec / 1000);
+#endif
+ continue;
+ }
+ if (0 == num_ready)
+ { /* select() finished by timeout, looks like no more packets are pending */
+ if (do_client)
+ externalErrorExitDesc ("Timeout waiting for sockets");
+ if (full_req_sent && (! full_req_recieved))
+ full_req_recieved = 1;
+ }
+ if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
+ mhdErrorExitDesc ("MHD_run_from_select() failed");
+ if (! client_accepted)
+ client_accepted = FD_ISSET (lstn_sk, &rs);
+ else
+ { /* Client connection was already accepted by MHD */
+ if (! some_data_recieved)
+ {
+ if (! do_client)
+ {
+ if (0 != num_ready)
+ { /* Connection was accepted before, "ready" socket means data */
+ some_data_recieved = 1;
+ }
+ }
+ else
+ {
+ if (2 == num_ready)
+ some_data_recieved = 1;
+ else if ((1 == num_ready) &&
+ ((MHD_INVALID_SOCKET == clnt->sckt) ||
+ ! FD_ISSET (clnt->sckt, &ws)))
+ some_data_recieved = 1;
+ }
+ }
+ }
+ if (do_client)
+ {
+ if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es))
+ clnt = NULL;
+ }
+ /* Use double timeout value here so MHD would be able to catch timeout
+ * internally */
+ } while (time (NULL) - start <= (TIMEOUTS_VAL * 2));
+
+ return ret;
+}
+
+
+/* Returns zero for successful response and non-zero for failed response */
+static int
+doClientQueryInThread (struct MHD_Daemon *d,
+ struct simpleQueryParams *p)
+{
+ const union MHD_DaemonInfo *dinfo;
+ struct _MHD_dumbClient *c;
+ int errornum;
+ int use_external_poll;
+
+ dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
+ if (NULL == dinfo)
+ mhdErrorExitDesc ("MHD_get_daemon_info() failed");
+ use_external_poll = (0 == (dinfo->flags
+ & MHD_USE_INTERNAL_POLLING_THREAD));
+
+ if (0 == p->queryPort)
+ externalErrorExit ();
+
+ c = _MHD_dumbClient_create (p->queryPort, p->method, p->queryPath,
+ p->headers, p->req_body, p->req_body_size,
+ p->chunked);
+ _MHD_dumbClient_set_send_limits (c, p->step_size, p->total_send_max);
+
+ /* 'internal' polling should not be used in this test */
+ mhd_assert (use_external_poll);
+ if (! use_external_poll)
+ errornum = _MHD_dumbClient_perform (c);
+ else
+ errornum = performQueryExternal (d, c);
+
+ if (errornum)
+ fprintf (stderr, "Request timeout out.\n");
+
+ _MHD_dumbClient_close (c);
+
+ return errornum;
+}
+
+
+void
+printTestResults (FILE *stream,
+ struct simpleQueryParams *qParam,
+ struct ahc_cls_type *ahc_param,
+ struct check_uri_cls *uri_cb_param,
+ struct term_notif_cb_param *term_result,
+ struct sckt_notif_cb_param *sckt_result)
+{
+ if (stderr != stream)
+ fflush (stderr);
+ fprintf (stream, " Request aborted at %u byte%s.",
+ (unsigned int) qParam->total_send_max,
+ 1 == qParam->total_send_max ? "" : "s");
+ if ((1 == sckt_result->num_started) && (1 == sckt_result->num_finished))
+ fprintf (stream, " One socket has been accepted and then closed.");
+ else
+ fprintf (stream, " Sockets have been accepted %u time%s"
+ " and closed %u time%s.", sckt_result->num_started,
+ (1 == sckt_result->num_started) ? "" : "s",
+ sckt_result->num_finished,
+ (1 == sckt_result->num_finished) ? "" : "s");
+ if (0 == uri_cb_param->cb_called)
+ fprintf (stream, " URI callback has NOT been called.");
+ else
+ fprintf (stream, " URI callback has been called %u time%s.",
+ uri_cb_param->cb_called,
+ 1 == uri_cb_param->cb_called ? "" : "s");
+ if (0 == ahc_param->cb_called)
+ fprintf (stream, " Access handler callback has NOT been called.");
+ else
+ fprintf (stream, " Access handler callback has been called %u time%s.",
+ ahc_param->cb_called,
+ 1 == ahc_param->cb_called ? "" : "s");
+ if (0 == term_result->num_called)
+ fprintf (stream, " Final notification callback has NOT been called.");
+ else
+ fprintf (stream, " Final notification callback has been called %u time%s "
+ "with %s code.", term_result->num_called,
+ (1 == term_result->num_called) ? "" : "s",
+ term_reason_str (term_result->term_reason));
+ fprintf (stream, "\n");
+ fflush (stream);
+}
+
+
+/* Perform test queries, shut down MHD daemon, and free parameters */
+static int
+performTestQueries (struct MHD_Daemon *d, int d_port,
+ struct ahc_cls_type *ahc_param,
+ struct check_uri_cls *uri_cb_param,
+ struct term_notif_cb_param *term_result,
+ struct sckt_notif_cb_param *sckt_result)
+{
+ struct simpleQueryParams qParam;
+ time_t start;
+ int ret = 0; /* Return value */
+ size_t req_total_size;
+ size_t limit_send_size;
+ size_t inc_size;
+ int expected_reason;
+ int found_right_reason;
+
+ /* Common parameters, to be individually overridden by specific test cases
+ * if needed */
+ qParam.queryPort = d_port;
+ qParam.method = MHD_HTTP_METHOD_PUT;
+ qParam.queryPath = EXPECTED_URI_BASE_PATH;
+ qParam.headers = REQ_HEADER_CT;
+ qParam.req_body = (uint8_t *) REQ_BODY;
+ qParam.req_body_size = MHD_STATICSTR_LEN_ (REQ_BODY);
+ qParam.chunked = upl_chunked;
+ qParam.step_size = by_step ? 1 : 0;
+
+ uri_cb_param->uri = EXPECTED_URI_BASE_PATH;
+
+ ahc_param->rq_url = EXPECTED_URI_BASE_PATH;
+ ahc_param->rq_method = MHD_HTTP_METHOD_PUT;
+ ahc_param->rp_data = "~";
+ ahc_param->rp_data_size = 1;
+ ahc_param->req_body = (const char *) qParam.req_body;
+ ahc_param->req_body_size = qParam.req_body_size;
+
+ do
+ {
+ struct _MHD_dumbClient *test_c;
+ struct simpleQueryParams *p = &qParam;
+ test_c = _MHD_dumbClient_create (p->queryPort, p->method, p->queryPath,
+ p->headers, p->req_body, p->req_body_size,
+ p->chunked);
+ req_total_size = test_c->req_size;
+ _MHD_dumbClient_close (test_c);
+ } while (0);
+
+ expected_reason = use_hard_close ?
+ MHD_REQUEST_TERMINATED_READ_ERROR :
+ MHD_REQUEST_TERMINATED_CLIENT_ABORT;
+ found_right_reason = 0;
+ if (0 != rate_limiter)
+ {
+ if (verbose)
+ {
+ printf ("Pausing for rate limiter...");
+ fflush (stdout);
+ }
+ _MHD_sleep (1150); /* Just a bit more than one second */
+ if (verbose)
+ {
+ printf (" OK\n");
+ fflush (stdout);
+ }
+ inc_size = ((req_total_size - 1) + (rate_limiter - 1)) / rate_limiter;
+ if (0 == inc_size)
+ inc_size = 1;
+ }
+ else
+ inc_size = 1;
+
+ start = time (NULL);
+ for (limit_send_size = 1; limit_send_size < req_total_size;
+ limit_send_size += inc_size)
+ {
+ int test_succeed;
+ test_succeed = 0;
+ /* Make sure that maximum size is tested */
+ if (req_total_size - inc_size < limit_send_size)
+ limit_send_size = req_total_size - 1;
+ qParam.total_send_max = limit_send_size;
+ /* To be updated by callbacks */
+ ahc_param->cb_called = 0;
+ uri_cb_param->cb_called = 0;
+ term_result->num_called = 0;
+ term_result->term_reason = -1;
+ sckt_result->num_started = 0;
+ sckt_result->num_finished = 0;
+
+ if (0 != doClientQueryInThread (d, &qParam))
+ fprintf (stderr, "FAILED: connection has NOT been closed by MHD.");
+ else
+ {
+ if ((-1 != term_result->term_reason) &&
+ (MHD_REQUEST_TERMINATED_READ_ERROR != term_result->term_reason) &&
+ (MHD_REQUEST_TERMINATED_CLIENT_ABORT != term_result->term_reason) )
+ fprintf (stderr, "FAILED: Wrong termination code.");
+ else if ((0 == term_result->num_called) &&
+ ((0 != uri_cb_param->cb_called) || (0 != ahc_param->cb_called)))
+ fprintf (stderr, "FAILED: Missing required call of final notification "
+ "callback.");
+ else if (1 < uri_cb_param->cb_called)
+ fprintf (stderr, "FAILED: Too many URI callbacks.");
+ else if ((0 != ahc_param->cb_called) && (0 == uri_cb_param->cb_called))
+ fprintf (stderr, "FAILED: URI callback has NOT been called "
+ "while Access Handler callback has been called.");
+ else if (1 < term_result->num_called)
+ fprintf (stderr, "FAILED: Too many final callbacks.");
+ else if (1 != sckt_result->num_started)
+ fprintf (stderr, "FAILED: Wrong number of sockets accepted.");
+ else if (1 != sckt_result->num_finished)
+ fprintf (stderr, "FAILED: Wrong number of sockets closed.");
+ else
+ {
+ test_succeed = 1;
+ if (expected_reason == term_result->term_reason)
+ found_right_reason = 1;
+ }
+ }
+
+ if (! test_succeed)
+ {
+ ret = 1;
+ printTestResults (stderr,
+ &qParam, ahc_param, uri_cb_param,
+ term_result, sckt_result);
+ }
+ else if (verbose)
+ {
+ printf ("SUCCEED:");
+ printTestResults (stdout,
+ &qParam, ahc_param, uri_cb_param,
+ term_result, sckt_result);
+ }
+
+ if (time (NULL) - start >
+ (time_t) ((TIMEOUTS_VAL * 25)
+ + (rate_limiter * FINAL_PACKETS_MS) / 1000 + 1))
+ {
+ ret |= 1 << 2;
+ fprintf (stderr, "FAILED: Test total time exceeded.\n");
+ break;
+ }
+ }
+
+ MHD_stop_daemon (d);
+ free (uri_cb_param);
+ free (ahc_param);
+ free (term_result);
+ free (sckt_result);
+
+ if (! found_right_reason)
+ {
+ fprintf (stderr, "FAILED: termination callback was not called with "
+ "expected (%s) reason.\n", term_reason_str (expected_reason));
+ fflush (stderr);
+ ret |= 1 << 1;
+ }
+
+ return ret;
+}
+
+
+enum testMhdThreadsType
+{
+ testMhdThreadExternal = 0,
+ testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD,
+ testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
+ | MHD_USE_INTERNAL_POLLING_THREAD,
+ testMhdThreadInternalPool
+};
+
+enum testMhdPollType
+{
+ testMhdPollBySelect = 0,
+ testMhdPollByPoll = MHD_USE_POLL,
+ testMhdPollByEpoll = MHD_USE_EPOLL,
+ testMhdPollAuto = MHD_USE_AUTO
+};
+
+/* Get number of threads for thread pool depending
+ * on used poll function and test type. */
+static unsigned int
+testNumThreadsForPool (enum testMhdPollType pollType)
+{
+ int numThreads = MHD_CPU_COUNT;
+ (void) pollType; /* Don't care about pollType for this test */
+ return numThreads; /* No practical limit for non-cleanup test */
+}
+
+
+static struct MHD_Daemon *
+startTestMhdDaemon (enum testMhdThreadsType thrType,
+ enum testMhdPollType pollType, int *pport,
+ struct ahc_cls_type **ahc_param,
+ struct check_uri_cls **uri_cb_param,
+ struct term_notif_cb_param **term_result,
+ struct sckt_notif_cb_param **sckt_result)
+{
+ struct MHD_Daemon *d;
+ const union MHD_DaemonInfo *dinfo;
+
+ if ((NULL == ahc_param) || (NULL == uri_cb_param) || (NULL == term_result))
+ externalErrorExit ();
+
+ *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type));
+ if (NULL == *ahc_param)
+ externalErrorExit ();
+ *uri_cb_param =
+ (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls));
+ if (NULL == *uri_cb_param)
+ externalErrorExit ();
+ *term_result =
+ (struct term_notif_cb_param *) malloc (sizeof(struct term_notif_cb_param));
+ if (NULL == *term_result)
+ externalErrorExit ();
+ *sckt_result =
+ (struct sckt_notif_cb_param *) malloc (sizeof(struct sckt_notif_cb_param));
+ if (NULL == *sckt_result)
+ externalErrorExit ();
+
+ if ( (0 == *pport) &&
+ (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
+ {
+ *pport = 4170;
+ if (use_shutdown)
+ *pport += 0;
+ if (use_close)
+ *pport += 1;
+ if (use_hard_close)
+ *pport += 1;
+ if (by_step)
+ *pport += 1 << 2;
+ if (upl_chunked)
+ *pport += 1 << 3;
+ if (! oneone)
+ *pport += 1 << 4;
+ }
+
+ if (testMhdThreadInternalPool != thrType)
+ d = MHD_start_daemon (((int) thrType) | ((int) pollType)
+ | (verbose ? MHD_USE_ERROR_LOG : 0),
+ *pport, NULL, NULL,
+ &ahcCheck, *ahc_param,
+ MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
+ *uri_cb_param,
+ MHD_OPTION_NOTIFY_COMPLETED, &term_cb, *term_result,
+ MHD_OPTION_NOTIFY_CONNECTION, &socket_cb,
+ *sckt_result,
+ MHD_OPTION_CONNECTION_TIMEOUT,
+ (unsigned) TIMEOUTS_VAL,
+ MHD_OPTION_END);
+ else
+ d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | ((int) pollType)
+ | (verbose ? MHD_USE_ERROR_LOG : 0),
+ *pport, NULL, NULL,
+ &ahcCheck, *ahc_param,
+ MHD_OPTION_THREAD_POOL_SIZE,
+ testNumThreadsForPool (pollType),
+ MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
+ *uri_cb_param,
+ MHD_OPTION_NOTIFY_COMPLETED, &term_cb, *term_result,
+ MHD_OPTION_NOTIFY_CONNECTION, &socket_cb,
+ *sckt_result,
+ MHD_OPTION_CONNECTION_TIMEOUT,
+ (unsigned) TIMEOUTS_VAL,
+ MHD_OPTION_END);
+
+ if (NULL == d)
+ mhdErrorExitDesc ("Failed to start MHD daemon");
+
+ if (0 == *pport)
+ {
+ dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
+ if ((NULL == dinfo) || (0 == dinfo->port))
+ mhdErrorExitDesc ("MHD_get_daemon_info() failed");
+ *pport = (int) dinfo->port;
+ if (0 == global_port)
+ global_port = *pport; /* Reuse the same port for all tests */
+ }
+
+ return d;
+}
+
+
+/* Test runners */
+
+
+static int
+testExternalGet (void)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+ struct term_notif_cb_param *term_result;
+ struct sckt_notif_cb_param *sckt_result;
+
+ d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port,
+ &ahc_param, &uri_cb_param, &term_result,
+ &sckt_result);
+
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result,
+ sckt_result);
+}
+
+
+#if 0 /* disabled runners, not suitable for this test */
+static int
+testInternalGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+ struct term_notif_cb_param *term_result;
+
+ d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port,
+ &ahc_param, &uri_cb_param, &term_result);
+
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result);
+}
+
+
+static int
+testMultithreadedGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+ struct term_notif_cb_param *term_result;
+
+ d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port,
+ &ahc_param, &uri_cb_param);
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result);
+}
+
+
+static int
+testMultithreadedPoolGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+ struct term_notif_cb_param *term_result;
+
+ d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port,
+ &ahc_param, &uri_cb_param);
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result);
+}
+
+
+#endif /* disabled runners, not suitable for this test */
+
+int
+main (int argc, char *const *argv)
+{
+ unsigned int errorCount = 0;
+ unsigned int test_result = 0;
+ verbose = 0;
+
+ if ((NULL == argv) || (0 == argv[0]))
+ return 99;
+ oneone = ! has_in_name (argv[0], "10");
+ use_shutdown = has_in_name (argv[0], "_shutdown") ? 1 : 0;
+ use_close = has_in_name (argv[0], "_close") ? 1 : 0;
+ use_hard_close = has_in_name (argv[0], "_hard_close") ? 1 : 0;
+ by_step = has_in_name (argv[0], "_steps") ? 1 : 0;
+ upl_chunked = has_in_name (argv[0], "_chunked") ? 1 : 0;
+#ifndef SO_LINGER
+ if (use_hard_close)
+ {
+ fprintf (stderr, "This test requires SO_LINGER socket option support.\n");
+ return 77;
+ }
+#endif /* ! SO_LINGER */
+ if (1 != use_shutdown + use_close)
+ return 99;
+ verbose =
+ ! (has_param (argc, argv, "-q") || has_param (argc, argv, "--quiet"));
+
+ test_global_init ();
+
+ /* Could be set to non-zero value to enforce using specific port
+ * in the test */
+ global_port = 0;
+ test_result = testExternalGet ();
+ if (test_result)
+ fprintf (stderr, "FAILED: testExternalGet (). Result: %u.\n", test_result);
+ else if (verbose)
+ printf ("PASSED: testExternalGet ().\n");
+ errorCount += test_result;
+#if 0 /* disabled runners, not suitable for this test */
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
+ {
+ test_result = testInternalGet (testMhdPollAuto);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto). "
+ "Result: %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+#ifdef _MHD_HEAVY_TESTS
+ /* Actually tests are not heavy, but took too long to complete while
+ * not really provide any additional results. */
+ test_result = testInternalGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect). "
+ "Result: %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ test_result = testMultithreadedPoolGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr,
+ "FAILED: testMultithreadedPoolGet (testMhdPollBySelect). "
+ "Result: %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ test_result = testMultithreadedGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr,
+ "FAILED: testMultithreadedGet (testMhdPollBySelect). "
+ "Result: %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
+ {
+ test_result = testInternalGet (testMhdPollByPoll);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll). "
+ "Result: %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
+ errorCount += test_result;
+ }
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
+ {
+ test_result = testInternalGet (testMhdPollByEpoll);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll). "
+ "Result: %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
+ errorCount += test_result;
+ }
+#else
+ /* Mute compiler warnings */
+ (void) testMultithreadedGet;
+ (void) testMultithreadedPoolGet;
+#endif /* _MHD_HEAVY_TESTS */
+ }
+#endif /* disabled runners, not suitable for this test */
+ if (0 != errorCount)
+ fprintf (stderr,
+ "Error (code: %u)\n",
+ errorCount);
+ else if (verbose)
+ printf ("All tests passed.\n");
+
+ test_global_cleanup ();
+
+ return (errorCount == 0) ? 0 : 1; /* 0 == pass */
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_http_reasons.c
^
|
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2017 Karlson2k (Evgeny Grin)
+ Copyright (C) 2017-2021 Karlson2k (Evgeny Grin)
This test tool is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@@ -25,26 +25,45 @@
#include "mhd_options.h"
#include <stdio.h>
+#include <string.h>
#include "microhttpd.h"
#include "mhd_str.h"
+static const char *const r_unknown = "unknown";
+
+/* Return zero when no error is detected */
static int
-expect_result (int code, const char*expected)
+expect_result (int code, const char *expected)
{
- const char*const reason = MHD_get_reason_phrase_for (code);
- if (MHD_str_equal_caseless_ (reason, expected))
- return 0;
- fprintf (stderr,
- "Incorrect reason returned for code %d:\n Returned: \"%s\" \tExpected: \"%s\"\n",
- code, reason, expected);
- return 1;
+ const char *const reason = MHD_get_reason_phrase_for (code);
+ const size_t len = MHD_get_reason_phrase_len_for (code);
+ size_t exp_len;
+ if (! MHD_str_equal_caseless_ (reason, expected))
+ {
+ fprintf (stderr,
+ "Incorrect reason returned for code %d:\n Returned: \"%s\" \tExpected: \"%s\"\n",
+ code, reason, expected);
+ return 1;
+ }
+ if (r_unknown == expected)
+ exp_len = 0;
+ else
+ exp_len = strlen (expected);
+ if (exp_len != len)
+ {
+ fprintf (stderr,
+ "Incorrect reason length returned for code %d:\n Returned: \"%u\" \tExpected: \"%u\"\n",
+ code, (unsigned) len, (unsigned) exp_len);
+ return 1;
+ }
+ return 0;
}
static int
expect_absent (int code)
{
- return expect_result (code, "unknown");
+ return expect_result (code, r_unknown);
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_md5.c
^
|
@@ -27,6 +27,7 @@
#include "md5.h"
#include "test_helpers.h"
#include <stdio.h>
+#include <stdlib.h>
static int verbose = 0; /* verbose level (0-1)*/
@@ -369,6 +370,48 @@
}
+/* Use data set number 7 as it has the longest sequence */
+#define DATA_POS 6
+#define MAX_OFFSET 31
+
+static int
+test_unaligned (void)
+{
+ int num_failed = 0;
+ unsigned int offset;
+ uint8_t *buf;
+ uint8_t *digest_buf;
+
+ const struct data_unit2 *const tdata = data_units2 + DATA_POS;
+
+ buf = malloc (tdata->bin_l.len + MAX_OFFSET);
+ digest_buf = malloc (MD5_DIGEST_SIZE + MAX_OFFSET);
+ if ((NULL == buf) || (NULL == digest_buf))
+ exit (99);
+
+ for (offset = MAX_OFFSET; offset >= 1; --offset)
+ {
+ struct MD5Context ctx;
+ uint8_t *unaligned_digest;
+ uint8_t *unaligned_buf;
+
+ unaligned_buf = buf + offset;
+ memcpy (unaligned_buf, tdata->bin_l.bin, tdata->bin_l.len);
+ unaligned_digest = digest_buf + MAX_OFFSET - offset;
+ memset (unaligned_digest, 0, MD5_DIGEST_SIZE);
+
+ MHD_MD5Init (&ctx);
+ MHD_MD5Update (&ctx, unaligned_buf, tdata->bin_l.len);
+ MHD_MD5Final (&ctx, unaligned_digest);
+ num_failed += check_result (__FUNCTION__, MAX_OFFSET - offset,
+ unaligned_digest, tdata->digest);
+ }
+ free (digest_buf);
+ free (buf);
+ return num_failed;
+}
+
+
int
main (int argc, char *argv[])
{
@@ -383,5 +426,7 @@
num_failed += test2_str ();
num_failed += test2_bin ();
+ num_failed += test_unaligned ();
+
return num_failed ? 1 : 0;
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_postprocessor.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2013, 2019, 2020 Christian Grothoff
+ Copyright (C) 2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -21,6 +22,7 @@
* @file test_postprocessor.c
* @brief Testcase for postprocessor
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "platform.h"
#include "microhttpd.h"
@@ -34,50 +36,78 @@
#include <unistd.h>
#endif
+#ifndef MHD_DEBUG_PP
+#define MHD_DEBUG_PP 0
+#endif /* MHD_DEBUG_PP */
+
+struct expResult
+{
+ const char *key;
+ const char *fname;
+ const char *cnt_type;
+ const char *tr_enc;
+ const char *data;
+};
+
/**
* Array of values that the value checker "wants".
* Each series of checks should be terminated by
* five NULL-entries.
*/
-const char *want[] = {
+struct expResult exp_results[] = {
#define URL_NOVALUE1_DATA "abc&x=5"
#define URL_NOVALUE1_START 0
- "abc", NULL, NULL, NULL, NULL,
- "x", NULL, NULL, NULL, "5",
-#define URL_NOVALUE1_END (URL_NOVALUE1_START + 10)
+ {"abc", NULL, NULL, NULL, /* NULL */ ""}, /* change after API update */
+ {"x", NULL, NULL, NULL, "5"},
+#define URL_NOVALUE1_END (URL_NOVALUE1_START + 2)
#define URL_NOVALUE2_DATA "abc=&x=5"
#define URL_NOVALUE2_START URL_NOVALUE1_END
- "abc", NULL, NULL, NULL, "",
- "x", NULL, NULL, NULL, "5",
-#define URL_NOVALUE2_END (URL_NOVALUE2_START + 10)
+ {"abc", NULL, NULL, NULL, ""},
+ {"x", NULL, NULL, NULL, "5"},
+#define URL_NOVALUE2_END (URL_NOVALUE2_START + 2)
+#define URL_NOVALUE3_DATA "xyz="
+#define URL_NOVALUE3_START URL_NOVALUE2_END
+ {"xyz", NULL, NULL, NULL, ""},
+#define URL_NOVALUE3_END (URL_NOVALUE3_START + 1)
+#define URL_NOVALUE4_DATA "xyz"
+#define URL_NOVALUE4_START URL_NOVALUE3_END
+ {"xyz", NULL, NULL, NULL, /* NULL */ ""}, /* change after API update */
+#define URL_NOVALUE4_END (URL_NOVALUE4_START + 1)
#define URL_DATA "abc=def&x=5"
-#define URL_START URL_NOVALUE2_END
- "abc", NULL, NULL, NULL, "def",
- "x", NULL, NULL, NULL, "5",
-#define URL_END (URL_START + 10)
- NULL, NULL, NULL, NULL, NULL,
+#define URL_START URL_NOVALUE4_END
+ {"abc", NULL, NULL, NULL, "def"},
+ {"x", NULL, NULL, NULL, "5"},
+#define URL_END (URL_START + 2)
+#define URL_ENC_DATA "space=%20&key%201=&crlf=%0D%0a&mix%09ed=%2001%0d%0A"
+#define URL_ENC_START URL_END
+ {"space", NULL, NULL, NULL, " "},
+ {"key 1", NULL, NULL, NULL, ""},
+ {"crlf", NULL, NULL, NULL, "\r\n"},
+ {"mix\ted", NULL, NULL, NULL, " 01\r\n"},
+#define URL_ENC_END (URL_ENC_START + 4)
+ {NULL, NULL, NULL, NULL, NULL},
#define FORM_DATA \
"--AaB03x\r\ncontent-disposition: form-data; name=\"field1\"\r\n\r\nJoe Blow\r\n--AaB03x\r\ncontent-disposition: form-data; name=\"pics\"; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\nContent-Transfer-Encoding: binary\r\n\r\nfiledata\r\n--AaB03x--\r\n"
-#define FORM_START (URL_END + 5)
- "field1", NULL, NULL, NULL, "Joe Blow",
- "pics", "file1.txt", "text/plain", "binary", "filedata",
-#define FORM_END (FORM_START + 10)
- NULL, NULL, NULL, NULL, NULL,
+#define FORM_START (URL_ENC_END + 1)
+ {"field1", NULL, NULL, NULL, "Joe Blow"},
+ {"pics", "file1.txt", "text/plain", "binary", "filedata"},
+#define FORM_END (FORM_START + 2)
+ {NULL, NULL, NULL, NULL, NULL},
#define FORM_NESTED_DATA \
"--AaB03x\r\ncontent-disposition: form-data; name=\"field1\"\r\n\r\nJane Blow\r\n--AaB03x\r\ncontent-disposition: form-data; name=\"pics\"\r\nContent-type: multipart/mixed, boundary=BbC04y\r\n\r\n--BbC04y\r\nContent-disposition: attachment; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\nfiledata1\r\n--BbC04y\r\nContent-disposition: attachment; filename=\"file2.gif\"\r\nContent-type: image/gif\r\nContent-Transfer-Encoding: binary\r\n\r\nfiledata2\r\n--BbC04y--\r\n--AaB03x--"
-#define FORM_NESTED_START (FORM_END + 5)
- "field1", NULL, NULL, NULL, "Jane Blow",
- "pics", "file1.txt", "text/plain", NULL, "filedata1",
- "pics", "file2.gif", "image/gif", "binary", "filedata2",
-#define FORM_NESTED_END (FORM_NESTED_START + 15)
- NULL, NULL, NULL, NULL, NULL,
+#define FORM_NESTED_START (FORM_END + 1)
+ {"field1", NULL, NULL, NULL, "Jane Blow"},
+ {"pics", "file1.txt", "text/plain", NULL, "filedata1"},
+ {"pics", "file2.gif", "image/gif", "binary", "filedata2"},
+#define FORM_NESTED_END (FORM_NESTED_START + 3)
+ {NULL, NULL, NULL, NULL, NULL},
#define URL_EMPTY_VALUE_DATA "key1=value1&key2=&key3="
-#define URL_EMPTY_VALUE_START (FORM_NESTED_END + 5)
- "key1", NULL, NULL, NULL, "value1",
- "key2", NULL, NULL, NULL, "",
- "key3", NULL, NULL, NULL, "",
-#define URL_EMPTY_VALUE_END (URL_EMPTY_VALUE_START + 15)
- NULL, NULL, NULL, NULL, NULL
+#define URL_EMPTY_VALUE_START (FORM_NESTED_END + 1)
+ {"key1", NULL, NULL, NULL, "value1"},
+ {"key2", NULL, NULL, NULL, ""},
+ {"key3", NULL, NULL, NULL, ""},
+#define URL_EMPTY_VALUE_END (URL_EMPTY_VALUE_START + 3)
+ {NULL, NULL, NULL, NULL, NULL}
};
@@ -92,6 +122,17 @@
}
+static int
+mismatch2 (const char *data, const char *expected, size_t offset, size_t size)
+{
+ if (data == expected)
+ return 0;
+ if ((data == NULL) || (expected == NULL))
+ return 1;
+ return 0 != memcmp (data, expected + offset, size);
+}
+
+
static enum MHD_Result
value_checker (void *cls,
enum MHD_ValueKind kind,
@@ -103,62 +144,68 @@
uint64_t off,
size_t size)
{
- int *want_off = cls;
- int idx = *want_off;
+ unsigned int *idxp = cls;
+ struct expResult *expect = exp_results + *idxp;
(void) kind; /* Unused. Silent compiler warning. */
-#if 0
+#if MHD_DEBUG_PP
fprintf (stderr,
- "VC: `%s' `%s' `%s' `%s' `%.*s' (%d)\n",
- key, filename, content_type, transfer_encoding,
- (int) size,
- data,
+ "VC: `%s' `%s' `%s' `%s' (+%u)`%.*s' (%d)\n",
+ key ? key : "(NULL)",
+ filename ? filename : "(NULL)",
+ content_type ? content_type : "(NULL)",
+ transfer_encoding ? transfer_encoding : "(NULL)",
+ (unsigned int) off,
+ (int) (data ? size : 6),
+ data ? data : "(NULL)",
(int) size);
#endif
+ if (*idxp == (unsigned int) -1)
+ exit (99);
if ( (0 != off) && (0 == size) )
{
- if (NULL == want[idx + 4])
- *want_off = idx + 5;
+ if (NULL == expect->data)
+ *idxp += 1;
return MHD_YES;
}
- if ((idx < 0) ||
- (want[idx] == NULL) ||
- (0 != strcmp (key, want[idx])) ||
- (mismatch (filename, want[idx + 1])) ||
- (mismatch (content_type, want[idx + 2])) ||
- (mismatch (transfer_encoding, want[idx + 3])) ||
- (0 != memcmp (data,
- &want[idx + 4][off],
- size)))
+ if ((expect->key == NULL) ||
+ (0 != strcmp (key, expect->key)) ||
+ (mismatch (filename, expect->fname)) ||
+ (mismatch (content_type, expect->cnt_type)) ||
+ (mismatch (transfer_encoding, expect->tr_enc)) ||
+ (mismatch2 (data, expect->data, off, size)))
{
- *want_off = -1;
+ *idxp = (unsigned int) -1;
fprintf (stderr,
"Failed with: `%s' `%s' `%s' `%s' `%.*s'\n",
- key, filename, content_type, transfer_encoding,
- (int) size,
- data);
+ key ? key : "(NULL)",
+ filename ? filename : "(NULL)",
+ content_type ? content_type : "(NULL)",
+ transfer_encoding ? transfer_encoding : "(NULL)",
+ (int) (data ? size : 6),
+ data ? data : "(NULL)");
fprintf (stderr,
"Wanted: `%s' `%s' `%s' `%s' `%s'\n",
- want[idx],
- want[idx + 1],
- want[idx + 2],
- want[idx + 3],
- want[idx + 4]);
+ expect->key ? expect->key : "(NULL)",
+ expect->fname ? expect->fname : "(NULL)",
+ expect->cnt_type ? expect->cnt_type : "(NULL)",
+ expect->tr_enc ? expect->tr_enc : "(NULL)",
+ expect->data ? expect->data : "(NULL)");
fprintf (stderr,
- "Unexpected result: %d/%d/%d/%d/%d/%d/%d\n",
- (idx < 0),
- (want[idx] == NULL),
- (NULL != want[idx]) && (0 != strcmp (key, want[idx])),
- (mismatch (filename, want[idx + 1])),
- (mismatch (content_type, want[idx + 2])),
- (mismatch (transfer_encoding, want[idx + 3])),
- (0 != memcmp (data, &want[idx + 4][off], size)));
+ "Unexpected result: %d/%d/%d/%d/%d/%d\n",
+ (expect->key == NULL),
+ (NULL != expect->key) && (0 != strcmp (key, expect->key)),
+ (mismatch (filename, expect->fname)),
+ (mismatch (content_type, expect->cnt_type)),
+ (mismatch (transfer_encoding, expect->tr_enc)),
+ (mismatch2 (data, expect->data, off, size)));
return MHD_NO;
}
- if ( ( (NULL == want[idx + 4]) &&
+ if ( ( (NULL == expect->data) &&
(0 == off + size) ) ||
- (off + size == strlen (want[idx + 4])) )
- *want_off = idx + 5;
+ ( (NULL != expect->data) &&
+ (off + size == strlen (expect->data)) ) )
+ *idxp += 1;
return MHD_YES;
}
@@ -168,48 +215,66 @@
unsigned int want_end,
const char *url_data)
{
- struct MHD_Connection connection;
- struct MHD_HTTP_Header header;
- struct MHD_PostProcessor *pp;
- unsigned int want_off = want_start;
- size_t i;
- size_t delta;
- size_t size;
+ size_t step;
+ int errors = 0;
+ const size_t size = strlen (url_data);
+
+ for (step = 1; size >= step; ++step)
+ {
+ struct MHD_Connection connection;
+ struct MHD_HTTP_Header header;
+ struct MHD_PostProcessor *pp;
+ unsigned int want_off = want_start;
+ size_t i;
- memset (&connection, 0, sizeof (struct MHD_Connection));
- memset (&header, 0, sizeof (struct MHD_HTTP_Header));
- connection.headers_received = &header;
- header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
- header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
- header.header_size = MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_TYPE);
- header.value_size = MHD_STATICSTR_LEN_ (
- MHD_HTTP_POST_ENCODING_FORM_URLENCODED);
- header.kind = MHD_HEADER_KIND;
- pp = MHD_create_post_processor (&connection,
- 1024,
- &value_checker,
- &want_off);
- i = 0;
- size = strlen (url_data);
- while (i < size)
- {
- delta = 1 + MHD_random_ () % (size - i);
- MHD_post_process (pp,
- &url_data[i],
- delta);
- i += delta;
- }
- MHD_destroy_post_processor (pp);
- if (want_off != want_end)
- {
- fprintf (stderr,
- "Test failed in line %u: %u != %u\n",
- (unsigned int) __LINE__,
- want_off,
- want_end);
- return 1;
+ memset (&connection, 0, sizeof (struct MHD_Connection));
+ memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+ connection.headers_received = &header;
+ header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+ header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+ header.header_size = MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_TYPE);
+ header.value_size =
+ MHD_STATICSTR_LEN_ (MHD_HTTP_POST_ENCODING_FORM_URLENCODED);
+ header.kind = MHD_HEADER_KIND;
+ pp = MHD_create_post_processor (&connection,
+ 1024,
+ &value_checker,
+ &want_off);
+ if (NULL == pp)
+ {
+ fprintf (stderr, "Failed to create post processor.\n"
+ "Line: %u\n", (unsigned int) __LINE__);
+ exit (50);
+ }
+ for (i = 0; size > i; i += step)
+ {
+ size_t left = size - i;
+ if (MHD_YES != MHD_post_process (pp,
+ &url_data[i],
+ (left > step) ? step : left))
+ {
+ fprintf (stderr, "Failed to process the data.\n"
+ "i: %u. step: %u.\n"
+ "Line: %u\n", (unsigned) i, (unsigned) step,
+ (unsigned int) __LINE__);
+ exit (49);
+ }
+ }
+ MHD_destroy_post_processor (pp);
+ if (want_off != want_end)
+ {
+ fprintf (stderr,
+ "Test failed in line %u.\tStep: %u.\tData: \"%s\"\n" \
+ " Got: %u\tExpected: %u\n",
+ (unsigned int) __LINE__,
+ (unsigned int) step,
+ url_data,
+ want_off,
+ want_end);
+ errors++;
+ }
}
- return 0;
+ return errors;
}
@@ -221,12 +286,68 @@
errorCount += test_urlencoding_case (URL_START,
URL_END,
URL_DATA);
+ errorCount += test_urlencoding_case (URL_ENC_START,
+ URL_ENC_END,
+ URL_ENC_DATA);
errorCount += test_urlencoding_case (URL_NOVALUE1_START,
URL_NOVALUE1_END,
URL_NOVALUE1_DATA);
errorCount += test_urlencoding_case (URL_NOVALUE2_START,
URL_NOVALUE2_END,
URL_NOVALUE2_DATA);
+ errorCount += test_urlencoding_case (URL_NOVALUE3_START,
+ URL_NOVALUE3_END,
+ URL_NOVALUE3_DATA);
+ errorCount += test_urlencoding_case (URL_NOVALUE4_START,
+ URL_NOVALUE4_START, /* No advance */
+ URL_NOVALUE4_DATA);
+ errorCount += test_urlencoding_case (URL_EMPTY_VALUE_START,
+ URL_EMPTY_VALUE_END,
+ URL_EMPTY_VALUE_DATA);
+
+ errorCount += test_urlencoding_case (URL_START,
+ URL_END,
+ URL_DATA "\n");
+ errorCount += test_urlencoding_case (URL_ENC_START,
+ URL_ENC_END,
+ URL_ENC_DATA "\n");
+ errorCount += test_urlencoding_case (URL_NOVALUE1_START,
+ URL_NOVALUE1_END,
+ URL_NOVALUE1_DATA "\n");
+ errorCount += test_urlencoding_case (URL_NOVALUE2_START,
+ URL_NOVALUE2_END,
+ URL_NOVALUE2_DATA "\n");
+ errorCount += test_urlencoding_case (URL_NOVALUE3_START,
+ URL_NOVALUE3_END,
+ URL_NOVALUE3_DATA "\n");
+ errorCount += test_urlencoding_case (URL_NOVALUE4_START,
+ URL_NOVALUE4_END, /* With advance */
+ URL_NOVALUE4_DATA "\n");
+ errorCount += test_urlencoding_case (URL_EMPTY_VALUE_START,
+ URL_EMPTY_VALUE_END,
+ URL_EMPTY_VALUE_DATA "\n");
+
+ errorCount += test_urlencoding_case (URL_START,
+ URL_END,
+ "&&" URL_DATA);
+ errorCount += test_urlencoding_case (URL_ENC_START,
+ URL_ENC_END,
+ "&&" URL_ENC_DATA);
+ errorCount += test_urlencoding_case (URL_NOVALUE1_START,
+ URL_NOVALUE1_END,
+ "&&" URL_NOVALUE1_DATA);
+ errorCount += test_urlencoding_case (URL_NOVALUE2_START,
+ URL_NOVALUE2_END,
+ "&&" URL_NOVALUE2_DATA);
+ errorCount += test_urlencoding_case (URL_NOVALUE3_START,
+ URL_NOVALUE3_END,
+ "&&" URL_NOVALUE3_DATA);
+ errorCount += test_urlencoding_case (URL_NOVALUE4_START,
+ URL_NOVALUE4_START, /* No advance */
+ "&&" URL_NOVALUE4_DATA);
+ errorCount += test_urlencoding_case (URL_EMPTY_VALUE_START,
+ URL_EMPTY_VALUE_END,
+ "&&" URL_EMPTY_VALUE_DATA);
if (0 != errorCount)
fprintf (stderr,
"Test failed in line %u with %u errors\n",
@@ -243,9 +364,9 @@
struct MHD_HTTP_Header header;
struct MHD_PostProcessor *pp;
unsigned int want_off;
- size_t size = strlen (FORM_DATA);
+ size_t size = MHD_STATICSTR_LEN_ (FORM_DATA);
size_t splitpoint;
- char xdata[size + 3];
+ char xdata[MHD_STATICSTR_LEN_ (FORM_DATA) + 3];
/* fill in evil garbage at the beginning */
xdata[0] = '-';
@@ -268,8 +389,28 @@
header.kind = MHD_HEADER_KIND;
pp = MHD_create_post_processor (&connection,
1024, &value_checker, &want_off);
- MHD_post_process (pp, xdata, splitpoint);
- MHD_post_process (pp, &xdata[splitpoint], size - splitpoint);
+ if (NULL == pp)
+ {
+ fprintf (stderr, "Failed to create post processor.\n"
+ "Line: %u\n", (unsigned int) __LINE__);
+ exit (50);
+ }
+ if (MHD_YES != MHD_post_process (pp, xdata, splitpoint))
+ {
+ fprintf (stderr,
+ "Test failed in line %u at point %d\n",
+ (unsigned int) __LINE__,
+ (int) splitpoint);
+ exit (49);
+ }
+ if (MHD_YES != MHD_post_process (pp, &xdata[splitpoint], size - splitpoint))
+ {
+ fprintf (stderr,
+ "Test failed in line %u at point %d\n",
+ (unsigned int) __LINE__,
+ (int) splitpoint);
+ exit (49);
+ }
MHD_destroy_post_processor (pp);
if (want_off != FORM_END)
{
@@ -309,8 +450,29 @@
header.kind = MHD_HEADER_KIND;
pp = MHD_create_post_processor (&connection,
1024, &value_checker, &want_off);
- MHD_post_process (pp, FORM_DATA, splitpoint);
- MHD_post_process (pp, &FORM_DATA[splitpoint], size - splitpoint);
+ if (NULL == pp)
+ {
+ fprintf (stderr, "Failed to create post processor.\n"
+ "Line: %u\n", (unsigned int) __LINE__);
+ exit (50);
+ }
+ if (MHD_YES != MHD_post_process (pp, FORM_DATA, splitpoint))
+ {
+ fprintf (stderr,
+ "Test failed in line %u at point %d\n",
+ (unsigned int) __LINE__,
+ (int) splitpoint);
+ exit (49);
+ }
+ if (MHD_YES != MHD_post_process (pp, &FORM_DATA[splitpoint],
+ size - splitpoint))
+ {
+ fprintf (stderr,
+ "Test failed in line %u at point %d\n",
+ (unsigned int) __LINE__,
+ (int) splitpoint);
+ exit (49);
+ }
MHD_destroy_post_processor (pp);
if (want_off != FORM_END)
{
@@ -347,12 +509,27 @@
header.value_size = strlen (header.value);
pp = MHD_create_post_processor (&connection,
1024, &value_checker, &want_off);
+ if (NULL == pp)
+ {
+ fprintf (stderr, "Failed to create post processor.\n"
+ "Line: %u\n", (unsigned int) __LINE__);
+ exit (50);
+ }
i = 0;
size = strlen (FORM_DATA);
while (i < size)
{
delta = 1 + MHD_random_ () % (size - i);
- MHD_post_process (pp, &FORM_DATA[i], delta);
+ if (MHD_YES != MHD_post_process (pp,
+ &FORM_DATA[i],
+ delta))
+ {
+ fprintf (stderr, "Failed to process the data.\n"
+ "i: %u. delta: %u.\n"
+ "Line: %u\n", (unsigned) i, (unsigned) delta,
+ (unsigned int) __LINE__);
+ exit (49);
+ }
i += delta;
}
MHD_destroy_post_processor (pp);
@@ -389,12 +566,27 @@
header.value_size = strlen (header.value);
pp = MHD_create_post_processor (&connection,
1024, &value_checker, &want_off);
+ if (NULL == pp)
+ {
+ fprintf (stderr, "Failed to create post processor.\n"
+ "Line: %u\n", (unsigned int) __LINE__);
+ exit (50);
+ }
i = 0;
size = strlen (FORM_NESTED_DATA);
while (i < size)
{
delta = 1 + MHD_random_ () % (size - i);
- MHD_post_process (pp, &FORM_NESTED_DATA[i], delta);
+ if (MHD_YES != MHD_post_process (pp,
+ &FORM_NESTED_DATA[i],
+ delta))
+ {
+ fprintf (stderr, "Failed to process the data.\n"
+ "i: %u. delta: %u.\n"
+ "Line: %u\n", (unsigned) i, (unsigned) delta,
+ (unsigned int) __LINE__);
+ exit (49);
+ }
i += delta;
}
MHD_destroy_post_processor (pp);
@@ -409,48 +601,6 @@
}
-static int
-test_empty_value (void)
-{
- struct MHD_Connection connection;
- struct MHD_HTTP_Header header;
- struct MHD_PostProcessor *pp;
- unsigned int want_off = URL_EMPTY_VALUE_START;
- size_t i;
- size_t delta;
- size_t size;
-
- memset (&connection, 0, sizeof (struct MHD_Connection));
- memset (&header, 0, sizeof (struct MHD_HTTP_Header));
- connection.headers_received = &header;
- header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
- header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
- header.header_size = strlen (header.header);
- header.value_size = strlen (header.value);
- header.kind = MHD_HEADER_KIND;
- pp = MHD_create_post_processor (&connection,
- 1024, &value_checker, &want_off);
- i = 0;
- size = strlen (URL_EMPTY_VALUE_DATA);
- while (i < size)
- {
- delta = 1 + MHD_random_ () % (size - i);
- MHD_post_process (pp, &URL_EMPTY_VALUE_DATA[i], delta);
- i += delta;
- }
- MHD_destroy_post_processor (pp);
- if (want_off != URL_EMPTY_VALUE_END)
- {
- fprintf (stderr,
- "Test failed in line %u at offset %d\n",
- (unsigned int) __LINE__,
- (int) want_off);
- return 8;
- }
- return 0;
-}
-
-
static enum MHD_Result
value_checker2 (void *cls,
enum MHD_ValueKind kind,
@@ -494,6 +644,12 @@
1024,
&value_checker2,
NULL);
+ if (NULL == pp)
+ {
+ fprintf (stderr, "Failed to create post processor.\n"
+ "Line: %u\n", (unsigned int) __LINE__);
+ exit (50);
+ }
buf = malloc (i);
if (NULL == buf)
return 1;
@@ -519,6 +675,137 @@
}
+static int
+test_empty_key (void)
+{
+ const char form_data[] = "=abcdef";
+ size_t step;
+ const size_t size = MHD_STATICSTR_LEN_ (form_data);
+
+ for (step = 1; size >= step; ++step)
+ {
+ size_t i;
+ struct MHD_Connection connection;
+ struct MHD_HTTP_Header header;
+ struct MHD_PostProcessor *pp;
+ memset (&connection, 0, sizeof (struct MHD_Connection));
+ memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+
+ connection.headers_received = &header;
+ connection.headers_received_tail = &header;
+ header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+ header.header_size = MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_TYPE);
+ header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+ header.value_size =
+ MHD_STATICSTR_LEN_ (MHD_HTTP_POST_ENCODING_FORM_URLENCODED);
+ header.kind = MHD_HEADER_KIND;
+ pp = MHD_create_post_processor (&connection,
+ 1024, &value_checker2, NULL);
+ if (NULL == pp)
+ {
+ fprintf (stderr, "Failed to create post processor.\n"
+ "Line: %u\n", (unsigned int) __LINE__);
+ exit (50);
+ }
+ for (i = 0; size > i; i += step)
+ {
+ if (MHD_NO != MHD_post_process (pp,
+ form_data + i,
+ (step > size - i) ? (size - i) : step))
+ {
+ fprintf (stderr, "Succeed to process the broken data.\n"
+ "i: %u. step: %u.\n"
+ "Line: %u\n", (unsigned) i, (unsigned) step,
+ (unsigned int) __LINE__);
+ exit (49);
+ }
+ }
+ MHD_destroy_post_processor (pp);
+ }
+ return 0;
+}
+
+
+static int
+test_double_value (void)
+{
+ const char form_data[] = URL_DATA "=abcdef";
+ size_t step;
+ const size_t size = MHD_STATICSTR_LEN_ (form_data);
+ const size_t safe_size = MHD_STATICSTR_LEN_ (URL_DATA);
+
+ for (step = 1; size >= step; ++step)
+ {
+ size_t i;
+ struct MHD_Connection connection;
+ struct MHD_HTTP_Header header;
+ struct MHD_PostProcessor *pp;
+ unsigned int results_off = URL_START;
+ unsigned int results_final = results_off + 1; /* First value is correct */
+ memset (&connection, 0, sizeof (struct MHD_Connection));
+ memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+
+ connection.headers_received = &header;
+ connection.headers_received_tail = &header;
+ header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+ header.header_size = MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_TYPE);
+ header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+ header.value_size =
+ MHD_STATICSTR_LEN_ (MHD_HTTP_POST_ENCODING_FORM_URLENCODED);
+ header.kind = MHD_HEADER_KIND;
+ pp = MHD_create_post_processor (&connection,
+ 1024, &value_checker, &results_off);
+ if (NULL == pp)
+ {
+ fprintf (stderr, "Failed to create post processor.\n"
+ "Line: %u\n", (unsigned int) __LINE__);
+ exit (50);
+ }
+ for (i = 0; size > i; i += step)
+ {
+ if (MHD_NO != MHD_post_process (pp,
+ form_data + i,
+ (step > size - i) ? (size - i) : step))
+ {
+ if (safe_size == i + step)
+ results_final = URL_END;
+ if (safe_size < i + step)
+ {
+ fprintf (stderr, "Succeed to process the broken data.\n"
+ "i: %u. step: %u.\n"
+ "Line: %u\n", (unsigned) i, (unsigned) step,
+ (unsigned int) __LINE__);
+ exit (49);
+ }
+ }
+ else
+ {
+ if (safe_size >= i + step)
+ {
+ fprintf (stderr, "Failed to process the data.\n"
+ "i: %u. step: %u.\n"
+ "Line: %u\n", (unsigned) i, (unsigned) step,
+ (unsigned int) __LINE__);
+ exit (49);
+ }
+ }
+ }
+ MHD_destroy_post_processor (pp);
+ if (results_final != results_off)
+ {
+ fprintf (stderr,
+ "Test failed in line %u.\tStep:%u\n Got: %u\tExpected: %u\n",
+ (unsigned int) __LINE__,
+ (unsigned int) step,
+ results_off,
+ results_final);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
int
main (int argc, char *const *argv)
{
@@ -530,7 +817,8 @@
errorCount += test_urlencoding ();
errorCount += test_multipart ();
errorCount += test_nested_multipart ();
- errorCount += test_empty_value ();
+ errorCount += test_empty_key ();
+ errorCount += test_double_value ();
errorCount += test_overflow ();
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_postprocessor_large.c
^
|
@@ -47,7 +47,7 @@
(void) transfer_encoding; (void) data; (void) off; /* Unused. Silent compiler warning. */
#if 0
fprintf (stderr,
- "VC: %llu %u `%s' `%s' `%s' `%s' `%.*s'\n",
+ "VC: %" PRIu64 " %u `%s' `%s' `%s' `%s' `%.*s'\n",
off, size,
key, filename, content_type, transfer_encoding, size, data);
#endif
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_postprocessor_md.c
^
|
@@ -130,10 +130,10 @@
(void) content_type; (void) transfer_encoding;
#if DEBUG
- printf ("%s\t%s@ %llu\n",
+ printf ("%s\t%s@ %" PRIu64 "\n",
key,
data,
- (unsigned long long) off);
+ off);
#endif
if (0 == strcmp (key, "text"))
{
@@ -235,7 +235,9 @@
struct MHD_PostProcessor *postprocessor;
(void) argc; (void) argv;
+ if (1)
{
+ found = 0;
postprocessor = malloc (sizeof (struct MHD_PostProcessor)
+ 0x1000 + 1);
if (NULL == postprocessor)
@@ -248,15 +250,20 @@
postprocessor->buffer_size = 0x1000;
postprocessor->state = PP_Init;
postprocessor->skip_rn = RN_Inactive;
- MHD_post_process (postprocessor, "xxxx=xxxx", 9);
- MHD_post_process (postprocessor, "&yyyy=yyyy&zzzz=&aaaa=", 22);
- MHD_post_process (postprocessor, "", 0);
+ if (MHD_YES != MHD_post_process (postprocessor, "xxxx=xxxx", 9))
+ exit (1);
+ if (MHD_YES != MHD_post_process (postprocessor, "&yyyy=yyyy&zzzz=&aaaa=",
+ 22))
+ exit (1);
+ if (MHD_YES != MHD_post_process (postprocessor, "", 0))
+ exit (1);
if (MHD_YES !=
MHD_destroy_post_processor (postprocessor))
exit (3);
if (found != 15)
exit (2);
}
+ if (1)
{
found = 0;
postprocessor = malloc (sizeof (struct MHD_PostProcessor)
@@ -271,13 +278,18 @@
postprocessor->buffer_size = 0x1000;
postprocessor->state = PP_Init;
postprocessor->skip_rn = RN_Inactive;
- MHD_post_process (postprocessor, "text=text%2", 11);
- MHD_post_process (postprocessor, "C+text", 6);
- MHD_post_process (postprocessor, "", 0);
- MHD_destroy_post_processor (postprocessor);
+ if (MHD_YES != MHD_post_process (postprocessor, "text=text%2", 11))
+ exit (1);
+ if (MHD_YES != MHD_post_process (postprocessor, "C+text", 6))
+ exit (1);
+ if (MHD_YES != MHD_post_process (postprocessor, "", 0))
+ exit (1);
+ if (MHD_YES != MHD_destroy_post_processor (postprocessor))
+ exit (1);
if (found != 1)
exit (4);
}
+ if (1)
{
found = 0;
postprocessor = malloc (sizeof (struct MHD_PostProcessor)
@@ -294,17 +306,38 @@
postprocessor->skip_rn = RN_Inactive;
{
const char *chunk =
- "x=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxxxxxx%2C%2Cxxxxxx%2Cxxxx%2C%2Cxxxxx%2Cxxxxxxxxx%2C%2Cxxx%2Cxxxxxxxxxxx_xxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxx%2C%2Cx%2Cxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxx%2Cxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxx%2C%2Cx%2Cxx%2C%2Cxxxx%2Cxxx%2C%2Cx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cx%2Cx%2Cxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxx%2Cxxxxxx%2Cxxxxxxxxx%2Cxxxxxxx%2Cxxxxx%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxx%2Cx%2Cxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxx%2Cx%2C%2Cx%2Cxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2Cxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2Cxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxx%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxx%2C%2C%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxx%2Cxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxx%2Cx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cx%2Cxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2Cxxxxxx%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxx%2C%2C%2Cxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxx%2C%2C%2Cxxxxxxxxx%2Cxxxxxxxx%2C"
+ "x=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxx%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2Cxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2Cxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxxxxxx%2C%2Cxxxxxx"
+ "%2Cxxxx%2C%2Cxxxxx%2Cxxxxxxxxx%2C%2Cxxx%2Cxxxxxxxxxxx_xxxxxxxxxxxxxxx"
+ "%2Cxxxxx%2Cxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxx"
+ "%2Cxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxx%2C%2Cxxxxx%2C%2Cx%2Cxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C"
+ "xxxxxxxx%2Cxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxx%2C%2Cxx%2C%2Cx%2Cxx%2C%2Cxxxx%2Cxxx"
+ "%2C%2Cx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cx%2Cx%2Cxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxx%2Cxxxxxx%2Cxxxxxxxxx%2Cxxxxxxx%2Cxxxxx%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxx%2Cx%2Cxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxx%2Cx%2C%2Cx%2Cxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2Cxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2Cxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxx%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxx%2C%2C%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxx%2Cxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxx%2Cx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cx%2Cxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2Cxxxxxx%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxx%2C%2C%2Cxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxx%2C%2C%2Cxxxxxxxxx%2Cxxxxxxxx%2C"
"&y=y&z=z";
- MHD_post_process (postprocessor, chunk, strlen (chunk) );
+ if (MHD_YES != MHD_post_process (postprocessor, chunk, strlen (chunk) ))
+ exit (1);
}
- MHD_post_process (postprocessor, "", 0);
- MHD_destroy_post_processor (postprocessor);
+ if (MHD_YES != MHD_post_process (postprocessor, "", 0))
+ exit (1);
+ if (MHD_YES != MHD_destroy_post_processor (postprocessor))
+ exit (1);
if (found != 1)
exit (5);
}
+ if (1)
{
const char *chunks[] = {
"t=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxx%2C%2Cx%2Cxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxx%2Cxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxx%2C%2Cx%2Cxx%2C%2Cxxxx%2Cxxx%2C%2Cx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cx%2Cx%2Cxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxx%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxx%2Cx%2Cxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2C%2C%2C%2Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%2Cxxxx%2Cxxxxxxxxx",
@@ -328,12 +361,15 @@
for (unsigned i = 0; i < ARRAY_LENGTH (chunks); ++i)
{
const char *chunk = chunks[i];
- MHD_post_process (postprocessor, chunk, strlen (chunk) );
+ if (MHD_YES != MHD_post_process (postprocessor, chunk, strlen (chunk) ))
+ exit (1);
}
- MHD_destroy_post_processor (postprocessor);
+ if (MHD_YES != MHD_destroy_post_processor (postprocessor))
+ exit (1);
if (found != 1)
return 6;
}
+ if (1)
{
const char *chunks[] = {
"XXXXXXXXXXXX=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&XXXXXX=&XXXXXXXXXXXXXX=XXXX+&XXXXXXXXXXXXXXX=XXXXXXXXX&XXXXXXXXXXXXX=XXXX%XX%XXXXXX&XXXXXXXXXXX=XXXXXXXXX&XXXXXXXXXXXXX=XXXXXXXXXX&XXXXXXXXXXXXXXX=XX&XXXXXXXXXXXXXXX=XXXXXXXXX&XXXXXXXXXXXXX=XXXXXX&XXXXXXXXXXX=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
@@ -355,9 +391,11 @@
for (unsigned i = 0; i < ARRAY_LENGTH (chunks); ++i)
{
const char *chunk = chunks[i];
- MHD_post_process (postprocessor, chunk, strlen (chunk) );
+ if (MHD_YES != MHD_post_process (postprocessor, chunk, strlen (chunk) ))
+ exit (1);
}
- MHD_destroy_post_processor (postprocessor);
+ if (MHD_YES != MHD_destroy_post_processor (postprocessor))
+ exit (1);
if (found != 12)
return 7;
}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_response_entries.c
^
|
@@ -0,0 +1,886 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2021 Karlson2k (Evgeny Grin)
+
+ This test_response_entries.c file is in the public domain
+*/
+
+/**
+ * @file test_response_entries.c
+ * @brief Test adding and removing response headers
+ * @author Karlson2k (Evgeny Grin)
+ */
+#include "mhd_options.h"
+#include "platform.h"
+#include <string.h>
+#include <microhttpd.h>
+
+
+static int
+expect_str (const char *actual, const char *expected)
+{
+ if (expected == actual)
+ return ! 0;
+ if (NULL == actual)
+ {
+ fprintf (stderr, "FAILED: result: NULL\n" \
+ " expected: \"%s\"",
+ expected);
+ return 0;
+ }
+ if (NULL == expected)
+ {
+ fprintf (stderr, "FAILED: result: \"%s\"\n" \
+ " expected: NULL",
+ actual);
+ return 0;
+ }
+ if (0 != strcmp (actual, expected))
+ {
+ fprintf (stderr, "FAILED: result: \"%s\"\n" \
+ " expected: \"%s\"",
+ actual, expected);
+ return 0;
+ }
+ return ! 0;
+}
+
+
+int
+main (int argc,
+ char *const *argv)
+{
+ (void) argc;
+ (void) argv; /* Unused. Silence compiler warning. */
+ struct MHD_Response *r;
+
+ r = MHD_create_response_from_buffer (0, "", MHD_RESPMEM_PERSISTENT);
+ if (NULL == r)
+ {
+ fprintf (stderr, "Cannot create a response.\n");
+ return 1;
+ }
+
+ /* ** Test basic header functions ** */
+
+ /* Add first header */
+ if (MHD_YES != MHD_add_response_header (r, "Header-Type-A", "value-a1"))
+ {
+ fprintf (stderr, "Cannot add header A1.\n");
+ MHD_destroy_response (r);
+ return 2;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Header-Type-A"), "value-a1"))
+ {
+ MHD_destroy_response (r);
+ return 2;
+ }
+ /* Add second header with the same name */
+ if (MHD_YES != MHD_add_response_header (r, "Header-Type-A", "value-a2"))
+ {
+ fprintf (stderr, "Cannot add header A2.\n");
+ MHD_destroy_response (r);
+ return 2;
+ }
+ /* Value of the first header must be returned */
+ if (! expect_str (MHD_get_response_header (r, "Header-Type-A"), "value-a1"))
+ {
+ MHD_destroy_response (r);
+ return 2;
+ }
+ /* Remove the first header */
+ if (MHD_YES != MHD_del_response_header (r, "Header-Type-A", "value-a1"))
+ {
+ fprintf (stderr, "Cannot remove header A1.\n");
+ MHD_destroy_response (r);
+ return 2;
+ }
+ /* Value of the ex-second header must be returned */
+ if (! expect_str (MHD_get_response_header (r, "Header-Type-A"), "value-a2"))
+ {
+ MHD_destroy_response (r);
+ return 2;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Header-Type-A", "value-a3"))
+ {
+ fprintf (stderr, "Cannot add header A2.\n");
+ MHD_destroy_response (r);
+ return 2;
+ }
+ /* Value of the ex-second header must be returned */
+ if (! expect_str (MHD_get_response_header (r, "Header-Type-A"), "value-a2"))
+ {
+ MHD_destroy_response (r);
+ return 2;
+ }
+ /* Remove the last header */
+ if (MHD_YES != MHD_del_response_header (r, "Header-Type-A", "value-a3"))
+ {
+ fprintf (stderr, "Cannot add header A2.\n");
+ MHD_destroy_response (r);
+ return 2;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Header-Type-A"), "value-a2"))
+ {
+ MHD_destroy_response (r);
+ return 2;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Header-Type-B"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 2;
+ }
+ if (MHD_NO != MHD_del_response_header (r, "Header-Type-C", "value-a3"))
+ {
+ fprintf (stderr, "Removed non-existing header.\n");
+ MHD_destroy_response (r);
+ return 2;
+ }
+ if (MHD_NO != MHD_del_response_header (r, "Header-Type-A", "value-c"))
+ {
+ fprintf (stderr, "Removed non-existing header value.\n");
+ MHD_destroy_response (r);
+ return 2;
+ }
+
+ /* ** Test "Connection:" header ** */
+
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "a,b,c,d,e"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with simple values.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "a, b, c, d, e"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection", "e,b,c,d,a"))
+ {
+ fprintf (stderr,
+ "Cannot remove \"Connection\" header with simple values.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+
+ if (MHD_YES != MHD_add_response_header (r, "Connection",
+ "i,k,l,m,n,o,p,close"))
+ {
+ fprintf (stderr,
+ "Cannot add \"Connection\" header with simple values and \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, i, k, l, m, n, o, p"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection",
+ "i,k,l,m,n,o,p,close"))
+ {
+ fprintf (stderr,
+ "Cannot remove \"Connection\" header with simple values and \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+
+ if (MHD_YES != MHD_add_response_header (r, "Connection",
+ "1,2,3,4,5,6,7,close"))
+ {
+ fprintf (stderr,
+ "Cannot add \"Connection\" header with simple values and \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, 1, 2, 3, 4, 5, 6, 7"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "8,9,close"))
+ {
+ fprintf (stderr,
+ "Cannot add second \"Connection\" header with simple values and \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, 1, 2, 3, 4, 5, 6, 7, 8, 9"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection", "1,3,5,7,9"))
+ {
+ fprintf (stderr,
+ "Cannot remove part of \"Connection\" header with simple values.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, 2, 4, 6, 8"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "10,12"))
+ {
+ fprintf (stderr,
+ "Cannot add third \"Connection\" header with simple values.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, 2, 4, 6, 8, 10, 12"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection",
+ "12 ,10 ,8 ,close"))
+ {
+ fprintf (stderr,
+ "Cannot remove part of \"Connection\" header with simple values and \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "2, 4, 6"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\" only.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, 2, 4, 6"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection", "4 ,5,6,7 8,"))
+ {
+ fprintf (stderr,
+ "Cannot remove part of \"Connection\" header with simple values and non-existing tokens.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "close, 2"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\" only.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "close, 2"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection",
+ "close, 10, 12, 22, nothing"))
+ {
+ fprintf (stderr,
+ "Cannot remove part of \"Connection\" header with \"close\" and non-existing tokens.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "2"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection", "2"))
+ {
+ fprintf (stderr,
+ "Cannot remove part of \"Connection\" header with simple values and non-existing tokens.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "close"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "close"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection", "close"))
+ {
+ fprintf (stderr, "Cannot remove \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close,other-token"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, other-token"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close, new-token"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, other-token, new-token"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection", "close, new-token"))
+ {
+ fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "other-token"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection", "other-token"))
+ {
+ fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+
+ if (MHD_YES != MHD_add_response_header (r, "Connection",
+ "close, one-long-token"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, one-long-token"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, one-long-token"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection",
+ "one-long-token,close"))
+ {
+ fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "close"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection",
+ "close, additional-token"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, additional-token"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection",
+ "additional-token,close"))
+ {
+ fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+
+
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "token-1,token-2"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "token-1, token-2"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "token-3"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "token-1, token-2, token-3"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, token-1, token-2, token-3"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, token-1, token-2, token-3"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close, token-4"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, token-1, token-2, token-3, token-4"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection", "close"))
+ {
+ fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "token-1, token-2, token-3, token-4"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "close, token-5"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with \"close\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, token-1, token-2, token-3, token-4, token-5"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_NO != MHD_del_response_header (r, "Connection",
+ "non-existing, token-9"))
+ {
+ fprintf (stderr,
+ "Non-existing tokens successfully removed from \"Connection\" header.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, token-1, token-2, token-3, token-4, token-5"))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_NO != MHD_add_response_header (r, "Connection",
+ ",,,,,,,,,,,, ,\t\t\t, , , "))
+ {
+ fprintf (stderr,
+ "Empty token was added successfully to \"Connection\" header.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection",
+ "close, token-1, token-2, token-3, token-4, token-5"))
+ {
+ fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (MHD_NO != MHD_add_response_header (r, "Connection",
+ ",,,,,,,,,,,, ,\t\t\t, , , "))
+ {
+ fprintf (stderr,
+ "Empty token was added successfully to \"Connection\" header.\n");
+ MHD_destroy_response (r);
+ return 3;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 3;
+ }
+
+ if (MHD_NO != MHD_add_response_header (r, "Connection", "keep-Alive"))
+ {
+ fprintf (stderr,
+ "Successfully added \"Connection\" header with \"keep-Alive\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "keep-Alive, Close"))
+ {
+ fprintf (stderr,
+ "Cannot add \"Connection\" header with \"keep-Alive, Close\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "close"))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_NO != MHD_add_response_header (r, "Connection", "keep-Alive"))
+ {
+ fprintf (stderr,
+ "Successfully added \"Connection\" header with \"keep-Alive\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "keep-Alive, Close"))
+ {
+ fprintf (stderr,
+ "Cannot add \"Connection\" header with \"keep-Alive, Close\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "close"))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection",
+ "close, additional-token"))
+ {
+ fprintf (stderr, "Cannot add \"Connection\" header with "
+ "\"close, additional-token\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, additional-token"))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_NO != MHD_add_response_header (r, "Connection", "keep-Alive"))
+ {
+ fprintf (stderr,
+ "Successfully added \"Connection\" header with \"keep-Alive\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, additional-token"))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection",
+ "additional-token,close"))
+ {
+ fprintf (stderr, "Cannot remove tokens from \"Connection\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+
+ if (MHD_YES != MHD_add_response_header (r, "Connection",
+ "Keep-aLive, token-1"))
+ {
+ fprintf (stderr,
+ "Cannot add \"Connection\" header with \"Keep-aLive, token-1\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), "token-1"))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection",
+ "Keep-aLive, token-2"))
+ {
+ fprintf (stderr,
+ "Cannot add \"Connection\" header with \"Keep-aLive, token-2\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "token-1, token-2"))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection",
+ "Keep-aLive, token-3, close"))
+ {
+ fprintf (stderr,
+ "Cannot add \"Connection\" header with \"Keep-aLive, token-3, close\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, token-1, token-2, token-3"))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection",
+ "close"))
+ {
+ fprintf (stderr, "Cannot remove \"close\" tokens from \"Connection\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "token-1, token-2, token-3"))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Connection", "Keep-aLive, close"))
+ {
+ fprintf (stderr,
+ "Cannot add \"Connection\" header with \"Keep-aLive, token-3, close\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"),
+ "close, token-1, token-2, token-3"))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Connection",
+ "close, token-1, Keep-Alive, token-2, token-3"))
+ {
+ fprintf (stderr, "Cannot remove \"close\" tokens from \"Connection\".\n");
+ MHD_destroy_response (r);
+ return 4;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Connection"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 4;
+ }
+
+ if (MHD_YES != MHD_add_response_header (r, "Date",
+ "Wed, 01 Apr 2015 00:00:00 GMT"))
+ {
+ fprintf (stderr,
+ "Cannot add \"Date\" header with \"Wed, 01 Apr 2015 00:00:00 GMT\".\n");
+ MHD_destroy_response (r);
+ return 5;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Date"),
+ "Wed, 01 Apr 2015 00:00:00 GMT"))
+ {
+ MHD_destroy_response (r);
+ return 5;
+ }
+ if (MHD_YES != MHD_add_response_header (r, "Date",
+ "Thu, 01 Apr 2021 00:00:00 GMT"))
+ {
+ fprintf (stderr,
+ "Cannot add \"Date\" header with \"Thu, 01 Apr 2021 00:00:00 GMT\".\n");
+ MHD_destroy_response (r);
+ return 5;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Date"),
+ "Thu, 01 Apr 2021 00:00:00 GMT"))
+ {
+ MHD_destroy_response (r);
+ return 5;
+ }
+ if (MHD_YES != MHD_del_response_header (r, "Date",
+ "Thu, 01 Apr 2021 00:00:00 GMT"))
+ {
+ fprintf (stderr, "Cannot remove \"Date\" header.\n");
+ MHD_destroy_response (r);
+ return 5;
+ }
+ if (! expect_str (MHD_get_response_header (r, "Date"), NULL))
+ {
+ MHD_destroy_response (r);
+ return 5;
+ }
+
+ if (MHD_YES != MHD_add_response_header (r, MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ "chunked"))
+ {
+ fprintf (stderr,
+ "Cannot add \"" MHD_HTTP_HEADER_TRANSFER_ENCODING \
+ "\" header with \"chunked\".\n");
+ MHD_destroy_response (r);
+ return 6;
+ }
+ if (! expect_str (MHD_get_response_header (r,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING),
+ "chunked"))
+ {
+ MHD_destroy_response (r);
+ return 6;
+ }
+ if (MHD_YES != MHD_add_response_header (r, MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ "chunked"))
+ {
+ fprintf (stderr,
+ "Cannot add \"" MHD_HTTP_HEADER_TRANSFER_ENCODING \
+ "\" second header with \"chunked\".\n");
+ MHD_destroy_response (r);
+ return 6;
+ }
+ if (! expect_str (MHD_get_response_header (r,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING),
+ "chunked"))
+ {
+ MHD_destroy_response (r);
+ return 6;
+ }
+ if (MHD_NO != MHD_add_response_header (r, MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ "identity"))
+ {
+ fprintf (stderr,
+ "Successfully added \"" MHD_HTTP_HEADER_TRANSFER_ENCODING \
+ "\" header with \"identity\".\n");
+ MHD_destroy_response (r);
+ return 6;
+ }
+ if (! expect_str (MHD_get_response_header (r,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING),
+ "chunked"))
+ {
+ MHD_destroy_response (r);
+ return 6;
+ }
+ if (MHD_YES != MHD_del_response_header (r, MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ "chunked"))
+ {
+ fprintf (stderr, "Cannot remove \"" MHD_HTTP_HEADER_TRANSFER_ENCODING \
+ "\" header.\n");
+ MHD_destroy_response (r);
+ return 6;
+ }
+ if (! expect_str (MHD_get_response_header (r,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING),
+ NULL))
+ {
+ MHD_destroy_response (r);
+ return 6;
+ }
+
+ MHD_destroy_response (r);
+ printf ("All tests has been successfully passed.\n");
+ return 0;
+}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_sha1.c
^
|
@@ -0,0 +1,418 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
+
+ This test tool 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 test tool 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/test_sha1.h
+ * @brief Unit tests for SHA-1 functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_options.h"
+#include "sha1.h"
+#include "test_helpers.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int verbose = 0; /* verbose level (0-1)*/
+
+
+struct str_with_len
+{
+ const char *const str;
+ const size_t len;
+};
+
+#define D_STR_W_LEN(s) {(s), (sizeof((s)) / sizeof(char)) - 1}
+
+struct data_unit1
+{
+ const struct str_with_len str_l;
+ const uint8_t digest[SHA1_DIGEST_SIZE];
+};
+
+static const struct data_unit1 data_units1[] = {
+ {D_STR_W_LEN ("abc"),
+ {0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71,
+ 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D}},
+ {D_STR_W_LEN ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"),
+ {0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 0x4A, 0xA1,
+ 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1}},
+ {D_STR_W_LEN (""),
+ {0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef,
+ 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09}},
+ {D_STR_W_LEN ("The quick brown fox jumps over the lazy dog"),
+ {0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1,
+ 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12}},
+ {D_STR_W_LEN ("1234567890!@~%&$@#{}[]\\/!?`."),
+ {0xa7, 0x08, 0x9e, 0x3d, 0xe1, 0x6b, 0x63, 0xa3, 0x2a, 0x43, 0xa5, 0xe7,
+ 0xf3, 0xb5, 0x4f, 0x80, 0x6a, 0xc8, 0x4f, 0x53}},
+ {D_STR_W_LEN ("Simple string."),
+ {0x6c, 0x6c, 0x9c, 0xef, 0x53, 0xff, 0x22, 0x39, 0x0c, 0x54, 0xc7, 0xba,
+ 0x6d, 0x98, 0xe0, 0xd5, 0x6c, 0x24, 0x0d, 0x55}},
+ {D_STR_W_LEN ("abcdefghijklmnopqrstuvwxyz"),
+ {0x32, 0xd1, 0x0c, 0x7b, 0x8c, 0xf9, 0x65, 0x70, 0xca, 0x04, 0xce, 0x37,
+ 0xf2, 0xa1, 0x9d, 0x84, 0x24, 0x0d, 0x3a, 0x89}},
+ {D_STR_W_LEN ("zyxwvutsrqponMLKJIHGFEDCBA"),
+ {0x59, 0x0f, 0xc8, 0xea, 0xde, 0xa2, 0x78, 0x65, 0x5a, 0xf2, 0xa1, 0xe5,
+ 0xb4, 0xc9, 0x61, 0xa4, 0x3a, 0xc3, 0x6a, 0x83}},
+ {D_STR_W_LEN ("abcdefghijklmnopqrstuvwxyzzyxwvutsrqponMLKJIHGFEDCBA"
+ "abcdefghijklmnopqrstuvwxyzzyxwvutsrqponMLKJIHGFEDCBA"),
+ {0xa7, 0x87, 0x4c, 0x16, 0xc0, 0x4e, 0xd6, 0x24, 0x5f, 0x25, 0xbe, 0x06,
+ 0x7b, 0x1b, 0x6b, 0xaf, 0xf4, 0x0e, 0x74, 0x0b}},
+};
+
+static const size_t units1_num = sizeof(data_units1) / sizeof(data_units1[0]);
+
+struct bin_with_len
+{
+ const uint8_t bin[512];
+ const size_t len;
+};
+
+struct data_unit2
+{
+ const struct bin_with_len bin_l;
+ const uint8_t digest[SHA1_DIGEST_SIZE];
+};
+
+/* Size must be less than 512 bytes! */
+static const struct data_unit2 data_units2[] = {
+ { { {97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122}, 26}, /* a..z ASCII sequence */
+ {0x32, 0xd1, 0x0c, 0x7b, 0x8c, 0xf9, 0x65, 0x70, 0xca, 0x04, 0xce, 0x37,
+ 0xf2, 0xa1, 0x9d, 0x84, 0x24, 0x0d, 0x3a, 0x89}},
+ { { {65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65},
+ 72 }, /* 'A' x 72 times */
+ {0x41, 0xf9, 0x07, 0x05, 0x04, 0xf9, 0xc8, 0x1a, 0xbf, 0xbb, 0x61, 0x4d,
+ 0xaa, 0xec, 0x3b, 0x26, 0xa2, 0xf9, 0x23, 0x7e}},
+ { { {19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42,43, 44, 45, 46, 47, 48, 49, 50, 51, 52,53, 54,
+ 55, 56, 57, 58, 59, 60,61, 62, 63, 64, 65, 66, 67,68, 69, 70, 71, 72,
+ 73}, 55}, /* 19..73 sequence */
+ {0xf2, 0x50, 0xb2, 0x79, 0x62, 0xcc, 0xff, 0x4b, 0x1b, 0x61, 0x4a, 0x6f,
+ 0x80, 0xec, 0x9b, 0x4d, 0xd0, 0xc0, 0xfb, 0x7b}},
+ { { {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31,32, 33, 34, 35, 36, 37, 38, 39, 40, 41,42, 43,
+ 44, 45, 46, 47, 48, 49,50, 51, 52, 53, 54, 55, 56,57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 67, 68, 69}, 63}, /* 7..69 sequence */
+ {0xf0, 0xa3, 0xfc, 0x4b, 0x90, 0x6f, 0x1b, 0x41, 0x68, 0xc8, 0x3e, 0x05,
+ 0xb0, 0xc5, 0xac, 0xb7, 0x3d, 0xcd, 0x6b, 0x0f}},
+ { { {38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61,62, 63, 64, 65, 66, 67, 68, 69, 70, 71,72, 73,
+ 74, 75, 76, 77, 78, 79,80, 81, 82, 83, 84, 85, 86,87, 88, 89, 90, 91,
+ 92}, 55}, /* 38..92 sequence */
+ {0x93, 0xa4, 0x9c, 0x5f, 0xda, 0x22, 0x63, 0x73, 0x85, 0xeb, 0x70, 0xd2,
+ 0x00, 0x52, 0x0c, 0xb0, 0x2e, 0x86, 0x9b, 0xa0 }},
+ { { {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,37, 38, 39,
+ 40, 41, 42, 43, 44, 45,46, 47, 48, 49, 50, 51, 52,53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,71, 72},72}, /* 1..72 sequence */
+ {0x8b, 0x30, 0xd3, 0x41, 0x89, 0xb6, 0x1b, 0x66, 0x5a, 0x1a, 0x9a, 0x51,
+ 0x64, 0x93, 0xab, 0x5e, 0x78, 0x81, 0x52, 0xb5}},
+ { { {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,86, 87, 88, 89, 90, 91, 92,
+ 93, 94, 95, 96, 97, 98, 99, 100,101, 102, 103, 104, 105, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114,115, 116, 117, 118, 119, 120, 121, 122,
+ 123, 124, 125, 126, 127,128, 129, 130, 131, 132, 133, 134, 135, 136,
+ 137, 138, 139, 140,141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154,155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167,168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
+ 179, 180,181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194,195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
+ 207,208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,221,
+ 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,235,
+ 236, 237, 238, 239, 240,241, 242, 243, 244, 245, 246, 247,248, 249, 250,
+ 251, 252, 253, 254, 255}, 256}, /* 0..255 sequence */
+ {0x49, 0x16, 0xd6, 0xbd, 0xb7, 0xf7, 0x8e, 0x68, 0x03, 0x69, 0x8c, 0xab,
+ 0x32, 0xd1, 0x58, 0x6e, 0xa4, 0x57, 0xdf, 0xc8}},
+ { { {199, 198, 197, 196, 195, 194, 193, 192, 191, 190, 189, 188, 187, 186,185,
+ 184, 183, 182, 181, 180,179, 178, 177, 176, 175, 174, 173,172, 171, 170,
+ 169, 168, 167, 166,165, 164, 163, 162, 161, 160,159, 158, 157, 156, 155,
+ 154, 153, 152, 151, 150, 149, 148, 147, 146,145, 144, 143, 142, 141,
+ 140,139}, 61}, /* 199..139 sequence */
+ {0xb3, 0xec, 0x61, 0x3c, 0xf7, 0x36, 0x57, 0x94, 0x61, 0xdb, 0xb2, 0x16,
+ 0x75, 0xfe, 0x34, 0x60, 0x99, 0x94, 0xb5, 0xfc}},
+ { { {255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242,
+ 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228,
+ 227, 226, 225, 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, 214,
+ 213, 212, 211, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200,
+ 199, 198, 197, 196, 195, 194, 193, 192, 191, 190, 189, 188, 187, 186,
+ 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 173, 172,
+ 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, 159, 158,
+ 157, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144,
+ 143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 133, 132, 131, 130,
+ 129, 128, 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116,
+ 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102,
+ 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85,
+ 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67,
+ 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49,
+ 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31,
+ 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,
+ 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, 255}, /* 255..1 sequence */
+ {0x99, 0x1d, 0xc6, 0x95, 0xe1, 0xaa, 0xcc, 0xc9, 0x35, 0x60, 0xbe, 0xe3,
+ 0xe2, 0x41, 0x2b, 0xa7, 0xab, 0x49, 0x32, 0xf7}},
+ { { {41, 35, 190, 132, 225, 108, 214, 174, 82, 144, 73, 241, 241, 187, 233,
+ 235, 179, 166, 219, 60, 135, 12, 62, 153, 36, 94, 13, 28, 6, 183, 71,
+ 222, 179, 18, 77, 200, 67, 187, 139, 166, 31, 3, 90, 125, 9, 56, 37, 31,
+ 93, 212, 203, 252, 150, 245, 69, 59, 19, 13, 137, 10, 28, 219, 174, 50,
+ 32, 154, 80, 238, 64, 120, 54, 253, 18, 73, 50, 246, 158, 125, 73, 220,
+ 173, 79, 20, 242, 68, 64, 102, 208, 107, 196, 48, 183, 50, 59, 161, 34,
+ 246, 34, 145, 157, 225, 139, 31, 218, 176, 202, 153, 2, 185, 114, 157,
+ 73, 44, 128, 126, 197, 153, 213, 233, 128, 178, 234, 201, 204, 83, 191,
+ 103, 214, 191, 20, 214, 126, 45, 220, 142, 102, 131, 239, 87, 73, 97,
+ 255, 105, 143, 97, 205, 209, 30, 157, 156, 22, 114, 114, 230, 29, 240,
+ 132, 79, 74, 119, 2, 215, 232, 57, 44, 83, 203, 201, 18, 30, 51, 116,
+ 158, 12, 244, 213, 212, 159, 212, 164, 89, 126, 53, 207, 50, 34, 244,
+ 204, 207, 211, 144, 45, 72, 211, 143, 117, 230, 217, 29, 42, 229, 192,
+ 247, 43, 120, 129, 135, 68, 14, 95, 80, 0, 212, 97, 141, 190, 123, 5,
+ 21, 7, 59, 51, 130, 31, 24, 112, 146, 218, 100, 84, 206, 177, 133, 62,
+ 105, 21, 248, 70, 106, 4, 150, 115, 14, 217, 22, 47, 103, 104, 212, 247,
+ 74, 74, 208, 87, 104}, 255}, /* pseudo-random data */
+ {0x9e, 0xb6, 0xce, 0x48, 0xf4, 0x6e, 0x9c, 0xf4, 0x1b, 0x4f, 0x9f, 0x66,
+ 0xdd, 0xe8, 0x41, 0x01, 0x71, 0xf6, 0xf5, 0xd6}}
+};
+
+static const size_t units2_num = sizeof(data_units2) / sizeof(data_units2[0]);
+
+
+/*
+ * Helper functions
+ */
+
+/**
+ * Print bin as hex
+ *
+ * @param bin binary data
+ * @param len number of bytes in bin
+ * @param hex pointer to len*2+1 bytes buffer
+ */
+static void
+bin2hex (const uint8_t *bin,
+ size_t len,
+ char *hex)
+{
+ while (len-- > 0)
+ {
+ unsigned int b1, b2;
+ b1 = (*bin >> 4) & 0xf;
+ *hex++ = (char) ((b1 > 9) ? (b1 + 'A' - 10) : (b1 + '0'));
+ b2 = *bin++ & 0xf;
+ *hex++ = (char) ((b2 > 9) ? (b2 + 'A' - 10) : (b2 + '0'));
+ }
+ *hex = 0;
+}
+
+
+static int
+check_result (const char *test_name,
+ unsigned int check_num,
+ const uint8_t calculated[SHA1_DIGEST_SIZE],
+ const uint8_t expected[SHA1_DIGEST_SIZE])
+{
+ int failed = memcmp (calculated, expected, SHA1_DIGEST_SIZE);
+ check_num++; /* Print 1-based numbers */
+ if (failed)
+ {
+ char calc_str[SHA1_DIGEST_STRING_SIZE];
+ char expc_str[SHA1_DIGEST_STRING_SIZE];
+ bin2hex (calculated, SHA1_DIGEST_SIZE, calc_str);
+ bin2hex (expected, SHA1_DIGEST_SIZE, expc_str);
+ fprintf (stderr,
+ "FAILED: %s check %u: calculated digest %s, expected digest %s.\n",
+ test_name, check_num, calc_str, expc_str);
+ fflush (stderr);
+ }
+ else if (verbose)
+ {
+ char calc_str[SHA1_DIGEST_STRING_SIZE];
+ bin2hex (calculated, SHA1_DIGEST_SIZE, calc_str);
+ printf (
+ "PASSED: %s check %u: calculated digest %s matches expected digest.\n",
+ test_name, check_num, calc_str);
+ fflush (stdout);
+ }
+ return failed ? 1 : 0;
+}
+
+
+/*
+ * Tests
+ */
+
+/* Calculated SHA-256 as one pass for whole data */
+static int
+test1_str (void)
+{
+ int num_failed = 0;
+ unsigned int i;
+
+ for (i = 0; i < units1_num; i++)
+ {
+ struct sha1_ctx ctx;
+ uint8_t digest[SHA1_DIGEST_SIZE];
+
+ MHD_SHA1_init (&ctx);
+ MHD_SHA1_update (&ctx, (const uint8_t*) data_units1[i].str_l.str,
+ data_units1[i].str_l.len);
+ MHD_SHA1_finish (&ctx, digest);
+ num_failed += check_result (__FUNCTION__, i, digest,
+ data_units1[i].digest);
+ }
+ return num_failed;
+}
+
+
+static int
+test1_bin (void)
+{
+ int num_failed = 0;
+ unsigned int i;
+
+ for (i = 0; i < units2_num; i++)
+ {
+ struct sha1_ctx ctx;
+ uint8_t digest[SHA1_DIGEST_SIZE];
+
+ MHD_SHA1_init (&ctx);
+ MHD_SHA1_update (&ctx, data_units2[i].bin_l.bin,
+ data_units2[i].bin_l.len);
+ MHD_SHA1_finish (&ctx, digest);
+ num_failed += check_result (__FUNCTION__, i, digest,
+ data_units2[i].digest);
+ }
+ return num_failed;
+}
+
+
+/* Calculated SHA-256 as two iterations for whole data */
+static int
+test2_str (void)
+{
+ int num_failed = 0;
+ unsigned int i;
+
+ for (i = 0; i < units1_num; i++)
+ {
+ struct sha1_ctx ctx;
+ uint8_t digest[SHA1_DIGEST_SIZE];
+ size_t part_s = data_units1[i].str_l.len / 4;
+
+ MHD_SHA1_init (&ctx);
+ MHD_SHA1_update (&ctx, (const uint8_t*) data_units1[i].str_l.str, part_s);
+ MHD_SHA1_update (&ctx, (const uint8_t*) data_units1[i].str_l.str + part_s,
+ data_units1[i].str_l.len - part_s);
+ MHD_SHA1_finish (&ctx, digest);
+ num_failed += check_result (__FUNCTION__, i, digest,
+ data_units1[i].digest);
+ }
+ return num_failed;
+}
+
+
+static int
+test2_bin (void)
+{
+ int num_failed = 0;
+ unsigned int i;
+
+ for (i = 0; i < units2_num; i++)
+ {
+ struct sha1_ctx ctx;
+ uint8_t digest[SHA1_DIGEST_SIZE];
+ size_t part_s = data_units2[i].bin_l.len * 2 / 3;
+
+ MHD_SHA1_init (&ctx);
+ MHD_SHA1_update (&ctx, data_units2[i].bin_l.bin, part_s);
+ MHD_SHA1_update (&ctx, data_units2[i].bin_l.bin + part_s,
+ data_units2[i].bin_l.len - part_s);
+ MHD_SHA1_finish (&ctx, digest);
+ num_failed += check_result (__FUNCTION__, i, digest,
+ data_units2[i].digest);
+ }
+ return num_failed;
+}
+
+
+/* Use data set number 7 as it has the longest sequence */
+#define DATA_POS 6
+#define MAX_OFFSET 31
+
+static int
+test_unaligned (void)
+{
+ int num_failed = 0;
+ unsigned int offset;
+ uint8_t *buf;
+ uint8_t *digest_buf;
+
+ const struct data_unit2 *const tdata = data_units2 + DATA_POS;
+
+ buf = malloc (tdata->bin_l.len + MAX_OFFSET);
+ digest_buf = malloc (SHA1_DIGEST_SIZE + MAX_OFFSET);
+ if ((NULL == buf) || (NULL == digest_buf))
+ exit (99);
+
+ for (offset = MAX_OFFSET; offset >= 1; --offset)
+ {
+ struct sha1_ctx ctx;
+ uint8_t *unaligned_digest;
+ uint8_t *unaligned_buf;
+
+ unaligned_buf = buf + offset;
+ memcpy (unaligned_buf, tdata->bin_l.bin, tdata->bin_l.len);
+ unaligned_digest = digest_buf + MAX_OFFSET - offset;
+ memset (unaligned_digest, 0, SHA1_DIGEST_SIZE);
+
+ MHD_SHA1_init (&ctx);
+ MHD_SHA1_update (&ctx, unaligned_buf, tdata->bin_l.len);
+ MHD_SHA1_finish (&ctx, unaligned_digest);
+ num_failed += check_result (__FUNCTION__, MAX_OFFSET - offset,
+ unaligned_digest, tdata->digest);
+ }
+ free (digest_buf);
+ free (buf);
+ return num_failed;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int num_failed = 0;
+ (void) has_in_name; /* Mute compiler warning. */
+ if (has_param (argc, argv, "-v") || has_param (argc, argv, "--verbose"))
+ verbose = 1;
+
+ num_failed += test1_str ();
+ num_failed += test1_bin ();
+
+ num_failed += test2_str ();
+ num_failed += test2_bin ();
+
+ num_failed += test_unaligned ();
+
+ return num_failed ? 1 : 0;
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_sha256.c
^
|
@@ -27,6 +27,7 @@
#include "sha256.h"
#include "test_helpers.h"
#include <stdio.h>
+#include <stdlib.h>
static int verbose = 0; /* verbose level (0-1)*/
@@ -310,7 +311,7 @@
char calc_str[SHA256_DIGEST_STRING_SIZE];
bin2hex (calculated, SHA256_DIGEST_SIZE, calc_str);
printf (
- "PASSED: %s check %u: calculated digest %s match expected digest.\n",
+ "PASSED: %s check %u: calculated digest %s matches expected digest.\n",
test_name, check_num, calc_str);
fflush (stdout);
}
@@ -337,7 +338,7 @@
MHD_SHA256_init (&ctx);
MHD_SHA256_update (&ctx, (const uint8_t*) data_units1[i].str_l.str,
data_units1[i].str_l.len);
- sha256_finish (&ctx, digest);
+ MHD_SHA256_finish (&ctx, digest);
num_failed += check_result (__FUNCTION__, i, digest,
data_units1[i].digest);
}
@@ -359,7 +360,7 @@
MHD_SHA256_init (&ctx);
MHD_SHA256_update (&ctx, data_units2[i].bin_l.bin,
data_units2[i].bin_l.len);
- sha256_finish (&ctx, digest);
+ MHD_SHA256_finish (&ctx, digest);
num_failed += check_result (__FUNCTION__, i, digest,
data_units2[i].digest);
}
@@ -384,7 +385,7 @@
MHD_SHA256_update (&ctx, (const uint8_t*) data_units1[i].str_l.str, part_s);
MHD_SHA256_update (&ctx, (const uint8_t*) data_units1[i].str_l.str + part_s,
data_units1[i].str_l.len - part_s);
- sha256_finish (&ctx, digest);
+ MHD_SHA256_finish (&ctx, digest);
num_failed += check_result (__FUNCTION__, i, digest,
data_units1[i].digest);
}
@@ -408,7 +409,7 @@
MHD_SHA256_update (&ctx, data_units2[i].bin_l.bin, part_s);
MHD_SHA256_update (&ctx, data_units2[i].bin_l.bin + part_s,
data_units2[i].bin_l.len - part_s);
- sha256_finish (&ctx, digest);
+ MHD_SHA256_finish (&ctx, digest);
num_failed += check_result (__FUNCTION__, i, digest,
data_units2[i].digest);
}
@@ -416,6 +417,48 @@
}
+/* Use data set number 7 as it has the longest sequence */
+#define DATA_POS 6
+#define MAX_OFFSET 31
+
+static int
+test_unaligned (void)
+{
+ int num_failed = 0;
+ unsigned int offset;
+ uint8_t *buf;
+ uint8_t *digest_buf;
+
+ const struct data_unit2 *const tdata = data_units2 + DATA_POS;
+
+ buf = malloc (tdata->bin_l.len + MAX_OFFSET);
+ digest_buf = malloc (SHA256_DIGEST_SIZE + MAX_OFFSET);
+ if ((NULL == buf) || (NULL == digest_buf))
+ exit (99);
+
+ for (offset = MAX_OFFSET; offset >= 1; --offset)
+ {
+ struct sha256_ctx ctx;
+ uint8_t *unaligned_digest;
+ uint8_t *unaligned_buf;
+
+ unaligned_buf = buf + offset;
+ memcpy (unaligned_buf, tdata->bin_l.bin, tdata->bin_l.len);
+ unaligned_digest = digest_buf + MAX_OFFSET - offset;
+ memset (unaligned_digest, 0, SHA256_DIGEST_SIZE);
+
+ MHD_SHA256_init (&ctx);
+ MHD_SHA256_update (&ctx, unaligned_buf, tdata->bin_l.len);
+ MHD_SHA256_finish (&ctx, unaligned_digest);
+ num_failed += check_result (__FUNCTION__, MAX_OFFSET - offset,
+ unaligned_digest, tdata->digest);
+ }
+ free (digest_buf);
+ free (buf);
+ return num_failed;
+}
+
+
int
main (int argc, char *argv[])
{
@@ -430,5 +473,7 @@
num_failed += test2_str ();
num_failed += test2_bin ();
+ num_failed += test_unaligned ();
+
return num_failed ? 1 : 0;
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_shutdown_select.c
^
|
@@ -22,10 +22,10 @@
* @file microhttpd/test_shutdown_select.c
* @brief Test whether shutdown socket triggers select()/poll()
* @details On some platforms shutting down the socket in one thread
- * trigger select() or poll() waiting for this socket in
- * other thread. libmicrohttpd depend on this behavior on
- * those platforms. This program check whether select()
- * and poll() (if available) works as expected.
+ * triggers select() or poll() waiting for this socket in
+ * other thread. libmicrohttpd depends on this behavior on
+ * these platforms. This program check whether select()
+ * and poll() (if available) work as expected.
* @author Karlson2k (Evgeny Grin)
*/
@@ -187,7 +187,7 @@
}
#ifdef MHD_WINSOCK_SOCKETS
- if (0 != ioctlsocket (fd, FIONBIO, &flags))
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
{
fprintf (stderr, "Failed to make socket non-blocking: %u\n",
(unsigned) sock_errno);
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_str.c
^
|
@@ -3315,6 +3315,1358 @@
int
+check_str_from_uint16 (void)
+{
+ size_t t_failed = 0;
+ size_t i, j;
+ char buf[70];
+ const char *erase =
+ "-@=sd#+&(pdiren456qwe#@C3S!DAS45AOIPUQWESAdFzxcv1s*()&#$%34`"
+ "32452d098poiden45SADFFDA3S4D3SDFdfgsdfgsSADFzxdvs$*()द`"
+ "adsf##$$@&*^%*^&56qwe#3C@S!DAScFAOIP$#%#$Ad1zs3v1$*()ӌ`";
+ static const size_t n_checks = sizeof(dstrs_w_values)
+ / sizeof(dstrs_w_values[0]);
+ int c_failed[n_checks];
+
+ memset (c_failed, 0, sizeof(c_failed));
+
+ for (j = 0; j < locale_name_count; j++)
+ {
+ set_test_locale (j); /* setlocale() can be slow! */
+ for (i = 0; i < n_checks; i++)
+ {
+ const struct str_with_value *const t = dstrs_w_values + i;
+ size_t b_size;
+ size_t rs;
+
+ if (c_failed[i])
+ continue; /* skip already failed checks */
+
+ if (t->str.len < t->num_of_digt)
+ {
+ fprintf (stderr,
+ "ERROR: dstrs_w_values[%u] has wrong num_of_digt (%u): num_of_digt is expected"
+ " to be less or equal to str.len (%u).\n",
+ (unsigned int) i, (unsigned int) t->num_of_digt, (unsigned
+ int) t->str.
+ len);
+ return -1;
+ }
+ if ('0' == t->str.str[0])
+ continue; /* Skip strings prefixed with zeros */
+ if (t->num_of_digt != t->str.len)
+ continue; /* Skip strings with suffixes */
+ if (UINT16_MAX < t->val)
+ continue; /* Too large value to convert */
+ if (sizeof(buf) < t->str.len + 1)
+ {
+ fprintf (stderr,
+ "ERROR: dstrs_w_values[%u] has too long (%u) string, "
+ "size of 'buf' should be increased.\n",
+ (unsigned int) i, (unsigned int) t->str.len);
+ return -1;
+ }
+ for (b_size = 0; b_size <= t->str.len + 1; ++b_size)
+ {
+ /* fill buffer with pseudo-random values */
+ memcpy (buf, erase, sizeof(buf));
+
+ rs = MHD_uint16_to_str (t->val, buf, b_size);
+
+ if (t->num_of_digt > b_size)
+ {
+ /* Must fail, buffer is too small for result */
+ if (0 != rs)
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint16_to_str(%" PRIu64 ", -> buf,"
+ " %d) returned %" PRIuPTR
+ ", while expecting 0."
+ " Locale: %s\n", t->val, (int) b_size, (intptr_t) rs,
+ get_current_locale_str ());
+ }
+ }
+ else
+ {
+ if (t->num_of_digt != rs)
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint16_to_str(%" PRIu64 ", -> buf,"
+ " %d) returned %" PRIuPTR
+ ", while expecting %d."
+ " Locale: %s\n", t->val, (int) b_size, (intptr_t) rs,
+ (int) t->num_of_digt, get_current_locale_str ());
+ }
+ else if (0 != memcmp (buf, t->str.str, t->num_of_digt))
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint16_to_str(%" PRIu64 ", -> \"%.*s\","
+ " %d) returned %" PRIuPTR "."
+ " Locale: %s\n", t->val, (int) rs, buf, (int) b_size,
+ (intptr_t) rs, get_current_locale_str ());
+ }
+ else if (0 != memcmp (buf + rs, erase + rs, sizeof(buf) - rs))
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint16_to_str(%" PRIu64 ", -> \"%.*s\","
+ " %d) returned %" PRIuPTR
+ " and touched data after the resulting string."
+ " Locale: %s\n", t->val, (int) rs, buf, (int) b_size,
+ (intptr_t) rs, get_current_locale_str ());
+ }
+ }
+ }
+ if ((verbose > 1) && (j == locale_name_count - 1) && ! c_failed[i])
+ printf ("PASSED: MHD_uint16_to_str(%" PRIu64 ", -> \"%.*s\", %d) "
+ "== %" PRIuPTR "\n",
+ t->val, (int) rs, buf, (int) b_size - 1, (intptr_t) rs);
+ }
+ }
+ return t_failed;
+}
+
+
+int
+check_str_from_uint64 (void)
+{
+ size_t t_failed = 0;
+ size_t i, j;
+ char buf[70];
+ const char *erase =
+ "-@=sd#+&(pdirenDSFGSe#@C&S!DAS*!AOIPUQWESAdFzxcvSs*()&#$%KH`"
+ "32452d098poiden45SADFFDA3S4D3SDFdfgsdfgsSADFzxdvs$*()द`"
+ "adsf##$$@&*^%*^&56qwe#3C@S!DAScFAOIP$#%#$Ad1zs3v1$*()ӌ`";
+ static const size_t n_checks = sizeof(dstrs_w_values)
+ / sizeof(dstrs_w_values[0]);
+ int c_failed[n_checks];
+
+ memset (c_failed, 0, sizeof(c_failed));
+
+ for (j = 0; j < locale_name_count; j++)
+ {
+ set_test_locale (j); /* setlocale() can be slow! */
+ for (i = 0; i < n_checks; i++)
+ {
+ const struct str_with_value *const t = dstrs_w_values + i;
+ size_t b_size;
+ size_t rs;
+
+ if (c_failed[i])
+ continue; /* skip already failed checks */
+
+ if (t->str.len < t->num_of_digt)
+ {
+ fprintf (stderr,
+ "ERROR: dstrs_w_values[%u] has wrong num_of_digt (%u): num_of_digt is expected"
+ " to be less or equal to str.len (%u).\n",
+ (unsigned int) i, (unsigned int) t->num_of_digt, (unsigned
+ int) t->str.
+ len);
+ return -1;
+ }
+ if ('0' == t->str.str[0])
+ continue; /* Skip strings prefixed with zeros */
+ if (t->num_of_digt != t->str.len)
+ continue; /* Skip strings with suffixes */
+ if (sizeof(buf) < t->str.len + 1)
+ {
+ fprintf (stderr,
+ "ERROR: dstrs_w_values[%u] has too long (%u) string, "
+ "size of 'buf' should be increased.\n",
+ (unsigned int) i, (unsigned int) t->str.len);
+ return -1;
+ }
+ for (b_size = 0; b_size <= t->str.len + 1; ++b_size)
+ {
+ /* fill buffer with pseudo-random values */
+ memcpy (buf, erase, sizeof(buf));
+
+ rs = MHD_uint64_to_str (t->val, buf, b_size);
+
+ if (t->num_of_digt > b_size)
+ {
+ /* Must fail, buffer is too small for result */
+ if (0 != rs)
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint64_to_str(%" PRIu64 ", -> buf,"
+ " %d) returned %" PRIuPTR
+ ", while expecting 0."
+ " Locale: %s\n", t->val, (int) b_size, (intptr_t) rs,
+ get_current_locale_str ());
+ }
+ }
+ else
+ {
+ if (t->num_of_digt != rs)
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint64_to_str(%" PRIu64 ", -> buf,"
+ " %d) returned %" PRIuPTR
+ ", while expecting %d."
+ " Locale: %s\n", t->val, (int) b_size, (intptr_t) rs,
+ (int) t->num_of_digt, get_current_locale_str ());
+ }
+ else if (0 != memcmp (buf, t->str.str, t->num_of_digt))
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint64_to_str(%" PRIu64 ", -> \"%.*s\","
+ " %d) returned %" PRIuPTR "."
+ " Locale: %s\n", t->val, (int) rs, buf, (int) b_size,
+ (intptr_t) rs, get_current_locale_str ());
+ }
+ else if (0 != memcmp (buf + rs, erase + rs, sizeof(buf) - rs))
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint64_to_str(%" PRIu64 ", -> \"%.*s\","
+ " %d) returned %" PRIuPTR
+ " and touched data after the resulting string."
+ " Locale: %s\n", t->val, (int) rs, buf, (int) b_size,
+ (intptr_t) rs, get_current_locale_str ());
+ }
+ }
+ }
+ if ((verbose > 1) && (j == locale_name_count - 1) && ! c_failed[i])
+ printf ("PASSED: MHD_uint64_to_str(%" PRIu64 ", -> \"%.*s\", %d) "
+ "== %" PRIuPTR "\n",
+ t->val, (int) rs, buf, (int) b_size - 1, (intptr_t) rs);
+ }
+ }
+ return t_failed;
+}
+
+
+int
+check_strx_from_uint32 (void)
+{
+ size_t t_failed = 0;
+ size_t i, j;
+ char buf[70];
+ const char *erase =
+ "jrlkjssfhjfvrjntJHLJ$@%$#adsfdkj;k$##$%#$%FGDF%$#^FDFG%$#$D`"
+ ";skjdhjflsdkjhdjfalskdjhdfalkjdhf$%##%$$#%FSDGFSDDGDFSSDSDF`"
+ "#5#$%#$#$DFSFDDFSGSDFSDF354FDDSGFDFfdssfddfswqemn,.zxih,.sx`";
+ static const size_t n_checks = sizeof(xdstrs_w_values)
+ / sizeof(xdstrs_w_values[0]);
+ int c_failed[n_checks];
+
+ memset (c_failed, 0, sizeof(c_failed));
+
+ for (j = 0; j < locale_name_count; j++)
+ {
+ set_test_locale (j); /* setlocale() can be slow! */
+ for (i = 0; i < n_checks; i++)
+ {
+ const struct str_with_value *const t = xdstrs_w_values + i;
+ size_t b_size;
+ size_t rs;
+
+ if (c_failed[i])
+ continue; /* skip already failed checks */
+
+ if (t->str.len < t->num_of_digt)
+ {
+ fprintf (stderr,
+ "ERROR: dstrs_w_values[%u] has wrong num_of_digt (%u): num_of_digt is expected"
+ " to be less or equal to str.len (%u).\n",
+ (unsigned int) i, (unsigned int) t->num_of_digt, (unsigned
+ int) t->str.
+ len);
+ return -1;
+ }
+ if ('0' == t->str.str[0])
+ continue; /* Skip strings prefixed with zeros */
+ if (t->num_of_digt != t->str.len)
+ continue; /* Skip strings with suffixes */
+ if (UINT32_MAX < t->val)
+ continue; /* Too large value to convert */
+ if (sizeof(buf) < t->str.len + 1)
+ {
+ fprintf (stderr,
+ "ERROR: dstrs_w_values[%u] has too long (%u) string, "
+ "size of 'buf' should be increased.\n",
+ (unsigned int) i, (unsigned int) t->str.len);
+ return -1;
+ }
+ for (b_size = 0; b_size <= t->str.len + 1; ++b_size)
+ {
+ /* fill buffer with pseudo-random values */
+ memcpy (buf, erase, sizeof(buf));
+
+ rs = MHD_uint32_to_strx (t->val, buf, b_size);
+
+ if (t->num_of_digt > b_size)
+ {
+ /* Must fail, buffer is too small for result */
+ if (0 != rs)
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint32_to_strx(0x%" PRIX64 ", -> buf,"
+ " %d) returned %" PRIuPTR
+ ", while expecting 0."
+ " Locale: %s\n", t->val, (int) b_size, (intptr_t) rs,
+ get_current_locale_str ());
+ }
+ }
+ else
+ {
+ if (t->num_of_digt != rs)
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint32_to_strx(0x%" PRIX64 ", -> buf,"
+ " %d) returned %" PRIuPTR
+ ", while expecting %d."
+ " Locale: %s\n", t->val, (int) b_size, (intptr_t) rs,
+ (int) t->num_of_digt, get_current_locale_str ());
+ }
+ else if (0 == MHD_str_equal_caseless_bin_n_ (buf, t->str.str,
+ t->num_of_digt))
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint32_to_strx(0x%" PRIX64 ", -> \"%.*s\","
+ " %d) returned %" PRIuPTR "."
+ " Locale: %s\n", t->val, (int) rs, buf, (int) b_size,
+ (intptr_t) rs, get_current_locale_str ());
+ }
+ else if (0 != memcmp (buf + rs, erase + rs, sizeof(buf) - rs))
+ {
+ if (0 == c_failed[i])
+ t_failed++;
+ c_failed[i] = ! 0;
+ fprintf (stderr,
+ "FAILED: MHD_uint32_to_strx(0x%" PRIX64 ", -> \"%.*s\","
+ " %d) returned %" PRIuPTR
+ " and touched data after the resulting string."
+ " Locale: %s\n", t->val, (int) rs, buf, (int) b_size,
+ (intptr_t) rs, get_current_locale_str ());
+ }
+ }
+ }
+ if ((verbose > 1) && (j == locale_name_count - 1) && ! c_failed[i])
+ printf ("PASSED: MHD_uint32_to_strx(0x%" PRIX64 ", -> \"%.*s\", %d) "
+ "== %" PRIuPTR "\n",
+ t->val, (int) rs, buf, (int) b_size - 1, (intptr_t) rs);
+ }
+ }
+ return t_failed;
+}
+
+
+static const struct str_with_value duint8_w_values_p1[] = {
+ {D_STR_W_LEN ("0"), 1, 0},
+ {D_STR_W_LEN ("1"), 1, 1},
+ {D_STR_W_LEN ("2"), 1, 2},
+ {D_STR_W_LEN ("3"), 1, 3},
+ {D_STR_W_LEN ("4"), 1, 4},
+ {D_STR_W_LEN ("5"), 1, 5},
+ {D_STR_W_LEN ("6"), 1, 6},
+ {D_STR_W_LEN ("7"), 1, 7},
+ {D_STR_W_LEN ("8"), 1, 8},
+ {D_STR_W_LEN ("9"), 1, 9},
+ {D_STR_W_LEN ("10"), 2, 10},
+ {D_STR_W_LEN ("11"), 2, 11},
+ {D_STR_W_LEN ("12"), 2, 12},
+ {D_STR_W_LEN ("13"), 2, 13},
+ {D_STR_W_LEN ("14"), 2, 14},
+ {D_STR_W_LEN ("15"), 2, 15},
+ {D_STR_W_LEN ("16"), 2, 16},
+ {D_STR_W_LEN ("17"), 2, 17},
+ {D_STR_W_LEN ("18"), 2, 18},
+ {D_STR_W_LEN ("19"), 2, 19},
+ {D_STR_W_LEN ("20"), 2, 20},
+ {D_STR_W_LEN ("21"), 2, 21},
+ {D_STR_W_LEN ("22"), 2, 22},
+ {D_STR_W_LEN ("23"), 2, 23},
+ {D_STR_W_LEN ("24"), 2, 24},
+ {D_STR_W_LEN ("25"), 2, 25},
+ {D_STR_W_LEN ("26"), 2, 26},
+ {D_STR_W_LEN ("27"), 2, 27},
+ {D_STR_W_LEN ("28"), 2, 28},
+ {D_STR_W_LEN ("29"), 2, 29},
+ {D_STR_W_LEN ("30"), 2, 30},
+ {D_STR_W_LEN ("31"), 2, 31},
+ {D_STR_W_LEN ("32"), 2, 32},
+ {D_STR_W_LEN ("33"), 2, 33},
+ {D_STR_W_LEN ("34"), 2, 34},
+ {D_STR_W_LEN ("35"), 2, 35},
+ {D_STR_W_LEN ("36"), 2, 36},
+ {D_STR_W_LEN ("37"), 2, 37},
+ {D_STR_W_LEN ("38"), 2, 38},
+ {D_STR_W_LEN ("39"), 2, 39},
+ {D_STR_W_LEN ("40"), 2, 40},
+ {D_STR_W_LEN ("41"), 2, 41},
+ {D_STR_W_LEN ("42"), 2, 42},
+ {D_STR_W_LEN ("43"), 2, 43},
+ {D_STR_W_LEN ("44"), 2, 44},
+ {D_STR_W_LEN ("45"), 2, 45},
+ {D_STR_W_LEN ("46"), 2, 46},
+ {D_STR_W_LEN ("47"), 2, 47},
+ {D_STR_W_LEN ("48"), 2, 48},
+ {D_STR_W_LEN ("49"), 2, 49},
+ {D_STR_W_LEN ("50"), 2, 50},
+ {D_STR_W_LEN ("51"), 2, 51},
+ {D_STR_W_LEN ("52"), 2, 52},
+ {D_STR_W_LEN ("53"), 2, 53},
+ {D_STR_W_LEN ("54"), 2, 54},
+ {D_STR_W_LEN ("55"), 2, 55},
+ {D_STR_W_LEN ("56"), 2, 56},
+ {D_STR_W_LEN ("57"), 2, 57},
+ {D_STR_W_LEN ("58"), 2, 58},
+ {D_STR_W_LEN ("59"), 2, 59},
+ {D_STR_W_LEN ("60"), 2, 60},
+ {D_STR_W_LEN ("61"), 2, 61},
+ {D_STR_W_LEN ("62"), 2, 62},
+ {D_STR_W_LEN ("63"), 2, 63},
+ {D_STR_W_LEN ("64"), 2, 64},
+ {D_STR_W_LEN ("65"), 2, 65},
+ {D_STR_W_LEN ("66"), 2, 66},
+ {D_STR_W_LEN ("67"), 2, 67},
+ {D_STR_W_LEN ("68"), 2, 68},
+ {D_STR_W_LEN ("69"), 2, 69},
+ {D_STR_W_LEN ("70"), 2, 70},
+ {D_STR_W_LEN ("71"), 2, 71},
+ {D_STR_W_LEN ("72"), 2, 72},
+ {D_STR_W_LEN ("73"), 2, 73},
+ {D_STR_W_LEN ("74"), 2, 74},
+ {D_STR_W_LEN ("75"), 2, 75},
+ {D_STR_W_LEN ("76"), 2, 76},
+ {D_STR_W_LEN ("77"), 2, 77},
+ {D_STR_W_LEN ("78"), 2, 78},
+ {D_STR_W_LEN ("79"), 2, 79},
+ {D_STR_W_LEN ("80"), 2, 80},
+ {D_STR_W_LEN ("81"), 2, 81},
+ {D_STR_W_LEN ("82"), 2, 82},
+ {D_STR_W_LEN ("83"), 2, 83},
+ {D_STR_W_LEN ("84"), 2, 84},
+ {D_STR_W_LEN ("85"), 2, 85},
+ {D_STR_W_LEN ("86"), 2, 86},
+ {D_STR_W_LEN ("87"), 2, 87},
+ {D_STR_W_LEN ("88"), 2, 88},
+ {D_STR_W_LEN ("89"), 2, 89},
+ {D_STR_W_LEN ("90"), 2, 90},
+ {D_STR_W_LEN ("91"), 2, 91},
+ {D_STR_W_LEN ("92"), 2, 92},
+ {D_STR_W_LEN ("93"), 2, 93},
+ {D_STR_W_LEN ("94"), 2, 94},
+ {D_STR_W_LEN ("95"), 2, 95},
+ {D_STR_W_LEN ("96"), 2, 96},
+ {D_STR_W_LEN ("97"), 2, 97},
+ {D_STR_W_LEN ("98"), 2, 98},
+ {D_STR_W_LEN ("99"), 2, 99},
+ {D_STR_W_LEN ("100"), 3, 100},
+ {D_STR_W_LEN ("101"), 3, 101},
+ {D_STR_W_LEN ("102"), 3, 102},
+ {D_STR_W_LEN ("103"), 3, 103},
+ {D_STR_W_LEN ("104"), 3, 104},
+ {D_STR_W_LEN ("105"), 3, 105},
+ {D_STR_W_LEN ("106"), 3, 106},
+ {D_STR_W_LEN ("107"), 3, 107},
+ {D_STR_W_LEN ("108"), 3, 108},
+ {D_STR_W_LEN ("109"), 3, 109},
+ {D_STR_W_LEN ("110"), 3, 110},
+ {D_STR_W_LEN ("111"), 3, 111},
+ {D_STR_W_LEN ("112"), 3, 112},
+ {D_STR_W_LEN ("113"), 3, 113},
+ {D_STR_W_LEN ("114"), 3, 114},
+ {D_STR_W_LEN ("115"), 3, 115},
+ {D_STR_W_LEN ("116"), 3, 116},
+ {D_STR_W_LEN ("117"), 3, 117},
+ {D_STR_W_LEN ("118"), 3, 118},
+ {D_STR_W_LEN ("119"), 3, 119},
+ {D_STR_W_LEN ("120"), 3, 120},
+ {D_STR_W_LEN ("121"), 3, 121},
+ {D_STR_W_LEN ("122"), 3, 122},
+ {D_STR_W_LEN ("123"), 3, 123},
+ {D_STR_W_LEN ("124"), 3, 124},
+ {D_STR_W_LEN ("125"), 3, 125},
+ {D_STR_W_LEN ("126"), 3, 126},
+ {D_STR_W_LEN ("127"), 3, 127},
+ {D_STR_W_LEN ("128"), 3, 128},
+ {D_STR_W_LEN ("129"), 3, 129},
+ {D_STR_W_LEN ("130"), 3, 130},
+ {D_STR_W_LEN ("131"), 3, 131},
+ {D_STR_W_LEN ("132"), 3, 132},
+ {D_STR_W_LEN ("133"), 3, 133},
+ {D_STR_W_LEN ("134"), 3, 134},
+ {D_STR_W_LEN ("135"), 3, 135},
+ {D_STR_W_LEN ("136"), 3, 136},
+ {D_STR_W_LEN ("137"), 3, 137},
+ {D_STR_W_LEN ("138"), 3, 138},
+ {D_STR_W_LEN ("139"), 3, 139},
+ {D_STR_W_LEN ("140"), 3, 140},
+ {D_STR_W_LEN ("141"), 3, 141},
+ {D_STR_W_LEN ("142"), 3, 142},
+ {D_STR_W_LEN ("143"), 3, 143},
+ {D_STR_W_LEN ("144"), 3, 144},
+ {D_STR_W_LEN ("145"), 3, 145},
+ {D_STR_W_LEN ("146"), 3, 146},
+ {D_STR_W_LEN ("147"), 3, 147},
+ {D_STR_W_LEN ("148"), 3, 148},
+ {D_STR_W_LEN ("149"), 3, 149},
+ {D_STR_W_LEN ("150"), 3, 150},
+ {D_STR_W_LEN ("151"), 3, 151},
+ {D_STR_W_LEN ("152"), 3, 152},
+ {D_STR_W_LEN ("153"), 3, 153},
+ {D_STR_W_LEN ("154"), 3, 154},
+ {D_STR_W_LEN ("155"), 3, 155},
+ {D_STR_W_LEN ("156"), 3, 156},
+ {D_STR_W_LEN ("157"), 3, 157},
+ {D_STR_W_LEN ("158"), 3, 158},
+ {D_STR_W_LEN ("159"), 3, 159},
+ {D_STR_W_LEN ("160"), 3, 160},
+ {D_STR_W_LEN ("161"), 3, 161},
+ {D_STR_W_LEN ("162"), 3, 162},
+ {D_STR_W_LEN ("163"), 3, 163},
+ {D_STR_W_LEN ("164"), 3, 164},
+ {D_STR_W_LEN ("165"), 3, 165},
+ {D_STR_W_LEN ("166"), 3, 166},
+ {D_STR_W_LEN ("167"), 3, 167},
+ {D_STR_W_LEN ("168"), 3, 168},
+ {D_STR_W_LEN ("169"), 3, 169},
+ {D_STR_W_LEN ("170"), 3, 170},
+ {D_STR_W_LEN ("171"), 3, 171},
+ {D_STR_W_LEN ("172"), 3, 172},
+ {D_STR_W_LEN ("173"), 3, 173},
+ {D_STR_W_LEN ("174"), 3, 174},
+ {D_STR_W_LEN ("175"), 3, 175},
+ {D_STR_W_LEN ("176"), 3, 176},
+ {D_STR_W_LEN ("177"), 3, 177},
+ {D_STR_W_LEN ("178"), 3, 178},
+ {D_STR_W_LEN ("179"), 3, 179},
+ {D_STR_W_LEN ("180"), 3, 180},
+ {D_STR_W_LEN ("181"), 3, 181},
+ {D_STR_W_LEN ("182"), 3, 182},
+ {D_STR_W_LEN ("183"), 3, 183},
+ {D_STR_W_LEN ("184"), 3, 184},
+ {D_STR_W_LEN ("185"), 3, 185},
+ {D_STR_W_LEN ("186"), 3, 186},
+ {D_STR_W_LEN ("187"), 3, 187},
+ {D_STR_W_LEN ("188"), 3, 188},
+ {D_STR_W_LEN ("189"), 3, 189},
+ {D_STR_W_LEN ("190"), 3, 190},
+ {D_STR_W_LEN ("191"), 3, 191},
+ {D_STR_W_LEN ("192"), 3, 192},
+ {D_STR_W_LEN ("193"), 3, 193},
+ {D_STR_W_LEN ("194"), 3, 194},
+ {D_STR_W_LEN ("195"), 3, 195},
+ {D_STR_W_LEN ("196"), 3, 196},
+ {D_STR_W_LEN ("197"), 3, 197},
+ {D_STR_W_LEN ("198"), 3, 198},
+ {D_STR_W_LEN ("199"), 3, 199},
+ {D_STR_W_LEN ("200"), 3, 200},
+ {D_STR_W_LEN ("201"), 3, 201},
+ {D_STR_W_LEN ("202"), 3, 202},
+ {D_STR_W_LEN ("203"), 3, 203},
+ {D_STR_W_LEN ("204"), 3, 204},
+ {D_STR_W_LEN ("205"), 3, 205},
+ {D_STR_W_LEN ("206"), 3, 206},
+ {D_STR_W_LEN ("207"), 3, 207},
+ {D_STR_W_LEN ("208"), 3, 208},
+ {D_STR_W_LEN ("209"), 3, 209},
+ {D_STR_W_LEN ("210"), 3, 210},
+ {D_STR_W_LEN ("211"), 3, 211},
+ {D_STR_W_LEN ("212"), 3, 212},
+ {D_STR_W_LEN ("213"), 3, 213},
+ {D_STR_W_LEN ("214"), 3, 214},
+ {D_STR_W_LEN ("215"), 3, 215},
+ {D_STR_W_LEN ("216"), 3, 216},
+ {D_STR_W_LEN ("217"), 3, 217},
+ {D_STR_W_LEN ("218"), 3, 218},
+ {D_STR_W_LEN ("219"), 3, 219},
+ {D_STR_W_LEN ("220"), 3, 220},
+ {D_STR_W_LEN ("221"), 3, 221},
+ {D_STR_W_LEN ("222"), 3, 222},
+ {D_STR_W_LEN ("223"), 3, 223},
+ {D_STR_W_LEN ("224"), 3, 224},
+ {D_STR_W_LEN ("225"), 3, 225},
+ {D_STR_W_LEN ("226"), 3, 226},
+ {D_STR_W_LEN ("227"), 3, 227},
+ {D_STR_W_LEN ("228"), 3, 228},
+ {D_STR_W_LEN ("229"), 3, 229},
+ {D_STR_W_LEN ("230"), 3, 230},
+ {D_STR_W_LEN ("231"), 3, 231},
+ {D_STR_W_LEN ("232"), 3, 232},
+ {D_STR_W_LEN ("233"), 3, 233},
+ {D_STR_W_LEN ("234"), 3, 234},
+ {D_STR_W_LEN ("235"), 3, 235},
+ {D_STR_W_LEN ("236"), 3, 236},
+ {D_STR_W_LEN ("237"), 3, 237},
+ {D_STR_W_LEN ("238"), 3, 238},
+ {D_STR_W_LEN ("239"), 3, 239},
+ {D_STR_W_LEN ("240"), 3, 240},
+ {D_STR_W_LEN ("241"), 3, 241},
+ {D_STR_W_LEN ("242"), 3, 242},
+ {D_STR_W_LEN ("243"), 3, 243},
+ {D_STR_W_LEN ("244"), 3, 244},
+ {D_STR_W_LEN ("245"), 3, 245},
+ {D_STR_W_LEN ("246"), 3, 246},
+ {D_STR_W_LEN ("247"), 3, 247},
+ {D_STR_W_LEN ("248"), 3, 248},
+ {D_STR_W_LEN ("249"), 3, 249},
+ {D_STR_W_LEN ("250"), 3, 250},
+ {D_STR_W_LEN ("251"), 3, 251},
+ {D_STR_W_LEN ("252"), 3, 252},
+ {D_STR_W_LEN ("253"), 3, 253},
+ {D_STR_W_LEN ("254"), 3, 254},
+ {D_STR_W_LEN ("255"), 3, 255},
+};
+
+static const struct str_with_value duint8_w_values_p2[] = {
+ {D_STR_W_LEN ("00"), 2, 0},
+ {D_STR_W_LEN ("01"), 2, 1},
+ {D_STR_W_LEN ("02"), 2, 2},
+ {D_STR_W_LEN ("03"), 2, 3},
+ {D_STR_W_LEN ("04"), 2, 4},
+ {D_STR_W_LEN ("05"), 2, 5},
+ {D_STR_W_LEN ("06"), 2, 6},
+ {D_STR_W_LEN ("07"), 2, 7},
+ {D_STR_W_LEN ("08"), 2, 8},
+ {D_STR_W_LEN ("09"), 2, 9},
+ {D_STR_W_LEN ("10"), 2, 10},
+ {D_STR_W_LEN ("11"), 2, 11},
+ {D_STR_W_LEN ("12"), 2, 12},
+ {D_STR_W_LEN ("13"), 2, 13},
+ {D_STR_W_LEN ("14"), 2, 14},
+ {D_STR_W_LEN ("15"), 2, 15},
+ {D_STR_W_LEN ("16"), 2, 16},
+ {D_STR_W_LEN ("17"), 2, 17},
+ {D_STR_W_LEN ("18"), 2, 18},
+ {D_STR_W_LEN ("19"), 2, 19},
+ {D_STR_W_LEN ("20"), 2, 20},
+ {D_STR_W_LEN ("21"), 2, 21},
+ {D_STR_W_LEN ("22"), 2, 22},
+ {D_STR_W_LEN ("23"), 2, 23},
+ {D_STR_W_LEN ("24"), 2, 24},
+ {D_STR_W_LEN ("25"), 2, 25},
+ {D_STR_W_LEN ("26"), 2, 26},
+ {D_STR_W_LEN ("27"), 2, 27},
+ {D_STR_W_LEN ("28"), 2, 28},
+ {D_STR_W_LEN ("29"), 2, 29},
+ {D_STR_W_LEN ("30"), 2, 30},
+ {D_STR_W_LEN ("31"), 2, 31},
+ {D_STR_W_LEN ("32"), 2, 32},
+ {D_STR_W_LEN ("33"), 2, 33},
+ {D_STR_W_LEN ("34"), 2, 34},
+ {D_STR_W_LEN ("35"), 2, 35},
+ {D_STR_W_LEN ("36"), 2, 36},
+ {D_STR_W_LEN ("37"), 2, 37},
+ {D_STR_W_LEN ("38"), 2, 38},
+ {D_STR_W_LEN ("39"), 2, 39},
+ {D_STR_W_LEN ("40"), 2, 40},
+ {D_STR_W_LEN ("41"), 2, 41},
+ {D_STR_W_LEN ("42"), 2, 42},
+ {D_STR_W_LEN ("43"), 2, 43},
+ {D_STR_W_LEN ("44"), 2, 44},
+ {D_STR_W_LEN ("45"), 2, 45},
+ {D_STR_W_LEN ("46"), 2, 46},
+ {D_STR_W_LEN ("47"), 2, 47},
+ {D_STR_W_LEN ("48"), 2, 48},
+ {D_STR_W_LEN ("49"), 2, 49},
+ {D_STR_W_LEN ("50"), 2, 50},
+ {D_STR_W_LEN ("51"), 2, 51},
+ {D_STR_W_LEN ("52"), 2, 52},
+ {D_STR_W_LEN ("53"), 2, 53},
+ {D_STR_W_LEN ("54"), 2, 54},
+ {D_STR_W_LEN ("55"), 2, 55},
+ {D_STR_W_LEN ("56"), 2, 56},
+ {D_STR_W_LEN ("57"), 2, 57},
+ {D_STR_W_LEN ("58"), 2, 58},
+ {D_STR_W_LEN ("59"), 2, 59},
+ {D_STR_W_LEN ("60"), 2, 60},
+ {D_STR_W_LEN ("61"), 2, 61},
+ {D_STR_W_LEN ("62"), 2, 62},
+ {D_STR_W_LEN ("63"), 2, 63},
+ {D_STR_W_LEN ("64"), 2, 64},
+ {D_STR_W_LEN ("65"), 2, 65},
+ {D_STR_W_LEN ("66"), 2, 66},
+ {D_STR_W_LEN ("67"), 2, 67},
+ {D_STR_W_LEN ("68"), 2, 68},
+ {D_STR_W_LEN ("69"), 2, 69},
+ {D_STR_W_LEN ("70"), 2, 70},
+ {D_STR_W_LEN ("71"), 2, 71},
+ {D_STR_W_LEN ("72"), 2, 72},
+ {D_STR_W_LEN ("73"), 2, 73},
+ {D_STR_W_LEN ("74"), 2, 74},
+ {D_STR_W_LEN ("75"), 2, 75},
+ {D_STR_W_LEN ("76"), 2, 76},
+ {D_STR_W_LEN ("77"), 2, 77},
+ {D_STR_W_LEN ("78"), 2, 78},
+ {D_STR_W_LEN ("79"), 2, 79},
+ {D_STR_W_LEN ("80"), 2, 80},
+ {D_STR_W_LEN ("81"), 2, 81},
+ {D_STR_W_LEN ("82"), 2, 82},
+ {D_STR_W_LEN ("83"), 2, 83},
+ {D_STR_W_LEN ("84"), 2, 84},
+ {D_STR_W_LEN ("85"), 2, 85},
+ {D_STR_W_LEN ("86"), 2, 86},
+ {D_STR_W_LEN ("87"), 2, 87},
+ {D_STR_W_LEN ("88"), 2, 88},
+ {D_STR_W_LEN ("89"), 2, 89},
+ {D_STR_W_LEN ("90"), 2, 90},
+ {D_STR_W_LEN ("91"), 2, 91},
+ {D_STR_W_LEN ("92"), 2, 92},
+ {D_STR_W_LEN ("93"), 2, 93},
+ {D_STR_W_LEN ("94"), 2, 94},
+ {D_STR_W_LEN ("95"), 2, 95},
+ {D_STR_W_LEN ("96"), 2, 96},
+ {D_STR_W_LEN ("97"), 2, 97},
+ {D_STR_W_LEN ("98"), 2, 98},
+ {D_STR_W_LEN ("99"), 2, 99},
+ {D_STR_W_LEN ("100"), 3, 100},
+ {D_STR_W_LEN ("101"), 3, 101},
+ {D_STR_W_LEN ("102"), 3, 102},
+ {D_STR_W_LEN ("103"), 3, 103},
+ {D_STR_W_LEN ("104"), 3, 104},
+ {D_STR_W_LEN ("105"), 3, 105},
+ {D_STR_W_LEN ("106"), 3, 106},
+ {D_STR_W_LEN ("107"), 3, 107},
+ {D_STR_W_LEN ("108"), 3, 108},
+ {D_STR_W_LEN ("109"), 3, 109},
+ {D_STR_W_LEN ("110"), 3, 110},
+ {D_STR_W_LEN ("111"), 3, 111},
+ {D_STR_W_LEN ("112"), 3, 112},
+ {D_STR_W_LEN ("113"), 3, 113},
+ {D_STR_W_LEN ("114"), 3, 114},
+ {D_STR_W_LEN ("115"), 3, 115},
+ {D_STR_W_LEN ("116"), 3, 116},
+ {D_STR_W_LEN ("117"), 3, 117},
+ {D_STR_W_LEN ("118"), 3, 118},
+ {D_STR_W_LEN ("119"), 3, 119},
+ {D_STR_W_LEN ("120"), 3, 120},
+ {D_STR_W_LEN ("121"), 3, 121},
+ {D_STR_W_LEN ("122"), 3, 122},
+ {D_STR_W_LEN ("123"), 3, 123},
+ {D_STR_W_LEN ("124"), 3, 124},
+ {D_STR_W_LEN ("125"), 3, 125},
+ {D_STR_W_LEN ("126"), 3, 126},
+ {D_STR_W_LEN ("127"), 3, 127},
+ {D_STR_W_LEN ("128"), 3, 128},
+ {D_STR_W_LEN ("129"), 3, 129},
+ {D_STR_W_LEN ("130"), 3, 130},
+ {D_STR_W_LEN ("131"), 3, 131},
+ {D_STR_W_LEN ("132"), 3, 132},
+ {D_STR_W_LEN ("133"), 3, 133},
+ {D_STR_W_LEN ("134"), 3, 134},
+ {D_STR_W_LEN ("135"), 3, 135},
+ {D_STR_W_LEN ("136"), 3, 136},
+ {D_STR_W_LEN ("137"), 3, 137},
+ {D_STR_W_LEN ("138"), 3, 138},
+ {D_STR_W_LEN ("139"), 3, 139},
+ {D_STR_W_LEN ("140"), 3, 140},
+ {D_STR_W_LEN ("141"), 3, 141},
+ {D_STR_W_LEN ("142"), 3, 142},
+ {D_STR_W_LEN ("143"), 3, 143},
+ {D_STR_W_LEN ("144"), 3, 144},
+ {D_STR_W_LEN ("145"), 3, 145},
+ {D_STR_W_LEN ("146"), 3, 146},
+ {D_STR_W_LEN ("147"), 3, 147},
+ {D_STR_W_LEN ("148"), 3, 148},
+ {D_STR_W_LEN ("149"), 3, 149},
+ {D_STR_W_LEN ("150"), 3, 150},
+ {D_STR_W_LEN ("151"), 3, 151},
+ {D_STR_W_LEN ("152"), 3, 152},
+ {D_STR_W_LEN ("153"), 3, 153},
+ {D_STR_W_LEN ("154"), 3, 154},
+ {D_STR_W_LEN ("155"), 3, 155},
+ {D_STR_W_LEN ("156"), 3, 156},
+ {D_STR_W_LEN ("157"), 3, 157},
+ {D_STR_W_LEN ("158"), 3, 158},
+ {D_STR_W_LEN ("159"), 3, 159},
+ {D_STR_W_LEN ("160"), 3, 160},
+ {D_STR_W_LEN ("161"), 3, 161},
+ {D_STR_W_LEN ("162"), 3, 162},
+ {D_STR_W_LEN ("163"), 3, 163},
+ {D_STR_W_LEN ("164"), 3, 164},
+ {D_STR_W_LEN ("165"), 3, 165},
+ {D_STR_W_LEN ("166"), 3, 166},
+ {D_STR_W_LEN ("167"), 3, 167},
+ {D_STR_W_LEN ("168"), 3, 168},
+ {D_STR_W_LEN ("169"), 3, 169},
+ {D_STR_W_LEN ("170"), 3, 170},
+ {D_STR_W_LEN ("171"), 3, 171},
+ {D_STR_W_LEN ("172"), 3, 172},
+ {D_STR_W_LEN ("173"), 3, 173},
+ {D_STR_W_LEN ("174"), 3, 174},
+ {D_STR_W_LEN ("175"), 3, 175},
+ {D_STR_W_LEN ("176"), 3, 176},
+ {D_STR_W_LEN ("177"), 3, 177},
+ {D_STR_W_LEN ("178"), 3, 178},
+ {D_STR_W_LEN ("179"), 3, 179},
+ {D_STR_W_LEN ("180"), 3, 180},
+ {D_STR_W_LEN ("181"), 3, 181},
+ {D_STR_W_LEN ("182"), 3, 182},
+ {D_STR_W_LEN ("183"), 3, 183},
+ {D_STR_W_LEN ("184"), 3, 184},
+ {D_STR_W_LEN ("185"), 3, 185},
+ {D_STR_W_LEN ("186"), 3, 186},
+ {D_STR_W_LEN ("187"), 3, 187},
+ {D_STR_W_LEN ("188"), 3, 188},
+ {D_STR_W_LEN ("189"), 3, 189},
+ {D_STR_W_LEN ("190"), 3, 190},
+ {D_STR_W_LEN ("191"), 3, 191},
+ {D_STR_W_LEN ("192"), 3, 192},
+ {D_STR_W_LEN ("193"), 3, 193},
+ {D_STR_W_LEN ("194"), 3, 194},
+ {D_STR_W_LEN ("195"), 3, 195},
+ {D_STR_W_LEN ("196"), 3, 196},
+ {D_STR_W_LEN ("197"), 3, 197},
+ {D_STR_W_LEN ("198"), 3, 198},
+ {D_STR_W_LEN ("199"), 3, 199},
+ {D_STR_W_LEN ("200"), 3, 200},
+ {D_STR_W_LEN ("201"), 3, 201},
+ {D_STR_W_LEN ("202"), 3, 202},
+ {D_STR_W_LEN ("203"), 3, 203},
+ {D_STR_W_LEN ("204"), 3, 204},
+ {D_STR_W_LEN ("205"), 3, 205},
+ {D_STR_W_LEN ("206"), 3, 206},
+ {D_STR_W_LEN ("207"), 3, 207},
+ {D_STR_W_LEN ("208"), 3, 208},
+ {D_STR_W_LEN ("209"), 3, 209},
+ {D_STR_W_LEN ("210"), 3, 210},
+ {D_STR_W_LEN ("211"), 3, 211},
+ {D_STR_W_LEN ("212"), 3, 212},
+ {D_STR_W_LEN ("213"), 3, 213},
+ {D_STR_W_LEN ("214"), 3, 214},
+ {D_STR_W_LEN ("215"), 3, 215},
+ {D_STR_W_LEN ("216"), 3, 216},
+ {D_STR_W_LEN ("217"), 3, 217},
+ {D_STR_W_LEN ("218"), 3, 218},
+ {D_STR_W_LEN ("219"), 3, 219},
+ {D_STR_W_LEN ("220"), 3, 220},
+ {D_STR_W_LEN ("221"), 3, 221},
+ {D_STR_W_LEN ("222"), 3, 222},
+ {D_STR_W_LEN ("223"), 3, 223},
+ {D_STR_W_LEN ("224"), 3, 224},
+ {D_STR_W_LEN ("225"), 3, 225},
+ {D_STR_W_LEN ("226"), 3, 226},
+ {D_STR_W_LEN ("227"), 3, 227},
+ {D_STR_W_LEN ("228"), 3, 228},
+ {D_STR_W_LEN ("229"), 3, 229},
+ {D_STR_W_LEN ("230"), 3, 230},
+ {D_STR_W_LEN ("231"), 3, 231},
+ {D_STR_W_LEN ("232"), 3, 232},
+ {D_STR_W_LEN ("233"), 3, 233},
+ {D_STR_W_LEN ("234"), 3, 234},
+ {D_STR_W_LEN ("235"), 3, 235},
+ {D_STR_W_LEN ("236"), 3, 236},
+ {D_STR_W_LEN ("237"), 3, 237},
+ {D_STR_W_LEN ("238"), 3, 238},
+ {D_STR_W_LEN ("239"), 3, 239},
+ {D_STR_W_LEN ("240"), 3, 240},
+ {D_STR_W_LEN ("241"), 3, 241},
+ {D_STR_W_LEN ("242"), 3, 242},
+ {D_STR_W_LEN ("243"), 3, 243},
+ {D_STR_W_LEN ("244"), 3, 244},
+ {D_STR_W_LEN ("245"), 3, 245},
+ {D_STR_W_LEN ("246"), 3, 246},
+ {D_STR_W_LEN ("247"), 3, 247},
+ {D_STR_W_LEN ("248"), 3, 248},
+ {D_STR_W_LEN ("249"), 3, 249},
+ {D_STR_W_LEN ("250"), 3, 250},
+ {D_STR_W_LEN ("251"), 3, 251},
+ {D_STR_W_LEN ("252"), 3, 252},
+ {D_STR_W_LEN ("253"), 3, 253},
+ {D_STR_W_LEN ("254"), 3, 254},
+ {D_STR_W_LEN ("255"), 3, 255}
+};
+
+static const struct str_with_value duint8_w_values_p3[] = {
+ {D_STR_W_LEN ("000"), 3, 0},
+ {D_STR_W_LEN ("001"), 3, 1},
+ {D_STR_W_LEN ("002"), 3, 2},
+ {D_STR_W_LEN ("003"), 3, 3},
+ {D_STR_W_LEN ("004"), 3, 4},
+ {D_STR_W_LEN ("005"), 3, 5},
+ {D_STR_W_LEN ("006"), 3, 6},
+ {D_STR_W_LEN ("007"), 3, 7},
+ {D_STR_W_LEN ("008"), 3, 8},
+ {D_STR_W_LEN ("009"), 3, 9},
+ {D_STR_W_LEN ("010"), 3, 10},
+ {D_STR_W_LEN ("011"), 3, 11},
+ {D_STR_W_LEN ("012"), 3, 12},
+ {D_STR_W_LEN ("013"), 3, 13},
+ {D_STR_W_LEN ("014"), 3, 14},
+ {D_STR_W_LEN ("015"), 3, 15},
+ {D_STR_W_LEN ("016"), 3, 16},
+ {D_STR_W_LEN ("017"), 3, 17},
+ {D_STR_W_LEN ("018"), 3, 18},
+ {D_STR_W_LEN ("019"), 3, 19},
+ {D_STR_W_LEN ("020"), 3, 20},
+ {D_STR_W_LEN ("021"), 3, 21},
+ {D_STR_W_LEN ("022"), 3, 22},
+ {D_STR_W_LEN ("023"), 3, 23},
+ {D_STR_W_LEN ("024"), 3, 24},
+ {D_STR_W_LEN ("025"), 3, 25},
+ {D_STR_W_LEN ("026"), 3, 26},
+ {D_STR_W_LEN ("027"), 3, 27},
+ {D_STR_W_LEN ("028"), 3, 28},
+ {D_STR_W_LEN ("029"), 3, 29},
+ {D_STR_W_LEN ("030"), 3, 30},
+ {D_STR_W_LEN ("031"), 3, 31},
+ {D_STR_W_LEN ("032"), 3, 32},
+ {D_STR_W_LEN ("033"), 3, 33},
+ {D_STR_W_LEN ("034"), 3, 34},
+ {D_STR_W_LEN ("035"), 3, 35},
+ {D_STR_W_LEN ("036"), 3, 36},
+ {D_STR_W_LEN ("037"), 3, 37},
+ {D_STR_W_LEN ("038"), 3, 38},
+ {D_STR_W_LEN ("039"), 3, 39},
+ {D_STR_W_LEN ("040"), 3, 40},
+ {D_STR_W_LEN ("041"), 3, 41},
+ {D_STR_W_LEN ("042"), 3, 42},
+ {D_STR_W_LEN ("043"), 3, 43},
+ {D_STR_W_LEN ("044"), 3, 44},
+ {D_STR_W_LEN ("045"), 3, 45},
+ {D_STR_W_LEN ("046"), 3, 46},
+ {D_STR_W_LEN ("047"), 3, 47},
+ {D_STR_W_LEN ("048"), 3, 48},
+ {D_STR_W_LEN ("049"), 3, 49},
+ {D_STR_W_LEN ("050"), 3, 50},
+ {D_STR_W_LEN ("051"), 3, 51},
+ {D_STR_W_LEN ("052"), 3, 52},
+ {D_STR_W_LEN ("053"), 3, 53},
+ {D_STR_W_LEN ("054"), 3, 54},
+ {D_STR_W_LEN ("055"), 3, 55},
+ {D_STR_W_LEN ("056"), 3, 56},
+ {D_STR_W_LEN ("057"), 3, 57},
+ {D_STR_W_LEN ("058"), 3, 58},
+ {D_STR_W_LEN ("059"), 3, 59},
+ {D_STR_W_LEN ("060"), 3, 60},
+ {D_STR_W_LEN ("061"), 3, 61},
+ {D_STR_W_LEN ("062"), 3, 62},
+ {D_STR_W_LEN ("063"), 3, 63},
+ {D_STR_W_LEN ("064"), 3, 64},
+ {D_STR_W_LEN ("065"), 3, 65},
+ {D_STR_W_LEN ("066"), 3, 66},
+ {D_STR_W_LEN ("067"), 3, 67},
+ {D_STR_W_LEN ("068"), 3, 68},
+ {D_STR_W_LEN ("069"), 3, 69},
+ {D_STR_W_LEN ("070"), 3, 70},
+ {D_STR_W_LEN ("071"), 3, 71},
+ {D_STR_W_LEN ("072"), 3, 72},
+ {D_STR_W_LEN ("073"), 3, 73},
+ {D_STR_W_LEN ("074"), 3, 74},
+ {D_STR_W_LEN ("075"), 3, 75},
+ {D_STR_W_LEN ("076"), 3, 76},
+ {D_STR_W_LEN ("077"), 3, 77},
+ {D_STR_W_LEN ("078"), 3, 78},
+ {D_STR_W_LEN ("079"), 3, 79},
+ {D_STR_W_LEN ("080"), 3, 80},
+ {D_STR_W_LEN ("081"), 3, 81},
+ {D_STR_W_LEN ("082"), 3, 82},
+ {D_STR_W_LEN ("083"), 3, 83},
+ {D_STR_W_LEN ("084"), 3, 84},
+ {D_STR_W_LEN ("085"), 3, 85},
+ {D_STR_W_LEN ("086"), 3, 86},
+ {D_STR_W_LEN ("087"), 3, 87},
+ {D_STR_W_LEN ("088"), 3, 88},
+ {D_STR_W_LEN ("089"), 3, 89},
+ {D_STR_W_LEN ("090"), 3, 90},
+ {D_STR_W_LEN ("091"), 3, 91},
+ {D_STR_W_LEN ("092"), 3, 92},
+ {D_STR_W_LEN ("093"), 3, 93},
+ {D_STR_W_LEN ("094"), 3, 94},
+ {D_STR_W_LEN ("095"), 3, 95},
+ {D_STR_W_LEN ("096"), 3, 96},
+ {D_STR_W_LEN ("097"), 3, 97},
+ {D_STR_W_LEN ("098"), 3, 98},
+ {D_STR_W_LEN ("099"), 3, 99},
+ {D_STR_W_LEN ("100"), 3, 100},
+ {D_STR_W_LEN ("101"), 3, 101},
+ {D_STR_W_LEN ("102"), 3, 102},
+ {D_STR_W_LEN ("103"), 3, 103},
+ {D_STR_W_LEN ("104"), 3, 104},
+ {D_STR_W_LEN ("105"), 3, 105},
+ {D_STR_W_LEN ("106"), 3, 106},
+ {D_STR_W_LEN ("107"), 3, 107},
+ {D_STR_W_LEN ("108"), 3, 108},
+ {D_STR_W_LEN ("109"), 3, 109},
+ {D_STR_W_LEN ("110"), 3, 110},
+ {D_STR_W_LEN ("111"), 3, 111},
+ {D_STR_W_LEN ("112"), 3, 112},
+ {D_STR_W_LEN ("113"), 3, 113},
+ {D_STR_W_LEN ("114"), 3, 114},
+ {D_STR_W_LEN ("115"), 3, 115},
+ {D_STR_W_LEN ("116"), 3, 116},
+ {D_STR_W_LEN ("117"), 3, 117},
+ {D_STR_W_LEN ("118"), 3, 118},
+ {D_STR_W_LEN ("119"), 3, 119},
+ {D_STR_W_LEN ("120"), 3, 120},
+ {D_STR_W_LEN ("121"), 3, 121},
+ {D_STR_W_LEN ("122"), 3, 122},
+ {D_STR_W_LEN ("123"), 3, 123},
+ {D_STR_W_LEN ("124"), 3, 124},
+ {D_STR_W_LEN ("125"), 3, 125},
+ {D_STR_W_LEN ("126"), 3, 126},
+ {D_STR_W_LEN ("127"), 3, 127},
+ {D_STR_W_LEN ("128"), 3, 128},
+ {D_STR_W_LEN ("129"), 3, 129},
+ {D_STR_W_LEN ("130"), 3, 130},
+ {D_STR_W_LEN ("131"), 3, 131},
+ {D_STR_W_LEN ("132"), 3, 132},
+ {D_STR_W_LEN ("133"), 3, 133},
+ {D_STR_W_LEN ("134"), 3, 134},
+ {D_STR_W_LEN ("135"), 3, 135},
+ {D_STR_W_LEN ("136"), 3, 136},
+ {D_STR_W_LEN ("137"), 3, 137},
+ {D_STR_W_LEN ("138"), 3, 138},
+ {D_STR_W_LEN ("139"), 3, 139},
+ {D_STR_W_LEN ("140"), 3, 140},
+ {D_STR_W_LEN ("141"), 3, 141},
+ {D_STR_W_LEN ("142"), 3, 142},
+ {D_STR_W_LEN ("143"), 3, 143},
+ {D_STR_W_LEN ("144"), 3, 144},
+ {D_STR_W_LEN ("145"), 3, 145},
+ {D_STR_W_LEN ("146"), 3, 146},
+ {D_STR_W_LEN ("147"), 3, 147},
+ {D_STR_W_LEN ("148"), 3, 148},
+ {D_STR_W_LEN ("149"), 3, 149},
+ {D_STR_W_LEN ("150"), 3, 150},
+ {D_STR_W_LEN ("151"), 3, 151},
+ {D_STR_W_LEN ("152"), 3, 152},
+ {D_STR_W_LEN ("153"), 3, 153},
+ {D_STR_W_LEN ("154"), 3, 154},
+ {D_STR_W_LEN ("155"), 3, 155},
+ {D_STR_W_LEN ("156"), 3, 156},
+ {D_STR_W_LEN ("157"), 3, 157},
+ {D_STR_W_LEN ("158"), 3, 158},
+ {D_STR_W_LEN ("159"), 3, 159},
+ {D_STR_W_LEN ("160"), 3, 160},
+ {D_STR_W_LEN ("161"), 3, 161},
+ {D_STR_W_LEN ("162"), 3, 162},
+ {D_STR_W_LEN ("163"), 3, 163},
+ {D_STR_W_LEN ("164"), 3, 164},
+ {D_STR_W_LEN ("165"), 3, 165},
+ {D_STR_W_LEN ("166"), 3, 166},
+ {D_STR_W_LEN ("167"), 3, 167},
+ {D_STR_W_LEN ("168"), 3, 168},
+ {D_STR_W_LEN ("169"), 3, 169},
+ {D_STR_W_LEN ("170"), 3, 170},
+ {D_STR_W_LEN ("171"), 3, 171},
+ {D_STR_W_LEN ("172"), 3, 172},
+ {D_STR_W_LEN ("173"), 3, 173},
+ {D_STR_W_LEN ("174"), 3, 174},
+ {D_STR_W_LEN ("175"), 3, 175},
+ {D_STR_W_LEN ("176"), 3, 176},
+ {D_STR_W_LEN ("177"), 3, 177},
+ {D_STR_W_LEN ("178"), 3, 178},
+ {D_STR_W_LEN ("179"), 3, 179},
+ {D_STR_W_LEN ("180"), 3, 180},
+ {D_STR_W_LEN ("181"), 3, 181},
+ {D_STR_W_LEN ("182"), 3, 182},
+ {D_STR_W_LEN ("183"), 3, 183},
+ {D_STR_W_LEN ("184"), 3, 184},
+ {D_STR_W_LEN ("185"), 3, 185},
+ {D_STR_W_LEN ("186"), 3, 186},
+ {D_STR_W_LEN ("187"), 3, 187},
+ {D_STR_W_LEN ("188"), 3, 188},
+ {D_STR_W_LEN ("189"), 3, 189},
+ {D_STR_W_LEN ("190"), 3, 190},
+ {D_STR_W_LEN ("191"), 3, 191},
+ {D_STR_W_LEN ("192"), 3, 192},
+ {D_STR_W_LEN ("193"), 3, 193},
+ {D_STR_W_LEN ("194"), 3, 194},
+ {D_STR_W_LEN ("195"), 3, 195},
+ {D_STR_W_LEN ("196"), 3, 196},
+ {D_STR_W_LEN ("197"), 3, 197},
+ {D_STR_W_LEN ("198"), 3, 198},
+ {D_STR_W_LEN ("199"), 3, 199},
+ {D_STR_W_LEN ("200"), 3, 200},
+ {D_STR_W_LEN ("201"), 3, 201},
+ {D_STR_W_LEN ("202"), 3, 202},
+ {D_STR_W_LEN ("203"), 3, 203},
+ {D_STR_W_LEN ("204"), 3, 204},
+ {D_STR_W_LEN ("205"), 3, 205},
+ {D_STR_W_LEN ("206"), 3, 206},
+ {D_STR_W_LEN ("207"), 3, 207},
+ {D_STR_W_LEN ("208"), 3, 208},
+ {D_STR_W_LEN ("209"), 3, 209},
+ {D_STR_W_LEN ("210"), 3, 210},
+ {D_STR_W_LEN ("211"), 3, 211},
+ {D_STR_W_LEN ("212"), 3, 212},
+ {D_STR_W_LEN ("213"), 3, 213},
+ {D_STR_W_LEN ("214"), 3, 214},
+ {D_STR_W_LEN ("215"), 3, 215},
+ {D_STR_W_LEN ("216"), 3, 216},
+ {D_STR_W_LEN ("217"), 3, 217},
+ {D_STR_W_LEN ("218"), 3, 218},
+ {D_STR_W_LEN ("219"), 3, 219},
+ {D_STR_W_LEN ("220"), 3, 220},
+ {D_STR_W_LEN ("221"), 3, 221},
+ {D_STR_W_LEN ("222"), 3, 222},
+ {D_STR_W_LEN ("223"), 3, 223},
+ {D_STR_W_LEN ("224"), 3, 224},
+ {D_STR_W_LEN ("225"), 3, 225},
+ {D_STR_W_LEN ("226"), 3, 226},
+ {D_STR_W_LEN ("227"), 3, 227},
+ {D_STR_W_LEN ("228"), 3, 228},
+ {D_STR_W_LEN ("229"), 3, 229},
+ {D_STR_W_LEN ("230"), 3, 230},
+ {D_STR_W_LEN ("231"), 3, 231},
+ {D_STR_W_LEN ("232"), 3, 232},
+ {D_STR_W_LEN ("233"), 3, 233},
+ {D_STR_W_LEN ("234"), 3, 234},
+ {D_STR_W_LEN ("235"), 3, 235},
+ {D_STR_W_LEN ("236"), 3, 236},
+ {D_STR_W_LEN ("237"), 3, 237},
+ {D_STR_W_LEN ("238"), 3, 238},
+ {D_STR_W_LEN ("239"), 3, 239},
+ {D_STR_W_LEN ("240"), 3, 240},
+ {D_STR_W_LEN ("241"), 3, 241},
+ {D_STR_W_LEN ("242"), 3, 242},
+ {D_STR_W_LEN ("243"), 3, 243},
+ {D_STR_W_LEN ("244"), 3, 244},
+ {D_STR_W_LEN ("245"), 3, 245},
+ {D_STR_W_LEN ("246"), 3, 246},
+ {D_STR_W_LEN ("247"), 3, 247},
+ {D_STR_W_LEN ("248"), 3, 248},
+ {D_STR_W_LEN ("249"), 3, 249},
+ {D_STR_W_LEN ("250"), 3, 250},
+ {D_STR_W_LEN ("251"), 3, 251},
+ {D_STR_W_LEN ("252"), 3, 252},
+ {D_STR_W_LEN ("253"), 3, 253},
+ {D_STR_W_LEN ("254"), 3, 254},
+ {D_STR_W_LEN ("255"), 3, 255}
+};
+
+
+static const struct str_with_value *duint8_w_values_p[3] =
+{duint8_w_values_p1, duint8_w_values_p2, duint8_w_values_p3};
+
+int
+check_str_from_uint8_pad (void)
+{
+ int i;
+ int pad;
+ int t_failed = 0;
+
+ if ((256 != sizeof(duint8_w_values_p1) / sizeof(duint8_w_values_p1[0])) ||
+ (256 != sizeof(duint8_w_values_p2) / sizeof(duint8_w_values_p2[0])) ||
+ (256 != sizeof(duint8_w_values_p3) / sizeof(duint8_w_values_p3[0])))
+ {
+ fprintf (stderr,
+ "ERROR: wrong number of items in duint8_w_values_p*.\n");
+ return -1;
+ }
+ for (pad = 0; pad <= 3; pad++)
+ {
+ int table_num = pad - 1;
+ if (0 == pad)
+ table_num = 0;
+
+ for (i = 0; i <= 255; i++)
+ {
+ const struct str_with_value *const t = duint8_w_values_p[table_num] + i;
+ size_t b_size;
+ size_t rs;
+ char buf[8];
+
+ if (t->str.len < t->num_of_digt)
+ {
+ fprintf (stderr,
+ "ERROR: dstrs_w_values[%u] has wrong num_of_digt (%u): num_of_digt is expected"
+ " to be less or equal to str.len (%u).\n",
+ (unsigned int) i, (unsigned int) t->num_of_digt, (unsigned
+ int) t->str.
+ len);
+ return -1;
+ }
+ if (sizeof(buf) < t->str.len + 1)
+ {
+ fprintf (stderr,
+ "ERROR: dstrs_w_values[%u] has too long (%u) string, "
+ "size of 'buf' should be increased.\n",
+ (unsigned int) i, (unsigned int) t->str.len);
+ return -1;
+ }
+ for (b_size = 0; b_size <= t->str.len + 1; ++b_size)
+ {
+ /* fill buffer with pseudo-random values */
+ memset (buf, '#', sizeof(buf));
+
+ rs = MHD_uint8_to_str_pad (t->val, pad, buf, b_size);
+
+ if (t->num_of_digt > b_size)
+ {
+ /* Must fail, buffer is too small for result */
+ if (0 != rs)
+ {
+ t_failed++;
+ fprintf (stderr,
+ "FAILED: MHD_uint8_to_str_pad(%" PRIu64 ", %d, -> buf,"
+ " %d) returned %" PRIuPTR
+ ", while expecting 0.\n", t->val, (int) pad, (int) b_size,
+ (intptr_t) rs);
+ }
+ }
+ else
+ {
+ if (t->num_of_digt != rs)
+ {
+ t_failed++;
+ fprintf (stderr,
+ "FAILED: MHD_uint8_to_str_pad(%" PRIu64 ", %d, -> buf,"
+ " %d) returned %" PRIuPTR
+ ", while expecting %d.\n", t->val, (int) pad,
+ (int) b_size, (intptr_t) rs, (int) t->num_of_digt);
+ }
+ else if (0 != memcmp (buf, t->str.str, t->num_of_digt))
+ {
+ t_failed++;
+ fprintf (stderr,
+ "FAILED: MHD_uint8_to_str_pad(%" PRIu64 ", %d, "
+ "-> \"%.*s\", %d) returned %" PRIuPTR ".\n",
+ t->val, (int) pad, (int) rs, buf,
+ (int) b_size, (intptr_t) rs);
+ }
+ else if (0 != memcmp (buf + rs, "########", sizeof(buf) - rs))
+ {
+ t_failed++;
+ fprintf (stderr,
+ "FAILED: MHD_uint8_to_str_pad(%" PRIu64 ", %d,"
+ " -> \"%.*s\", %d) returned %" PRIuPTR
+ " and touched data after the resulting string.\n",
+ t->val, (int) pad, (int) rs, buf, (int) b_size,
+ (intptr_t) rs);
+ }
+ }
+ }
+ }
+ }
+ if ((verbose > 1) && (0 == t_failed))
+ printf ("PASSED: MHD_uint8_to_str_pad.\n");
+
+ return t_failed;
+}
+
+
+int
+run_str_from_X_tests (void)
+{
+ int str_from_uint16;
+ int str_from_uint64;
+ int strx_from_uint32;
+ int str_from_uint8_pad;
+ int failures;
+
+ failures = 0;
+
+ str_from_uint16 = check_str_from_uint16 ();
+ if (str_from_uint16 != 0)
+ {
+ if (str_from_uint16 < 0)
+ {
+ fprintf (stderr,
+ "ERROR: test internal error in check_str_from_uint16().\n");
+ return 99;
+ }
+ fprintf (stderr,
+ "FAILED: testcase check_str_from_uint16() failed.\n\n");
+ failures += str_from_uint16;
+ }
+ else if (verbose > 1)
+ printf (
+ "PASSED: testcase check_str_from_uint16() successfully passed.\n\n");
+
+ str_from_uint64 = check_str_from_uint64 ();
+ if (str_from_uint64 != 0)
+ {
+ if (str_from_uint64 < 0)
+ {
+ fprintf (stderr,
+ "ERROR: test internal error in check_str_from_uint16().\n");
+ return 99;
+ }
+ fprintf (stderr,
+ "FAILED: testcase check_str_from_uint16() failed.\n\n");
+ failures += str_from_uint64;
+ }
+ else if (verbose > 1)
+ printf (
+ "PASSED: testcase check_str_from_uint16() successfully passed.\n\n");
+ strx_from_uint32 = check_strx_from_uint32 ();
+ if (strx_from_uint32 != 0)
+ {
+ if (strx_from_uint32 < 0)
+ {
+ fprintf (stderr,
+ "ERROR: test internal error in check_strx_from_uint32().\n");
+ return 99;
+ }
+ fprintf (stderr,
+ "FAILED: testcase check_strx_from_uint32() failed.\n\n");
+ failures += strx_from_uint32;
+ }
+ else if (verbose > 1)
+ printf (
+ "PASSED: testcase check_strx_from_uint32() successfully passed.\n\n");
+
+ str_from_uint8_pad = check_str_from_uint8_pad ();
+ if (str_from_uint8_pad != 0)
+ {
+ if (str_from_uint8_pad < 0)
+ {
+ fprintf (stderr,
+ "ERROR: test internal error in check_str_from_uint8_pad().\n");
+ return 99;
+ }
+ fprintf (stderr,
+ "FAILED: testcase check_str_from_uint8_pad() failed.\n\n");
+ failures += str_from_uint8_pad;
+ }
+ else if (verbose > 1)
+ printf (
+ "PASSED: testcase check_str_from_uint8_pad() successfully passed.\n\n");
+
+ if (failures)
+ {
+ if (verbose > 0)
+ printf ("At least one test failed.\n");
+
+ return 1;
+ }
+
+ if (verbose > 0)
+ printf ("All tests passed successfully.\n");
+
+ return 0;
+}
+
+
+int
main (int argc, char *argv[])
{
if (has_param (argc, argv, "-v") || has_param (argc, argv, "--verbose") ||
@@ -3328,5 +4680,8 @@
if (has_in_name (argv[0], "_to_value"))
return run_str_to_X_tests ();
+ if (has_in_name (argv[0], "_from_value"))
+ return run_str_from_X_tests ();
+
return run_eq_neq_str_tests ();
}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_str_token_remove.c
^
|
@@ -0,0 +1,248 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2017-2021 Karlson2k (Evgeny Grin)
+
+ This test tool 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 test tool 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/test_str_token.c
+ * @brief Unit tests for MHD_str_remove_token_caseless_() function
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_options.h"
+#include <string.h>
+#include <stdio.h>
+#include "mhd_str.h"
+#include "mhd_assert.h"
+
+
+static int
+expect_result_n (const char *str, size_t str_len,
+ const char *token, size_t token_len,
+ const char *expected, size_t expected_len,
+ const bool expected_removed)
+{
+ char buf_in[1024];
+ char buf_token[256];
+ char buf_out[1024];
+ size_t buf_len;
+
+ mhd_assert (sizeof(buf_in) > str_len + 2);
+ mhd_assert (sizeof(buf_token) > token_len + 2);
+ mhd_assert (sizeof(buf_out) > expected_len + 2);
+
+ memset (buf_in, '#', sizeof(buf_in));
+ memset (buf_token, '#', sizeof(buf_token));
+ memcpy (buf_in, str, str_len); /* Copy without zero-termination */
+ memcpy (buf_token, token, token_len); /* Copy without zero-termination */
+
+ for (buf_len = 0; buf_len <= expected_len + 3; ++buf_len)
+ {
+ bool res;
+ ssize_t result_len;
+ memset (buf_out, '$', sizeof(buf_out));
+
+ result_len = buf_len;
+
+ res = MHD_str_remove_token_caseless_ (buf_in, str_len, buf_token, token_len,
+ buf_out, &result_len);
+ if (buf_len < expected_len)
+ { /* The result should not fit into the buffer */
+ if (res || (0 < result_len))
+ {
+ fprintf (stderr,
+ "MHD_str_remove_token_caseless_() FAILED:\n"
+ "\tMHD_str_remove_token_caseless_(\"%.*s\", %lu,"
+ " \"%.*s\", %lu, buf, &(%ld->%ld)) returned %s\n",
+ (int) str_len + 2, buf_in, (unsigned long) str_len,
+ (int) token_len + 2, buf_token, (unsigned long) token_len,
+ (long) buf_len, (long) result_len, res ? "true" : "false");
+ return 1;
+ }
+ }
+ else
+ { /* The result should fit into the buffer */
+ if ( (expected_removed != res) ||
+ (expected_len != (size_t) result_len) ||
+ ((0 != result_len) && (0 != memcmp (expected, buf_out,
+ result_len))) ||
+ ('$' != buf_out[result_len]))
+ {
+ fprintf (stderr,
+ "MHD_str_remove_token_caseless_() FAILED:\n"
+ "\tMHD_str_remove_token_caseless_(\"%.*s\", %lu,"
+ " \"%.*s\", %lu, \"%.*s\", &(%ld->%ld)) returned %s\n",
+ (int) str_len + 2, buf_in, (unsigned long) str_len,
+ (int) token_len + 2, buf_token, (unsigned long) token_len,
+ (int) expected_len + 2, buf_out,
+ (long) buf_len, (long) result_len,
+ res ? "true" : "false");
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+#define expect_result(s,t,e,found) \
+ expect_result_n ((s),MHD_STATICSTR_LEN_ (s), \
+ (t),MHD_STATICSTR_LEN_ (t), \
+ (e),MHD_STATICSTR_LEN_ (e), found)
+
+int
+check_result (void)
+{
+ int errcount = 0;
+ errcount += expect_result ("string", "string", "", true);
+ errcount += expect_result ("String", "string", "", true);
+ errcount += expect_result ("string", "String", "", true);
+ errcount += expect_result ("strinG", "String", "", true);
+ errcount += expect_result ("\t strinG", "String", "", true);
+ errcount += expect_result ("strinG\t ", "String", "", true);
+ errcount += expect_result (" \t tOkEn ", "toKEN", "", true);
+ errcount += expect_result ("not token\t, tOkEn ", "toKEN", "not token",
+ true);
+ errcount += expect_result ("not token,\t tOkEn, more token", "toKEN",
+ "not token, more token", true);
+ errcount += expect_result ("not token,\t tOkEn\t, more token", "toKEN",
+ "not token, more token", true);
+ errcount += expect_result (",,,,,,test,,,,", "TESt", "", true);
+ errcount += expect_result (",,,,,\t,test,,,,", "TESt", "", true);
+ errcount += expect_result (",,,,,,test, ,,,", "TESt", "", true);
+ errcount += expect_result (",,,,,, test,,,,", "TESt", "", true);
+ errcount += expect_result (",,,,,, test not,test,,", "TESt", "test not",
+ true);
+ errcount += expect_result (",,,,,, test not,,test,,", "TESt", "test not",
+ true);
+ errcount += expect_result (",,,,,, test not ,test,,", "TESt", "test not",
+ true);
+ errcount += expect_result (",,,,,, test", "TESt", "", true);
+ errcount += expect_result (",,,,,, test ", "TESt", "", true);
+ errcount += expect_result ("no test,,,,,, test ", "TESt", "no test",
+ true);
+ errcount += expect_result ("the-token,, the-token , the-token" \
+ ",the-token ,the-token", "the-token", "", true);
+ errcount += expect_result (" the-token,, the-token , the-token," \
+ "the-token ,the-token ", "the-token", "", true);
+ errcount += expect_result (" the-token ,, the-token , the-token," \
+ "the-token , the-token ", "the-token", "", true);
+ errcount += expect_result ("the-token,a, the-token , the-token,b," \
+ "the-token , c,the-token", "the-token", "a, b, c",
+ true);
+ errcount += expect_result (" the-token, a, the-token , the-token, b," \
+ "the-token ,c ,the-token ", "the-token",
+ "a, b, c", true);
+ errcount += expect_result (" the-token , a , the-token , the-token, b ," \
+ "the-token , c , the-token ", "the-token",
+ "a, b, c",true);
+ errcount += expect_result ("the-token,aa, the-token , the-token,bb," \
+ "the-token , cc,the-token", "the-token",
+ "aa, bb, cc", true);
+ errcount += expect_result (" the-token, aa, the-token , the-token, bb," \
+ "the-token ,cc ,the-token ", "the-token",
+ "aa, bb, cc", true);
+ errcount += expect_result (" the-token , aa , the-token , the-token, bb ," \
+ "the-token , cc , the-token ", "the-token",
+ "aa, bb, cc", true);
+
+ errcount += expect_result ("strin", "string", "strin", false);
+ errcount += expect_result ("Stringer", "string", "Stringer", false);
+ errcount += expect_result ("sstring", "String", "sstring", false);
+ errcount += expect_result ("string", "Strin", "string", false);
+ errcount += expect_result ("\t( strinG", "String", "( strinG", false);
+ errcount += expect_result (")strinG\t ", "String", ")strinG", false);
+ errcount += expect_result (" \t tOkEn t ", "toKEN", "tOkEn t", false);
+ errcount += expect_result ("not token\t, tOkEner ", "toKEN",
+ "not token, tOkEner", false);
+ errcount += expect_result ("not token,\t tOkEns, more token", "toKEN",
+ "not token, tOkEns, more token", false);
+ errcount += expect_result ("not token,\t tOkEns\t, more token", "toKEN",
+ "not token, tOkEns, more token", false);
+ errcount += expect_result (",,,,,,testing,,,,", "TESt", "testing", false);
+ errcount += expect_result (",,,,,\t,test,,,,", "TESting", "test", false);
+ errcount += expect_result ("tests,,,,,,quest, ,,,", "TESt", "tests, quest",
+ false);
+ errcount += expect_result (",,,,,, testы,,,,", "TESt", "testы", false);
+ errcount += expect_result (",,,,,, test not,хtest,,", "TESt",
+ "test not, хtest", false);
+ errcount += expect_result ("testing,,,,,, test not,,test2,,", "TESt",
+ "testing, test not, test2", false);
+ errcount += expect_result (",testi,,,,, test not ,test,,", "TESting",
+ "testi, test not, test", false);
+ errcount += expect_result (",,,,,,2 test", "TESt", "2 test", false);
+ errcount += expect_result (",,,,,,test test ", "test", "test test",
+ false);
+ errcount += expect_result ("no test,,,,,,test test", "test",
+ "no test, test test", false);
+ errcount += expect_result (",,,,,,,,,,,,,,,,,,,", "the-token", "", false);
+ errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,", "the-token",
+ "a, b, c, d, e, f, g", false);
+ errcount += expect_result (",,,,,,,,,,,,,,,,,,,", "", "", false);
+ errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,", "",
+ "a, b, c, d, e, f, g", false);
+ errcount += expect_result ("a,b,c,d,e,f,g", "", "a, b, c, d, e, f, g",
+ false);
+ errcount += expect_result ("a1,b1,c1,d1,e1,f1,g1", "",
+ "a1, b1, c1, d1, e1, f1, g1", false);
+
+ errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token",
+ "the-token", "a, b, c, d, e, f, g", true);
+ errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token,",
+ "the-token", "a, b, c, d, e, f, g", true);
+ errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token,x",
+ "the-token", "a, b, c, d, e, f, g, x", true);
+ errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token x",
+ "the-token", "a, b, c, d, e, f, g, the-token x",
+ false);
+ errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token x,",
+ "the-token", "a, b, c, d, e, f, g, the-token x",
+ false);
+ errcount += expect_result (",a,b,c,d,e,f,g,,,,,,,,,,,,the-token x,x",
+ "the-token", "a, b, c, d, e, f, g," \
+ " the-token x, x", false);
+ errcount += expect_result ("the-token,a,b,c,d,e,f,g,,,,,,,,,,,,the-token",
+ "the-token", "a, b, c, d, e, f, g", true);
+ errcount += expect_result ("the-token ,a,b,c,d,e,f,g,,,,,,,,,,,,the-token,",
+ "the-token", "a, b, c, d, e, f, g", true);
+ errcount += expect_result ("the-token,a,b,c,d,e,f,g,,,,,,,,,,,,the-token,x",
+ "the-token", "a, b, c, d, e, f, g, x", true);
+ errcount += expect_result ("the-token x,a,b,c,d,e,f,g,,,,,,,,,,,," \
+ "the-token x", "the-token",
+ "the-token x, a, b, c, d, e, f, g, the-token x",
+ false);
+ errcount += expect_result ("the-token x,a,b,c,d,e,f,g,,,,,,,,,,,," \
+ "the-token x,", "the-token",
+ "the-token x, a, b, c, d, e, f, g, the-token x",
+ false);
+ errcount += expect_result ("the-token x,a,b,c,d,e,f,g,,,,,,,,,,,," \
+ "the-token x,x", "the-token",
+ "the-token x, a, b, c, d, e, f, g, " \
+ "the-token x, x", false);
+
+ return errcount;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int errcount = 0;
+ (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+ errcount += check_result ();
+ return errcount == 0 ? 0 : 1;
+}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_str_tokens_remove.c
^
|
@@ -0,0 +1,282 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2017-2021 Karlson2k (Evgeny Grin)
+
+ This test tool 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 test tool 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/test_str_token.c
+ * @brief Unit tests for MHD_str_remove_tokens_caseless_() function
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_options.h"
+#include <string.h>
+#include <stdio.h>
+#include "mhd_str.h"
+#include "mhd_assert.h"
+
+
+static int
+expect_result_n (const char *str, size_t str_len,
+ const char *tokens, size_t tokens_len,
+ const char *expected, size_t expected_len,
+ const bool expected_removed)
+{
+ char buf_in[1024];
+ char buf_tokens[256];
+ bool res;
+ size_t result_len;
+
+ mhd_assert (sizeof(buf_in) > str_len + 2);
+ mhd_assert (sizeof(buf_tokens) > tokens_len + 2);
+
+ memset (buf_tokens, '#', sizeof(buf_tokens));
+ memcpy (buf_tokens, tokens, tokens_len); /* Copy without zero-termination */
+ memset (buf_in, '$', sizeof(buf_in));
+ memcpy (buf_in, str, str_len); /* Copy without zero-termination */
+
+ result_len = str_len;
+
+ res = MHD_str_remove_tokens_caseless_ (buf_in, &result_len,
+ buf_tokens, tokens_len);
+
+ if ( (expected_removed != res) ||
+ (expected_len != result_len) ||
+ ((0 != result_len) && (0 != memcmp (expected, buf_in, result_len))) ||
+ ('$' != buf_in[str_len]))
+ {
+ fprintf (stderr,
+ "MHD_str_remove_tokens_caseless_() FAILED:\n"
+ "\tRESULT: "
+ "\tMHD_str_remove_token_caseless_(\"%s\"->\"%.*s\", &(%lu->%lu),"
+ " \"%.*s\", %lu) returned %s\n",
+ str,
+ (int) result_len, buf_in,
+ (unsigned long) str_len, (unsigned long) result_len,
+ (int) tokens_len, buf_tokens, (unsigned long) tokens_len,
+ res ? "true" : "false");
+ fprintf (stderr,
+ "\tEXPECTED: "
+ "\tMHD_str_remove_token_caseless_(\"%s\"->\"%s\", &(%lu->%lu),"
+ " \"%.*s\", %lu) returned %s\n",
+ str,
+ expected,
+ (unsigned long) str_len, (unsigned long) expected_len,
+ (int) tokens_len, buf_tokens, (unsigned long) tokens_len,
+ expected_removed ? "true" : "false");
+ return 1;
+ }
+ return 0;
+}
+
+
+#define expect_result(s,t,e,found) \
+ expect_result_n ((s),MHD_STATICSTR_LEN_ (s), \
+ (t),MHD_STATICSTR_LEN_ (t), \
+ (e),MHD_STATICSTR_LEN_ (e), found)
+
+int
+check_result (void)
+{
+ int errcount = 0;
+ errcount += expect_result ("string", "string", "", true);
+ errcount += expect_result ("String", "string", "", true);
+ errcount += expect_result ("string", "String", "", true);
+ errcount += expect_result ("strinG", "String", "", true);
+ errcount += expect_result ("strinG", "String\t", "", true);
+ errcount += expect_result ("strinG", "\tString", "", true);
+ errcount += expect_result ("tOkEn", " \t toKEN ", "", true);
+ errcount += expect_result ("not-token, tOkEn", "token", "not-token",
+ true);
+ errcount += expect_result ("not-token1, tOkEn1, token", "token1",
+ "not-token1, token",
+ true);
+ errcount += expect_result ("token, tOkEn1", "token1", "token",
+ true);
+ errcount += expect_result ("not-token, tOkEn", " \t toKEN", "not-token",
+ true);
+ errcount += expect_result ("not-token, tOkEn, more-token", "toKEN\t",
+ "not-token, more-token", true);
+ errcount += expect_result ("not-token, tOkEn, more-token", "\t toKEN,,,,,",
+ "not-token, more-token", true);
+ errcount += expect_result ("a, b, c, d", ",,,,,a", "b, c, d", true);
+ errcount += expect_result ("a, b, c, d", "a,,,,,,", "b, c, d", true);
+ errcount += expect_result ("a, b, c, d", ",,,,a,,,,,,", "b, c, d", true);
+ errcount += expect_result ("a, b, c, d", "\t \t,,,,a,, , ,,,\t",
+ "b, c, d", true);
+ errcount += expect_result ("a, b, c, d", "b, c, d", "a", true);
+ errcount += expect_result ("a, b, c, d", "a, b, c, d", "", true);
+ errcount += expect_result ("a, b, c, d", "d, c, b, a", "", true);
+ errcount += expect_result ("a, b, c, d", "b, d, a, c", "", true);
+ errcount += expect_result ("a, b, c, d, e", "b, d, a, c", "e", true);
+ errcount += expect_result ("e, a, b, c, d", "b, d, a, c", "e", true);
+ errcount += expect_result ("e, a, b, c, d, e", "b, d, a, c", "e, e", true);
+ errcount += expect_result ("a, b, c, d", "b,c,d", "a", true);
+ errcount += expect_result ("a, b, c, d", "a,b,c,d", "", true);
+ errcount += expect_result ("a, b, c, d", "d,c,b,a", "", true);
+ errcount += expect_result ("a, b, c, d", "b,d,a,c", "", true);
+ errcount += expect_result ("a, b, c, d, e", "b,d,a,c", "e", true);
+ errcount += expect_result ("e, a, b, c, d", "b,d,a,c", "e", true);
+ errcount += expect_result ("e, a, b, c, d, e", "b,d,a,c", "e, e", true);
+ errcount += expect_result ("a, b, c, d", "d,,,,,,,,,c,b,a", "", true);
+ errcount += expect_result ("a, b, c, d", "b,d,a,c,,,,,,,,,,", "", true);
+ errcount += expect_result ("a, b, c, d, e", ",,,,\t,,,,b,d,a,c,\t", "e",
+ true);
+ errcount += expect_result ("e, a, b, c, d", "b,d,a,c", "e", true);
+ errcount += expect_result ("token, a, b, c, d", "token", "a, b, c, d", true);
+ errcount += expect_result ("token1, a, b, c, d", "token1", "a, b, c, d",
+ true);
+ errcount += expect_result ("token12, a, b, c, d", "token12", "a, b, c, d",
+ true);
+ errcount += expect_result ("token123, a, b, c, d", "token123", "a, b, c, d",
+ true);
+ errcount += expect_result ("token1234, a, b, c, d", "token1234", "a, b, c, d",
+ true);
+ errcount += expect_result ("token12345, a, b, c, d", "token12345",
+ "a, b, c, d", true);
+ errcount += expect_result ("token123456, a, b, c, d", "token123456",
+ "a, b, c, d", true);
+ errcount += expect_result ("token1234567, a, b, c, d", "token1234567",
+ "a, b, c, d", true);
+ errcount += expect_result ("token12345678, a, b, c, d", "token12345678",
+ "a, b, c, d", true);
+
+ errcount += expect_result ("", "a", "", false);
+ errcount += expect_result ("", "", "", false);
+ errcount += expect_result ("a, b, c, d", "bb, dd, aa, cc", "a, b, c, d",
+ false);
+ errcount += expect_result ("a, b, c, d, e", "bb, dd, aa, cc", "a, b, c, d, e",
+ false);
+ errcount += expect_result ("e, a, b, c, d", "bb, dd, aa, cc", "e, a, b, c, d",
+ false);
+ errcount += expect_result ("e, a, b, c, d, e", "bb, dd, aa, cc",
+ "e, a, b, c, d, e", false);
+ errcount += expect_result ("aa, bb, cc, dd", "b, d, a, c", "aa, bb, cc, dd",
+ false);
+ errcount += expect_result ("aa, bb, cc, dd, ee", "b, d, a, c",
+ "aa, bb, cc, dd, ee", false);
+ errcount += expect_result ("ee, aa, bb, cc, dd", "b, d, a, c",
+ "ee, aa, bb, cc, dd", false);
+ errcount += expect_result ("ee, aa, bb, cc, dd, ee", "b, d, a, c",
+ "ee, aa, bb, cc, dd, ee", false);
+
+ errcount += expect_result ("TESt", ",,,,,,test,,,,", "", true);
+ errcount += expect_result ("TESt", ",,,,,\t,test,,,,", "", true);
+ errcount += expect_result ("TESt", ",,,,,,test, ,,,", "", true);
+ errcount += expect_result ("TESt", ",,,,,, test,,,,", "", true);
+ errcount += expect_result ("TESt", ",,,,,, test-not,test,,", "",
+ true);
+ errcount += expect_result ("TESt", ",,,,,, test-not,,test,,", "",
+ true);
+ errcount += expect_result ("TESt", ",,,,,, test-not ,test,,", "",
+ true);
+ errcount += expect_result ("TESt", ",,,,,, test", "", true);
+ errcount += expect_result ("TESt", ",,,,,, test ", "", true);
+ errcount += expect_result ("TESt", "no-test,,,,,, test ", "",
+ true);
+
+ errcount += expect_result ("the-token, a, the-token, b, the-token, " \
+ "the-token, c, the-token", "the-token", "a, b, c",
+ true);
+ errcount += expect_result ("aa, the-token, bb, the-token, cc, the-token, " \
+ "the-token, dd, the-token", "the-token",
+ "aa, bb, cc, dd", true);
+ errcount += expect_result ("the-token, a, the-token, b, the-token, " \
+ "the-token, c, the-token, e", "the-token",
+ "a, b, c, e", true);
+ errcount += expect_result ("aa, the-token, bb, the-token, cc, the-token, " \
+ "the-token, dd, the-token, ee", "the-token",
+ "aa, bb, cc, dd, ee", true);
+ errcount += expect_result ("the-token, the-token, the-token, " \
+ "the-token, the-token", "the-token", "", true);
+ errcount += expect_result ("the-token, a, the-token, the-token, b, " \
+ "the-token, c, the-token, a", "c,a,b",
+ "the-token, the-token, the-token, the-token, the-token",
+ true);
+ errcount += expect_result ("the-token, xx, the-token, the-token, zz, " \
+ "the-token, yy, the-token, ww", "ww,zz,yy",
+ "the-token, xx, the-token, the-token, the-token, the-token",
+ true);
+ errcount += expect_result ("the-token, a, the-token, the-token, b, " \
+ "the-token, c, the-token, a", " c,\t a,b,,,",
+ "the-token, the-token, the-token, the-token, the-token",
+ true);
+ errcount += expect_result ("the-token, xx, the-token, the-token, zz, " \
+ "the-token, yy, the-token, ww",
+ ",,,,ww,\t zz, yy",
+ "the-token, xx, the-token, the-token, the-token, the-token",
+ true);
+ errcount += expect_result ("the-token, a, the-token, the-token, b, " \
+ "the-token, c, the-token, a", ",,,,c,\t a,b",
+ "the-token, the-token, the-token, the-token, the-token",
+ true);
+ errcount += expect_result ("the-token, xx, the-token, the-token, zz, " \
+ "the-token, yy, the-token, ww", " ww,\t zz,yy,,,,",
+ "the-token, xx, the-token, the-token, the-token, the-token",
+ true);
+ errcount += expect_result ("close, 2", "close",
+ "2", true);
+ errcount += expect_result ("close, 22", "close",
+ "22", true);
+ errcount += expect_result ("close, nothing", "close",
+ "nothing", true);
+ errcount += expect_result ("close, 2", "2",
+ "close", true);
+ errcount += expect_result ("close", "close",
+ "", true);
+ errcount += expect_result ("close, nothing", "close, token",
+ "nothing", true);
+ errcount += expect_result ("close, nothing", "nothing, token",
+ "close", true);
+ errcount += expect_result ("close, 2", "close, 10, 12, 22, nothing",
+ "2", true);
+
+ errcount += expect_result ("strin", "string", "strin", false);
+ errcount += expect_result ("Stringer", "string", "Stringer", false);
+ errcount += expect_result ("sstring", "String", "sstring", false);
+ errcount += expect_result ("string", "Strin", "string", false);
+ errcount += expect_result ("String", "\t(-strinG", "String", false);
+ errcount += expect_result ("String", ")strinG\t ", "String", false);
+ errcount += expect_result ("not-token, tOkEner", "toKEN",
+ "not-token, tOkEner", false);
+ errcount += expect_result ("not-token, tOkEns, more-token", "toKEN",
+ "not-token, tOkEns, more-token", false);
+ errcount += expect_result ("tests, quest", "TESt", "tests, quest",
+ false);
+ errcount += expect_result ("testы", "TESt", "testы", false);
+ errcount += expect_result ("test-not, хtest", "TESt",
+ "test-not, хtest", false);
+ errcount += expect_result ("testing, test not, test2", "TESt",
+ "testing, test not, test2", false);
+ errcount += expect_result ("", ",,,,,,,,,,,,,,,,,,,the-token", "", false);
+ errcount += expect_result ("a1, b1, c1, d1, e1, f1, g1", "",
+ "a1, b1, c1, d1, e1, f1, g1", false);
+
+ return errcount;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int errcount = 0;
+ (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+ errcount += check_result ();
+ if (0 == errcount)
+ printf ("All tests were passed without errors.\n");
+ return errcount == 0 ? 0 : 1;
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_upgrade.c
^
|
@@ -235,12 +235,12 @@
GNUTLS_CRD_CERTIFICATE,
s->tls_crd))
{
-#if GNUTLS_VERSION_NUMBER + 0 >= 0x030109
+#if (GNUTLS_VERSION_NUMBER + 0 >= 0x030109) && ! defined(_WIN64)
gnutls_transport_set_int (s->tls_s, (int) (s->fd));
-#else /* GnuTLS before 3.1.9 */
+#else /* GnuTLS before 3.1.9 or Win x64 */
gnutls_transport_set_ptr (s->tls_s,
(gnutls_transport_ptr_t) (intptr_t) (s->fd));
-#endif /* GnuTLS before 3.1.9 */
+#endif /* GnuTLS before 3.1.9 or Win x64 */
return s;
}
gnutls_certificate_free_credentials (s->tls_crd);
@@ -456,7 +456,7 @@
void **con_cls,
enum MHD_RequestTerminationCode toe)
{
- pthread_t*ppth = *con_cls;
+ pthread_t *ppth = *con_cls;
(void) cls;
(void) connection; /* Unused. Silent compiler warning. */
@@ -464,7 +464,7 @@
(toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) &&
(toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) )
abort ();
- if (! pthread_equal (**((pthread_t**) con_cls),
+ if (! pthread_equal (**((pthread_t **) con_cls),
pthread_self ()))
abort ();
if (NULL != ppth)
@@ -554,7 +554,6 @@
* Change socket to blocking.
*
* @param fd the socket to manipulate
- * @return non-zero if succeeded, zero otherwise
*/
static void
make_blocking (MHD_socket fd)
@@ -564,14 +563,15 @@
flags = fcntl (fd, F_GETFL);
if (-1 == flags)
- return;
+ abort ();
if ((flags & ~O_NONBLOCK) != flags)
if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
abort ();
#elif defined(MHD_WINSOCK_SOCKETS)
- unsigned long flags = 1;
+ unsigned long flags = 0;
- ioctlsocket (fd, FIONBIO, &flags);
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ abort ();
#endif /* MHD_WINSOCK_SOCKETS */
}
@@ -863,7 +863,7 @@
if (NULL == *con_cls)
abort ();
- if (! pthread_equal (**((pthread_t**) con_cls), pthread_self ()))
+ if (! pthread_equal (**((pthread_t **) con_cls), pthread_self ()))
abort ();
resp = MHD_create_response_for_upgrade (&upgrade_cb,
NULL);
@@ -898,7 +898,7 @@
FD_ZERO (&rs);
FD_ZERO (&ws);
FD_ZERO (&es);
- max_fd = -1;
+ max_fd = MHD_INVALID_SOCKET;
to = 1000;
if (MHD_YES !=
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/test_upgrade_large.c
^
|
@@ -400,12 +400,12 @@
GNUTLS_CRD_CERTIFICATE,
s->tls_crd))
{
-#if GNUTLS_VERSION_NUMBER + 0 >= 0x030109
+#if (GNUTLS_VERSION_NUMBER + 0 >= 0x030109) && ! defined(_WIN64)
gnutls_transport_set_int (s->tls_s, (int) (s->fd));
-#else /* GnuTLS before 3.1.9 */
+#else /* GnuTLS before 3.1.9 or Win x64 */
gnutls_transport_set_ptr (s->tls_s,
(gnutls_transport_ptr_t) (intptr_t) (s->fd));
-#endif /* GnuTLS before 3.1.9 */
+#endif /* GnuTLS before 3.1.9 or Win x64 */
return s;
}
gnutls_certificate_free_credentials (s->tls_crd);
@@ -630,7 +630,7 @@
void **con_cls,
enum MHD_RequestTerminationCode toe)
{
- pthread_t*ppth = *con_cls;
+ pthread_t *ppth = *con_cls;
(void) cls;
(void) connection; /* Unused. Silent compiler warning. */
@@ -638,7 +638,7 @@
(toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) &&
(toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) )
abort ();
- if (! pthread_equal (**((pthread_t**) con_cls),
+ if (! pthread_equal (**((pthread_t **) con_cls),
pthread_self ()))
abort ();
if (NULL != ppth)
@@ -728,7 +728,6 @@
* Change socket to blocking.
*
* @param fd the socket to manipulate
- * @return non-zero if succeeded, zero otherwise
*/
static void
make_blocking (MHD_socket fd)
@@ -738,14 +737,15 @@
flags = fcntl (fd, F_GETFL);
if (-1 == flags)
- return;
+ abort ();
if ((flags & ~O_NONBLOCK) != flags)
if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
abort ();
#elif defined(MHD_WINSOCK_SOCKETS)
- unsigned long flags = 1;
+ unsigned long flags = 0;
- ioctlsocket (fd, FIONBIO, &flags);
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ abort ();
#endif /* MHD_WINSOCK_SOCKETS */
}
@@ -1050,7 +1050,7 @@
if (NULL == *con_cls)
abort ();
- if (! pthread_equal (**((pthread_t**) con_cls), pthread_self ()))
+ if (! pthread_equal (**((pthread_t **) con_cls), pthread_self ()))
abort ();
resp = MHD_create_response_for_upgrade (&upgrade_cb,
NULL);
@@ -1085,7 +1085,7 @@
FD_ZERO (&rs);
FD_ZERO (&ws);
FD_ZERO (&es);
- max_fd = -1;
+ max_fd = MHD_INVALID_SOCKET;
to = 1000;
FD_SET (MHD_itc_r_fd_ (kicker), &rs);
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd/tsearch.c
^
|
@@ -9,8 +9,14 @@
* Totally public domain.
*/
+#include "mhd_options.h"
#include "tsearch.h"
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif /* HAVE_STDDEF_H */
+#ifdef HAVE_STDLIB_H
#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
typedef struct node
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/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
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd_ws/mhd_websocket.c
^
|
@@ -0,0 +1,2443 @@
+/*
+ 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 management) */
+ MHD_WebSocketMallocCallback malloc;
+ /* The function pointer to realloc for payload (can be used to use different memory management) */
+ MHD_WebSocketReallocCallback realloc;
+ /* The function pointer to free for payload (can be used to use different memory management) */
+ MHD_WebSocketFreeCallback free;
+ /* A closure for the random number generator (only used for client mode; usually not required) */
+ void *cls_rng;
+ /* The random number generator (only used for client mode; usually not required) */
+ MHD_WebSocketRandomNumberGenerator rng;
+ /* 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
+};
+
+static void
+MHD_websocket_copy_payload (char *dst,
+ const char *src,
+ size_t len,
+ uint32_t 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 enum MHD_WEBSOCKET_STATUS
+MHD_websocket_decode_header_complete (struct MHD_WebSocketStream *ws,
+ char **payload,
+ size_t *payload_len);
+
+static enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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 uint32_t
+MHD_websocket_generate_mask (struct MHD_WebSocketStream *ws);
+
+static uint16_t
+MHD_htons (uint16_t value);
+
+static uint64_t
+MHD_htonll (uint64_t value);
+
+
+/**
+ * Checks whether the HTTP version is 1.1 or above.
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_http_version (const char *http_version)
+{
+ /* validate parameters */
+ if (NULL == http_version)
+ {
+ /* Like with the other check routines, */
+ /* NULL is threated as "value not given" and not as parameter error */
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+
+ /* Check whether the version has a valid format */
+ /* RFC 1945 3.1: The format must be "HTTP/x.x" where x is */
+ /* any digit and must appear at least once */
+ if (('H' != http_version[0]) ||
+ ('T' != http_version[1]) ||
+ ('T' != http_version[2]) ||
+ ('P' != http_version[3]) ||
+ ('/' != http_version[4]))
+ {
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+
+ /* Find the major and minor part of the version */
+ /* RFC 1945 3.1: Both numbers must be threated as separate integers. */
+ /* Leading zeros must be ignored and both integers may have multiple digits */
+ const char *major = NULL;
+ const char *dot = NULL;
+ size_t i = 5;
+ for (;;)
+ {
+ char c = http_version[i];
+ if (('0' <= c) && ('9' >= c))
+ {
+ if ((NULL == major) ||
+ ((http_version + i == major + 1) && ('0' == *major)) )
+ {
+ major = http_version + i;
+ }
+ ++i;
+ }
+ else if ('.' == http_version[i])
+ {
+ dot = http_version + i;
+ ++i;
+ break;
+ }
+ else
+ {
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+ }
+ const char *minor = NULL;
+ const char *end = NULL;
+ for (;;)
+ {
+ char c = http_version[i];
+ if (('0' <= c) && ('9' >= c))
+ {
+ if ((NULL == minor) ||
+ ((http_version + i == minor + 1) && ('0' == *minor)) )
+ {
+ minor = http_version + i;
+ }
+ ++i;
+ }
+ else if (0 == c)
+ {
+ end = http_version + i;
+ break;
+ }
+ else
+ {
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+ }
+ if ((NULL == major) || (NULL == dot) || (NULL == minor) || (NULL == end))
+ {
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+ if ((2 <= dot - major) || ('2' <= *major) ||
+ (('1' == *major) && ((2 <= end - minor) || ('1' <= *minor))) )
+ {
+ return MHD_WEBSOCKET_STATUS_OK;
+ }
+
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+}
+
+
+/**
+ * Checks whether the "Connection" request header has the 'Upgrade' token.
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_connection_header (const char *connection_header)
+{
+ /* validate parameters */
+ if (NULL == connection_header)
+ {
+ /* To be compatible with the return value */
+ /* of MHD_lookup_connection_value, */
+ /* NULL is threated as "value not given" and not as parameter error */
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+
+ /* Check whether the Connection includes an Upgrade token */
+ /* RFC 7230 6.1: Multiple tokens may appear. */
+ /* RFC 7230 3.2.6: Tokens are comma separated */
+ const char *token_start = NULL;
+ const char *token_end = NULL;
+ for (size_t i = 0; ; ++i)
+ {
+ char c = connection_header[i];
+
+ /* RFC 7230 3.2.6: The list of allowed characters is a token is: */
+ /* "!" / "#" / "$" / "%" / "&" / "'" / "*" / */
+ /* "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" */
+ /* DIGIT / ALPHA */
+ if (('!' == c) || ('#' == c) || ('$' == c) || ('%' == c) ||
+ ('&' == c) || ('\'' == c) || ('*' == c) ||
+ ('+' == c) || ('-' == c) || ('.' == c) || ('^' == c) ||
+ ('_' == c) || ('`' == c) || ('|' == c) || ('~' == c) ||
+ (('0' <= c) && ('9' >= c)) ||
+ (('A' <= c) && ('Z' >= c)) || (('a' <= c) && ('z' >= c)) )
+ {
+ /* This is a valid token character */
+ if (NULL == token_start)
+ {
+ token_start = connection_header + i;
+ }
+ token_end = connection_header + i + 1;
+ }
+ else if ((' ' == c) || ('\t' == c))
+ {
+ /* White-spaces around tokens will be ignored */
+ }
+ else if ((',' == c) || (0 == c))
+ {
+ /* Check the token (case-insensitive) */
+ if (NULL != token_start)
+ {
+ if (7 == (token_end - token_start) )
+ {
+ if ( (('U' == token_start[0]) || ('u' == token_start[0])) &&
+ (('P' == token_start[1]) || ('p' == token_start[1])) &&
+ (('G' == token_start[2]) || ('g' == token_start[2])) &&
+ (('R' == token_start[3]) || ('r' == token_start[3])) &&
+ (('A' == token_start[4]) || ('a' == token_start[4])) &&
+ (('D' == token_start[5]) || ('d' == token_start[5])) &&
+ (('E' == token_start[6]) || ('e' == token_start[6])) )
+ {
+ /* The token equals to "Upgrade" */
+ return MHD_WEBSOCKET_STATUS_OK;
+ }
+ }
+ }
+ if (0 == c)
+ {
+ break;
+ }
+ token_start = NULL;
+ token_end = NULL;
+ }
+ else
+ {
+ /* RFC 7230 3.2.6: Other characters are not allowed */
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+ }
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+}
+
+
+/**
+ * Checks whether the "Upgrade" request header has the "websocket" keyword.
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_upgrade_header (const char *upgrade_header)
+{
+ /* validate parameters */
+ if (NULL == upgrade_header)
+ {
+ /* To be compatible with the return value */
+ /* of MHD_lookup_connection_value, */
+ /* NULL is threated as "value not given" and not as parameter error */
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+
+ /* Check whether the Connection includes an Upgrade token */
+ /* RFC 7230 6.1: Multiple tokens may appear. */
+ /* RFC 7230 3.2.6: Tokens are comma separated */
+ const char *keyword_start = NULL;
+ const char *keyword_end = NULL;
+ for (size_t i = 0; ; ++i)
+ {
+ char c = upgrade_header[i];
+
+ /* RFC 7230 3.2.6: The list of allowed characters is a token is: */
+ /* "!" / "#" / "$" / "%" / "&" / "'" / "*" / */
+ /* "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" */
+ /* DIGIT / ALPHA */
+ /* We also allow "/" here as the sub-delimiter for the protocol version */
+ if (('!' == c) || ('#' == c) || ('$' == c) || ('%' == c) ||
+ ('&' == c) || ('\'' == c) || ('*' == c) ||
+ ('+' == c) || ('-' == c) || ('.' == c) || ('^' == c) ||
+ ('_' == c) || ('`' == c) || ('|' == c) || ('~' == c) ||
+ ('/' == c) ||
+ (('0' <= c) && ('9' >= c)) ||
+ (('A' <= c) && ('Z' >= c)) || (('a' <= c) && ('z' >= c)) )
+ {
+ /* This is a valid token character */
+ if (NULL == keyword_start)
+ {
+ keyword_start = upgrade_header + i;
+ }
+ keyword_end = upgrade_header + i + 1;
+ }
+ else if ((' ' == c) || ('\t' == c))
+ {
+ /* White-spaces around tokens will be ignored */
+ }
+ else if ((',' == c) || (0 == c))
+ {
+ /* Check the token (case-insensitive) */
+ if (NULL != keyword_start)
+ {
+ if (9 == (keyword_end - keyword_start) )
+ {
+ if ( (('W' == keyword_start[0]) || ('w' == keyword_start[0])) &&
+ (('E' == keyword_start[1]) || ('e' == keyword_start[1])) &&
+ (('B' == keyword_start[2]) || ('b' == keyword_start[2])) &&
+ (('S' == keyword_start[3]) || ('s' == keyword_start[3])) &&
+ (('O' == keyword_start[4]) || ('o' == keyword_start[4])) &&
+ (('C' == keyword_start[5]) || ('c' == keyword_start[5])) &&
+ (('K' == keyword_start[6]) || ('k' == keyword_start[6])) &&
+ (('E' == keyword_start[7]) || ('e' == keyword_start[7])) &&
+ (('T' == keyword_start[8]) || ('t' == keyword_start[8])) )
+ {
+ /* The keyword equals to "websocket" */
+ return MHD_WEBSOCKET_STATUS_OK;
+ }
+ }
+ }
+ if (0 == c)
+ {
+ break;
+ }
+ keyword_start = NULL;
+ keyword_end = NULL;
+ }
+ else
+ {
+ /* RFC 7230 3.2.6: Other characters are not allowed */
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+ }
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+}
+
+
+/**
+ * Checks whether the "Sec-WebSocket-Version" request header
+ * equals to "13"
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_check_version_header (const char *version_header)
+{
+ /* validate parameters */
+ if (NULL == version_header)
+ {
+ /* To be compatible with the return value */
+ /* of MHD_lookup_connection_value, */
+ /* NULL is threated as "value not given" and not as parameter error */
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+
+ if (('1' == version_header[0]) &&
+ ('3' == version_header[1]) &&
+ (0 == version_header[2]))
+ {
+ /* The version equals to "13" */
+ return MHD_WEBSOCKET_STATUS_OK;
+ }
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+}
+
+
+/**
+ * Creates the response for the Sec-WebSocket-Accept header
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+MHD_websocket_create_accept_header (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_accept)
+ {
+ return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+ }
+ if (NULL == sec_websocket_key)
+ {
+ /* NULL is not a parameter error, */
+ /* because MHD_lookup_connection_value returns NULL */
+ /* if the header wasn't found */
+ return MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER;
+ }
+
+ /* 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;
+ MHD_SHA1_init (&ctx);
+ MHD_SHA1_update (&ctx, (const uint8_t *) sec_websocket_key, length);
+ MHD_SHA1_update (&ctx, (const uint8_t *) suffix, 36);
+ MHD_SHA1_finish (&ctx, (uint8_t *) 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 enum MHD_WEBSOCKET_STATUS
+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,
+ NULL,
+ NULL);
+}
+
+
+/**
+ * Initializes a new websocket stream with
+ * additional parameters for allocation functions
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+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,
+ void *cls_rng,
+ MHD_WebSocketRandomNumberGenerator callback_rng)
+{
+ /* 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) ||
+ ((0 != (flags & MHD_WEBSOCKET_FLAG_CLIENT)) &&
+ (NULL == callback_rng)))
+ {
+ 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_->cls_rng = cls_rng;
+ ws_->rng = callback_rng;
+ ws_->validity = MHD_WEBSOCKET_VALIDITY_VALID;
+
+ /* return stream */
+ *ws = ws_;
+
+ return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Frees a previously allocated websocket stream
+ */
+_MHD_EXTERN enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_VALIDITY
+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 enum MHD_WEBSOCKET_STATUS
+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 communication 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 */
+ *((uint32_t *) 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) MHD_htons (
+ *((uint16_t *) &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 */
+ *((uint32_t *) 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 = MHD_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 */
+ *((uint32_t *) 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++];
+ *((uint32_t *) ws->mask_key) = *((uint32_t *) &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,
+ *((uint32_t *) 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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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_continue = MHD_WebSocket_Opcode_Continuation ==
+ (ws->frame_header [0] & 0x0F);
+ 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)) &&
+ (0 != is_continue))
+ {
+ data_type |= 0x40; /* 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 = 0;
+ 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;
+ if (0 != is_continue)
+ return ws->data_type | 0x20; /* mark as middle fragment */
+ else
+ return ws->data_type | 0x10; /* mark as first 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;
+ if (0 != is_continue)
+ return ws->data_type | 0x20; /* mark as middle fragment */
+ else
+ return ws->data_type | 0x10; /* mark as first 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 enum MHD_WEBSOCKET_STATUS
+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 = MHD_htons (*((uint16_t *) 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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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;
+ uint32_t mask = 0 != is_masked ? MHD_websocket_generate_mask (ws) : 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;
+ *((uint16_t *) result) = MHD_htons ((uint16_t) payload_len);
+ result += 2;
+ }
+ else
+ {
+ *(result++) = is_masked | 127;
+ *((uint64_t *) result) = MHD_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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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 enum MHD_WEBSOCKET_STATUS
+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;
+ uint32_t mask = is_masked != 0 ? MHD_websocket_generate_mask (ws) : 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 enum MHD_WEBSOCKET_STATUS
+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;
+ uint32_t mask = is_masked != 0 ? MHD_websocket_generate_mask (ws) : 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 */
+ uint16_t reason_code_nb = MHD_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,
+ uint32_t 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];
+ *((uint32_t *) 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;
+}
+
+
+/**
+ * Generates a mask for masking by calling
+ * a random number generator.
+ */
+static uint32_t
+MHD_websocket_generate_mask (struct MHD_WebSocketStream *ws)
+{
+ unsigned char mask_[4];
+ if (NULL != ws->rng)
+ {
+ size_t offset = 0;
+ while (offset < 4)
+ {
+ size_t encoded = ws->rng (ws->cls_rng,
+ mask_ + offset,
+ 4 - offset);
+ offset += encoded;
+ }
+ }
+ else
+ {
+ /* this case should never happen */
+ mask_ [0] = 0;
+ mask_ [1] = 0;
+ mask_ [2] = 0;
+ mask_ [3] = 0;
+ }
+
+ return *((uint32_t *) mask_);
+}
+
+
+/**
+ * Calls the malloc function associated with the websocket steam
+ */
+_MHD_EXTERN void *
+MHD_websocket_malloc (struct MHD_WebSocketStream *ws,
+ size_t buf_len)
+{
+ if (NULL == ws)
+ {
+ return NULL;
+ }
+
+ return ws->malloc (buf_len);
+}
+
+
+/**
+ * Calls the realloc function associated with the websocket steam
+ */
+_MHD_EXTERN void *
+MHD_websocket_realloc (struct MHD_WebSocketStream *ws,
+ void *buf,
+ size_t new_buf_len)
+{
+ if (NULL == ws)
+ {
+ return NULL;
+ }
+
+ return ws->realloc (buf, new_buf_len);
+}
+
+
+/**
+ * Calls the free function associated with the websocket steam
+ */
+_MHD_EXTERN int
+MHD_websocket_free (struct MHD_WebSocketStream *ws,
+ void *buf)
+{
+ if (NULL == ws)
+ {
+ return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR;
+ }
+
+ ws->free (buf);
+
+ return MHD_WEBSOCKET_STATUS_OK;
+}
+
+
+/**
+ * Converts a 16 bit value into network byte order (MSB first)
+ * in dependence of the host system
+ */
+static uint16_t
+MHD_htons (uint16_t value)
+{
+ uint16_t endian = 0x0001;
+
+ if (((char *) &endian)[0] == 0x01)
+ {
+ /* least significant byte first */
+ ((char *) &endian)[0] = ((char *) &value)[1];
+ ((char *) &endian)[1] = ((char *) &value)[0];
+ return endian;
+ }
+ else
+ {
+ /* most significant byte first */
+ return value;
+ }
+}
+
+
+/**
+ * Converts a 64 bit value into network byte order (MSB first)
+ * in dependence of the host system
+ */
+static uint64_t
+MHD_htonll (uint64_t value)
+{
+ uint64_t endian = 0x0000000000000001;
+
+ if (((char *) &endian)[0] == 0x01)
+ {
+ /* least significant byte first */
+ ((char *) &endian)[0] = ((char *) &value)[7];
+ ((char *) &endian)[1] = ((char *) &value)[6];
+ ((char *) &endian)[2] = ((char *) &value)[5];
+ ((char *) &endian)[3] = ((char *) &value)[4];
+ ((char *) &endian)[4] = ((char *) &value)[3];
+ ((char *) &endian)[5] = ((char *) &value)[2];
+ ((char *) &endian)[6] = ((char *) &value)[1];
+ ((char *) &endian)[7] = ((char *) &value)[0];
+ return endian;
+ }
+ else
+ {
+ /* most significant byte first */
+ return value;
+ }
+}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd_ws/sha1.c
^
|
@@ -0,0 +1,378 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
+
+ libmicrohttpd 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file microhttpd/sha1.c
+ * @brief Calculation of SHA-1 digest as defined in FIPS PUB 180-4 (2015)
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "sha1.h"
+
+#include <string.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif /* HAVE_MEMORY_H */
+#include "mhd_bithelpers.h"
+#include "mhd_assert.h"
+
+/**
+ * Initialise structure for SHA-1 calculation.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ */
+void
+MHD_SHA1_init (void *ctx_)
+{
+ struct sha1_ctx *const ctx = ctx_;
+ /* Initial hash values, see FIPS PUB 180-4 paragraph 5.3.1 */
+ /* Just some "magic" numbers defined by standard */
+ ctx->H[0] = UINT32_C (0x67452301);
+ ctx->H[1] = UINT32_C (0xefcdab89);
+ ctx->H[2] = UINT32_C (0x98badcfe);
+ ctx->H[3] = UINT32_C (0x10325476);
+ ctx->H[4] = UINT32_C (0xc3d2e1f0);
+
+ /* Initialise number of bytes. */
+ ctx->count = 0;
+}
+
+
+/**
+ * Base of SHA-1 transformation.
+ * Gets full 512 bits / 64 bytes block of data and updates hash values;
+ * @param H hash values
+ * @param data data, must be exactly 64 bytes long
+ */
+static void
+sha1_transform (uint32_t H[_SHA1_DIGEST_LENGTH],
+ const uint8_t data[SHA1_BLOCK_SIZE])
+{
+ /* Working variables,
+ see FIPS PUB 180-4 paragraph 6.1.3 */
+ uint32_t a = H[0];
+ uint32_t b = H[1];
+ uint32_t c = H[2];
+ uint32_t d = H[3];
+ uint32_t e = H[4];
+
+ /* Data buffer, used as cyclic buffer.
+ See FIPS PUB 180-4 paragraphs 5.2.1, 6.1.3 */
+ uint32_t W[16];
+
+ /* 'Ch' and 'Maj' macro functions are defined with
+ widely-used optimization.
+ See FIPS PUB 180-4 formulae 4.1. */
+#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) )
+#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
+ /* Unoptimized (original) versions: */
+/* #define Ch(x,y,z) ( ( (x) & (y) ) ^ ( ~(x) & (z) ) ) */
+/* #define Maj(x,y,z) ( ((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)) ) */
+#define Par(x,y,z) ( (x) ^ (y) ^ (z) )
+
+ /* Single step of SHA-1 computation,
+ see FIPS PUB 180-4 paragraph 6.1.3 step 3.
+ * Note: instead of reassigning all working variables on each step,
+ variables are rotated for each step:
+ SHA1STEP32 (a, b, c, d, e, func, K00, W[0]);
+ SHA1STEP32 (e, a, b, c, d, func, K00, W[1]);
+ so current 'vC' will be used as 'vD' on the next step,
+ current 'vE' will be used as 'vA' on the next step.
+ * Note: 'wt' must be used exactly one time in this macro as it change other data as well
+ every time when used. */
+
+#define SHA1STEP32(vA,vB,vC,vD,vE,ft,kt,wt) do { \
+ (vE) += _MHD_ROTL32 ((vA), 5) + ft ((vB), (vC), (vD)) + (kt) + (wt); \
+ (vB) = _MHD_ROTL32 ((vB), 30); } while (0)
+
+ /* Get value of W(t) from input data buffer,
+ See FIPS PUB 180-4 paragraph 6.1.3.
+ Input data must be read in big-endian bytes order,
+ see FIPS PUB 180-4 paragraph 3.1.2. */
+#define GET_W_FROM_DATA(buf,t) \
+ _MHD_GET_32BIT_BE (((const uint8_t*) (buf)) + (t) * SHA1_BYTES_IN_WORD)
+
+#ifndef _MHD_GET_32BIT_BE_UNALIGNED
+ if (0 != (((uintptr_t) data) % _MHD_UINT32_ALIGN))
+ {
+ /* Copy the unaligned input data to the aligned buffer */
+ memcpy (W, data, SHA1_BLOCK_SIZE);
+ /* The W[] buffer itself will be used as the source of the data,
+ * but data will be reloaded in correct bytes order during
+ * the next steps */
+ data = (uint8_t *) W;
+ }
+#endif /* _MHD_GET_32BIT_BE_UNALIGNED */
+
+/* SHA-1 values of Kt for t=0..19, see FIPS PUB 180-4 paragraph 4.2.1. */
+#define K00 UINT32_C(0x5a827999)
+/* SHA-1 values of Kt for t=20..39, see FIPS PUB 180-4 paragraph 4.2.1.*/
+#define K20 UINT32_C(0x6ed9eba1)
+/* SHA-1 values of Kt for t=40..59, see FIPS PUB 180-4 paragraph 4.2.1.*/
+#define K40 UINT32_C(0x8f1bbcdc)
+/* SHA-1 values of Kt for t=60..79, see FIPS PUB 180-4 paragraph 4.2.1.*/
+#define K60 UINT32_C(0xca62c1d6)
+
+ /* During first 16 steps, before making any calculations on each step,
+ the W element is read from input data buffer as big-endian value and
+ stored in array of W elements. */
+ /* Note: instead of using K constants as array, all K values are specified
+ individually for each step. */
+ SHA1STEP32 (a, b, c, d, e, Ch, K00, W[0] = GET_W_FROM_DATA (data, 0));
+ SHA1STEP32 (e, a, b, c, d, Ch, K00, W[1] = GET_W_FROM_DATA (data, 1));
+ SHA1STEP32 (d, e, a, b, c, Ch, K00, W[2] = GET_W_FROM_DATA (data, 2));
+ SHA1STEP32 (c, d, e, a, b, Ch, K00, W[3] = GET_W_FROM_DATA (data, 3));
+ SHA1STEP32 (b, c, d, e, a, Ch, K00, W[4] = GET_W_FROM_DATA (data, 4));
+ SHA1STEP32 (a, b, c, d, e, Ch, K00, W[5] = GET_W_FROM_DATA (data, 5));
+ SHA1STEP32 (e, a, b, c, d, Ch, K00, W[6] = GET_W_FROM_DATA (data, 6));
+ SHA1STEP32 (d, e, a, b, c, Ch, K00, W[7] = GET_W_FROM_DATA (data, 7));
+ SHA1STEP32 (c, d, e, a, b, Ch, K00, W[8] = GET_W_FROM_DATA (data, 8));
+ SHA1STEP32 (b, c, d, e, a, Ch, K00, W[9] = GET_W_FROM_DATA (data, 9));
+ SHA1STEP32 (a, b, c, d, e, Ch, K00, W[10] = GET_W_FROM_DATA (data, 10));
+ SHA1STEP32 (e, a, b, c, d, Ch, K00, W[11] = GET_W_FROM_DATA (data, 11));
+ SHA1STEP32 (d, e, a, b, c, Ch, K00, W[12] = GET_W_FROM_DATA (data, 12));
+ SHA1STEP32 (c, d, e, a, b, Ch, K00, W[13] = GET_W_FROM_DATA (data, 13));
+ SHA1STEP32 (b, c, d, e, a, Ch, K00, W[14] = GET_W_FROM_DATA (data, 14));
+ SHA1STEP32 (a, b, c, d, e, Ch, K00, W[15] = GET_W_FROM_DATA (data, 15));
+
+ /* 'W' generation and assignment for 16 <= t <= 79.
+ See FIPS PUB 180-4 paragraph 6.1.3.
+ As only last 16 'W' are used in calculations, it is possible to
+ use 16 elements array of W as cyclic buffer. */
+#define Wgen(w,t) _MHD_ROTL32((w)[(t + 13) & 0xf] ^ (w)[(t + 8) & 0xf] \
+ ^ (w)[(t + 2) & 0xf] ^ (w)[t & 0xf], 1)
+
+ /* During last 60 steps, before making any calculations on each step,
+ W element is generated from W elements of cyclic buffer and generated value
+ stored back in cyclic buffer. */
+ /* Note: instead of using K constants as array, all K values are specified
+ individually for each step, see FIPS PUB 180-4 paragraph 4.2.1. */
+ SHA1STEP32 (e, a, b, c, d, Ch, K00, W[16 & 0xf] = Wgen (W, 16));
+ SHA1STEP32 (d, e, a, b, c, Ch, K00, W[17 & 0xf] = Wgen (W, 17));
+ SHA1STEP32 (c, d, e, a, b, Ch, K00, W[18 & 0xf] = Wgen (W, 18));
+ SHA1STEP32 (b, c, d, e, a, Ch, K00, W[19 & 0xf] = Wgen (W, 19));
+ SHA1STEP32 (a, b, c, d, e, Par, K20, W[20 & 0xf] = Wgen (W, 20));
+ SHA1STEP32 (e, a, b, c, d, Par, K20, W[21 & 0xf] = Wgen (W, 21));
+ SHA1STEP32 (d, e, a, b, c, Par, K20, W[22 & 0xf] = Wgen (W, 22));
+ SHA1STEP32 (c, d, e, a, b, Par, K20, W[23 & 0xf] = Wgen (W, 23));
+ SHA1STEP32 (b, c, d, e, a, Par, K20, W[24 & 0xf] = Wgen (W, 24));
+ SHA1STEP32 (a, b, c, d, e, Par, K20, W[25 & 0xf] = Wgen (W, 25));
+ SHA1STEP32 (e, a, b, c, d, Par, K20, W[26 & 0xf] = Wgen (W, 26));
+ SHA1STEP32 (d, e, a, b, c, Par, K20, W[27 & 0xf] = Wgen (W, 27));
+ SHA1STEP32 (c, d, e, a, b, Par, K20, W[28 & 0xf] = Wgen (W, 28));
+ SHA1STEP32 (b, c, d, e, a, Par, K20, W[29 & 0xf] = Wgen (W, 29));
+ SHA1STEP32 (a, b, c, d, e, Par, K20, W[30 & 0xf] = Wgen (W, 30));
+ SHA1STEP32 (e, a, b, c, d, Par, K20, W[31 & 0xf] = Wgen (W, 31));
+ SHA1STEP32 (d, e, a, b, c, Par, K20, W[32 & 0xf] = Wgen (W, 32));
+ SHA1STEP32 (c, d, e, a, b, Par, K20, W[33 & 0xf] = Wgen (W, 33));
+ SHA1STEP32 (b, c, d, e, a, Par, K20, W[34 & 0xf] = Wgen (W, 34));
+ SHA1STEP32 (a, b, c, d, e, Par, K20, W[35 & 0xf] = Wgen (W, 35));
+ SHA1STEP32 (e, a, b, c, d, Par, K20, W[36 & 0xf] = Wgen (W, 36));
+ SHA1STEP32 (d, e, a, b, c, Par, K20, W[37 & 0xf] = Wgen (W, 37));
+ SHA1STEP32 (c, d, e, a, b, Par, K20, W[38 & 0xf] = Wgen (W, 38));
+ SHA1STEP32 (b, c, d, e, a, Par, K20, W[39 & 0xf] = Wgen (W, 39));
+ SHA1STEP32 (a, b, c, d, e, Maj, K40, W[40 & 0xf] = Wgen (W, 40));
+ SHA1STEP32 (e, a, b, c, d, Maj, K40, W[41 & 0xf] = Wgen (W, 41));
+ SHA1STEP32 (d, e, a, b, c, Maj, K40, W[42 & 0xf] = Wgen (W, 42));
+ SHA1STEP32 (c, d, e, a, b, Maj, K40, W[43 & 0xf] = Wgen (W, 43));
+ SHA1STEP32 (b, c, d, e, a, Maj, K40, W[44 & 0xf] = Wgen (W, 44));
+ SHA1STEP32 (a, b, c, d, e, Maj, K40, W[45 & 0xf] = Wgen (W, 45));
+ SHA1STEP32 (e, a, b, c, d, Maj, K40, W[46 & 0xf] = Wgen (W, 46));
+ SHA1STEP32 (d, e, a, b, c, Maj, K40, W[47 & 0xf] = Wgen (W, 47));
+ SHA1STEP32 (c, d, e, a, b, Maj, K40, W[48 & 0xf] = Wgen (W, 48));
+ SHA1STEP32 (b, c, d, e, a, Maj, K40, W[49 & 0xf] = Wgen (W, 49));
+ SHA1STEP32 (a, b, c, d, e, Maj, K40, W[50 & 0xf] = Wgen (W, 50));
+ SHA1STEP32 (e, a, b, c, d, Maj, K40, W[51 & 0xf] = Wgen (W, 51));
+ SHA1STEP32 (d, e, a, b, c, Maj, K40, W[52 & 0xf] = Wgen (W, 52));
+ SHA1STEP32 (c, d, e, a, b, Maj, K40, W[53 & 0xf] = Wgen (W, 53));
+ SHA1STEP32 (b, c, d, e, a, Maj, K40, W[54 & 0xf] = Wgen (W, 54));
+ SHA1STEP32 (a, b, c, d, e, Maj, K40, W[55 & 0xf] = Wgen (W, 55));
+ SHA1STEP32 (e, a, b, c, d, Maj, K40, W[56 & 0xf] = Wgen (W, 56));
+ SHA1STEP32 (d, e, a, b, c, Maj, K40, W[57 & 0xf] = Wgen (W, 57));
+ SHA1STEP32 (c, d, e, a, b, Maj, K40, W[58 & 0xf] = Wgen (W, 58));
+ SHA1STEP32 (b, c, d, e, a, Maj, K40, W[59 & 0xf] = Wgen (W, 59));
+ SHA1STEP32 (a, b, c, d, e, Par, K60, W[60 & 0xf] = Wgen (W, 60));
+ SHA1STEP32 (e, a, b, c, d, Par, K60, W[61 & 0xf] = Wgen (W, 61));
+ SHA1STEP32 (d, e, a, b, c, Par, K60, W[62 & 0xf] = Wgen (W, 62));
+ SHA1STEP32 (c, d, e, a, b, Par, K60, W[63 & 0xf] = Wgen (W, 63));
+ SHA1STEP32 (b, c, d, e, a, Par, K60, W[64 & 0xf] = Wgen (W, 64));
+ SHA1STEP32 (a, b, c, d, e, Par, K60, W[65 & 0xf] = Wgen (W, 65));
+ SHA1STEP32 (e, a, b, c, d, Par, K60, W[66 & 0xf] = Wgen (W, 66));
+ SHA1STEP32 (d, e, a, b, c, Par, K60, W[67 & 0xf] = Wgen (W, 67));
+ SHA1STEP32 (c, d, e, a, b, Par, K60, W[68 & 0xf] = Wgen (W, 68));
+ SHA1STEP32 (b, c, d, e, a, Par, K60, W[69 & 0xf] = Wgen (W, 69));
+ SHA1STEP32 (a, b, c, d, e, Par, K60, W[70 & 0xf] = Wgen (W, 70));
+ SHA1STEP32 (e, a, b, c, d, Par, K60, W[71 & 0xf] = Wgen (W, 71));
+ SHA1STEP32 (d, e, a, b, c, Par, K60, W[72 & 0xf] = Wgen (W, 72));
+ SHA1STEP32 (c, d, e, a, b, Par, K60, W[73 & 0xf] = Wgen (W, 73));
+ SHA1STEP32 (b, c, d, e, a, Par, K60, W[74 & 0xf] = Wgen (W, 74));
+ SHA1STEP32 (a, b, c, d, e, Par, K60, W[75 & 0xf] = Wgen (W, 75));
+ SHA1STEP32 (e, a, b, c, d, Par, K60, W[76 & 0xf] = Wgen (W, 76));
+ SHA1STEP32 (d, e, a, b, c, Par, K60, W[77 & 0xf] = Wgen (W, 77));
+ SHA1STEP32 (c, d, e, a, b, Par, K60, W[78 & 0xf] = Wgen (W, 78));
+ SHA1STEP32 (b, c, d, e, a, Par, K60, W[79 & 0xf] = Wgen (W, 79));
+
+ /* Compute intermediate hash.
+ See FIPS PUB 180-4 paragraph 6.1.3 step 4. */
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+}
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param data bytes to add to hash
+ * @param length number of bytes in @a data
+ */
+void
+MHD_SHA1_update (void *ctx_,
+ const uint8_t *data,
+ size_t length)
+{
+ struct sha1_ctx *const ctx = ctx_;
+ unsigned bytes_have; /**< Number of bytes in buffer */
+
+ mhd_assert ((data != NULL) || (length == 0));
+
+ if (0 == length)
+ return; /* Do nothing */
+
+ /* Note: (count & (SHA1_BLOCK_SIZE-1))
+ equal (count % SHA1_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned) (ctx->count & (SHA1_BLOCK_SIZE - 1));
+ ctx->count += length;
+
+ if (0 != bytes_have)
+ {
+ unsigned bytes_left = SHA1_BLOCK_SIZE - bytes_have;
+ if (length >= bytes_left)
+ { /* Combine new data with the data in the buffer and
+ process the full block. */
+ memcpy (ctx->buffer + bytes_have,
+ data,
+ bytes_left);
+ data += bytes_left;
+ length -= bytes_left;
+ sha1_transform (ctx->H, ctx->buffer);
+ bytes_have = 0;
+ }
+ }
+
+ while (SHA1_BLOCK_SIZE <= length)
+ { /* Process any full blocks of new data directly,
+ without copying to the buffer. */
+ sha1_transform (ctx->H, data);
+ data += SHA1_BLOCK_SIZE;
+ length -= SHA1_BLOCK_SIZE;
+ }
+
+ if (0 != length)
+ { /* Copy incomplete block of new data (if any)
+ to the buffer. */
+ memcpy (ctx->buffer + bytes_have, data, length);
+ }
+}
+
+
+/**
+ * Size of "length" padding addition in bytes.
+ * See FIPS PUB 180-4 paragraph 5.1.1.
+ */
+#define SHA1_SIZE_OF_LEN_ADD (64 / 8)
+
+/**
+ * Finalise SHA-1 calculation, return digest.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param[out] digest set to the hash, must be #SHA1_DIGEST_SIZE bytes
+ */
+void
+MHD_SHA1_finish (void *ctx_,
+ uint8_t digest[SHA1_DIGEST_SIZE])
+{
+ struct sha1_ctx *const ctx = ctx_;
+ uint64_t num_bits; /**< Number of processed bits */
+ unsigned bytes_have; /**< Number of bytes in buffer */
+
+ num_bits = ctx->count << 3;
+ /* Note: (count & (SHA1_BLOCK_SIZE-1))
+ equals (count % SHA1_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned) (ctx->count & (SHA1_BLOCK_SIZE - 1));
+
+ /* Input data must be padded with bit "1" and with length of data in bits.
+ See FIPS PUB 180-4 paragraph 5.1.1. */
+ /* Data is always processed in form of bytes (not by individual bits),
+ therefore position of first padding bit in byte is always predefined (0x80). */
+ /* Buffer always have space at least for one byte (as full buffers are
+ processed immediately). */
+ ctx->buffer[bytes_have++] = 0x80;
+
+ if (SHA1_BLOCK_SIZE - bytes_have < SHA1_SIZE_OF_LEN_ADD)
+ { /* No space in current block to put total length of message.
+ Pad current block with zeros and process it. */
+ if (SHA1_BLOCK_SIZE > bytes_have)
+ memset (ctx->buffer + bytes_have, 0, SHA1_BLOCK_SIZE - bytes_have);
+ /* Process full block. */
+ sha1_transform (ctx->H, ctx->buffer);
+ /* Start new block. */
+ bytes_have = 0;
+ }
+
+ /* Pad the rest of the buffer with zeros. */
+ memset (ctx->buffer + bytes_have, 0,
+ SHA1_BLOCK_SIZE - SHA1_SIZE_OF_LEN_ADD - bytes_have);
+ /* Put the number of bits in the processed message as a big-endian value. */
+ _MHD_PUT_64BIT_BE_SAFE (ctx->buffer + SHA1_BLOCK_SIZE - SHA1_SIZE_OF_LEN_ADD,
+ num_bits);
+ /* Process the full final block. */
+ sha1_transform (ctx->H, ctx->buffer);
+
+ /* Put final hash/digest in BE mode */
+#ifndef _MHD_PUT_32BIT_BE_UNALIGNED
+ if (0 != ((uintptr_t) digest) % _MHD_UINT32_ALIGN)
+ {
+ uint32_t alig_dgst[_SHA1_DIGEST_LENGTH];
+ _MHD_PUT_32BIT_BE (alig_dgst + 0, ctx->H[0]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 1, ctx->H[1]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 2, ctx->H[2]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 3, ctx->H[3]);
+ _MHD_PUT_32BIT_BE (alig_dgst + 4, ctx->H[4]);
+ /* Copy result to unaligned destination address */
+ memcpy (digest, alig_dgst, SHA1_DIGEST_SIZE);
+ }
+ else
+#else /* _MHD_PUT_32BIT_BE_UNALIGNED */
+ if (1)
+#endif /* _MHD_PUT_32BIT_BE_UNALIGNED */
+ {
+ _MHD_PUT_32BIT_BE (digest + 0 * SHA1_BYTES_IN_WORD, ctx->H[0]);
+ _MHD_PUT_32BIT_BE (digest + 1 * SHA1_BYTES_IN_WORD, ctx->H[1]);
+ _MHD_PUT_32BIT_BE (digest + 2 * SHA1_BYTES_IN_WORD, ctx->H[2]);
+ _MHD_PUT_32BIT_BE (digest + 3 * SHA1_BYTES_IN_WORD, ctx->H[3]);
+ _MHD_PUT_32BIT_BE (digest + 4 * SHA1_BYTES_IN_WORD, ctx->H[4]);
+ }
+
+ /* Erase potentially sensitive data. */
+ memset (ctx, 0, sizeof(struct sha1_ctx));
+}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd_ws/sha1.h
^
|
@@ -0,0 +1,110 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2019-2021 Karlson2k (Evgeny Grin)
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file microhttpd/sha1.h
+ * @brief Calculation of SHA-1 digest
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_SHA1_H
+#define MHD_SHA1_H 1
+
+#include "mhd_options.h"
+#include <stdint.h>
+#ifdef HAVE_STDDEF_H
+#include <stddef.h> /* for size_t */
+#endif /* HAVE_STDDEF_H */
+
+/**
+ * SHA-1 digest is kept internally as 5 32-bit words.
+ */
+#define _SHA1_DIGEST_LENGTH 5
+
+/**
+ * Number of bits in single SHA-1 word
+ */
+#define SHA1_WORD_SIZE_BITS 32
+
+/**
+ * Number of bytes in single SHA-1 word
+ */
+#define SHA1_BYTES_IN_WORD (SHA1_WORD_SIZE_BITS / 8)
+
+/**
+ * Size of SHA-1 digest in bytes
+ */
+#define SHA1_DIGEST_SIZE (_SHA1_DIGEST_LENGTH * SHA1_BYTES_IN_WORD)
+
+/**
+ * Size of SHA-1 digest string in chars including termination NUL
+ */
+#define SHA1_DIGEST_STRING_SIZE ((SHA1_DIGEST_SIZE) * 2 + 1)
+
+/**
+ * Size of single processing block in bits
+ */
+#define SHA1_BLOCK_SIZE_BITS 512
+
+/**
+ * Size of single processing block in bytes
+ */
+#define SHA1_BLOCK_SIZE (SHA1_BLOCK_SIZE_BITS / 8)
+
+
+struct sha1_ctx
+{
+ uint32_t H[_SHA1_DIGEST_LENGTH]; /**< Intermediate hash value / digest at end of calculation */
+ uint8_t buffer[SHA1_BLOCK_SIZE]; /**< SHA256 input data buffer */
+ uint64_t count; /**< number of bytes, mod 2^64 */
+};
+
+/**
+ * Initialise structure for SHA-1 calculation.
+ *
+ * @param ctx must be a `struct sha1_ctx *`
+ */
+void
+MHD_SHA1_init (void *ctx_);
+
+
+/**
+ * Process portion of bytes.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param data bytes to add to hash
+ * @param length number of bytes in @a data
+ */
+void
+MHD_SHA1_update (void *ctx_,
+ const uint8_t *data,
+ size_t length);
+
+
+/**
+ * Finalise SHA-1 calculation, return digest.
+ *
+ * @param ctx_ must be a `struct sha1_ctx *`
+ * @param[out] digest set to the hash, must be #SHA1_DIGEST_SIZE bytes
+ */
+void
+MHD_SHA1_finish (void *ctx_,
+ uint8_t digest[SHA1_DIGEST_SIZE]);
+
+#endif /* MHD_SHA1_H */
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd_ws/test_websocket.c
^
|
@@ -0,0 +1,10105 @@
+/*
+ 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 <stdint.h>
+#include <time.h>
+
+#if SIZE_MAX >= 0x100000000
+ #define ENABLE_64BIT_TESTS 1
+#endif
+
+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);
+}
+
+
+/**
+ * Custom `rng()` function used for client mode tests
+ */
+static size_t
+test_rng (void *cls, void *buf, size_t buf_len)
+{
+ for (size_t i = 0; i < buf_len; ++i)
+ {
+ ((char *) buf) [i] = (char) (rand () % 0xFF);
+ }
+
+ return buf_len;
+}
+
+
+/**
+ * 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_init2 (&ws,
+ flags,
+ max_payload_size,
+ malloc,
+ realloc,
+ free,
+ NULL,
+ test_rng);
+ 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_init2()`
+ */
+int
+test_inits ()
+{
+ int failed = 0;
+ struct MHD_WebSocketStream *ws;
+ int ret;
+
+ /*
+ ------------------------------------------------------------------------------
+ All valid flags
+ ------------------------------------------------------------------------------
+ */
+ /* Regular test: all valid flags for init (only the even ones work) */
+ for (int i = 0; i < 7; ++i)
+ {
+ ws = NULL;
+ ret = MHD_websocket_stream_init (&ws,
+ i,
+ 0);
+ if (((0 == (i & MHD_WEBSOCKET_FLAG_CLIENT)) &&
+ ((MHD_WEBSOCKET_STATUS_OK != ret) ||
+ (NULL == ws))) ||
+ ((0 != (i & MHD_WEBSOCKET_FLAG_CLIENT)) &&
+ ((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,
+ NULL,
+ test_rng);
+ 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,
+ NULL,
+ NULL);
+ 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,
+ NULL,
+ NULL);
+ 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,
+ NULL,
+ NULL);
+ 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,
+ NULL,
+ NULL);
+ 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;
+ }
+#ifdef ENABLE_64BIT_TESTS
+ /* 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,
+ NULL,
+ NULL);
+ 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,
+ NULL,
+ NULL);
+ 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;
+ }
+#endif
+
+ /*
+ ------------------------------------------------------------------------------
+ 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,
+ NULL,
+ 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;
+ }
+ /* 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,
+ NULL,
+ 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;
+ }
+ /* 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,
+ NULL,
+ 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;
+ }
+ /* 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,
+ NULL,
+ 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;
+ }
+ /* Regular test: rng given for server mode (will be ignored) */
+ ws = NULL;
+ ret = MHD_websocket_stream_init2 (&ws,
+ MHD_WEBSOCKET_FLAG_SERVER
+ | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+ 0,
+ test_malloc,
+ test_realloc,
+ test_free,
+ NULL,
+ test_rng);
+ if ((MHD_WEBSOCKET_STATUS_OK != 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;
+ }
+ /* Regular test: cls_rng given for server mode (will be ignored) */
+ ws = NULL;
+ ret = MHD_websocket_stream_init2 (&ws,
+ MHD_WEBSOCKET_FLAG_SERVER
+ | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+ 0,
+ test_malloc,
+ test_realloc,
+ test_free,
+ (void *) 12345,
+ test_rng);
+ if ((MHD_WEBSOCKET_STATUS_OK != 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;
+ }
+ /* Regular test: rng given for client mode */
+ ws = NULL;
+ ret = MHD_websocket_stream_init2 (&ws,
+ MHD_WEBSOCKET_FLAG_CLIENT
+ | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+ 0,
+ test_malloc,
+ test_realloc,
+ test_free,
+ NULL,
+ test_rng);
+ if ((MHD_WEBSOCKET_STATUS_OK != 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: rng not given for client mode */
+ ws = NULL;
+ ret = MHD_websocket_stream_init2 (&ws,
+ MHD_WEBSOCKET_FLAG_CLIENT
+ | MHD_WEBSOCKET_FLAG_NO_FRAGMENTS,
+ 0,
+ test_malloc,
+ test_realloc,
+ test_free,
+ NULL,
+ NULL);
+ if ((MHD_WEBSOCKET_STATUS_PARAMETER_ERROR != ret) ||
+ (NULL != ws) )
+ {
+ fprintf (stderr,
+ "Init test failed in line %u %u.\n",
+ (unsigned int) __LINE__, ret);
+ ++failed;
+ }
+ if (NULL != ws)
+ {
+ MHD_websocket_stream_free (ws);
+ ws = NULL;
+ }
+
+ return failed != 0 ? 0x01 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_create_accept_header()`
+ */
+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_header ("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_header (NULL,
+ accept_key);
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != 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_header ("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_FIRST_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 with 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_FIRST_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: 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\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
+ 36,
+ "\x01\x02\x03",
+ 3,
+ MHD_WEBSOCKET_STATUS_BINARY_FIRST_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\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
+ 36,
+ "\x04\x05\x06",
+ 3,
+ MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT,
+ MHD_WEBSOCKET_VALIDITY_VALID,
+ 18);
+ /* Regular test: Fragmented binary frame without payload, fragments to the caller, 3rd call */
+ failed += test_decode_single (__LINE__,
+ MHD_WEBSOCKET_FLAG_SERVER
+ | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+ 0,
+ 3,
+ 0,
+ "\x02\x83\x00\x00\x00\x00\x01\x02\x03\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
+ 36,
+ "\x07\x08\x09",
+ 3,
+ MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT,
+ MHD_WEBSOCKET_VALIDITY_VALID,
+ 27);
+ /* Regular test: Fragmented binary frame without payload, fragments to the caller, 4th call */
+ failed += test_decode_single (__LINE__,
+ MHD_WEBSOCKET_FLAG_SERVER
+ | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS,
+ 0,
+ 4,
+ 0,
+ "\x02\x83\x00\x00\x00\x00\x01\x02\x03\x00\x83\x00\x00\x00\x00\x04\x05\x06\x00\x83\x00\x00\x00\x00\x07\x08\x09\x80\x83\x00\x00\x00\x00\x0A\x0B\x0C",
+ 36,
+ "\x0A\x0B\x0C",
+ 3,
+ MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT,
+ MHD_WEBSOCKET_VALIDITY_VALID,
+ 36);
+ /* 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_FIRST_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_FIRST_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);
+ /* Regular test: Fragmented, masked text frame, we are the server and want fragments, 1st 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\x00\x81\x3d\x37\xfa\x21\x51\x80\x81\x37\x37\xfa\x21\x58",
+ 23,
+ "Hel",
+ 3,
+ MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT,
+ MHD_WEBSOCKET_VALIDITY_VALID,
+ 9);
+ /* Regular test: Fragmented, masked text frame, we are the server and want fragments, 2nd 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\x00\x81\x3d\x37\xfa\x21\x51\x80\x81\x37\x37\xfa\x21\x58",
+ 23,
+ "l",
+ 1,
+ MHD_WEBSOCKET_STATUS_TEXT_NEXT_FRAGMENT,
+ MHD_WEBSOCKET_VALIDITY_VALID,
+ 16);
+ /* Regular test: Fragmented, masked text frame, we are the server and want fragments, 3rd 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\x00\x81\x3d\x37\xfa\x21\x51\x80\x81\x37\x37\xfa\x21\x58",
+ 23,
+ "o",
+ 1,
+ MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT,
+ MHD_WEBSOCKET_VALIDITY_VALID,
+ 23);
+
+
+ /*
+ ------------------------------------------------------------------------------
+ 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;
+ }
+#ifdef ENABLE_64BIT_TESTS
+ /* 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);
+#else
+ /* Edge test (fail): Maximum allowed length
+ (the size is allowed, but the system cannot handle this amount of memory) */
+ 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_MAXIMUM_SIZE_EXCEEDED,
+ MHD_WEBSOCKET_VALIDITY_INVALID,
+ 10);
+#endif
+ /* 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_FIRST_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 maximum 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 maximum 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_FIRST_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_FIRST_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_FIRST_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_FIRST_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_FIRST_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_FIRST_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;
+ }
+
+ MHD_websocket_stream_free (ws);
+ }
+ 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,
+ NULL,
+ NULL))
+ {
+ 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,
+ NULL,
+ NULL))
+ {
+ /* 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,
+ NULL,
+ NULL))
+ {
+ 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,
+ NULL,
+ NULL))
+ {
+ 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,
+ NULL,
+ NULL))
+ {
+ 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,
+ NULL,
+ NULL))
+ {
+ 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,
+ NULL,
+ NULL))
+ {
+ 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_init2 (&wsc,
+ MHD_WEBSOCKET_FLAG_CLIENT,
+ 0,
+ malloc,
+ realloc,
+ free,
+ NULL,
+ test_rng))
+ {
+ 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;
+ }
+#ifdef ENABLE_64BIT_TESTS
+ /* 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;
+ }
+#endif
+
+ /*
+ ------------------------------------------------------------------------------
+ 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,
+ NULL,
+ NULL))
+ {
+ /* 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_init2 (&wsc,
+ MHD_WEBSOCKET_FLAG_CLIENT,
+ 0,
+ malloc,
+ realloc,
+ free,
+ NULL,
+ test_rng))
+ {
+ 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;
+ }
+#ifdef ENABLE_64BIT_TESTS
+ /* 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;
+ }
+#endif
+
+ /*
+ ------------------------------------------------------------------------------
+ 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,
+ NULL,
+ NULL))
+ {
+ /* 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_init2 (&wsc,
+ MHD_WEBSOCKET_FLAG_CLIENT,
+ 0,
+ malloc,
+ realloc,
+ free,
+ NULL,
+ test_rng))
+ {
+ 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_init2 (&wss,
+ MHD_WEBSOCKET_FLAG_SERVER,
+ 0,
+ malloc,
+ realloc,
+ free,
+ NULL,
+ test_rng))
+ {
+ 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,
+ NULL,
+ NULL))
+ {
+ /* 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_init2 (&wsc,
+ MHD_WEBSOCKET_FLAG_CLIENT,
+ 0,
+ malloc,
+ realloc,
+ free,
+ NULL,
+ test_rng))
+ {
+ 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,
+ NULL,
+ NULL))
+ {
+ /* 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_init2 (&wsc,
+ MHD_WEBSOCKET_FLAG_CLIENT,
+ 0,
+ malloc,
+ realloc,
+ free,
+ NULL,
+ test_rng))
+ {
+ 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,
+ NULL,
+ NULL))
+ {
+ /* 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;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_check_http_version()`
+ */
+int
+test_check_http_version ()
+{
+ int failed = 0;
+ int ret;
+
+ /*
+ ------------------------------------------------------------------------------
+ Version check with valid HTTP version syntax
+ ------------------------------------------------------------------------------
+ */
+ /* Regular test: HTTP/1.1 */
+ ret = MHD_websocket_check_http_version ("HTTP/1.1");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Regular test: HTTP/1.2 */
+ ret = MHD_websocket_check_http_version ("HTTP/1.2");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Regular test: HTTP/1.10 */
+ ret = MHD_websocket_check_http_version ("HTTP/1.10");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Regular test: HTTP/2.0 */
+ ret = MHD_websocket_check_http_version ("HTTP/2.0");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Regular test: HTTP/3.0 */
+ ret = MHD_websocket_check_http_version ("HTTP/3.0");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: HTTP/1.0 */
+ ret = MHD_websocket_check_http_version ("HTTP/1.0");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: HTTP/0.9 */
+ ret = MHD_websocket_check_http_version ("HTTP/0.9");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Version check edge cases
+ ------------------------------------------------------------------------------
+ */
+ /* Edge test (success): HTTP/123.45 */
+ ret = MHD_websocket_check_http_version ("HTTP/123.45");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): HTTP/1.45 */
+ ret = MHD_websocket_check_http_version ("HTTP/1.45");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): HTTP/01.1 */
+ ret = MHD_websocket_check_http_version ("HTTP/01.1");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): HTTP/0001.1 */
+ ret = MHD_websocket_check_http_version ("HTTP/0001.1");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): HTTP/1.01 */
+ ret = MHD_websocket_check_http_version ("HTTP/1.01");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): HTTP/1.0001 */
+ ret = MHD_websocket_check_http_version ("HTTP/1.0001");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): HTTP/0001.0001 */
+ ret = MHD_websocket_check_http_version ("HTTP/0001.0001");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): HTTP/2.000 */
+ ret = MHD_websocket_check_http_version ("HTTP/2.000");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): HTTP/0.0 */
+ ret = MHD_websocket_check_http_version ("HTTP/0.0");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): HTTP/00.0 */
+ ret = MHD_websocket_check_http_version ("HTTP/00.0");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): HTTP/00.0 */
+ ret = MHD_websocket_check_http_version ("HTTP/0.00");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Invalid version syntax
+ ------------------------------------------------------------------------------
+ */
+ /* Fail test: (empty string) */
+ ret = MHD_websocket_check_http_version ("");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: http/1.1 */
+ ret = MHD_websocket_check_http_version ("http/1.1");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: "HTTP / 1.1" */
+ ret = MHD_websocket_check_http_version ("HTTP / 1.1");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Missing parameters
+ ------------------------------------------------------------------------------
+ */
+ /* Fail test: NULL as version */
+ ret = MHD_websocket_check_http_version (NULL);
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_http_version test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ return failed != 0 ? 0x200 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_check_connection_header()`
+ */
+int
+test_check_connection_header ()
+{
+ int failed = 0;
+ int ret;
+
+ /*
+ ------------------------------------------------------------------------------
+ Check with valid Connection header syntax
+ ------------------------------------------------------------------------------
+ */
+ /* Regular test: Upgrade */
+ ret = MHD_websocket_check_connection_header ("Upgrade");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Regular test: keep-alive, Upgrade */
+ ret = MHD_websocket_check_connection_header ("keep-alive, Upgrade");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: keep-alive */
+ ret = MHD_websocket_check_connection_header ("keep-alive");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: close */
+ ret = MHD_websocket_check_connection_header ("close");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Connection check edge cases
+ ------------------------------------------------------------------------------
+ */
+ /* Edge test (success): keep-alive,Upgrade */
+ ret = MHD_websocket_check_connection_header ("keep-alive,Upgrade");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): Upgrade, keep-alive */
+ ret = MHD_websocket_check_connection_header ("Upgrade, keep-alive");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): Upgrade,keep-alive */
+ ret = MHD_websocket_check_connection_header ("Upgrade,keep-alive");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): Transfer-Encoding,Upgrade,keep-alive */
+ ret = MHD_websocket_check_connection_header (
+ "Transfer-Encoding,Upgrade,keep-alive");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): Transfer-Encoding , Upgrade , keep-alive */
+ ret = MHD_websocket_check_connection_header (
+ "Transfer-Encoding , Upgrade , keep-alive");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): upgrade */
+ ret = MHD_websocket_check_connection_header ("upgrade");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): UPGRADE */
+ ret = MHD_websocket_check_connection_header ("UPGRADE");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): All allowed token characters, then upgrade token */
+ ret = MHD_websocket_check_connection_header (
+ "!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz,Upgrade");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): Different, allowed whitespaces */
+ ret = MHD_websocket_check_connection_header (" \tUpgrade \t");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): Different, disallowed whitespaces */
+ ret = MHD_websocket_check_connection_header ("\rUpgrade");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): Different, disallowed whitespaces */
+ ret = MHD_websocket_check_connection_header ("\nUpgrade");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): Different, disallowed whitespaces */
+ ret = MHD_websocket_check_connection_header ("\vUpgrade");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): Different, disallowed whitespaces */
+ ret = MHD_websocket_check_connection_header ("\fUpgrade");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Invalid header syntax
+ ------------------------------------------------------------------------------
+ */
+ /* Fail test: (empty string) */
+ ret = MHD_websocket_check_connection_header ("");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: (Disallowed) multiple word token with the term "Upgrade" in it */
+ ret = MHD_websocket_check_connection_header ("Upgrade or Downgrade");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: Invalid characters */
+ ret = MHD_websocket_check_connection_header ("\"Upgrade\"");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Missing parameters
+ ------------------------------------------------------------------------------
+ */
+ /* Fail test: NULL as connection */
+ ret = MHD_websocket_check_connection_header (NULL);
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_connection_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ return failed != 0 ? 0x400 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_check_upgrade_header()`
+ */
+int
+test_check_upgrade_header ()
+{
+ int failed = 0;
+ int ret;
+
+ /*
+ ------------------------------------------------------------------------------
+ Check with valid Upgrade header syntax
+ ------------------------------------------------------------------------------
+ */
+ /* Regular test: websocket */
+ ret = MHD_websocket_check_upgrade_header ("websocket");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: HTTP/2.0 */
+ ret = MHD_websocket_check_upgrade_header ("HTTP/2.0");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Upgrade check edge cases
+ ------------------------------------------------------------------------------
+ */
+ /* Edge test (success): websocket,HTTP/2.0 */
+ ret = MHD_websocket_check_upgrade_header ("websocket,HTTP/2.0");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): websocket ,HTTP/2.0 */
+ ret = MHD_websocket_check_upgrade_header (" websocket ,HTTP/2.0");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): HTTP/2.0, websocket */
+ ret = MHD_websocket_check_upgrade_header ("HTTP/2.0, websocket ");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): websocket/13 */
+ ret = MHD_websocket_check_upgrade_header ("websocket/13");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): WeBsOcKeT */
+ ret = MHD_websocket_check_upgrade_header ("WeBsOcKeT");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): WEBSOCKET */
+ ret = MHD_websocket_check_upgrade_header ("WEBSOCKET");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): All allowed token characters plus /, then websocket keyowrd */
+ ret = MHD_websocket_check_upgrade_header (
+ "!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/,websocket");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (success): Different, allowed whitespaces */
+ ret = MHD_websocket_check_upgrade_header (" \twebsocket \t");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): Different, disallowed whitespaces */
+ ret = MHD_websocket_check_upgrade_header ("\rwebsocket");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): Different, disallowed whitespaces */
+ ret = MHD_websocket_check_upgrade_header ("\nwebsocket");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): Different, disallowed whitespaces */
+ ret = MHD_websocket_check_upgrade_header ("\vwebsocket");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): Different, disallowed whitespaces */
+ ret = MHD_websocket_check_upgrade_header ("\fwebsocket");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Invalid header syntax
+ ------------------------------------------------------------------------------
+ */
+ /* Fail test: (empty string) */
+ ret = MHD_websocket_check_upgrade_header ("");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: (Disallowed) multiple word token with the term "websocket" in it */
+ ret = MHD_websocket_check_upgrade_header ("websocket or something");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: Invalid characters */
+ ret = MHD_websocket_check_upgrade_header ("\"websocket\"");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Missing parameters
+ ------------------------------------------------------------------------------
+ */
+ /* Fail test: NULL as upgrade */
+ ret = MHD_websocket_check_upgrade_header (NULL);
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_upgrade_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ return failed != 0 ? 0x800 : 0x00;
+}
+
+
+/**
+ * Test procedure for `MHD_websocket_check_version_header()`
+ */
+int
+test_check_version_header ()
+{
+ int failed = 0;
+ int ret;
+
+ /*
+ ------------------------------------------------------------------------------
+ Check with valid Upgrade header syntax
+ ------------------------------------------------------------------------------
+ */
+ /* Regular test: 13 */
+ ret = MHD_websocket_check_version_header ("13");
+ if (MHD_WEBSOCKET_STATUS_OK != ret)
+ {
+ fprintf (stderr,
+ "check_version_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Version check edge cases
+ ------------------------------------------------------------------------------
+ */
+ /* Edge test (fail): 14 */
+ ret = MHD_websocket_check_version_header ("14");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_version_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): 12 */
+ ret = MHD_websocket_check_version_header ("12");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_version_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): 0 */
+ ret = MHD_websocket_check_version_header ("1");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_version_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): 1 */
+ ret = MHD_websocket_check_version_header ("1");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_version_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): 130 */
+ ret = MHD_websocket_check_version_header ("130");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_version_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Edge test (fail): " 13" */
+ ret = MHD_websocket_check_version_header (" 13");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_version_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Invalid header syntax
+ ------------------------------------------------------------------------------
+ */
+ /* Fail test: (empty string) */
+ ret = MHD_websocket_check_version_header ("");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_version_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+ /* Fail test: Invalid characters */
+ ret = MHD_websocket_check_version_header ("abc");
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_version_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ /*
+ ------------------------------------------------------------------------------
+ Missing parameters
+ ------------------------------------------------------------------------------
+ */
+ /* Fail test: NULL as version */
+ ret = MHD_websocket_check_version_header (NULL);
+ if (MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER != ret)
+ {
+ fprintf (stderr,
+ "check_version_header test failed in line %u.\n",
+ (unsigned int) __LINE__);
+ ++failed;
+ }
+
+ return failed != 0 ? 0x1000 : 0x00;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+ unsigned int errorCount = 0;
+ (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+
+ /* seed random number generator */
+ 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 ();
+ errorCount += test_check_http_version ();
+ errorCount += test_check_connection_header ();
+ errorCount += test_check_upgrade_header ();
+ errorCount += test_check_version_header ();
+
+ /* output result */
+ if (errorCount != 0)
+ fprintf (stderr, "Error (code: %u)\n", errorCount);
+
+ return errorCount != 0; /* 0 == pass */
+}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/microhttpd_ws/test_websocket_browser.c
^
|
@@ -0,0 +1,570 @@
+/*
+ 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_browser.c
+ * @brief Testcase for WebSocket decoding/encoding with external browser
+ * @author David Gausmann
+ */
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#else
+#include <winsock2.h>
+#endif
+#include "microhttpd.h"
+#include "microhttpd_ws.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <time.h>
+#include <errno.h>
+
+#define PORT 80
+
+#define PAGE \
+ "<!DOCTYPE html>\n" \
+ "<html>\n" \
+ "<head>\n" \
+ "<meta charset=\"UTF-8\">\n" \
+ "<title>Websocket External Test with Webbrowser</title>\n" \
+ "<script>\n" \
+ "\n" \
+ "let current_mode = 0;\n" \
+ "let current_step = 0;\n" \
+ "let sent_payload = null;\n" \
+ "let charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_!@%&/\\\\';\n" \
+ "let step_to_bytes = [ 0, 1, 2, 3, 122, 123, 124, 125, 126, 127, 128, 32766, 32767, 32768, 65534, 65535, 65536, 65537, 1048576, 10485760 ];\n" \
+ "let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
+ " + '://' +\n" \
+ " window.location.host + '/websocket';\n" \
+ "let socket = null;\n" \
+ "\n" \
+ "window.onload = function (event) {\n" \
+ " if (!window.WebSocket) {\n" \
+ " document.write ('ERROR: The WebSocket class is not supported by your browser.<br>');\n" \
+ " }\n" \
+ " if (!window.fetch) {\n" \
+ " document.write ('ERROR: The fetch-API is not supported by your browser.<br>');\n" \
+ " }\n" \
+ " document.write ('Starting tests.<br>');\n" \
+ " runTest ();\n" \
+ "}\n" \
+ "\n" \
+ "function runTest () {\n" \
+ " switch (current_mode) {\n" \
+ " case 0:\n" \
+ " document.write ('TEXT');\n" \
+ " break;\n" \
+ " case 1:\n" \
+ " document.write ('BINARY');\n" \
+ " break;\n" \
+ " }\n" \
+ " document.write (', ' + step_to_bytes[current_step] + ' Bytes: ');\n" \
+ " socket = new WebSocket(url);\n" \
+ " socket.binaryType = 'arraybuffer';\n" \
+ " socket.onopen = function (event) {\n" \
+ " switch (current_mode) {\n" \
+ " case 0:\n" \
+ " sent_payload = randomText (step_to_bytes[current_step]);\n" \
+ " socket.send (sent_payload);\n" \
+ " break;\n" \
+ " case 1:\n" \
+ " sent_payload = randomBinary (step_to_bytes[current_step]);\n" \
+ " socket.send (sent_payload);\n" \
+ " break;\n" \
+ " }\n" \
+ " }\n" \
+ "\n" \
+ " socket.onclose = function (event) {\n" \
+ " socket.onmessage = null;\n" \
+ " socket.onclose = null;\n" \
+ " socket.onerror = null;\n" \
+ " document.write ('CLOSED unexpectedly.<br>');\n" \
+ " notifyError ();\n" \
+ " }\n" \
+ "\n" \
+ " socket.onerror = function (event) {\n" \
+ " socket.onmessage = null;\n" \
+ " socket.onclose = null;\n" \
+ " socket.onerror = null;\n" \
+ " document.write ('ERROR.<br>');\n" \
+ " notifyError ();\n" \
+ " }\n" \
+ "\n" \
+ " socket.onmessage = async function (event) {\n" \
+ " if (compareData (event.data, sent_payload)) {\n" \
+ " document.write ('SUCCESS.<br>');\n" \
+ " socket.onmessage = null;\n" \
+ " socket.onclose = null;\n" \
+ " socket.onerror = null;\n" \
+ " socket.close();\n" \
+ " socket = null;\n" \
+ " if (step_to_bytes.length <= ++current_step) {\n" \
+ " current_step = 0;\n" \
+ " if (1 < ++current_mode) {\n" \
+ " document.write ('FINISHED ALL TESTS.<br>');\n" \
+ " return;\n" \
+ " }\n" \
+ " }\n" \
+ " runTest ();\n" \
+ " }" \
+ " }\n" \
+ "}\n" \
+ "\n" \
+ "function compareData (data, data2) {\n" \
+ " if (typeof (data) === 'string' && typeof (data2) === 'string') {\n" \
+ " return (data === data2); \n" \
+ " } \n" \
+ " else if ((data instanceof ArrayBuffer) && (data2 instanceof ArrayBuffer)) {\n" \
+ " let view1 = new Uint8Array (data);\n" \
+ " let view2 = new Uint8Array (data2);\n" \
+ " if (view1.length != view2.length)\n" \
+ " return false;\n" \
+ " for (let i = 0; i < view1.length; ++i) {\n" \
+ " if (view1[i] !== view2[i])\n" \
+ " return false;\n" \
+ " }\n" \
+ " return true;\n" \
+ " }\n" \
+ " else\n" \
+ " {\n" \
+ " return false;\n" \
+ " }\n" \
+ "}\n" \
+ "\n" \
+ "function randomText (length) {\n" \
+ " let result = new Array (length);\n" \
+ " for (let i = 0; i < length; ++i)\n" \
+ " result [i] = charset [~~(Math.random () * charset.length)];\n" \
+ " return result.join ('');\n" \
+ "}\n" \
+ "\n" \
+ "function randomBinary (length) {\n" \
+ " let buffer = new ArrayBuffer (length);\n" \
+ " let view = new Uint8Array (buffer);\n" \
+ " for (let i = 0; i < length; ++i)\n" \
+ " view [i] = ~~(Math.random () * 256);\n" \
+ " return buffer;\n" \
+ "}\n" \
+ "\n" \
+ "function notifyError () {\n" \
+ " fetch('error/' + (0 == current_mode ? 'text' : 'binary') + '/' + step_to_bytes[current_step]);\n" \
+ "}\n" \
+ "\n" \
+ "</script>\n" \
+ "</head>\n" \
+ "<body>\n" \
+ "</body>\n" \
+ "</html>"
+
+#define PAGE_NOT_FOUND \
+ "404 Not Found"
+
+#define PAGE_INVALID_WEBSOCKET_REQUEST \
+ "Invalid WebSocket request!"
+
+static void
+send_all (MHD_socket fd,
+ const char *buf,
+ size_t len);
+
+static void
+make_blocking (MHD_socket fd);
+
+static void
+upgrade_handler (void *cls,
+ struct MHD_Connection *connection,
+ void *con_cls,
+ const char *extra_in,
+ size_t extra_in_size,
+ MHD_socket fd,
+ struct MHD_UpgradeResponseHandle *urh)
+{
+ /* make the socket blocking (operating-system-dependent code) */
+ make_blocking (fd);
+
+ /* create a websocket stream for this connection */
+ struct MHD_WebSocketStream *ws;
+ int result = MHD_websocket_stream_init (&ws,
+ 0,
+ 0);
+ if (0 != result)
+ {
+ /* Couldn't create the websocket stream.
+ * So we close the socket and leave
+ */
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ return;
+ }
+
+ /* Let's wait for incoming data */
+ const size_t buf_len = 256;
+ char buf[buf_len];
+ ssize_t got;
+ while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
+ {
+ got = recv (fd,
+ buf,
+ sizeof (buf),
+ 0);
+ if (0 >= got)
+ {
+ /* the TCP/IP socket has been closed */
+ fprintf (stderr,
+ "Error (The socket has been closed unexpectedly)\n");
+ break;
+ }
+
+ /* parse the entire received data */
+ size_t buf_offset = 0;
+ while (buf_offset < (size_t) got)
+ {
+ size_t new_offset = 0;
+ char *payload_data = NULL;
+ size_t payload_len = 0;
+ char *frame_data = NULL;
+ size_t frame_len = 0;
+ int status = MHD_websocket_decode (ws,
+ buf + buf_offset,
+ ((size_t) got) - buf_offset,
+ &new_offset,
+ &payload_data,
+ &payload_len);
+ if (0 > status)
+ {
+ /* an error occurred and the connection must be closed */
+ printf ("Decoding failed: status=%d, passed=%u\n", status,
+ ((size_t) got) - buf_offset);
+ if (NULL != payload_data)
+ {
+ MHD_websocket_free (ws, payload_data);
+ }
+ break;
+ }
+ else
+ {
+ buf_offset += new_offset;
+ if (0 < status)
+ {
+ /* the frame is complete */
+ printf (
+ "Decoding succeeded: type=%d, passed=%u, parsed=%u, payload_len=%d\n",
+ status, ((size_t) got) - buf_offset, new_offset, payload_len);
+ switch (status)
+ {
+ case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
+ case MHD_WEBSOCKET_STATUS_BINARY_FRAME:
+ /* The client has sent some data. */
+ if ((NULL != payload_data) || (0 == payload_len))
+ {
+ /* Send the received data back to the client */
+ if (MHD_WEBSOCKET_STATUS_TEXT_FRAME == status)
+ {
+ result = MHD_websocket_encode_text (ws,
+ payload_data,
+ payload_len,
+ 0,
+ &frame_data,
+ &frame_len,
+ NULL);
+ }
+ else
+ {
+ result = MHD_websocket_encode_binary (ws,
+ payload_data,
+ payload_len,
+ 0,
+ &frame_data,
+ &frame_len);
+ }
+ if (0 == result)
+ {
+ send_all (fd,
+ frame_data,
+ frame_len);
+ }
+ }
+ else
+ {
+ /* should never happen */
+ fprintf (stderr,
+ "Error (Empty buffer with payload_len != 0)\n");
+ }
+ break;
+
+ default:
+ /* Other frame types are ignored
+ * in this test script.
+ */
+ break;
+ }
+ }
+ if (NULL != payload_data)
+ {
+ MHD_websocket_free (ws, payload_data);
+ }
+ if (NULL != frame_data)
+ {
+ MHD_websocket_free (ws, frame_data);
+ }
+ }
+ }
+ }
+
+ /* free the websocket stream */
+ MHD_websocket_stream_free (ws);
+
+ /* close the socket when it is not needed anymore */
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+}
+
+
+/* This helper function is used for the case that
+ * we need to resend some data
+ */
+static void
+send_all (MHD_socket fd,
+ const char *buf,
+ size_t len)
+{
+ ssize_t ret;
+ size_t off;
+
+ for (off = 0; off < len; off += ret)
+ {
+ ret = send (fd,
+ &buf[off],
+ (int) (len - off),
+ 0);
+ if (0 > ret)
+ {
+ if (EAGAIN == errno)
+ {
+ ret = 0;
+ continue;
+ }
+ break;
+ }
+ if (0 == ret)
+ break;
+ }
+}
+
+
+/* This helper function contains operating-system-dependent code and
+ * is used to make a socket blocking.
+ */
+static void
+make_blocking (MHD_socket fd)
+{
+#ifndef _WIN32
+ int flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (-1 == flags)
+ return;
+ if ((flags & ~O_NONBLOCK) != flags)
+ if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+ abort ();
+#else
+ unsigned long flags = 0;
+
+ ioctlsocket (fd, FIONBIO, &flags);
+#endif
+}
+
+
+static enum MHD_Result
+access_handler (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **ptr)
+{
+ static int aptr;
+ struct MHD_Response *response;
+ int ret;
+
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) upload_data; /* Unused. Silent compiler warning. */
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO; /* unexpected method */
+ if (&aptr != *ptr)
+ {
+ /* do never respond on first call */
+ *ptr = &aptr;
+ return MHD_YES;
+ }
+ *ptr = NULL; /* reset when done */
+
+ if (0 == strcmp (url, "/"))
+ {
+ /* Default page for visiting the server */
+ struct MHD_Response *response = MHD_create_response_from_buffer (
+ strlen (PAGE),
+ PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ }
+ else if (0 == strncmp (url, "/error/", 7))
+ {
+ /* Report error */
+ fprintf (stderr, "Error in test (%s)\n", url + 7);
+
+ struct MHD_Response *response = MHD_create_response_from_buffer (
+ 0,
+ "",
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ }
+ else if (0 == strcmp (url, "/websocket"))
+ {
+ char is_valid = 1;
+ const char *value = NULL;
+ char sec_websocket_accept[29];
+
+ if (0 != MHD_websocket_check_http_version (version))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONNECTION);
+ if (0 != MHD_websocket_check_connection_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_UPGRADE);
+ if (0 != MHD_websocket_check_upgrade_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
+ if (0 != MHD_websocket_check_version_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
+ if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
+ {
+ is_valid = 0;
+ }
+
+ if (1 == is_valid)
+ {
+ /* upgrade the connection */
+ response = MHD_create_response_for_upgrade (&upgrade_handler,
+ NULL);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONNECTION,
+ "Upgrade");
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_UPGRADE,
+ "websocket");
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
+ sec_websocket_accept);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_SWITCHING_PROTOCOLS,
+ response);
+ MHD_destroy_response (response);
+ }
+ else
+ {
+ /* return error page */
+ struct MHD_Response *response = MHD_create_response_from_buffer (
+ strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
+ PAGE_INVALID_WEBSOCKET_REQUEST,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_BAD_REQUEST,
+ response);
+ MHD_destroy_response (response);
+ }
+ }
+ else
+ {
+ struct MHD_Response *response = MHD_create_response_from_buffer (
+ strlen (PAGE_NOT_FOUND),
+ PAGE_NOT_FOUND,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_FOUND,
+ response);
+ MHD_destroy_response (response);
+ }
+
+ return ret;
+}
+
+
+int
+main (int argc,
+ char *const *argv)
+{
+ (void) argc; /* Unused. Silent compiler warning. */
+ (void) argv; /* Unused. Silent compiler warning. */
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
+ | MHD_USE_THREAD_PER_CONNECTION
+ | MHD_ALLOW_UPGRADE
+ | MHD_USE_ERROR_LOG,
+ PORT, NULL, NULL,
+ &access_handler, NULL,
+ MHD_OPTION_END);
+
+ if (NULL == daemon)
+ {
+ fprintf (stderr, "Error (Couldn't start daemon for testing)\n");
+ return 1;
+ }
+ printf ("The server is listening now.\n");
+ printf ("Access the server now with a websocket-capable webbrowser.\n\n");
+ printf ("Press return to close.\n");
+
+ (void) getc (stdin);
+
+ MHD_stop_daemon (daemon);
+
+ return 0;
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/.gitignore
^
|
@@ -24,8 +24,27 @@
/test_iplimit11
/test_get_sendfile11
/test_get_sendfile
+/test_get_close
+/test_get_close10
+/test_get_keep_alive
+/test_get_keep_alive10
/test_get_response_cleanup
/test_get_chunked
+/test_get_chunked_close
+/test_get_chunked_string
+/test_get_chunked_close_string
+/test_get_chunked_empty
+/test_get_chunked_close_empty
+/test_get_chunked_string_empty
+/test_get_chunked_close_string_empty
+/test_get_chunked_sized
+/test_get_chunked_close_sized
+/test_get_chunked_empty_sized
+/test_get_chunked_close_empty_sized
+/test_get_chunked_forced
+/test_get_chunked_close_forced
+/test_get_chunked_empty_forced
+/test_get_chunked_close_empty_forced
/test_get11
/test_get
/test_digestauth_with_arguments
@@ -120,3 +139,13 @@
/test_get_iovec11
/test_get_wait
/test_get_wait11
+/test_toolarge_method
+/test_toolarge_url
+/test_toolarge_request_header_name
+/test_toolarge_request_header_value
+/test_toolarge_request_headers
+/test_toolarge_reply_header_name
+/test_toolarge_reply_header_value
+/test_toolarge_reply_headers
+/test_tricky_url
+/test_tricky_header2
\ No newline at end of file
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/Makefile.am
^
|
@@ -1,6 +1,8 @@
# This Makefile.am is in the public domain
EMPTY_ITEM =
+@HEAVY_TESTS_NOTPARALLEL@
+
SUBDIRS = .
if USE_COVERAGE
@@ -18,6 +20,9 @@
-I$(top_srcdir)/src/include \
$(LIBCURL_CPPFLAGS)
+LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+ @LIBCURL@
THREAD_ONLY_TESTS = \
test_urlparse \
@@ -82,6 +87,10 @@
test_get \
test_get_iovec \
test_get_sendfile \
+ test_get_close \
+ test_get_close10 \
+ test_get_keep_alive \
+ test_get_keep_alive10 \
test_delete \
test_patch \
test_put \
@@ -90,6 +99,16 @@
test_process_headers \
test_process_arguments \
test_parse_cookies \
+ test_toolarge_method \
+ test_toolarge_url \
+ test_toolarge_request_header_name \
+ test_toolarge_request_header_value \
+ test_toolarge_request_headers \
+ test_toolarge_reply_header_name \
+ test_toolarge_reply_header_value \
+ test_toolarge_reply_headers \
+ test_tricky_url \
+ test_tricky_header2 \
test_large_put \
test_get11 \
test_get_iovec11 \
@@ -99,6 +118,21 @@
test_large_put11 \
test_large_put_inc11 \
test_get_chunked \
+ test_get_chunked_close \
+ test_get_chunked_string \
+ test_get_chunked_close_string \
+ test_get_chunked_empty \
+ test_get_chunked_close_empty \
+ test_get_chunked_string_empty \
+ test_get_chunked_close_string_empty \
+ test_get_chunked_sized \
+ test_get_chunked_close_sized \
+ test_get_chunked_empty_sized \
+ test_get_chunked_close_empty_sized \
+ test_get_chunked_forced \
+ test_get_chunked_close_forced \
+ test_get_chunked_empty_forced \
+ test_get_chunked_close_empty_forced \
test_put_chunked \
test_callback \
$(EMPTY_ITEM)
@@ -136,55 +170,38 @@
endif
TESTS = $(check_PROGRAMS)
-
-noinst_LIBRARIES = libcurl_version_check.a
endif
-libcurl_version_check_a_SOURCES = \
- curl_version_check.c
-
test_concurrent_stop_SOURCES = \
test_concurrent_stop.c
test_concurrent_stop_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_concurrent_stop_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(PTHREAD_LIBS) @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_get_SOURCES = \
test_get.c
-test_get_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_quiesce_SOURCES = \
test_quiesce.c
test_quiesce_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_quiesce_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(PTHREAD_LIBS) @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_quiesce_stream_SOURCES = \
test_quiesce_stream.c
test_quiesce_stream_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_quiesce_stream_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(PTHREAD_LIBS) @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_callback_SOURCES = \
test_callback.c
-test_callback_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
perf_get_SOURCES = \
perf_get.c \
gauger.h mhd_has_in_name.h
-perf_get_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
perf_get_concurrent_SOURCES = \
perf_get_concurrent.c \
@@ -192,8 +209,7 @@
perf_get_concurrent_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
perf_get_concurrent_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(PTHREAD_LIBS) @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
perf_get_concurrent11_SOURCES = \
perf_get_concurrent.c \
@@ -201,38 +217,28 @@
perf_get_concurrent11_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
perf_get_concurrent11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(PTHREAD_LIBS) @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_digestauth_SOURCES = \
test_digestauth.c
test_digestauth_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBGCRYPT_LIBS@ @LIBCURL@
+ @LIBGCRYPT_LIBS@ $(LDADD)
test_digestauth_sha256_SOURCES = \
test_digestauth_sha256.c
test_digestauth_sha256_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBGCRYPT_LIBS@ @LIBCURL@
+ @LIBGCRYPT_LIBS@ $(LDADD)
test_digestauth_with_arguments_SOURCES = \
test_digestauth_with_arguments.c
test_digestauth_with_arguments_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBGCRYPT_LIBS@ @LIBCURL@
+ @LIBGCRYPT_LIBS@ $(LDADD)
test_get_iovec_SOURCES = \
test_get_iovec.c mhd_has_in_name.h
-test_get_iovec_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_get_sendfile_SOURCES = \
test_get_sendfile.c mhd_has_in_name.h
-test_get_sendfile_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_get_wait_SOURCES = \
test_get_wait.c \
@@ -240,8 +246,7 @@
test_get_wait_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_get_wait_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(PTHREAD_LIBS) @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_get_wait11_SOURCES = \
test_get_wait.c \
@@ -249,14 +254,10 @@
test_get_wait11_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_get_wait11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(PTHREAD_LIBS) @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_urlparse_SOURCES = \
test_urlparse.c mhd_has_in_name.h
-test_urlparse_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_get_response_cleanup_SOURCES = \
test_get_response_cleanup.c mhd_has_in_name.h
@@ -265,194 +266,201 @@
test_get_chunked_SOURCES = \
test_get_chunked.c
-test_get_chunked_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
+
+test_get_chunked_close_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_string_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_close_string_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_empty_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_close_empty_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_string_empty_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_close_string_empty_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_sized_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_close_sized_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_empty_sized_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_close_empty_sized_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_forced_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_close_forced_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_empty_forced_SOURCES = \
+ test_get_chunked.c
+
+test_get_chunked_close_empty_forced_SOURCES = \
+ test_get_chunked.c
test_post_SOURCES = \
test_post.c mhd_has_in_name.h
-test_post_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_process_headers_SOURCES = \
test_process_headers.c mhd_has_in_name.h
-test_process_headers_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_parse_cookies_SOURCES = \
test_parse_cookies.c mhd_has_in_name.h
-test_parse_cookies_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_process_arguments_SOURCES = \
test_process_arguments.c mhd_has_in_name.h
-test_process_arguments_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_postform_SOURCES = \
test_postform.c mhd_has_in_name.h
test_postform_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBGCRYPT_LIBS@ @LIBCURL@
+ @LIBGCRYPT_LIBS@ $(LDADD)
test_post_loop_SOURCES = \
test_post_loop.c mhd_has_in_name.h
-test_post_loop_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_delete_SOURCES = \
test_delete.c mhd_has_in_name.h
-test_delete_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_patch_SOURCES = \
test_patch.c mhd_has_in_name.h
-test_patch_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_patch11_SOURCES = \
test_patch.c mhd_has_in_name.h
-test_patch11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_put_SOURCES = \
test_put.c mhd_has_in_name.h
-test_put_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_put_chunked_SOURCES = \
test_put_chunked.c
-test_put_chunked_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_add_conn_SOURCES = \
test_add_conn.c $(top_srcdir)/src/microhttpd/test_helpers.h
test_add_conn_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_add_conn_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_add_conn_nolisten_SOURCES = \
test_add_conn.c $(top_srcdir)/src/microhttpd/test_helpers.h
test_add_conn_nolisten_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_add_conn_nolisten_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_add_conn_cleanup_SOURCES = \
test_add_conn.c $(top_srcdir)/src/microhttpd/test_helpers.h
test_add_conn_cleanup_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_add_conn_cleanup_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_add_conn_cleanup_nolisten_SOURCES = \
test_add_conn.c $(top_srcdir)/src/microhttpd/test_helpers.h
test_add_conn_cleanup_nolisten_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_add_conn_cleanup_nolisten_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_get11_SOURCES = \
test_get.c
-test_get11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_get_iovec11_SOURCES = \
test_get_iovec.c mhd_has_in_name.h
-test_get_iovec11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_get_sendfile11_SOURCES = \
test_get_sendfile.c mhd_has_in_name.h
-test_get_sendfile11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
+
+test_get_close_SOURCES = \
+ test_get_close_keep_alive.c $(top_srcdir)/src/microhttpd/test_helpers.h
+
+test_get_close10_SOURCES = \
+ test_get_close_keep_alive.c $(top_srcdir)/src/microhttpd/test_helpers.h
+
+test_get_keep_alive_SOURCES = \
+ test_get_close_keep_alive.c $(top_srcdir)/src/microhttpd/test_helpers.h
+
+test_get_keep_alive10_SOURCES = \
+ test_get_close_keep_alive.c $(top_srcdir)/src/microhttpd/test_helpers.h
test_post11_SOURCES = \
test_post.c mhd_has_in_name.h
-test_post11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_postform11_SOURCES = \
test_postform.c mhd_has_in_name.h
test_postform11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBGCRYPT_LIBS@ @LIBCURL@
+ @LIBGCRYPT_LIBS@ $(LDADD)
test_post_loop11_SOURCES = \
test_post_loop.c mhd_has_in_name.h
-test_post_loop11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_put11_SOURCES = \
test_put.c mhd_has_in_name.h
-test_put11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_large_put_SOURCES = \
test_large_put.c
-test_large_put_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_large_put11_SOURCES = \
test_large_put.c
-test_large_put11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_large_put_inc11_SOURCES = \
test_large_put.c
-test_large_put_inc11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_long_header_SOURCES = \
test_long_header.c mhd_has_in_name.h
-test_long_header_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_long_header11_SOURCES = \
test_long_header.c mhd_has_in_name.h
-test_long_header11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_iplimit11_SOURCES = \
test_iplimit.c mhd_has_in_name.h
-test_iplimit11_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_termination_SOURCES = \
test_termination.c
-test_termination_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
test_timeout_SOURCES = \
test_timeout.c mhd_has_in_name.h
-test_timeout_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- @LIBCURL@
+
+test_toolarge_method_SOURCES = \
+ test_toolarge.c ../microhttpd/test_helpers.h
+
+test_toolarge_url_SOURCES = \
+ test_toolarge.c ../microhttpd/test_helpers.h
+
+test_toolarge_request_header_name_SOURCES = \
+ test_toolarge.c ../microhttpd/test_helpers.h
+
+test_toolarge_request_header_value_SOURCES = \
+ test_toolarge.c ../microhttpd/test_helpers.h
+
+test_toolarge_request_headers_SOURCES = \
+ test_toolarge.c ../microhttpd/test_helpers.h
+
+test_toolarge_reply_header_name_SOURCES = \
+ test_toolarge.c ../microhttpd/test_helpers.h
+
+test_toolarge_reply_header_value_SOURCES = \
+ test_toolarge.c ../microhttpd/test_helpers.h
+
+test_toolarge_reply_headers_SOURCES = \
+ test_toolarge.c ../microhttpd/test_helpers.h
+
+test_tricky_url_SOURCES = \
+ test_tricky.c ../microhttpd/test_helpers.h
+
+test_tricky_header2_SOURCES = \
+ test_tricky.c ../microhttpd/test_helpers.h
+
\ No newline at end of file
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/Makefile.am
^
|
@@ -7,6 +7,19 @@
AM_CFLAGS = --coverage
endif
+.NOTPARALLEL:
+
+MHD_CPU_COUNT_DEF = -DMHD_CPU_COUNT=$(CPU_COUNT)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/src/microhttpd \
+ $(LIBCURL_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS)
+
+LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+ $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
if HAVE_GNUTLS_SNI
TEST_HTTPS_SNI = test_https_sni
endif
@@ -17,37 +30,28 @@
test_https_get_parallel_threads
endif
-.NOTPARALLEL:
-
-MHD_CPU_COUNT_DEF = -DMHD_CPU_COUNT=$(CPU_COUNT)
-
-AM_CPPFLAGS = \
- -I$(top_srcdir)/src/include \
- -I$(top_srcdir)/src/microhttpd \
- -I$(top_srcdir)/src/platform \
- $(LIBCURL_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS)
-
THREAD_ONLY_TESTS = \
test_tls_options \
test_tls_authentication \
$(HTTPS_PARALLEL_TESTS) \
$(TEST_HTTPS_SNI) \
test_https_session_info \
- test_https_time_out \
test_https_multi_daemon \
test_https_get \
test_empty_response \
test_https_get_iovec \
$(EMPTY_ITEM)
+if !HAVE_GNUTLS_MTHREAD_BROKEN
+THREAD_ONLY_TESTS += \
+ test_https_time_out \
+ $(EMPTY_ITEM)
+endif
+
check_PROGRAMS = \
test_https_get_select
-if USE_POSIX_THREADS
-check_PROGRAMS += \
- $(THREAD_ONLY_TESTS)
-endif
-if USE_W32_THREADS
+if USE_THREADS
check_PROGRAMS += \
$(THREAD_ONLY_TESTS)
endif
@@ -57,7 +61,6 @@
host1.crt host1.key \
host2.crt host2.key
-
TESTS = \
$(check_PROGRAMS)
@@ -67,20 +70,13 @@
tls_test_keys.h \
tls_test_common.h \
tls_test_common.c
-test_https_time_out_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
test_tls_options_SOURCES = \
test_tls_options.c \
tls_test_keys.h \
tls_test_common.h \
- tls_test_common.c
-test_tls_options_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
+ tls_test_common.c \
+ curl_version_check.c
test_https_get_parallel_SOURCES = \
test_https_get_parallel.c \
@@ -92,19 +88,13 @@
test_https_get_parallel_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_https_get_parallel_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(PTHREAD_LIBS) $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_empty_response_SOURCES = \
test_empty_response.c \
tls_test_keys.h \
tls_test_common.h \
tls_test_common.c
-test_empty_response_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
test_https_get_parallel_threads_SOURCES = \
test_https_get_parallel_threads.c \
@@ -116,59 +106,37 @@
test_https_get_parallel_threads_CFLAGS = \
$(PTHREAD_CFLAGS) $(AM_CFLAGS)
test_https_get_parallel_threads_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(PTHREAD_LIBS) $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
+ $(PTHREAD_LIBS) $(LDADD)
test_tls_authentication_SOURCES = \
test_tls_authentication.c \
tls_test_keys.h \
tls_test_common.h \
tls_test_common.c
-test_tls_authentication_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
test_https_session_info_SOURCES = \
test_https_session_info.c \
tls_test_keys.h \
tls_test_common.h \
tls_test_common.c
-test_https_session_info_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
test_https_multi_daemon_SOURCES = \
test_https_multi_daemon.c \
tls_test_keys.h \
tls_test_common.h \
tls_test_common.c
-test_https_multi_daemon_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
test_https_get_SOURCES = \
test_https_get.c \
tls_test_keys.h \
tls_test_common.h \
tls_test_common.c
-test_https_get_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
test_https_get_iovec_SOURCES = \
test_https_get_iovec.c \
tls_test_keys.h \
tls_test_common.h \
tls_test_common.c
-test_https_get_iovec_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
if HAVE_GNUTLS_SNI
test_https_sni_SOURCES = \
@@ -179,10 +147,6 @@
test_https_sni_CPPFLAGS = \
$(AM_CPPFLAGS) \
-DABS_SRCDIR=\"$(abs_srcdir)\"
-test_https_sni_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
endif
test_https_get_select_SOURCES = \
@@ -190,7 +154,3 @@
tls_test_keys.h \
tls_test_common.h \
tls_test_common.c
-test_https_get_select_LDADD = \
- $(top_builddir)/src/testcurl/libcurl_version_check.a \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la \
- $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) @LIBGCRYPT_LIBS@ @LIBCURL@
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/curl_version_check.c
^
|
@@ -0,0 +1,176 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2016-2021 Evgeny Grin (Karlson2k)
+
+ 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 2, 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 curl_version_check.c
+ * @brief verify required cURL version is available to run tests
+ * @author Sagie Amir
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+static int
+parse_version_number (const char **s)
+{
+ int i = 0;
+ char num[17];
+
+ while (i < 16 && ((**s >= '0') & (**s <= '9')))
+ {
+ num[i] = **s;
+ (*s)++;
+ i++;
+ }
+
+ num[i] = '\0';
+
+ return atoi (num);
+}
+
+
+static const char *
+parse_version_string (const char *s, int *major, int *minor, int *micro)
+{
+ if (! s)
+ return NULL;
+ *major = parse_version_number (&s);
+ if (*s != '.')
+ return NULL;
+ s++;
+ *minor = parse_version_number (&s);
+ if (*s != '.')
+ return NULL;
+ s++;
+ *micro = parse_version_number (&s);
+ return s;
+}
+
+
+/*
+ * check local libcurl version matches required version
+ */
+int
+curl_check_version (const char *req_version)
+{
+ const char *ver;
+ const char *curl_ver;
+#ifdef HTTPS_SUPPORT
+ const char *ssl_ver;
+ const char *req_ssl_ver;
+#endif /* HTTPS_SUPPORT */
+
+ int loc_major, loc_minor, loc_micro;
+ int rq_major, rq_minor, rq_micro;
+
+ ver = curl_version ();
+#ifdef HAVE_MESSAGES
+ fprintf (stderr, "curl version: %s\n", ver);
+#endif
+ /*
+ * this call relies on the cURL string to be of the exact following format :
+ * 'libcurl/7.16.4 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/0.6.5' OR
+ * 'libcurl/7.18.2 GnuTLS/2.4.0 zlib/1.2.3.3 libidn/0.6.5'
+ */
+ curl_ver = strchr (ver, '/');
+ if (curl_ver == NULL)
+ return -1;
+ curl_ver++;
+ /* Parse version numbers */
+ if ( (NULL == parse_version_string (req_version, &rq_major, &rq_minor,
+ &rq_micro)) ||
+ (NULL == parse_version_string (curl_ver, &loc_major, &loc_minor,
+ &loc_micro)) )
+ return -1;
+
+ /* Compare version numbers. */
+ if (((loc_major > rq_major)
+ || ((loc_major == rq_major) && (loc_minor > rq_minor))
+ || ((loc_major == rq_major) && (loc_minor == rq_minor)
+ && (loc_micro > rq_micro)) || ((loc_major == rq_major)
+ && (loc_minor == rq_minor)
+ && (loc_micro == rq_micro) )) == 0)
+ {
+ fprintf (stderr,
+ "Error: running curl test depends on local libcurl version > %s\n",
+ req_version);
+ return -1;
+ }
+
+ /*
+ * enforce required gnutls/openssl version.
+ * TODO use curl version string to assert use of gnutls
+ */
+#ifdef HTTPS_SUPPORT
+ ssl_ver = strchr (curl_ver, ' ');
+ if (ssl_ver == NULL)
+ return -1;
+ ssl_ver++;
+ if (strncmp ("GnuTLS", ssl_ver, strlen ("GNUtls")) == 0)
+ {
+ ssl_ver = strchr (ssl_ver, '/');
+ req_ssl_ver = MHD_REQ_CURL_GNUTLS_VERSION;
+ }
+ else if (strncmp ("OpenSSL", ssl_ver, strlen ("OpenSSL")) == 0)
+ {
+ ssl_ver = strchr (ssl_ver, '/');
+ req_ssl_ver = MHD_REQ_CURL_OPENSSL_VERSION;
+ }
+ else if (strncmp ("NSS", ssl_ver, strlen ("NSS")) == 0)
+ {
+ ssl_ver = strchr (ssl_ver, '/');
+ req_ssl_ver = MHD_REQ_CURL_NSS_VERSION;
+ }
+ else
+ {
+ fprintf (stderr, "Error: unrecognized curl ssl library\n");
+ return -1;
+ }
+ if (ssl_ver == NULL)
+ return -1;
+ ssl_ver++;
+ if ( (NULL == parse_version_string (req_ssl_ver, &rq_major, &rq_minor,
+ &rq_micro)) ||
+ (NULL == parse_version_string (ssl_ver, &loc_major, &loc_minor,
+ &loc_micro)) )
+ return -1;
+
+ if (((loc_major > rq_major)
+ || ((loc_major == rq_major) && (loc_minor > rq_minor))
+ || ((loc_major == rq_major) && (loc_minor == rq_minor)
+ && (loc_micro > rq_micro)) || ((loc_major == rq_major)
+ && (loc_minor == rq_minor)
+ && (loc_micro == rq_micro) )) == 0)
+ {
+ fprintf (stderr,
+ "Error: running curl test depends on local libcurl SSL version > %s\n",
+ req_ssl_ver);
+ return -1;
+ }
+#endif /* HTTPS_SUPPORT */
+ return 0;
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_empty_response.c
^
|
@@ -108,7 +108,7 @@
}
port = (int) dinfo->port;
}
- if (curl_uses_nss_ssl () == 0)
+ if (curl_tls_is_nss ())
{
aes256_sha = "rsa_aes_256_sha";
}
@@ -189,24 +189,37 @@
else
(void) sleep (1);
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
}
if (multi != NULL)
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_https_get.c
^
|
@@ -111,8 +111,8 @@
(void) upload_data;
(void) upload_data_size; /* Unused. Silent compiler warning. */
- if (0 != strcasecmp ("GET",
- method))
+ if (0 != strcmp ("GET",
+ method))
return MHD_NO; /* unexpected method */
if (&ptr != *unused)
{
@@ -253,7 +253,7 @@
return 77;
}
- if (curl_uses_nss_ssl () == 0)
+ if (curl_tls_is_nss ())
{
aes256_sha_tlsv1 = "rsa_aes_256_sha";
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_https_get_iovec.c
^
|
@@ -128,7 +128,7 @@
iov[j].iov_len = TESTSTR_SIZE / TESTSTR_IOVCNT;
for (i = 0; i < (int) (TESTSTR_IOVLEN / sizeof(int)); ++i)
- ((int*) iov[j].iov_base)[i] = i + (j * TESTSTR_IOVLEN / sizeof(int));
+ ((int *) iov[j].iov_base)[i] = i + (j * TESTSTR_IOVLEN / sizeof(int));
}
response = MHD_create_response_from_iovec (iov,
@@ -261,8 +261,8 @@
(void) upload_data;
(void) upload_data_size; /* Unused. Silent compiler warning. */
- if (0 != strcasecmp ("GET",
- method))
+ if (0 != strcmp ("GET",
+ method))
return MHD_NO; /* unexpected method */
if (&ptr != *unused)
{
@@ -408,7 +408,7 @@
return 77;
}
- if (curl_uses_nss_ssl () == 0)
+ if (curl_tls_is_nss ())
{
aes256_sha_tlsv1 = "rsa_aes_256_sha";
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_https_get_parallel.c
^
|
@@ -159,7 +159,7 @@
fprintf (stderr, "Curl does not support SSL. Cannot run the test.\n");
return 77;
}
- if (curl_uses_nss_ssl () == 0)
+ if (curl_tls_is_nss ())
aes256_sha = "rsa_aes_256_sha";
#ifdef EPOLL_SUPPORT
errorCount +=
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_https_get_parallel_threads.c
^
|
@@ -169,14 +169,14 @@
curl_global_cleanup ();
return 77;
}
- if (0 != strncmp (ssl_version, "GnuTLS", 6))
+ if (! curl_tls_is_gnutls ())
{
fprintf (stderr, "This test can be run only with libcurl-gnutls.\n");
curl_global_cleanup ();
return 77;
}
- if (curl_uses_nss_ssl () == 0)
+ if (curl_tls_is_nss ())
{
aes256_sha = "rsa_aes_256_sha";
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_https_get_select.c
^
|
@@ -125,7 +125,7 @@
port = (int) dinfo->port;
}
- if (curl_uses_nss_ssl () == 0)
+ if (curl_tls_is_nss ())
aes256_sha = "rsa_aes_256_sha";
c = curl_easy_init ();
@@ -213,24 +213,37 @@
else
(void) sleep (1);
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_https_multi_daemon.c
^
|
@@ -155,7 +155,7 @@
return 99;
}
- if (curl_uses_nss_ssl () == 0)
+ if (curl_tls_is_nss ())
{
aes256_sha = "rsa_aes_256_sha";
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_https_session_info.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2016 Christian Grothoff
+ Copyright (C) 2016-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file mhds_session_info_test.c
* @brief Testcase for libmicrohttpd HTTPS connection querying operations
* @author Sagie Amir
+ * @author Karlson2k (Evgeny Grin)
*/
#include "platform.h"
@@ -55,7 +57,7 @@
if (NULL == *ptr)
{
- *ptr = (void*) &query_session_ahc;
+ *ptr = (void *) &query_session_ahc;
return MHD_YES;
}
@@ -143,7 +145,7 @@
port = (int) dinfo->port;
}
- if (curl_uses_nss_ssl () == 0)
+ if (curl_tls_is_nss ())
{
aes256_sha = "rsa_aes_256_sha";
}
@@ -217,7 +219,7 @@
curl_global_cleanup ();
return 77;
}
- if (0 != strncmp (ssl_version, "GnuTLS", 6))
+ if (! curl_tls_is_gnutls ())
{
fprintf (stderr, "This test can be run only with libcurl-gnutls.\n");
curl_global_cleanup ();
@@ -228,6 +230,8 @@
curl_global_cleanup ();
return errorCount != 0 ? 1 : 0;
#else /* LIBCURL_VERSION_NUM < 0x072200 */
+ (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+ (void) query_session_ahc; /* Mute compiler warning */
return 77;
#endif /* LIBCURL_VERSION_NUM < 0x072200 */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_https_sni.c
^
|
@@ -132,11 +132,11 @@
*/
static int
sni_callback (gnutls_session_t session,
- const gnutls_datum_t*req_ca_dn,
+ const gnutls_datum_t *req_ca_dn,
int nreqs,
- const gnutls_pk_algorithm_t*pk_algos,
+ const gnutls_pk_algorithm_t *pk_algos,
int pk_algos_length,
- gnutls_pcert_st**pcert,
+ gnutls_pcert_st **pcert,
unsigned int *pcert_length,
gnutls_privkey_t *pkey)
{
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_https_time_out.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2014-2021 Karlson2k (Evgeny Grin)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -23,6 +24,7 @@
* @brief: daemon TLS alert response test-case
*
* @author Sagie Amir
+ * @author Karlson2k (Evgeny Grin)
*/
#include "platform.h"
@@ -31,6 +33,16 @@
#ifdef MHD_HTTPS_REQUIRE_GRYPT
#include <gcrypt.h>
#endif /* MHD_HTTPS_REQUIRE_GRYPT */
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif /* HAVE_SIGNAL_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif /* HAVE_TIME_H */
+
#include "mhd_sockets.h" /* only macros used */
@@ -44,7 +56,72 @@
extern const char srv_key_pem[];
extern const char srv_self_signed_cert_pem[];
-static const int TIME_OUT = 3;
+static const int TIME_OUT = 2;
+
+static unsigned int num_connects = 0;
+static unsigned int num_disconnects = 0;
+
+
+/**
+ * Pause execution for specified number of milliseconds.
+ * @param ms the number of milliseconds to sleep
+ */
+void
+_MHD_sleep (uint32_t ms)
+{
+#if defined(_WIN32)
+ Sleep (ms);
+#elif defined(HAVE_NANOSLEEP)
+ struct timespec slp = {ms / 1000, (ms % 1000) * 1000000};
+ struct timespec rmn;
+ int num_retries = 0;
+ while (0 != nanosleep (&slp, &rmn))
+ {
+ if (num_retries++ > 8)
+ break;
+ slp = rmn;
+ }
+#elif defined(HAVE_USLEEP)
+ uint64_t us = ms * 1000;
+ do
+ {
+ uint64_t this_sleep;
+ if (999999 < us)
+ this_sleep = 999999;
+ else
+ this_sleep = us;
+ /* Ignore return value as it could be void */
+ usleep (this_sleep);
+ us -= this_sleep;
+ } while (us > 0);
+#else
+ sleep ((ms + 999) / 1000);
+#endif
+}
+
+
+void
+socket_cb (void *cls,
+ struct MHD_Connection *c,
+ void **socket_context,
+ enum MHD_ConnectionNotificationCode toe)
+{
+ struct sckt_notif_cb_param *param = (struct sckt_notif_cb_param *) cls;
+ if (NULL == socket_context)
+ abort ();
+ if (NULL == c)
+ abort ();
+ if (NULL == param)
+ abort ();
+
+ if (MHD_CONNECTION_NOTIFY_STARTED == toe)
+ num_connects++;
+ else if (MHD_CONNECTION_NOTIFY_CLOSED == toe)
+ num_disconnects++;
+ else
+ abort ();
+}
+
static int
test_tls_session_time_out (gnutls_session_t session, int port)
@@ -57,7 +134,7 @@
if (sd == MHD_INVALID_SOCKET)
{
fprintf (stderr, "Failed to create socket: %s\n", strerror (errno));
- return -1;
+ return 2;
}
memset (&sa, '\0', sizeof (struct sockaddr_in));
@@ -65,35 +142,40 @@
sa.sin_port = htons (port);
sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
- gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) (intptr_t) sd);
-
ret = connect (sd, (struct sockaddr *) &sa, sizeof (struct sockaddr_in));
if (ret < 0)
{
fprintf (stderr, "Error: %s\n", MHD_E_FAILED_TO_CONNECT);
MHD_socket_close_chk_ (sd);
- return -1;
+ return 2;
}
+#if (GNUTLS_VERSION_NUMBER + 0 >= 0x030109) && ! defined(_WIN64)
+ gnutls_transport_set_int (session, (int) (sd));
+#else /* GnuTLS before 3.1.9 or Win64 */
+ gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) (intptr_t) (sd));
+#endif /* GnuTLS before 3.1.9 or Win64 */
+
ret = gnutls_handshake (session);
if (ret < 0)
{
fprintf (stderr, "Handshake failed\n");
MHD_socket_close_chk_ (sd);
- return -1;
+ return 2;
}
- (void) sleep (TIME_OUT + 1);
+ _MHD_sleep (TIME_OUT * 1000 + 1200);
/* check that server has closed the connection */
- /* TODO better RST trigger */
- if (send (sd, "", 1, 0) == 0)
+ if (1 == num_disconnects)
{
fprintf (stderr, "Connection failed to time-out\n");
MHD_socket_close_chk_ (sd);
- return -1;
+ return 1;
}
+ else if (0 != num_disconnects)
+ abort ();
MHD_socket_close_chk_ (sd);
return 0;
@@ -106,8 +188,6 @@
int errorCount = 0;
struct MHD_Daemon *d;
gnutls_session_t session;
- gnutls_datum_t key;
- gnutls_datum_t cert;
gnutls_certificate_credentials_t xcred;
int port;
(void) argc; /* Unused. Silent compiler warning. */
@@ -117,13 +197,30 @@
else
port = 3070;
+#ifdef MHD_SEND_SPIPE_SUPPRESS_NEEDED
+#if defined(HAVE_SIGNAL_H) && defined(SIGPIPE)
+ if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
+ {
+ fprintf (stderr, "Error suppressing SIGPIPE signal.\n");
+ exit (99);
+ }
+#else /* ! HAVE_SIGNAL_H || ! SIGPIPE */
+ fprintf (stderr, "Cannot suppress SIGPIPE signal.\n");
+ /* exit (77); */
+#endif
+#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED */
+
#ifdef MHD_HTTPS_REQUIRE_GRYPT
gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
#ifdef GCRYCTL_INITIALIZATION_FINISHED
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
#endif
#endif /* MHD_HTTPS_REQUIRE_GRYPT */
- gnutls_global_init ();
+ if (GNUTLS_E_SUCCESS != gnutls_global_init ())
+ {
+ fprintf (stderr, "Cannot initialize GnuTLS.\n");
+ exit (99);
+ }
gnutls_global_set_log_level (11);
d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
@@ -151,13 +248,13 @@
port = (int) dinfo->port;
}
- if (0 != setup_session (&session, &key, &cert, &xcred))
+ if (0 != setup_session (&session, &xcred))
{
fprintf (stderr, "failed to setup session\n");
return 1;
}
errorCount += test_tls_session_time_out (session, port);
- teardown_session (session, &key, &cert, xcred);
+ teardown_session (session, xcred);
print_test_result (errorCount, argv[0]);
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_tls_authentication.c
^
|
@@ -114,7 +114,7 @@
return 99;
}
fclose (crt);
- if (curl_uses_nss_ssl () == 0)
+ if (curl_tls_is_nss ())
{
aes256_sha = "rsa_aes_256_sha";
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_tls_extensions.c
^
|
@@ -210,8 +210,6 @@
FILE *test_fd;
struct MHD_Daemon *d;
gnutls_session_t session;
- gnutls_datum_t key;
- gnutls_datum_t cert;
gnutls_certificate_credentials_t xcred;
const int ext_arr[] = {
GNUTLS_EXTENSION_SERVER_NAME,
@@ -266,25 +264,25 @@
}
i = 0;
- setup_session (&session, &key, &cert, &xcred);
+ setup_session (&session, &xcred);
errorCount += test_hello_extension (session, port, ext_arr[i], 1, 16);
- teardown_session (session, &key, &cert, xcred);
+ teardown_session (session, xcred);
#if 1
i = 0;
while (ext_arr[i] != -1)
{
- setup_session (&session, &key, &cert, &xcred);
+ setup_session (&session, &xcred);
errorCount += test_hello_extension (session, port, ext_arr[i], 1, 16);
- teardown_session (session, &key, &cert, xcred);
+ teardown_session (session, xcred);
- setup_session (&session, &key, &cert, &xcred);
+ setup_session (&session, &xcred);
errorCount += test_hello_extension (session, port, ext_arr[i], 3, 8);
- teardown_session (session, &key, &cert, xcred);
+ teardown_session (session, xcred);
/* this test specifically tests the issue raised in CVE-2008-1948 */
- setup_session (&session, &key, &cert, &xcred);
+ setup_session (&session, &xcred);
errorCount += test_hello_extension (session, port, ext_arr[i], 6, 0);
- teardown_session (session, &key, &cert, xcred);
+ teardown_session (session, xcred);
i++;
}
#endif
@@ -296,5 +294,5 @@
curl_global_cleanup ();
fclose (test_fd);
- return errorCount;
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/test_tls_options.c
^
|
@@ -109,6 +109,9 @@
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
#endif
#endif /* MHD_HTTPS_REQUIRE_GRYPT */
+ if (! testsuite_curl_global_init ())
+ return 99;
+
if (curl_check_version (MHD_REQ_CURL_VERSION))
{
return 77;
@@ -119,22 +122,20 @@
fprintf (stderr, "Curl does not support SSL. Cannot run the test.\n");
return 77;
}
- if (0 != strncmp (ssl_version, "GnuTLS", 6))
+
+ if (curl_tls_is_schannel () || curl_tls_is_sectransport ())
{
- fprintf (stderr, "This test can be run only with libcurl-gnutls.\n");
+ fprintf (stderr,
+ "libcurl TLS backend does not support this test. Skipping.\n");
return 77;
}
- if (! testsuite_curl_global_init ())
- return 99;
-
- if (curl_uses_nss_ssl () == 0)
+ if (curl_tls_is_nss ())
{
aes128_sha = "rsa_aes_128_sha";
aes256_sha = "rsa_aes_256_sha";
}
-
if (0 !=
test_wrap ("TLS1.0-AES-SHA1",
&test_https_transfer, NULL, port, daemon_flags,
@@ -152,17 +153,17 @@
fprintf (stderr,
"The following handshake should fail (and print an error message)...\n");
if (0 !=
- test_wrap ("TLS1.0 vs SSL3",
+ test_wrap ("TLS1.1 vs TLS1.0",
&test_unmatching_ssl_version, NULL, port, daemon_flags,
aes256_sha,
- CURL_SSLVERSION_SSLv3,
+ CURL_SSLVERSION_TLSv1_1,
MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem,
MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem,
MHD_OPTION_HTTPS_PRIORITIES,
"NONE:+VERS-TLS1.0:+AES-256-CBC:+SHA1:+RSA:+COMP-NULL",
MHD_OPTION_END))
{
- fprintf (stderr, "TLS1.0 vs SSL3 test failed\n");
+ fprintf (stderr, "TLS1.1 vs TLS1.0 test failed\n");
errorCount++;
}
curl_global_cleanup ();
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/tls_test_common.c
^
|
@@ -70,6 +70,7 @@
CURL *c;
struct CBC cbc;
CURLcode errornum;
+ CURLcode e;
char url[255];
size_t len;
(void) cls; /* Unused. Silence compiler warning. */
@@ -92,29 +93,51 @@
#if DEBUG_HTTPS_TEST
curl_easy_setopt (c, CURLOPT_VERBOSE, 1L);
#endif
- curl_easy_setopt (c, CURLOPT_URL, url);
- curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
- curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L);
- curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L);
- curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
- curl_easy_setopt (c, CURLOPT_FILE, &cbc);
+ if ((CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_URL, url))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_0))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+ ©Buffer))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_FILE, &cbc))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L))))
+ {
+ fprintf (stderr, "curl_easy_setopt failed: `%s'\n",
+ curl_easy_strerror (e));
+ curl_easy_cleanup (c);
+ free (cbc.buf);
+ return e;
+ }
/* TLS options */
- curl_easy_setopt (c, CURLOPT_SSLVERSION, proto_version);
- curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, cipher_suite);
-
- /* perform peer authentication */
- /* TODO merge into send_curl_req */
- curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, ver_peer);
- if (ver_peer)
- curl_easy_setopt (c, CURLOPT_CAINFO, ca_cert_file_name);
- curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L);
- curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
-
- /* NOTE: use of CONNECTTIMEOUT without also
- setting NOSIGNAL results in really weird
- crashes on my system! */
- curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+ if ((CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSLVERSION,
+ proto_version))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST,
+ cipher_suite))) ||
+
+ /* perform peer authentication */
+ /* TODO merge into send_curl_req */
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER,
+ ver_peer))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L))))
+ {
+ fprintf (stderr, "HTTPS curl_easy_setopt failed: `%s'\n",
+ curl_easy_strerror (e));
+ curl_easy_cleanup (c);
+ free (cbc.buf);
+ return e;
+ }
+ if (ver_peer &&
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_CAINFO, ca_cert_file_name)))
+ {
+ fprintf (stderr, "HTTPS curl_easy_setopt failed: `%s'\n",
+ curl_easy_strerror (e));
+ curl_easy_cleanup (c);
+ free (cbc.buf);
+ return e;
+ }
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
fprintf (stderr, "curl_easy_perform failed: `%s'\n",
@@ -250,35 +273,54 @@
{
CURL *c;
CURLcode errornum;
+ CURLcode e;
c = curl_easy_init ();
#if DEBUG_HTTPS_TEST
curl_easy_setopt (c, CURLOPT_VERBOSE, CURL_VERBOS_LEVEL);
#endif
- curl_easy_setopt (c, CURLOPT_URL, url);
- curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
- curl_easy_setopt (c, CURLOPT_TIMEOUT, 60L);
- curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 60L);
+ if ((CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_URL, url))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_0))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_TIMEOUT, 60L))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 60L))) ||
+
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L))))
+ {
+ fprintf (stderr, "curl_easy_setopt failed: `%s'\n",
+ curl_easy_strerror (e));
+ curl_easy_cleanup (c);
+ return e;
+ }
if (cbc != NULL)
{
- curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
- curl_easy_setopt (c, CURLOPT_FILE, cbc);
+ if ((CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+ ©Buffer))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_FILE, cbc))))
+ {
+ fprintf (stderr, "curl_easy_setopt failed: `%s'\n",
+ curl_easy_strerror (e));
+ curl_easy_cleanup (c);
+ return e;
+ }
}
/* TLS options */
- curl_easy_setopt (c, CURLOPT_SSLVERSION, proto_version);
- curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, cipher_suite);
+ if ((CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSLVERSION,
+ proto_version))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST,
+ cipher_suite))) ||
+ /* currently skip any peer authentication */
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L))) ||
+ (CURLE_OK != (e = curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L))))
+ {
+ fprintf (stderr, "HTTPS curl_easy_setopt failed: `%s'\n",
+ curl_easy_strerror (e));
+ curl_easy_cleanup (c);
+ return e;
+ }
- /* currently skip any peer authentication */
- curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L);
-
- curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
-
- /* NOTE: use of CONNECTTIMEOUT without also
- setting NOSIGNAL results in really weird
- crashes on my system! */
- curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
fprintf (stderr, "curl_easy_perform failed: `%s'\n",
@@ -462,62 +504,33 @@
int
setup_session (gnutls_session_t *session,
- gnutls_datum_t *key,
- gnutls_datum_t *cert,
gnutls_certificate_credentials_t *xcred)
{
- int ret;
- const char *err_pos;
-
- gnutls_certificate_allocate_credentials (xcred);
- key->size = strlen (srv_key_pem) + 1;
- key->data = malloc (key->size);
- if (NULL == key->data)
- {
- gnutls_certificate_free_credentials (*xcred);
- return -1;
- }
- memcpy (key->data, srv_key_pem, key->size);
- cert->size = strlen (srv_self_signed_cert_pem) + 1;
- cert->data = malloc (cert->size);
- if (NULL == cert->data)
- {
- gnutls_certificate_free_credentials (*xcred);
- free (key->data);
- return -1;
- }
- memcpy (cert->data, srv_self_signed_cert_pem, cert->size);
- gnutls_certificate_set_x509_key_mem (*xcred, cert, key,
- GNUTLS_X509_FMT_PEM);
- gnutls_init (session, GNUTLS_CLIENT);
- ret = gnutls_priority_set_direct (*session,
- "NORMAL", &err_pos);
- if (ret < 0)
+ if (GNUTLS_E_SUCCESS == gnutls_init (session, GNUTLS_CLIENT))
{
+ if (GNUTLS_E_SUCCESS == gnutls_set_default_priority (*session))
+ {
+ if (GNUTLS_E_SUCCESS == gnutls_certificate_allocate_credentials (xcred))
+ {
+ if (GNUTLS_E_SUCCESS == gnutls_credentials_set (*session,
+ GNUTLS_CRD_CERTIFICATE,
+ *xcred))
+ {
+ return 0;
+ }
+ gnutls_certificate_free_credentials (*xcred);
+ }
+ }
gnutls_deinit (*session);
- gnutls_certificate_free_credentials (*xcred);
- free (key->data);
- return -1;
}
- gnutls_credentials_set (*session,
- GNUTLS_CRD_CERTIFICATE,
- *xcred);
- return 0;
+ return -1;
}
int
teardown_session (gnutls_session_t session,
- gnutls_datum_t *key,
- gnutls_datum_t *cert,
gnutls_certificate_credentials_t xcred)
{
- free (key->data);
- key->data = NULL;
- key->size = 0;
- free (cert->data);
- cert->data = NULL;
- cert->size = 0;
gnutls_deinit (session);
gnutls_certificate_free_credentials (xcred);
return 0;
@@ -565,6 +578,100 @@
}
+static int inited_tls_is_gnutls = 0;
+static int inited_tls_is_openssl = 0;
+
+int
+curl_tls_is_gnutls (void)
+{
+ const char *tlslib;
+ if (inited_tls_is_gnutls)
+ return 1;
+ if (inited_tls_is_openssl)
+ return 0;
+
+ tlslib = curl_version_info (CURLVERSION_NOW)->ssl_version;
+ if (NULL == tlslib)
+ return 0;
+ if (0 == strncmp (tlslib, "GnuTLS/", 7))
+ return 1;
+
+ /* Multi-backends handled during initialization by setting variable */
+ return 0;
+}
+
+
+int
+curl_tls_is_nss (void)
+{
+ const char *tlslib;
+ if (inited_tls_is_gnutls)
+ return 0;
+ if (inited_tls_is_openssl)
+ return 0;
+
+ tlslib = curl_version_info (CURLVERSION_NOW)->ssl_version;
+ if (NULL == tlslib)
+ return 0;
+ if (0 == strncmp (tlslib, "NSS/", 4))
+ return 1;
+
+ /* Handle multi-backends with selected backend */
+ if (NULL != strstr (tlslib," NSS/"))
+ return 1;
+
+ return 0;
+}
+
+
+int
+curl_tls_is_schannel (void)
+{
+ const char *tlslib;
+ if (inited_tls_is_gnutls)
+ return 0;
+ if (inited_tls_is_openssl)
+ return 0;
+
+ tlslib = curl_version_info (CURLVERSION_NOW)->ssl_version;
+ if (NULL == tlslib)
+ return 0;
+ if ((0 == strncmp (tlslib, "Schannel", 8)) || (0 == strncmp (tlslib, "WinSSL",
+ 6)))
+ return 1;
+
+ /* Handle multi-backends with selected backend */
+ if ((NULL != strstr (tlslib," Schannel")) || (NULL != strstr (tlslib,
+ " WinSSL")))
+ return 1;
+
+ return 0;
+}
+
+
+int
+curl_tls_is_sectransport (void)
+{
+ const char *tlslib;
+ if (inited_tls_is_gnutls)
+ return 0;
+ if (inited_tls_is_openssl)
+ return 0;
+
+ tlslib = curl_version_info (CURLVERSION_NOW)->ssl_version;
+ if (NULL == tlslib)
+ return 0;
+ if (0 == strncmp (tlslib, "SecureTransport", 15))
+ return 1;
+
+ /* Handle multi-backends with selected backend */
+ if (NULL != strstr (tlslib," SecureTransport"))
+ return 1;
+
+ return 0;
+}
+
+
int
testsuite_curl_global_init (void)
{
@@ -572,10 +679,15 @@
#if LIBCURL_VERSION_NUM >= 0x073800
if (CURLSSLSET_OK != curl_global_sslset (CURLSSLBACKEND_GNUTLS, NULL, NULL))
{
- if (CURLSSLSET_TOO_LATE == curl_global_sslset (CURLSSLBACKEND_OPENSSL, NULL,
- NULL))
+ CURLsslset e;
+ e = curl_global_sslset (CURLSSLBACKEND_OPENSSL, NULL, NULL);
+ if (CURLSSLSET_TOO_LATE == e)
fprintf (stderr, "WARNING: libcurl was already initialised.\n");
+ else if (CURLSSLSET_OK == e)
+ inited_tls_is_openssl = 1;
}
+ else
+ inited_tls_is_gnutls = 1;
#endif /* LIBCURL_VERSION_NUM >= 0x07380 */
res = curl_global_init (CURL_GLOBAL_ALL);
if (CURLE_OK != res)
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/https/tls_test_common.h
^
|
@@ -75,8 +75,16 @@
curl_check_version (const char *req_version, ...);
int
-curl_uses_nss_ssl (void);
+curl_tls_is_gnutls (void);
+int
+curl_tls_is_nss (void);
+
+int
+curl_tls_is_schannel (void);
+
+int
+curl_tls_is_sectransport (void);
FILE *
setup_ca_cert (void);
@@ -138,14 +146,10 @@
int
setup_session (gnutls_session_t *session,
- gnutls_datum_t *key,
- gnutls_datum_t *cert,
gnutls_certificate_credentials_t *xcred);
int
teardown_session (gnutls_session_t session,
- gnutls_datum_t *key,
- gnutls_datum_t *cert,
gnutls_certificate_credentials_t xcred);
int
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/mhd_has_in_name.h
^
|
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- Copyright (C) 2016-2019 Karlson2k (Evgeny Grin)
+ Copyright (C) 2016-2021 Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -61,5 +61,5 @@
}
if (name_pos == pos)
return 0;
- return strstr (prog_name + name_pos, marker) != (char*) 0;
+ return strstr (prog_name + name_pos, marker) != (char *) 0;
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/perf_get.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009, 2011 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -34,6 +35,7 @@
* not universally meaningful (i.e. when comparing
* multithreaded vs. single-threaded or select/poll).
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -225,7 +227,7 @@
"http://127.0.0.1:%d/hello_world",
port);
start_timer ();
- for (i = 0; i<ROUNDS; i++)
+ for (i = 0; i < ROUNDS; i++)
{
cbc.pos = 0;
c = curl_easy_init ();
@@ -304,7 +306,7 @@
"http://127.0.0.1:%d/hello_world",
port);
start_timer ();
- for (i = 0; i<ROUNDS; i++)
+ for (i = 0; i < ROUNDS; i++)
{
cbc.pos = 0;
c = curl_easy_init ();
@@ -387,7 +389,7 @@
"http://127.0.0.1:%d/hello_world",
port);
start_timer ();
- for (i = 0; i<ROUNDS; i++)
+ for (i = 0; i < ROUNDS; i++)
{
cbc.pos = 0;
c = curl_easy_init ();
@@ -487,7 +489,7 @@
MHD_stop_daemon (d);
return 512;
}
- for (i = 0; i<ROUNDS; i++)
+ for (i = 0; i < ROUNDS; i++)
{
cbc.pos = 0;
c = curl_easy_init ();
@@ -543,35 +545,57 @@
tv.tv_usec = 1000;
if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
{
-#ifdef MHD_POSIX_SOCKETS
+ #ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
-#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
-#endif
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ #else
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
+ #endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
+ {
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
+ }
+ if (! curl_fine)
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_easy_cleanup (c);
- c = NULL;
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
}
+ curl_multi_remove_handle (multi, c);
+ curl_easy_cleanup (c);
+ c = NULL;
+ break;
}
/* two possibilities here; as select sets are
tiny, this makes virtually no difference
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/perf_get_concurrent.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009, 2011 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -30,6 +31,7 @@
* (since MHD is actually better); only the relative
* scores between MHD versions are meaningful.
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -186,7 +188,7 @@
CURL *c;
CURLcode errornum;
unsigned int i;
- char *const url = (char*) param;
+ char *const url = (char *) param;
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, url);
@@ -203,7 +205,7 @@
setting NOSIGNAL results in really weird
crashes on my system! */
curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
- for (i = 0; i<ROUNDS; i++)
+ for (i = 0; i < ROUNDS; i++)
{
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
@@ -233,19 +235,19 @@
sizeof (url),
"http://127.0.0.1:%d/hello_world",
port);
- for (j = 0; j<PAR; j++)
+ for (j = 0; j < PAR; j++)
{
- if (0 != pthread_create (&par[j], NULL, &thread_gets, (void*) url))
+ if (0 != pthread_create (&par[j], NULL, &thread_gets, (void *) url))
{
for (j--; j >= 0; j--)
pthread_join (par[j], NULL);
return "pthread_create error";
}
}
- for (j = 0; j<PAR; j++)
+ for (j = 0; j < PAR; j++)
{
char *ret_val;
- if ((0 != pthread_join (par[j], (void**) &ret_val)) ||
+ if ((0 != pthread_join (par[j], (void **) &ret_val)) ||
(NULL != ret_val) )
err = ret_val;
}
@@ -287,7 +289,7 @@
port = (int) dinfo->port;
}
start_timer ();
- ret_val = do_gets ((void*) (intptr_t) port);
+ ret_val = do_gets ((void *) (intptr_t) port);
if (! ret_val)
stop (test_desc);
MHD_stop_daemon (d);
@@ -338,7 +340,7 @@
port = (int) dinfo->port;
}
start_timer ();
- ret_val = do_gets ((void*) (intptr_t) port);
+ ret_val = do_gets ((void *) (intptr_t) port);
if (! ret_val)
stop (test_desc);
MHD_stop_daemon (d);
@@ -387,7 +389,7 @@
port = (int) dinfo->port;
}
start_timer ();
- ret_val = do_gets ((void*) (intptr_t) port);
+ ret_val = do_gets ((void *) (intptr_t) port);
if (! ret_val)
stop (test_desc);
MHD_stop_daemon (d);
@@ -435,7 +437,7 @@
port = (int) dinfo->port;
}
if (0 != pthread_create (&pid, NULL,
- &do_gets, (void*) (intptr_t) port))
+ &do_gets, (void *) (intptr_t) port))
{
MHD_stop_daemon (d);
return 512;
@@ -461,30 +463,33 @@
if (-1 == select (max + 1, &rs, &ws, &es, &tv))
{
#ifdef MHD_POSIX_SOCKETS
- if (EINTR == errno)
- continue;
- fprintf (stderr,
- "select failed: %s\n",
- strerror (errno));
-#else
- if ((WSAEINVAL == WSAGetLastError ()) && (0 == rs.fd_count) && (0 ==
- ws.
- fd_count)
- && (0 == es.fd_count) )
+ if (EINTR != errno)
{
- Sleep (1000);
- continue;
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
}
-#endif
ret |= 1024;
break;
+#else
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
+#endif
}
MHD_run_from_select (d, &rs, &ws, &es);
}
stop ("external select");
MHD_stop_daemon (d);
- if ((0 != pthread_join (pid, (void**) &ret_val)) ||
+ if ((0 != pthread_join (pid, (void **) &ret_val)) ||
(NULL != ret_val) )
{
fprintf (stderr,
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_add_conn.c
^
|
@@ -1,7 +1,8 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009, 2011 Christian Grothoff
- Copyright (C) 2020 Karlson2k (Evgeny Grin) - large rework, multithreading.
+ Copyright (C) 2014-2020 Evgeny Grin (Karlson2k) - large rework,
+ multithreading.
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -157,7 +158,7 @@
(void) upload_data;
(void) upload_data_size; /* Unused. Silence compiler warning. */
- if (0 != strcasecmp (me, method))
+ if (0 != strcmp (me, method))
return MHD_NO; /* unexpected method */
if (&ptr != *unused)
{
@@ -272,7 +273,7 @@
externalErrorExitDesc ("socket() failed");
#ifdef MHD_POSIX_SOCKETS
- setsockopt (skt, SOL_SOCKET, SO_REUSEADDR, (void*) &on, sizeof (on));
+ setsockopt (skt, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof (on));
/* Ignore possible error */
#endif /* MHD_POSIX_SOCKETS */
@@ -280,7 +281,7 @@
sin.sin_family = AF_INET;
sin.sin_port = htons (*pport);
sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
- if (0 != bind (skt, (struct sockaddr*) &sin, sizeof(sin)))
+ if (0 != bind (skt, (struct sockaddr *) &sin, sizeof(sin)))
externalErrorExitDesc ("bind() failed");
if (0 != listen (skt, SOMAXCONN))
@@ -369,7 +370,7 @@
(void) doAcceptAndAddConnInThread (p);
- return (void*) p;
+ return (void *) p;
}
@@ -380,7 +381,7 @@
param->result = eMarker;
if (0 != pthread_create (¶m->addConnThread, NULL, &doAcceptAndAddConn,
- (void*) param))
+ (void *) param))
externalErrorExitDesc ("pthread_create() failed");
}
@@ -390,7 +391,7 @@
{
struct addConnParam *result;
- if (0 != pthread_join (param->addConnThread, (void**) &result))
+ if (0 != pthread_join (param->addConnThread, (void **) &result))
externalErrorExitDesc ("pthread_join() failed");
if (param != result)
@@ -519,7 +520,7 @@
static void *
doCurlQuery (void *param)
{
- struct curlQueryParams *p = (struct curlQueryParams*) param;
+ struct curlQueryParams *p = (struct curlQueryParams *) param;
(void) doCurlQueryInThread (p);
@@ -534,7 +535,7 @@
param->queryError = eMarker;
if (0 != pthread_create (¶m->queryThread, NULL, &doCurlQuery,
- (void*) param))
+ (void *) param))
externalErrorExitDesc ("pthread_create() failed");
}
@@ -544,7 +545,7 @@
{
struct curlQueryParams *result;
- if (0 != pthread_join (param->queryThread, (void**) &result))
+ if (0 != pthread_join (param->queryThread, (void **) &result))
externalErrorExitDesc ("pthread_join() failed");
if (param != result)
@@ -922,14 +923,22 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- externalErrorExitDesc ("select() failed");
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- externalErrorExitDesc ("select() failed");
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
if (FD_ISSET (aParam.lstn_sk, &rs))
@@ -1044,12 +1053,11 @@
MHD_socket fd2;
struct addConnParam aParam;
int ret = 0; /* Return value of the test */
- const int c_no_listen = no_listen; /* Local const value to mute analyzer */
d = startTestMhdDaemon (testMhdThreadInternal, pollType,
&d_port);
- if (! c_no_listen)
+ if (! no_listen)
{
fd1 = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (MHD_INVALID_SOCKET == fd1)
@@ -1062,6 +1070,8 @@
if (connect (fd1, (struct sockaddr *) (&sin), sizeof(sin)) < 0)
externalErrorExitDesc ("socket() failed");
}
+ else
+ fd1 = MHD_INVALID_SOCKET;
aParam.d = d;
aParam.lstn_sk = createListeningSocket (&a_port); /* Sets a_port */
@@ -1083,7 +1093,7 @@
MHD_stop_daemon (d);
- if (! c_no_listen)
+ if (MHD_INVALID_SOCKET != fd1)
(void) MHD_socket_close_ (fd1);
(void) MHD_socket_close_ (aParam.lstn_sk);
(void) MHD_socket_close_ (fd2);
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_callback.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009, 2011 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @brief Testcase for MHD not calling the callback too often
* @author Jan Seeger
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
#include "platform.h"
@@ -54,6 +56,7 @@
}
fprintf (stderr,
"Handler called after returning END_OF_STREAM!\n");
+ abort ();
return MHD_CONTENT_READER_END_WITH_ERROR;
}
@@ -170,7 +173,7 @@
{
curl_easy_cleanup (c);
MHD_stop_daemon (d);
- return 1;
+ return 99;
}
mret = curl_multi_add_handle (multi, c);
if (mret != CURLM_OK)
@@ -178,7 +181,7 @@
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
MHD_stop_daemon (d);
- return 2;
+ return 99;
}
extra = 10;
while ( (c != NULL) || (--extra > 0) )
@@ -198,7 +201,7 @@
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
MHD_stop_daemon (d);
- return 3;
+ return 99;
}
}
if (MHD_YES !=
@@ -216,37 +219,58 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
if (NULL != multi)
{
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
}
MHD_run (d);
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_concurrent_stop.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009, 2011, 2015, 2016 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file test_concurrent_stop.c
* @brief test stopping server while concurrent GETs are ongoing
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
#include "platform.h"
@@ -103,7 +105,7 @@
watchdog_continue = 1;
watchdog_obj = obj_name;
if (0 != pthread_create (&watchdog_tid, NULL, &thread_watchdog,
- (void*) (intptr_t) timeout))
+ (void *) (intptr_t) timeout))
{
fprintf (stderr, "Failed to start watchdog.\n");
_exit (99);
@@ -171,7 +173,7 @@
{
CURL *c;
CURLcode errornum;
- char *const url = (char*) param;
+ char *const url = (char *) param;
c = NULL;
c = curl_easy_init ();
@@ -222,9 +224,9 @@
"http://127.0.0.1:%d/hello_world",
port);
- for (j = 0; j<PAR; j++)
+ for (j = 0; j < PAR; j++)
{
- if (0 != pthread_create (&par[j], NULL, &thread_gets, (void*) url))
+ if (0 != pthread_create (&par[j], NULL, &thread_gets, (void *) url))
{
fprintf (stderr, "pthread_create failed.\n");
continue_requesting = 0;
@@ -236,7 +238,7 @@
}
}
(void) sleep (1);
- for (j = 0; j<PAR; j++)
+ for (j = 0; j < PAR; j++)
{
pthread_join (par[j], NULL);
}
@@ -249,7 +251,7 @@
{
pthread_t tid;
continue_requesting = 1;
- if (0 != pthread_create (&tid, NULL, &do_gets, (void*) (intptr_t) port))
+ if (0 != pthread_create (&tid, NULL, &do_gets, (void *) (intptr_t) port))
{
fprintf (stderr, "pthread_create failed.\n");
_exit (99);
@@ -381,5 +383,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_delete.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2016 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file daemontest_delete.c
* @brief Testcase for libmicrohttpd DELETE operations
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -95,7 +97,7 @@
enum MHD_Result ret;
(void) version; (void) unused; /* Unused. Silent compiler warning. */
- if (0 != strcasecmp ("DELETE", method))
+ if (0 != strcmp ("DELETE", method))
return MHD_NO; /* unexpected method */
if ((*done) == 0)
{
@@ -113,7 +115,7 @@
*done = 1;
return MHD_YES;
}
- response = MHD_create_response_from_buffer (strlen (url), (void*) url,
+ response = MHD_create_response_from_buffer (strlen (url), (void *) url,
MHD_RESPMEM_MUST_COPY);
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
MHD_destroy_response (response);
@@ -471,35 +473,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -539,5 +562,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_digestauth.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2010 Christian Grothoff
+ Copyright (C) 2016-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file daemontest_digestauth.c
* @brief Testcase for libmicrohttpd Digest Auth
* @author Amr Ali
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -215,7 +217,7 @@
GetLastError ());
return 1;
}
- b = CryptGenRandom (cc, 8, (BYTE*) rnd);
+ b = CryptGenRandom (cc, 8, (BYTE *) rnd);
if (b == 0)
{
fprintf (stderr,
@@ -306,5 +308,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_digestauth_sha256.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2010, 2018 Christian Grothoff
+ Copyright (C) 2019-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -23,6 +24,7 @@
* @brief Testcase for libmicrohttpd Digest Auth with SHA256
* @author Amr Ali
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -219,7 +221,7 @@
GetLastError ());
return 1;
}
- b = CryptGenRandom (cc, 8, (BYTE*) rnd);
+ b = CryptGenRandom (cc, 8, (BYTE *) rnd);
if (b == 0)
{
fprintf (stderr,
@@ -319,5 +321,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_digestauth_with_arguments.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2010, 2012 Christian Grothoff
+ Copyright (C) 2016-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file daemontest_digestauth_with_arguments.c
* @brief Testcase for libmicrohttpd Digest Auth with arguments
* @author Amr Ali
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
#include "platform.h"
@@ -194,7 +196,7 @@
GetLastError ());
return 1;
}
- b = CryptGenRandom (cc, 8, (BYTE*) rnd);
+ b = CryptGenRandom (cc, 8, (BYTE *) rnd);
if (b == 0)
{
fprintf (stderr, "Failed to generate 8 random bytes: %lu\n",
@@ -279,5 +281,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_get.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009, 2011 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -20,8 +21,8 @@
/**
* @file test_get.c
* @brief Testcase for libmicrohttpd GET operations
- * TODO: test parsing of query
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
#include "platform.h"
@@ -116,7 +117,7 @@
(void) upload_data;
(void) upload_data_size; /* Unused. Silence compiler warning. */
- if (0 != strcasecmp (me, method))
+ if (0 != strcmp (me, method))
return MHD_NO; /* unexpected method */
if (&ptr != *unused)
{
@@ -508,35 +509,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- _exit (99);
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -734,7 +756,7 @@
(void) upload_data;
(void) upload_data_size; /* Unused. Silent compiler warning. */
- if (0 != strcasecmp ("GET", method))
+ if (0 != strcmp ("GET", method))
return MHD_NO; /* unexpected method */
if (&ptr != *unused)
{
@@ -999,5 +1021,5 @@
else if (verbose)
printf ("All tests passed.\n");
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_get_chunked.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -19,13 +20,10 @@
*/
/**
- * @file daemontest_get_chunked.c
+ * @file test_get_chunked.c
* @brief Testcase for libmicrohttpd GET operations with chunked content encoding
- * TODO:
- * - how to test that chunking was actually used?
- * - use CURLOPT_HEADERFUNCTION to validate
- * footer was sent
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -40,6 +38,8 @@
#include <unistd.h>
#endif
+#include "mhd_has_in_name.h"
+
#if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
#undef MHD_CPU_COUNT
#endif
@@ -47,6 +47,71 @@
#define MHD_CPU_COUNT 2
#endif
+#define HDR_CHUNKED_ENCODING MHD_HTTP_HEADER_TRANSFER_ENCODING ": chunked"
+#define RESP_FOOTER_NAME "Footer"
+#define RESP_FOOTER_VALUE "working"
+#define RESP_FOOTER RESP_FOOTER_NAME ": " RESP_FOOTER_VALUE
+
+#define RESP_BLOCK_SIZE 128
+#define RESP_BLOCK_QUANTIY 10
+#define RESP_SIZE (RESP_BLOCK_SIZE * RESP_BLOCK_QUANTIY)
+
+/**
+ * Use "Connection: close" header?
+ */
+int conn_close;
+
+/**
+ * Use static string response instead of callback-generated?
+ */
+int resp_string;
+
+/**
+ * Use response with known size?
+ */
+int resp_sized;
+
+/**
+ * Use empty (zero-sized) response?
+ */
+int resp_empty;
+
+/**
+ * Force chunked response by response header?
+ */
+int chunked_forced;
+
+/**
+ * MHD port used for testing
+ */
+int port_global;
+
+
+struct headers_check_result
+{
+ int found_chunked;
+ int found_footer;
+};
+
+size_t
+lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
+ void *userdata)
+{
+ const size_t data_size = size * nitems;
+ struct headers_check_result *check_res =
+ (struct headers_check_result *) userdata;
+
+ if ((data_size == strlen (HDR_CHUNKED_ENCODING) + 2) &&
+ (0 == memcmp (buffer, HDR_CHUNKED_ENCODING "\r\n", data_size)))
+ check_res->found_chunked = 1;
+ if ((data_size == strlen (RESP_FOOTER) + 2) &&
+ (0 == memcmp (buffer, RESP_FOOTER "\r\n", data_size)))
+ check_res->found_footer = 1;
+
+ return data_size;
+}
+
+
struct CBC
{
char *buf;
@@ -82,17 +147,20 @@
{
struct MHD_Response **responseptr = cls;
- if (pos == 128 * 10)
- {
- MHD_add_response_footer (*responseptr,
- "Footer",
- "working");
- return MHD_CONTENT_READER_END_OF_STREAM;
+ if (resp_empty || (pos == RESP_SIZE - RESP_BLOCK_SIZE))
+ { /* Add footer with the last block */
+ if (MHD_YES != MHD_add_response_footer (*responseptr,
+ RESP_FOOTER_NAME,
+ RESP_FOOTER_VALUE))
+ abort ();
}
- if (max < 128)
+ if (resp_empty || (pos == RESP_SIZE))
+ return MHD_CONTENT_READER_END_OF_STREAM;
+
+ if (max < RESP_BLOCK_SIZE)
abort (); /* should not happen in this testcase... */
- memset (buf, 'A' + (pos / 128), 128);
- return 128;
+ memset (buf, 'A' + (pos / RESP_BLOCK_SIZE), RESP_BLOCK_SIZE);
+ return RESP_BLOCK_SIZE;
}
@@ -117,7 +185,6 @@
static int aptr;
const char *me = cls;
struct MHD_Response *response;
- struct MHD_Response **responseptr;
enum MHD_Result ret;
(void) url;
@@ -133,20 +200,64 @@
*ptr = &aptr;
return MHD_YES;
}
- responseptr = malloc (sizeof (struct MHD_Response *));
- if (NULL == responseptr)
- return MHD_NO;
- response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
- 1024,
- &crc,
- responseptr,
- &crcf);
+ if (! resp_string)
+ {
+ struct MHD_Response **responseptr;
+ responseptr = malloc (sizeof (struct MHD_Response *));
+ if (NULL == responseptr)
+ _exit (99);
+
+ response = MHD_create_response_from_callback (resp_sized ?
+ RESP_SIZE : MHD_SIZE_UNKNOWN,
+ 1024,
+ &crc,
+ responseptr,
+ &crcf);
+ *responseptr = response;
+ }
+ else
+ {
+ if (! resp_empty)
+ {
+ size_t pos;
+ static const size_t resp_size = RESP_SIZE;
+ char *buf = malloc (resp_size);
+ if (NULL == buf)
+ _exit (99);
+ for (pos = 0; pos < resp_size; pos += RESP_BLOCK_SIZE)
+ memset (buf + pos, 'A' + (pos / RESP_BLOCK_SIZE), RESP_BLOCK_SIZE);
+
+ response = MHD_create_response_from_buffer (resp_size, buf,
+ MHD_RESPMEM_MUST_COPY);
+ free (buf);
+ }
+ else
+ response = MHD_create_response_from_buffer (0, NULL,
+ MHD_RESPMEM_PERSISTENT);
+ }
if (NULL == response)
+ abort ();
+ if (chunked_forced)
{
- free (responseptr);
- return MHD_NO;
+ if (MHD_NO == MHD_add_response_header (response,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ "chunked"))
+ abort ();
+ }
+ if (MHD_NO == MHD_add_response_header (response,
+ MHD_HTTP_HEADER_TRAILER,
+ RESP_FOOTER_NAME))
+ abort ();
+
+ if (resp_string || (resp_sized && resp_empty))
+ {
+ /* There is no chance to add footer later */
+ if (MHD_YES != MHD_add_response_footer (response,
+ RESP_FOOTER_NAME,
+ RESP_FOOTER_VALUE))
+ abort ();
}
- *responseptr = response;
+
ret = MHD_queue_response (connection,
MHD_HTTP_OK,
response);
@@ -159,9 +270,21 @@
validate (struct CBC cbc, int ebase)
{
int i;
- char buf[128];
+ char buf[RESP_BLOCK_SIZE];
+
+ if (resp_empty)
+ {
+ if (0 != cbc.pos)
+ {
+ fprintf (stderr,
+ "Got %u bytes instead of zero!\n",
+ (unsigned int) cbc.pos);
+ return 1;
+ }
+ return 0;
+ }
- if (cbc.pos != 128 * 10)
+ if (cbc.pos != RESP_SIZE)
{
fprintf (stderr,
"Got %u bytes instead of 1280!\n",
@@ -169,14 +292,15 @@
return ebase;
}
- for (i = 0; i < 10; i++)
+ for (i = 0; i < RESP_BLOCK_QUANTIY; i++)
{
- memset (buf, 'A' + i, 128);
- if (0 != memcmp (buf, &cbc.buf[i * 128], 128))
+ memset (buf, 'A' + i, RESP_BLOCK_SIZE);
+ if (0 != memcmp (buf, &cbc.buf[i * RESP_BLOCK_SIZE], RESP_BLOCK_SIZE))
{
fprintf (stderr,
"Got `%.*s'\nWant `%.*s'\n",
- 128, buf, 128, &cbc.buf[i * 128]);
+ RESP_BLOCK_SIZE, &cbc.buf[i * RESP_BLOCK_SIZE],
+ RESP_BLOCK_SIZE, buf);
return ebase * 2;
}
}
@@ -193,12 +317,10 @@
struct CBC cbc;
CURLcode errornum;
int port;
+ struct curl_slist *h_list = NULL;
+ struct headers_check_result hdr_check;
- if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
- port = 0;
- else
- port = 1170;
-
+ port = port_global;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
@@ -215,7 +337,11 @@
MHD_stop_daemon (d); return 32;
}
port = (int) dinfo->port;
+ if (0 == port_global)
+ port_global = port; /* Re-use the same port for all checks */
}
+ hdr_check.found_chunked = 0;
+ hdr_check.found_footer = 0;
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
curl_easy_setopt (c, CURLOPT_PORT, (long) port);
@@ -225,21 +351,41 @@
curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
- /* NOTE: use of CONNECTTIMEOUT without also
- * setting NOSIGNAL results in really weird
- * crashes on my system! */
+ curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
+ curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+ if (conn_close)
+ {
+ h_list = curl_slist_append (h_list, "Connection: close");
+ if (NULL == h_list)
+ abort ();
+ curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
+ }
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
fprintf (stderr,
"curl_easy_perform failed: `%s'\n",
curl_easy_strerror (errornum));
curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
MHD_stop_daemon (d);
return 2;
}
curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
MHD_stop_daemon (d);
+ if (1 != hdr_check.found_chunked)
+ {
+ fprintf (stderr,
+ "Chunked encoding header was not found in the response\n");
+ return 8;
+ }
+ if (1 != hdr_check.found_footer)
+ {
+ fprintf (stderr,
+ "The specified footer was not found in the response\n");
+ return 16;
+ }
return validate (cbc, 4);
}
@@ -253,12 +399,10 @@
struct CBC cbc;
CURLcode errornum;
int port;
+ struct curl_slist *h_list = NULL;
+ struct headers_check_result hdr_check;
- if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
- port = 0;
- else
- port = 1171;
-
+ port = port_global;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
@@ -276,6 +420,8 @@
MHD_stop_daemon (d); return 32;
}
port = (int) dinfo->port;
+ if (0 == port_global)
+ port_global = port; /* Re-use the same port for all checks */
}
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
@@ -286,21 +432,43 @@
curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
- /* NOTE: use of CONNECTTIMEOUT without also
- * setting NOSIGNAL results in really weird
- * crashes on my system! */
curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+ hdr_check.found_chunked = 0;
+ hdr_check.found_footer = 0;
+ curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
+ curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
+ if (conn_close)
+ {
+ h_list = curl_slist_append (h_list, "Connection: close");
+ if (NULL == h_list)
+ abort ();
+ curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
+ }
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
fprintf (stderr,
"curl_easy_perform failed: `%s'\n",
curl_easy_strerror (errornum));
curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
MHD_stop_daemon (d);
return 32;
}
curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
MHD_stop_daemon (d);
+ if (1 != hdr_check.found_chunked)
+ {
+ fprintf (stderr,
+ "Chunked encoding header was not found in the response\n");
+ return 8;
+ }
+ if (1 != hdr_check.found_footer)
+ {
+ fprintf (stderr,
+ "The specified footer was not found in the response\n");
+ return 16;
+ }
return validate (cbc, 64);
}
@@ -314,12 +482,10 @@
struct CBC cbc;
CURLcode errornum;
int port;
+ struct curl_slist *h_list = NULL;
+ struct headers_check_result hdr_check;
- if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
- port = 0;
- else
- port = 1172;
-
+ port = port_global;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
@@ -338,6 +504,8 @@
MHD_stop_daemon (d); return 32;
}
port = (int) dinfo->port;
+ if (0 == port_global)
+ port_global = port; /* Re-use the same port for all checks */
}
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
@@ -348,21 +516,43 @@
curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
- /* NOTE: use of CONNECTTIMEOUT without also
- * setting NOSIGNAL results in really weird
- * crashes on my system! */
curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+ hdr_check.found_chunked = 0;
+ hdr_check.found_footer = 0;
+ curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
+ curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
+ if (conn_close)
+ {
+ h_list = curl_slist_append (h_list, "Connection: close");
+ if (NULL == h_list)
+ abort ();
+ curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
+ }
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
fprintf (stderr,
"curl_easy_perform failed: `%s'\n",
curl_easy_strerror (errornum));
curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
MHD_stop_daemon (d);
return 32;
}
curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
MHD_stop_daemon (d);
+ if (1 != hdr_check.found_chunked)
+ {
+ fprintf (stderr,
+ "Chunked encoding header was not found in the response\n");
+ return 8;
+ }
+ if (1 != hdr_check.found_footer)
+ {
+ fprintf (stderr,
+ "The specified footer was not found in the response\n");
+ return 16;
+ }
return validate (cbc, 64);
}
@@ -390,12 +580,10 @@
time_t start;
struct timeval tv;
int port;
+ struct curl_slist *h_list = NULL;
+ struct headers_check_result hdr_check;
- if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
- port = 0;
- else
- port = 1173;
-
+ port = port_global;
multi = NULL;
cbc.buf = buf;
cbc.size = 2048;
@@ -413,6 +601,8 @@
MHD_stop_daemon (d); return 32;
}
port = (int) dinfo->port;
+ if (0 == port_global)
+ port_global = port; /* Re-use the same port for all checks */
}
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
@@ -423,16 +613,24 @@
curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
- /* NOTE: use of CONNECTTIMEOUT without also
- * setting NOSIGNAL results in really weird
- * crashes on my system! */
curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
-
+ hdr_check.found_chunked = 0;
+ hdr_check.found_footer = 0;
+ curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
+ curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
+ if (conn_close)
+ {
+ h_list = curl_slist_append (h_list, "Connection: close");
+ if (NULL == h_list)
+ abort ();
+ curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
+ }
multi = curl_multi_init ();
if (multi == NULL)
{
curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
MHD_stop_daemon (d);
return 512;
}
@@ -441,6 +639,7 @@
{
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
MHD_stop_daemon (d);
return 1024;
}
@@ -459,6 +658,7 @@
curl_multi_remove_handle (multi, c);
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
MHD_stop_daemon (d);
return 2048;
}
@@ -467,6 +667,7 @@
curl_multi_remove_handle (multi, c);
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
MHD_stop_daemon (d);
return 4096;
}
@@ -476,45 +677,81 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ curl_slist_free_all (h_list);
+ h_list = NULL;
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
+ MHD_stop_daemon (d);
if (multi != NULL)
{
curl_multi_remove_handle (multi, c);
curl_easy_cleanup (c);
curl_multi_cleanup (multi);
}
- MHD_stop_daemon (d);
+ curl_slist_free_all (h_list);
+ if (1 != hdr_check.found_chunked)
+ {
+ fprintf (stderr,
+ "Chunked encoding header was not found in the response\n");
+ return 8;
+ }
+ if (1 != hdr_check.found_footer)
+ {
+ fprintf (stderr,
+ "The specified footer was not found in the response\n");
+ return 16;
+ }
return validate (cbc, 8192);
}
@@ -527,6 +764,33 @@
if (0 != curl_global_init (CURL_GLOBAL_WIN32))
return 2;
+ conn_close = has_in_name (argv[0], "_close");
+ resp_string = has_in_name (argv[0], "_string");
+ resp_sized = has_in_name (argv[0], "_sized");
+ resp_empty = has_in_name (argv[0], "_empty");
+ chunked_forced = has_in_name (argv[0], "_forced");
+ if (resp_string)
+ resp_sized = ! 0;
+ if (resp_sized)
+ chunked_forced = ! 0;
+
+ if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
+ port_global = 0;
+ else
+ {
+ port_global = 4100;
+ if (conn_close)
+ port_global += 1 << 0;
+ if (resp_string)
+ port_global += 1 << 1;
+ if (resp_sized)
+ port_global += 1 << 2;
+ if (resp_empty)
+ port_global += 1 << 3;
+ if (chunked_forced)
+ port_global += 1 << 4;
+ }
+
if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
{
errorCount += testInternalGet ();
@@ -537,5 +801,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_get_close_keep_alive.c
^
|
@@ -0,0 +1,1134 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
+ Copyright (C) 2007, 2009, 2011 Christian Grothoff
+
+ 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 2, 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_get_close_keep_alive.c
+ * @brief Testcase for libmicrohttpd "Close" and "Keep-Alive" connection.
+ * @details Testcases for testing of MHD automatic choice between "Close" and
+ * "Keep-Alive" connections. Also tested selected HTTP version and
+ * "Connection:" headers.
+ * @author Karlson2k (Evgeny Grin)
+ * @author Christian Grothoff
+ */
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "test_helpers.h"
+#include "mhd_sockets.h" /* only macros used */
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif /* HAVE_STRINGS_H */
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#ifndef WINDOWS
+#include <unistd.h>
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
+#ifndef CURL_VERSION_BITS
+#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
+#endif /* ! CURL_VERSION_BITS */
+#ifndef CURL_AT_LEAST_VERSION
+#define CURL_AT_LEAST_VERSION(x,y,z) \
+ (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
+#endif /* ! CURL_AT_LEAST_VERSION */
+
+#if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
+#undef MHD_CPU_COUNT
+#endif
+#if ! defined(MHD_CPU_COUNT)
+#define MHD_CPU_COUNT 2
+#endif
+#if MHD_CPU_COUNT > 32
+#undef MHD_CPU_COUNT
+/* Limit to reasonable value */
+#define MHD_CPU_COUNT 32
+#endif /* MHD_CPU_COUNT > 32 */
+
+
+#if defined(HAVE___FUNC__)
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func(NULL, __func__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, __func__, __LINE__)
+#define libcurlErrorExit(ignore) \
+ _libcurlErrorExit_func(NULL, __func__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, __func__, __LINE__)
+#elif defined(HAVE___FUNCTION__)
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define libcurlErrorExit(ignore) \
+ _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#else
+#define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, NULL, __LINE__)
+#define libcurlErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, NULL, __LINE__)
+#endif
+
+
+static void
+_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "System or external library call failed");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+ fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+ fflush (stderr);
+ exit (99);
+}
+
+
+static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
+
+static void
+_libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "CURL library call failed");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+ if (0 != libcurl_errbuf[0])
+ fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf);
+
+ fflush (stderr);
+ exit (99);
+}
+
+
+/* Could be increased to facilitate debugging */
+#define TIMEOUTS_VAL 5
+
+#define EXPECTED_URI_BASE_PATH "/hello_world"
+#define EXPECTED_URI_QUERY "a=%26&b=c"
+#define EXPECTED_URI_FULL_PATH EXPECTED_URI_BASE_PATH "?" EXPECTED_URI_QUERY
+#define HDR_CONN_CLOSE_VALUE "close"
+#define HDR_CONN_CLOSE MHD_HTTP_HEADER_CONNECTION ": " \
+ HDR_CONN_CLOSE_VALUE
+#define HDR_CONN_KEEP_ALIVE_VALUE "keep-alive"
+#define HDR_CONN_KEEP_ALIVE MHD_HTTP_HEADER_CONNECTION ": " \
+ HDR_CONN_KEEP_ALIVE_VALUE
+
+/* Global parameters */
+static int oneone; /**< Use HTTP/1.1 instead of HTTP/1.0 for requests*/
+static int conn_close; /**< Don't use Keep-Alive */
+static int global_port; /**< MHD daemons listen port number */
+static int slow_reply = 0; /**< Slowdown MHD replies */
+static int ignore_response_errors = 0; /**< Do not fail test if CURL
+ returns error */
+static int response_timeout_val = TIMEOUTS_VAL;
+
+/* Current test parameters */
+/* Poor thread sync, but enough for the testing */
+static volatile int mhd_add_close; /**< Add "Connection: close" header by MHD */
+static volatile int mhd_set_10_cmptbl; /**< Set MHD_RF_HTTP_1_0_COMPATIBLE_STRICT response flag */
+static volatile int mhd_set_10_server; /**< Set MHD_RF_HTTP_1_0_SERVER response flag */
+static volatile int mhd_set_k_a_send; /**< Set MHD_RF_SEND_KEEP_ALIVE_HEADER response flag */
+
+/* Static helper variables */
+static struct curl_slist *curl_close_hdr; /**< CURL "Connection: close" header */
+static struct curl_slist *curl_k_alive_hdr; /**< CURL "Connection: keep-alive" header */
+static struct curl_slist *curl_both_hdrs; /**< CURL both "Connection: keep-alive" and "close" headers */
+
+static void
+test_global_init (void)
+{
+ libcurl_errbuf[0] = 0;
+
+ if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+ externalErrorExit ();
+
+ curl_close_hdr = NULL;
+ curl_close_hdr = curl_slist_append (curl_close_hdr,
+ HDR_CONN_CLOSE);
+ if (NULL == curl_close_hdr)
+ externalErrorExit ();
+
+ curl_k_alive_hdr = NULL;
+ curl_k_alive_hdr = curl_slist_append (curl_k_alive_hdr,
+ HDR_CONN_KEEP_ALIVE);
+ if (NULL == curl_k_alive_hdr)
+ externalErrorExit ();
+
+ curl_both_hdrs = NULL;
+ curl_both_hdrs = curl_slist_append (curl_both_hdrs,
+ HDR_CONN_KEEP_ALIVE);
+ if (NULL == curl_both_hdrs)
+ externalErrorExit ();
+ curl_both_hdrs = curl_slist_append (curl_both_hdrs,
+ HDR_CONN_CLOSE);
+ if (NULL == curl_both_hdrs)
+ externalErrorExit ();
+}
+
+
+static void
+test_global_cleanup (void)
+{
+ curl_slist_free_all (curl_both_hdrs);
+ curl_slist_free_all (curl_k_alive_hdr);
+ curl_slist_free_all (curl_close_hdr);
+
+ curl_global_cleanup ();
+}
+
+
+struct headers_check_result
+{
+ int found_http11;
+ int found_http10;
+ int found_conn_close;
+ int found_conn_keep_alive;
+};
+
+size_t
+lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
+ void *userdata)
+{
+ const size_t data_size = size * nitems;
+ struct headers_check_result *check_res =
+ (struct headers_check_result *) userdata;
+
+ if ((strlen (MHD_HTTP_VERSION_1_1) < data_size) &&
+ (0 == memcmp (MHD_HTTP_VERSION_1_1, buffer,
+ strlen (MHD_HTTP_VERSION_1_1))))
+ check_res->found_http11 = 1;
+ else if ((strlen (MHD_HTTP_VERSION_1_0) < data_size) &&
+ (0 == memcmp (MHD_HTTP_VERSION_1_0, buffer,
+ strlen (MHD_HTTP_VERSION_1_0))))
+ check_res->found_http10 = 1;
+ else if ((data_size == strlen (HDR_CONN_CLOSE) + 2) &&
+ (0 == strncasecmp (buffer, HDR_CONN_CLOSE "\r\n", data_size)))
+ check_res->found_conn_close = 1;
+ else if ((data_size == strlen (HDR_CONN_KEEP_ALIVE) + 2) &&
+ (0 == strncasecmp (buffer, HDR_CONN_KEEP_ALIVE "\r\n", data_size)))
+ check_res->found_conn_keep_alive = 1;
+
+ return data_size;
+}
+
+
+struct CBC
+{
+ char *buf;
+ size_t pos;
+ size_t size;
+};
+
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+ struct CBC *cbc = ctx;
+
+ if (cbc->pos + size * nmemb > cbc->size)
+ externalErrorExit (); /* overflow */
+ memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+ cbc->pos += size * nmemb;
+ return size * nmemb;
+}
+
+
+static void *
+log_cb (void *cls,
+ const char *uri,
+ struct MHD_Connection *con)
+{
+ (void) cls;
+ (void) con;
+ if (0 != strcmp (uri,
+ EXPECTED_URI_FULL_PATH))
+ {
+ fprintf (stderr,
+ "Wrong URI: `%s', line: %d\n",
+ uri, __LINE__);
+ exit (22);
+ }
+ return NULL;
+}
+
+
+static enum MHD_Result
+ahc_echo (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data, size_t *upload_data_size,
+ void **unused)
+{
+ static int ptr;
+ const char *me = cls;
+ struct MHD_Response *response;
+ enum MHD_Result ret;
+ (void) version;
+ (void) upload_data;
+ (void) upload_data_size; /* Unused. Silence compiler warning. */
+
+ if (0 != strcmp (me, method))
+ return MHD_NO; /* unexpected method */
+ if (&ptr != *unused)
+ {
+ *unused = &ptr;
+ return MHD_YES;
+ }
+ *unused = NULL;
+ if (slow_reply)
+ usleep (200000);
+
+ response = MHD_create_response_from_buffer (strlen (url),
+ (void *) url,
+ MHD_RESPMEM_MUST_COPY);
+ if (NULL == response)
+ {
+ fprintf (stderr, "Failed to create response. Line: %d\n", __LINE__);
+ exit (19);
+ }
+ if (mhd_add_close)
+ {
+ if (MHD_YES != MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONNECTION,
+ HDR_CONN_CLOSE_VALUE))
+ {
+ fprintf (stderr, "Failed to add header. Line: %d\n", __LINE__);
+ exit (19);
+ }
+ }
+ if (MHD_YES != MHD_set_response_options (response,
+ (mhd_set_10_cmptbl ?
+ MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
+ : 0)
+ | (mhd_set_10_server ?
+ MHD_RF_HTTP_1_0_SERVER
+ : 0)
+ | (mhd_set_k_a_send ?
+ MHD_RF_SEND_KEEP_ALIVE_HEADER
+ : 0), MHD_RO_END))
+ {
+ fprintf (stderr, "Failed to set response flags. Line: %d\n", __LINE__);
+ exit (19);
+ }
+
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ if (MHD_YES != ret)
+ {
+ fprintf (stderr, "Failed to queue response. Line: %d\n", __LINE__);
+ exit (19);
+ }
+ return ret;
+}
+
+
+struct curlQueryParams
+{
+ /* Destination path for CURL query */
+ const char *queryPath;
+
+ /* Destination port for CURL query */
+ int queryPort;
+
+ /* CURL query result error flag */
+ volatile int queryError;
+};
+
+
+static CURL *
+curlEasyInitForTest (const char *queryPath, int port, struct CBC *pcbc,
+ struct headers_check_result *hdr_chk_result,
+ int add_hdr_close, int add_hdr_k_alive)
+{
+ CURL *c;
+
+ c = curl_easy_init ();
+ if (NULL == c)
+ {
+ fprintf (stderr, "curl_easy_init() failed.\n");
+ externalErrorExit ();
+ }
+ if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, queryPath)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+ ©Buffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, pcbc)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
+ (long) response_timeout_val)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
+ (long) response_timeout_val)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
+ libcurl_errbuf)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION,
+ lcurl_hdr_callback)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA,
+ hdr_chk_result)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
+ (oneone) ?
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_1)) :
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_0)))
+ {
+ fprintf (stderr, "curl_easy_setopt() failed.\n");
+ externalErrorExit ();
+ }
+ if (add_hdr_close && add_hdr_k_alive)
+ { /* This combination is actually incorrect */
+ if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, curl_both_hdrs))
+ {
+ fprintf (stderr, "Set libcurl HTTP header failed.\n");
+ externalErrorExit ();
+ }
+ }
+ else if (add_hdr_close)
+ {
+ if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, curl_close_hdr))
+ {
+ fprintf (stderr, "Set libcurl HTTP header failed.\n");
+ externalErrorExit ();
+ }
+ }
+ else if (add_hdr_k_alive)
+ {
+ if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, curl_k_alive_hdr))
+ {
+ fprintf (stderr, "Set libcurl HTTP header failed.\n");
+ externalErrorExit ();
+ }
+ }
+
+ return c;
+}
+
+
+static void
+print_test_params (int add_hdr_close,
+ int add_hdr_k_alive)
+{
+ fprintf (stderr, "Request HTTP/%s| ", oneone ? "1.1" : "1.0");
+ fprintf (stderr, "Connection must be: %s| ",
+ conn_close ? "close" : "keep-alive");
+ fprintf (stderr, "Request \"close\": %s| ",
+ add_hdr_close ? " used" : "NOT used");
+ fprintf (stderr, "Request \"keep-alive\": %s| ",
+ add_hdr_k_alive ? " used" : "NOT used");
+ fprintf (stderr, "MHD response \"close\": %s| ",
+ mhd_add_close ? " used" : "NOT used");
+ fprintf (stderr, "MHD response 1.0 strict compatible: %s| ",
+ mhd_set_10_cmptbl ? "yes" : " NO");
+ fprintf (stderr, "MHD response 1.0 server: %s| ",
+ mhd_set_10_server ? "yes" : " NO");
+ fprintf (stderr, "MHD response send \"Keep-Alive\": %s|",
+ mhd_set_k_a_send ? "yes" : " NO");
+ fprintf (stderr, "\n*** ");
+}
+
+
+static CURLcode
+performQueryExternal (struct MHD_Daemon *d, CURL *c)
+{
+ CURLM *multi;
+ time_t start;
+ struct timeval tv;
+ CURLcode ret;
+
+ ret = CURLE_FAILED_INIT; /* will be replaced with real result */
+ multi = NULL;
+ multi = curl_multi_init ();
+ if (multi == NULL)
+ {
+ fprintf (stderr, "curl_multi_init() failed.\n");
+ externalErrorExit ();
+ }
+ if (CURLM_OK != curl_multi_add_handle (multi, c))
+ {
+ fprintf (stderr, "curl_multi_add_handle() failed.\n");
+ externalErrorExit ();
+ }
+
+ start = time (NULL);
+ while (time (NULL) - start <= TIMEOUTS_VAL)
+ {
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ MHD_socket maxMhdSk;
+ int maxCurlSk;
+ int running;
+
+ maxMhdSk = MHD_INVALID_SOCKET;
+ maxCurlSk = -1;
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ if (NULL != multi)
+ {
+ curl_multi_perform (multi, &running);
+ if (0 == running)
+ {
+ struct CURLMsg *msg;
+ int msgLeft;
+ int totalMsgs = 0;
+ do
+ {
+ msg = curl_multi_info_read (multi, &msgLeft);
+ if (NULL == msg)
+ {
+ fprintf (stderr, "curl_multi_info_read failed, NULL returned.\n");
+ externalErrorExit ();
+ }
+ totalMsgs++;
+ if (CURLMSG_DONE == msg->msg)
+ ret = msg->data.result;
+ } while (msgLeft > 0);
+ if (1 != totalMsgs)
+ {
+ fprintf (stderr,
+ "curl_multi_info_read returned wrong "
+ "number of results (%d).\n",
+ totalMsgs);
+ externalErrorExit ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ multi = NULL;
+ }
+ else
+ {
+ if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
+ {
+ fprintf (stderr, "curl_multi_fdset() failed.\n");
+ externalErrorExit ();
+ }
+ }
+ }
+ if (NULL == multi)
+ { /* libcurl has finished, check whether MHD still needs to perform cleanup */
+ unsigned long long to;
+ if ((MHD_YES != MHD_get_timeout (d, &to)) || (0 != to))
+ break; /* MHD finished as well */
+ }
+ if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
+ {
+ fprintf (stderr, "MHD_get_fdset() failed. Line: %d\n", __LINE__);
+ exit (11);
+ break;
+ }
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000;
+#ifdef MHD_POSIX_SOCKETS
+ if (maxMhdSk > maxCurlSk)
+ maxCurlSk = maxMhdSk;
+#endif /* MHD_POSIX_SOCKETS */
+ if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
+ {
+#ifdef MHD_POSIX_SOCKETS
+ if (EINTR != errno)
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+#else
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
+#endif
+ }
+ if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
+ {
+ fprintf (stderr, "MHD_run_from_select() failed. Line: %d\n", __LINE__);
+ exit (11);
+ }
+ }
+
+ return ret;
+}
+
+
+static unsigned int
+getMhdActiveConnections (struct MHD_Daemon *d)
+{
+ const union MHD_DaemonInfo *dinfo;
+ /* The next method is unreliable unless it's known that no
+ * connections are started or finished in parallel */
+ dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_CURRENT_CONNECTIONS);
+ if (NULL == dinfo)
+ {
+ fprintf (stderr, "MHD_get_daemon_info() failed.\n");
+ abort ();
+ }
+ return dinfo->num_connections;
+}
+
+
+static int
+doCurlQueryInThread (struct MHD_Daemon *d,
+ struct curlQueryParams *p,
+ int add_hdr_close,
+ int add_hdr_k_alive)
+{
+ const union MHD_DaemonInfo *dinfo;
+ CURL *c;
+ char buf[2048];
+ struct CBC cbc;
+ struct headers_check_result hdr_res;
+ CURLcode errornum;
+ int use_external_poll;
+
+ dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
+ if (NULL == dinfo)
+ {
+ fprintf (stderr, "MHD_get_daemon_info() failed.\n");
+ abort ();
+ }
+ use_external_poll = (0 == (dinfo->flags
+ & MHD_USE_INTERNAL_POLLING_THREAD));
+
+ if (NULL == p->queryPath)
+ abort ();
+
+ if (0 == p->queryPort)
+ abort ();
+
+ cbc.buf = buf;
+ cbc.size = sizeof(buf);
+ cbc.pos = 0;
+
+ hdr_res.found_http11 = 0;
+ hdr_res.found_http10 = 0;
+ hdr_res.found_conn_close = 0;
+ hdr_res.found_conn_keep_alive = 0;
+
+ c = curlEasyInitForTest (p->queryPath, p->queryPort, &cbc, &hdr_res,
+ add_hdr_close, add_hdr_k_alive);
+
+ if (! use_external_poll)
+ errornum = curl_easy_perform (c);
+ else
+ errornum = performQueryExternal (d, c);
+ if (ignore_response_errors)
+ {
+ p->queryError = 0;
+ curl_easy_cleanup (c);
+
+ return p->queryError;
+ }
+ if (CURLE_OK != errornum)
+ {
+ p->queryError = 1;
+ fprintf (stderr,
+ "libcurl query failed: `%s'\n",
+ curl_easy_strerror (errornum));
+ libcurlErrorExit ();
+ }
+ else
+ {
+ if (cbc.pos != strlen (EXPECTED_URI_BASE_PATH))
+ {
+ fprintf (stderr, "curl reports wrong size of MHD reply body data.\n");
+ p->queryError = 1;
+ }
+ else if (0 != strncmp (EXPECTED_URI_BASE_PATH, cbc.buf,
+ strlen (EXPECTED_URI_BASE_PATH)))
+ {
+ fprintf (stderr, "curl reports wrong MHD reply body data.\n");
+ p->queryError = 1;
+ }
+ else
+ p->queryError = 0;
+ }
+
+ if (! hdr_res.found_http11 && ! hdr_res.found_http10)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "No know HTTP versions were found in the "
+ "reply header. Line: %d\n", __LINE__);
+ exit (24);
+ }
+ else if (hdr_res.found_http11 && hdr_res.found_http10)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "Both HTTP/1.1 and HTTP/1.0 were found in the "
+ "reply header. Line: %d\n", __LINE__);
+ exit (24);
+ }
+
+ if (conn_close)
+ {
+ if (! hdr_res.found_conn_close)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "\"Connection: close\" was not found in"
+ " MHD reply headers.\n");
+ p->queryError |= 2;
+ }
+ if (hdr_res.found_conn_keep_alive)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "\"Connection: keep-alive\" was found in"
+ " MHD reply headers.\n");
+ p->queryError |= 2;
+ }
+ if (use_external_poll)
+ { /* The number of MHD connection can queried only with external poll.
+ * otherwise it creates a race condition. */
+ if (0 != getMhdActiveConnections (d))
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "MHD still has active connection "
+ "after response has been sent.\n");
+ p->queryError |= 2;
+ }
+ }
+ }
+ else
+ { /* Keep-Alive */
+ if (! oneone || mhd_set_10_server || mhd_set_k_a_send)
+ { /* Should have "Connection: Keep-Alive" */
+ if (! hdr_res.found_conn_keep_alive)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "\"Connection: keep-alive\" was not found in"
+ " MHD reply headers.\n");
+ p->queryError |= 2;
+ }
+ }
+ else
+ { /* Should NOT have "Connection: Keep-Alive" */
+ if (hdr_res.found_conn_keep_alive)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "\"Connection: keep-alive\" was found in"
+ " MHD reply headers.\n");
+ p->queryError |= 2;
+ }
+ }
+ if (hdr_res.found_conn_close)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "\"Connection: close\" was found in"
+ " MHD reply headers.\n");
+ p->queryError |= 2;
+ }
+ if (use_external_poll)
+ { /* The number of MHD connection can be queried only with external poll.
+ * otherwise it creates a race condition. */
+ unsigned int num_conn = getMhdActiveConnections (d);
+ if (0 == num_conn)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "MHD has no active connection "
+ "after response has been sent.\n");
+ p->queryError |= 2;
+ }
+ else if (1 != num_conn)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "MHD has wrong number of active connection (%u) "
+ "after response has been sent. Line: %d\n", num_conn,
+ __LINE__);
+ exit (23);
+ }
+ }
+ }
+
+#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION (7, 45, 0)
+ if (! use_external_poll)
+ {
+ /* libcurl closes connection socket with curl_multi_remove_handle () /
+ curl_multi_cleanup() */
+ curl_socket_t curl_sckt;
+ if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_ACTIVESOCKET, &curl_sckt))
+ {
+ fprintf (stderr,
+ "Failed to get libcurl active socket.\n");
+ libcurlErrorExit ();
+ }
+
+ if (conn_close && (CURL_SOCKET_BAD != curl_sckt))
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "libcurl still has active connection "
+ "after performing the test query.\n");
+ p->queryError |= 2;
+ }
+ else if (! conn_close && (CURL_SOCKET_BAD == curl_sckt))
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "libcurl has no active connection "
+ "after performing the test query.\n");
+ p->queryError |= 2;
+ }
+ }
+#endif
+
+ if (! mhd_set_10_server)
+ { /* Response must be HTTP/1.1 */
+ if (hdr_res.found_http10)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "Reply has HTTP/1.0 version, while it "
+ "must be HTTP/1.1.\n");
+ p->queryError |= 4;
+ }
+ }
+ else
+ { /* Response must be HTTP/1.0 */
+ if (hdr_res.found_http11)
+ {
+ print_test_params (add_hdr_close, add_hdr_k_alive);
+ fprintf (stderr, "Reply has HTTP/1.1 version, while it "
+ "must be HTTP/1.0.\n");
+ p->queryError |= 4;
+ }
+ }
+ curl_easy_cleanup (c);
+
+ return p->queryError;
+}
+
+
+/* Perform test queries and shut down MHD daemon */
+static int
+performTestQueries (struct MHD_Daemon *d, int d_port)
+{
+ struct curlQueryParams qParam;
+ int ret = 0; /* Return value */
+ int i = 0;
+ /* masks */
+ const int m_mhd_close = 1 << (i++);
+ const int m_10_cmptbl = 1 << (i++);
+ const int m_10_server = 1 << (i++);
+ const int m_k_a_send = 1 << (i++);
+ const int m_client_close = 1 << (i++);
+ const int m_client_k_alive = 1 << (i++);
+
+ qParam.queryPath = "http://127.0.0.1" EXPECTED_URI_FULL_PATH;
+ qParam.queryPort = d_port; /* Connect to the daemon */
+
+ for (i = (1 << i) - 1; 0 <= i; i--)
+ {
+ const int f_mhd_close = (0 == (i & m_mhd_close)); /**< Use MHD "close" header */
+ const int f_10_cmptbl = (0 == (i & m_10_cmptbl)); /**< Use MHD MHD_RF_HTTP_1_0_COMPATIBLE_STRICT flag */
+ const int f_10_server = (0 == (i & m_10_server)); /**< Use MHD MHD_RF_HTTP_1_0_SERVER flag */
+ const int f_k_a_send = (0 == (i & m_k_a_send)); /**< Use MHD MHD_RF_SEND_KEEP_ALIVE_HEADER flag */
+ const int f_client_close = (0 == (i & m_client_close)); /**< Use libcurl "close" header */
+ const int f_client_k_alive = (0 == (i & m_client_k_alive)); /**< Use libcurl "Keep-Alive" header */
+ int res_close; /**< Indicate the result of the test query should be closed connection */
+
+ if (f_mhd_close) /* Connection with server's "close" header must be always closed */
+ res_close = 1;
+ else if (f_client_close) /* Connection with client's "close" header must be always closed */
+ res_close = 1;
+ else if (f_10_cmptbl) /* Connection in strict HTTP/1.0 compatible mode must be always closed */
+ res_close = 1;
+ else if (! oneone || f_10_server)
+ /* With HTTP/1.0 client or server connection must be close unless client use "keep-alive" header */
+ res_close = ! f_client_k_alive;
+ else
+ res_close = 0; /* HTTP/1.1 is "keep-alive" by default */
+
+ if ((! ! res_close) != (! ! conn_close))
+ continue; /* Another mode is in test currently */
+
+ mhd_add_close = f_mhd_close;
+ mhd_set_10_cmptbl = f_10_cmptbl;
+ mhd_set_10_server = f_10_server;
+ mhd_set_k_a_send = f_k_a_send;
+
+ ret <<= 3; /* Remember errors for each step */
+ ret |= doCurlQueryInThread (d, &qParam, f_client_close, f_client_k_alive);
+ }
+
+ MHD_stop_daemon (d);
+
+ return ret;
+}
+
+
+enum testMhdThreadsType
+{
+ testMhdThreadExternal = 0,
+ testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD,
+ testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
+ | MHD_USE_INTERNAL_POLLING_THREAD,
+ testMhdThreadInternalPool
+};
+
+enum testMhdPollType
+{
+ testMhdPollBySelect = 0,
+ testMhdPollByPoll = MHD_USE_POLL,
+ testMhdPollByEpoll = MHD_USE_EPOLL,
+ testMhdPollAuto = MHD_USE_AUTO
+};
+
+/* Get number of threads for thread pool depending
+ * on used poll function and test type. */
+static unsigned int
+testNumThreadsForPool (enum testMhdPollType pollType)
+{
+ int numThreads = MHD_CPU_COUNT;
+ (void) pollType; /* Don't care about pollType for this test */
+ return numThreads; /* No practical limit for non-cleanup test */
+}
+
+
+static struct MHD_Daemon *
+startTestMhdDaemon (enum testMhdThreadsType thrType,
+ enum testMhdPollType pollType, int *pport)
+{
+ struct MHD_Daemon *d;
+ const union MHD_DaemonInfo *dinfo;
+
+ if ( (0 == *pport) &&
+ (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
+ {
+ *pport = 4050;
+ if (oneone)
+ *pport += 1;
+ if (! conn_close)
+ *pport += 2;
+ }
+
+ if (testMhdThreadInternalPool != thrType)
+ d = MHD_start_daemon (((int) thrType) | ((int) pollType)
+ | MHD_USE_ERROR_LOG,
+ *pport, NULL, NULL,
+ &ahc_echo, "GET",
+ MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
+ MHD_OPTION_END);
+ else
+ d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | ((int) pollType)
+ | MHD_USE_ERROR_LOG,
+ *pport, NULL, NULL,
+ &ahc_echo, "GET",
+ MHD_OPTION_THREAD_POOL_SIZE,
+ testNumThreadsForPool (pollType),
+ MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
+ MHD_OPTION_END);
+
+ if (NULL == d)
+ {
+ fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno);
+ abort ();
+ }
+
+ if (0 == *pport)
+ {
+ dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
+ if ((NULL == dinfo) || (0 == dinfo->port) )
+ {
+ fprintf (stderr, "MHD_get_daemon_info() failed.\n");
+ abort ();
+ }
+ *pport = (int) dinfo->port;
+ if (0 == global_port)
+ global_port = *pport; /* Reuse the same port for all tests */
+ }
+
+ return d;
+}
+
+
+/* Test runners */
+
+
+static int
+testExternalGet (void)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+
+ d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port);
+
+ return performTestQueries (d, d_port);
+}
+
+
+static int
+testInternalGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+
+ d = startTestMhdDaemon (testMhdThreadInternal, pollType,
+ &d_port);
+
+ return performTestQueries (d, d_port);
+}
+
+
+static int
+testMultithreadedGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+
+ d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType,
+ &d_port);
+ return performTestQueries (d, d_port);
+}
+
+
+static int
+testMultithreadedPoolGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+
+ d = startTestMhdDaemon (testMhdThreadInternalPool, pollType,
+ &d_port);
+ return performTestQueries (d, d_port);
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+ unsigned int errorCount = 0;
+ unsigned int test_result = 0;
+ int verbose = 0;
+
+ if ((NULL == argv) || (0 == argv[0]))
+ return 99;
+ oneone = ! has_in_name (argv[0], "10");
+ conn_close = has_in_name (argv[0], "_close");
+ if (! conn_close && ! has_in_name (argv[0], "_keep_alive"))
+ return 99;
+ verbose = ! has_param (argc, argv, "-q") || has_param (argc, argv, "--quiet");
+
+ test_global_init ();
+
+ /* Could be set to non-zero value to enforce using specific port
+ * in the test */
+ global_port = 0;
+ test_result = testExternalGet ();
+ if (test_result)
+ fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
+ else if (verbose)
+ printf ("PASSED: testExternalGet ().\n");
+ errorCount += test_result;
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
+ {
+ test_result = testInternalGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ test_result = testMultithreadedPoolGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr,
+ "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ test_result = testMultithreadedGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr,
+ "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
+ {
+ test_result = testInternalGet (testMhdPollByPoll);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
+ errorCount += test_result;
+ }
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
+ {
+ test_result = testInternalGet (testMhdPollByEpoll);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
+ errorCount += test_result;
+ }
+ }
+ if (0 != errorCount)
+ fprintf (stderr,
+ "Error (code: %u)\n",
+ errorCount);
+ else if (verbose)
+ printf ("All tests passed.\n");
+
+ test_global_cleanup ();
+
+ return (errorCount == 0) ? 0 : 1; /* 0 == pass */
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_get_empty.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009, 2011, 2019 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -21,6 +22,7 @@
* @file test_get_empty.c
* @brief Testcase for libmicrohttpd GET operations returning an empty body
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
#include "platform.h"
@@ -114,7 +116,7 @@
(void) upload_data;
(void) upload_data_size; /* Unused. Silence compiler warning. */
- if (0 != strcasecmp (me, method))
+ if (0 != strcmp (me, method))
return MHD_NO; /* unexpected method */
if (&ptr != *unused)
{
@@ -470,35 +472,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- _exit (99);
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -691,7 +714,7 @@
(void) upload_data;
(void) upload_data_size; /* Unused. Silent compiler warning. */
- if (0 != strcasecmp ("GET", method))
+ if (0 != strcmp ("GET", method))
return MHD_NO; /* unexpected method */
if (&ptr != *unused)
{
@@ -938,5 +961,5 @@
else if (verbose)
printf ("All tests passed.\n");
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_get_iovec.c
^
|
@@ -99,7 +99,7 @@
unsigned int i;
for (i = 0; i < TESTSTR_IOVCNT; ++i)
- free ((void*) iov[i].iov_base);
+ free ((void *) iov[i].iov_base);
free (iov);
}
@@ -245,7 +245,7 @@
for (j = 0; j < TESTSTR_IOVCNT; ++j)
{
if (NULL != iov[j].iov_base)
- free ((void*) iov[j].iov_base);
+ free ((void *) iov[j].iov_base);
}
free (iov);
return MHD_NO;
@@ -270,7 +270,7 @@
port += 10;
}
- cbc.buf = (char*) readbuf;
+ cbc.buf = (char *) readbuf;
cbc.size = sizeof(readbuf);
cbc.pos = 0;
@@ -350,7 +350,7 @@
port += 10;
}
- cbc.buf = (char*) readbuf;
+ cbc.buf = (char *) readbuf;
cbc.size = sizeof(readbuf);
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
@@ -422,7 +422,7 @@
port += 10;
}
- cbc.buf = (char*) readbuf;
+ cbc.buf = (char *) readbuf;
cbc.size = sizeof(readbuf);
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
@@ -510,7 +510,7 @@
}
multi = NULL;
- cbc.buf = (char*) readbuf;
+ cbc.buf = (char *) readbuf;
cbc.size = sizeof(readbuf);
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_ERROR_LOG,
@@ -592,35 +592,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -657,7 +678,7 @@
addr.sin_port = 0;
addr.sin_addr.s_addr = INADDR_ANY;
- cbc.buf = (char*) readbuf;
+ cbc.buf = (char *) readbuf;
cbc.size = sizeof(readbuf);
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
@@ -752,5 +773,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_get_response_cleanup.c
^
|
@@ -1,7 +1,7 @@
-/* DO NOT CHANGE THIS LINE */
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -23,6 +23,7 @@
* @file daemontest_get_response_cleanup.c
* @brief Testcase for libmicrohttpd response cleanup
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -60,8 +61,6 @@
#define MHD_CPU_COUNT 2
#endif
-#define TESTSTR "/* DO NOT CHANGE THIS LINE */"
-
static int oneone;
static int ok;
@@ -375,8 +374,25 @@
tv.tv_usec = 1000;
if (-1 == select (max + 1, &rs, &ws, &es, &tv))
{
+#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+#else
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
+#endif
}
MHD_run (d);
}
@@ -397,8 +413,25 @@
tv.tv_usec = 1000;
if (-1 == select (max + 1, &rs, &ws, &es, &tv))
{
+#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+#else
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
+#endif
}
MHD_run (d);
}
@@ -439,5 +472,5 @@
errorCount += testExternalGet ();
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_get_sendfile.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -21,6 +22,7 @@
* @file test_get_sendfile.c
* @brief Testcase for libmicrohttpd response from FD
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -449,35 +451,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -641,5 +664,5 @@
curl_global_cleanup ();
unlink (sourcefile);
free (sourcefile);
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_get_wait.c
^
|
@@ -1,7 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009, 2011 Christian Grothoff
- Copyright (C) 2016-2021 Karlson2k (Evgeny Grin)
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -189,7 +189,7 @@
}
if (0 != pthread_create (&get_tid, NULL,
- &thread_gets, (void*) (intptr_t) port))
+ &thread_gets, (void *) (intptr_t) port))
_exit (99);
/* As another thread sets "done" flag after ending of network
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_iplimit.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -20,9 +21,9 @@
/**
* @file test_iplimit.c
- * @brief Testcase for libmicrohttpd GET operations
- * TODO: test parsing of query
+ * @brief Testcase for libmicrohttpd limits per IP
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -134,7 +135,7 @@
d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
port, NULL, NULL,
&ahc_echo, "GET",
- MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2,
+ MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 2,
MHD_OPTION_END);
if (d == NULL)
return 16;
@@ -245,7 +246,7 @@
d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
port, NULL, NULL, &ahc_echo, "GET",
- MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2,
+ MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 2,
MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
MHD_OPTION_END);
if (d == NULL)
@@ -358,5 +359,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_large_put.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2008 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file test_large_put.c
* @brief Testcase for libmicrohttpd PUT operations
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -45,6 +47,111 @@
#define MHD_CPU_COUNT 2
#endif
+
+#if defined(HAVE___FUNC__)
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func(NULL, __func__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, __func__, __LINE__)
+#define libcurlErrorExit(ignore) \
+ _libcurlErrorExit_func(NULL, __func__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, __func__, __LINE__)
+#define mhdErrorExit(ignore) \
+ _mhdErrorExit_func(NULL, __func__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+ _mhdErrorExit_func(errDesc, __func__, __LINE__)
+#elif defined(HAVE___FUNCTION__)
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define libcurlErrorExit(ignore) \
+ _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define mhdErrorExit(ignore) \
+ _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+ _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#else
+#define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, NULL, __LINE__)
+#define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, NULL, __LINE__)
+#define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
+#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
+#endif
+
+
+_MHD_NORETURN static void
+_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "System or external library call failed");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+ fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+ fflush (stderr);
+ exit (99);
+}
+
+
+static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
+
+_MHD_NORETURN static void
+_libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "CURL library call failed");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+ if (0 != libcurl_errbuf[0])
+ fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf);
+
+ fflush (stderr);
+ exit (99);
+}
+
+
+_MHD_NORETURN static void
+_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "MHD unexpected error");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+
+ fflush (stderr);
+ exit (8);
+}
+
+
static int oneone;
static int incr_read; /* Use incremental read */
static int verbose; /* Be verbose */
@@ -60,7 +167,7 @@
size_t size;
};
-char*
+char *
alloc_init (size_t buf_size)
{
static const char template[] =
@@ -72,7 +179,7 @@
buf = malloc (buf_size);
if (NULL == buf)
- return NULL;
+ externalErrorExit ();
fill_ptr = buf;
to_fill = buf_size;
@@ -96,7 +203,7 @@
wrt = size * nmemb;
/* Check for overflow. */
if (wrt / size != nmemb)
- return 0;
+ libcurlErrorExitDesc ("Too large buffer size");
if (wrt > PUT_SIZE - (*pos))
wrt = PUT_SIZE - (*pos);
memcpy (stream, &put_buffer[*pos], wrt);
@@ -111,7 +218,7 @@
struct CBC *cbc = ctx;
if (cbc->pos + size * nmemb > cbc->size)
- return 0; /* overflow */
+ libcurlErrorExitDesc ("Too large buffer size");
memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
cbc->pos += size * nmemb;
return size * nmemb;
@@ -131,36 +238,46 @@
struct MHD_Response *response;
enum MHD_Result ret;
static size_t processed;
- (void) version; /* Unused. Silent compiler warning. */
+
+ if (NULL == cls)
+ mhdErrorExitDesc ("cls parameter is NULL");
+
+ if (0 != strcmp (version, oneone ?
+ MHD_HTTP_VERSION_1_1 : MHD_HTTP_VERSION_1_0))
+ mhdErrorExitDesc ("Unexpected HTTP version");
+
+ if (NULL == url)
+ mhdErrorExitDesc ("url parameter is NULL");
+
+ if (NULL == upload_data_size)
+ mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
if (0 != strcmp ("PUT", method))
- return MHD_NO; /* unexpected method */
+ mhdErrorExitDesc ("Unexpected request method"); /* unexpected method */
+
if ((*done) == 0)
{
size_t *pproc;
if (NULL == *pparam)
{
processed = 0;
- *pparam = &processed; /* Safe as long as only one parallel request served. */
+ /* Safe as long as only one parallel request served. */
+ *pparam = &processed;
}
- pproc = (size_t*) *pparam;
+ pproc = (size_t *) *pparam;
if (0 == *upload_data_size)
return MHD_YES; /* No data to process. */
if (*pproc + *upload_data_size > PUT_SIZE)
- {
- fprintf (stderr, "Incoming data larger than expected.\n");
- return MHD_NO;
- }
+ mhdErrorExitDesc ("Incoming data larger than expected");
+
if ( (! incr_read) && (*upload_data_size != PUT_SIZE) )
return MHD_YES; /* Wait until whole request is received. */
if (0 != memcmp (upload_data, put_buffer + (*pproc), *upload_data_size))
- {
- fprintf (stderr, "Incoming data does not match sent data.\n");
- return MHD_NO;
- }
+ mhdErrorExitDesc ("Incoming data does not match sent data");
+
*pproc += *upload_data_size;
*upload_data_size = 0; /* Current block of data is fully processed. */
@@ -171,6 +288,8 @@
response = MHD_create_response_from_buffer (strlen (url),
(void *) url,
MHD_RESPMEM_MUST_COPY);
+ if (NULL == response)
+ mhdErrorExitDesc ("Failed to create response");
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
MHD_destroy_response (response);
return ret;
@@ -211,37 +330,50 @@
(size_t) (incr_read ? 1024 : (PUT_SIZE * 4 / 3)),
MHD_OPTION_END);
if (d == NULL)
- return 1;
+ mhdErrorExit ();
if (0 == port)
{
const union MHD_DaemonInfo *dinfo;
dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
if ((NULL == dinfo) || (0 == dinfo->port) )
- {
- MHD_stop_daemon (d); return 32;
- }
+ mhdErrorExit ();
port = (int) dinfo->port;
}
+
c = curl_easy_init ();
- curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
- curl_easy_setopt (c, CURLOPT_PORT, (long) port);
- curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
- curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
- curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
- curl_easy_setopt (c, CURLOPT_READDATA, &pos);
- curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
- curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
- curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
- curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
- if (oneone)
- curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
- else
- curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
- curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
- /* NOTE: use of CONNECTTIMEOUT without also
- * setting NOSIGNAL results in really weird
- * crashes on my system! */
- curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+ if (NULL == c)
+ {
+ fprintf (stderr, "curl_easy_init() failed.\n");
+ externalErrorExit ();
+ }
+ if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
+ "http://127.0.0.1/hello_world")) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+ ©Buffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION,
+ &putBuffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
+ (long) 150)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
+ (long) 150)) ||
+ ((oneone) ?
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_1)) :
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_0))))
+ {
+ fprintf (stderr, "curl_easy_setopt() failed.\n");
+ externalErrorExit ();
+ }
+
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
fprintf (stderr,
@@ -254,9 +386,17 @@
curl_easy_cleanup (c);
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
- return 4;
+ {
+ fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
+ (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
+ (unsigned) strlen ("/hello_world"));
+ mhdErrorExitDesc ("Wrong returned data length");
+ }
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
- return 8;
+ {
+ fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
+ mhdErrorExitDesc ("Wrong returned data length");
+ }
return 0;
}
@@ -296,37 +436,50 @@
(size_t) (incr_read ? 1024 : (PUT_SIZE * 4)),
MHD_OPTION_END);
if (d == NULL)
- return 16;
+ mhdErrorExit ();
if (0 == port)
{
const union MHD_DaemonInfo *dinfo;
dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
if ((NULL == dinfo) || (0 == dinfo->port) )
- {
- MHD_stop_daemon (d); return 32;
- }
+ mhdErrorExit ();
port = (int) dinfo->port;
}
+
c = curl_easy_init ();
- curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
- curl_easy_setopt (c, CURLOPT_PORT, (long) port);
- curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
- curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
- curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
- curl_easy_setopt (c, CURLOPT_READDATA, &pos);
- curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
- curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
- curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
- curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
- if (oneone)
- curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
- else
- curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
- curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
- /* NOTE: use of CONNECTTIMEOUT without also
- * setting NOSIGNAL results in really weird
- * crashes on my system! */
- curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+ if (NULL == c)
+ {
+ fprintf (stderr, "curl_easy_init() failed.\n");
+ externalErrorExit ();
+ }
+ if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
+ "http://127.0.0.1/hello_world")) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+ ©Buffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION,
+ &putBuffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
+ (long) 150)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
+ (long) 150)) ||
+ ((oneone) ?
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_1)) :
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_0))))
+ {
+ fprintf (stderr, "curl_easy_setopt() failed.\n");
+ externalErrorExit ();
+ }
+
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
fprintf (stderr,
@@ -340,11 +493,16 @@
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
{
- fprintf (stderr, "Got invalid response `%.*s'\n", (int) cbc.pos, cbc.buf);
- return 64;
+ fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
+ (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
+ (unsigned) strlen ("/hello_world"));
+ mhdErrorExitDesc ("Wrong returned data length");
}
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
- return 128;
+ {
+ fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
+ mhdErrorExitDesc ("Wrong returned data length");
+ }
return 0;
}
@@ -384,37 +542,49 @@
(size_t) (incr_read ? 1024 : (PUT_SIZE * 4)),
MHD_OPTION_END);
if (d == NULL)
- return 16;
+ mhdErrorExit ();
if (0 == port)
{
const union MHD_DaemonInfo *dinfo;
dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
if ((NULL == dinfo) || (0 == dinfo->port) )
- {
- MHD_stop_daemon (d); return 32;
- }
+ mhdErrorExit ();
port = (int) dinfo->port;
}
+
c = curl_easy_init ();
- curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
- curl_easy_setopt (c, CURLOPT_PORT, (long) port);
- curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
- curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
- curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
- curl_easy_setopt (c, CURLOPT_READDATA, &pos);
- curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
- curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
- curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
- curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
- if (oneone)
- curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
- else
- curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
- curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
- /* NOTE: use of CONNECTTIMEOUT without also
- * setting NOSIGNAL results in really weird
- * crashes on my system! */
- curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+ if (NULL == c)
+ {
+ fprintf (stderr, "curl_easy_init() failed.\n");
+ externalErrorExit ();
+ }
+ if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
+ "http://127.0.0.1/hello_world")) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+ ©Buffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION,
+ &putBuffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
+ (long) 150)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
+ (long) 150)) ||
+ ((oneone) ?
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_1)) :
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_0))))
+ {
+ fprintf (stderr, "curl_easy_setopt() failed.\n");
+ externalErrorExit ();
+ }
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
fprintf (stderr,
@@ -428,11 +598,16 @@
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
{
- fprintf (stderr, "Got invalid response `%.*s'\n", (int) cbc.pos, cbc.buf);
- return 64;
+ fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
+ (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
+ (unsigned) strlen ("/hello_world"));
+ mhdErrorExitDesc ("Wrong returned data length");
}
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
- return 128;
+ {
+ fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
+ mhdErrorExitDesc ("Wrong returned data length");
+ }
return 0;
}
@@ -448,12 +623,6 @@
fd_set rs;
fd_set ws;
fd_set es;
- MHD_socket maxsock;
-#ifdef MHD_WINSOCK_SOCKETS
- int maxposixs; /* Max socket number unused on W32 */
-#else /* MHD_POSIX_SOCKETS */
-#define maxposixs maxsock
-#endif /* MHD_POSIX_SOCKETS */
int running;
struct CURLMsg *msg;
time_t start;
@@ -485,132 +654,153 @@
(size_t) (incr_read ? 1024 : (PUT_SIZE * 4)),
MHD_OPTION_END);
if (d == NULL)
- return 256;
+ mhdErrorExit ();
if (0 == port)
{
const union MHD_DaemonInfo *dinfo;
dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
if ((NULL == dinfo) || (0 == dinfo->port) )
- {
- MHD_stop_daemon (d); return 32;
- }
+ mhdErrorExit ();
port = (int) dinfo->port;
}
- c = curl_easy_init ();
- curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
- curl_easy_setopt (c, CURLOPT_PORT, (long) port);
- curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
- curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
- curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
- curl_easy_setopt (c, CURLOPT_READDATA, &pos);
- curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
- curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
- curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
- curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
- if (oneone)
- curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
- else
- curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
- curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
- /* NOTE: use of CONNECTTIMEOUT without also
- * setting NOSIGNAL results in really weird
- * crashes on my system! */
- curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+ c = curl_easy_init ();
+ if (NULL == c)
+ {
+ fprintf (stderr, "curl_easy_init() failed.\n");
+ externalErrorExit ();
+ }
+ if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
+ "http://127.0.0.1/hello_world")) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+ ©Buffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION,
+ &putBuffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
+ (long) 150)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
+ (long) 150)) ||
+ ((oneone) ?
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_1)) :
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_0))))
+ {
+ fprintf (stderr, "curl_easy_setopt() failed.\n");
+ externalErrorExit ();
+ }
multi = curl_multi_init ();
if (multi == NULL)
- {
- curl_easy_cleanup (c);
- MHD_stop_daemon (d);
- return 512;
- }
+ libcurlErrorExit ();
mret = curl_multi_add_handle (multi, c);
if (mret != CURLM_OK)
- {
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- MHD_stop_daemon (d);
- return 1024;
- }
+ libcurlErrorExit ();
+
start = time (NULL);
- while ((time (NULL) - start < 5) && (multi != NULL))
+ while ((time (NULL) - start < 45) && (multi != NULL))
{
- maxsock = MHD_INVALID_SOCKET;
- maxposixs = -1;
+ MHD_socket maxMHDsock;
+ int maxcurlsock;
+ maxMHDsock = MHD_INVALID_SOCKET;
+ maxcurlsock = -1;
FD_ZERO (&rs);
FD_ZERO (&ws);
FD_ZERO (&es);
- curl_multi_perform (multi, &running);
- mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
- if (mret != CURLM_OK)
- {
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- MHD_stop_daemon (d);
- return 2048;
- }
- if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
+ mret = curl_multi_perform (multi, &running);
+ if ((CURLM_OK != mret) && (CURLM_CALL_MULTI_PERFORM != mret))
{
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- MHD_stop_daemon (d);
- return 4096;
- }
+ fprintf (stderr, "curl_multi_perform() failed. Error: '%s'. ",
+ curl_multi_strerror (mret));
+ libcurlErrorExit ();
+ }
+ if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxcurlsock))
+ libcurlErrorExitDesc ("curl_multi_fdset() failed");
+ if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMHDsock))
+ mhdErrorExit ();
+
tv.tv_sec = 0;
tv.tv_usec = 1000;
- if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
+#ifndef MHD_WINSOCK_SOCKETS
+ if (maxMHDsock > maxcurlsock)
+ maxcurlsock = maxMHDsock;
+#endif /* MHD_WINSOCK_SOCKETS */
+ if (-1 == select (maxcurlsock + 1, &rs, &ws, &es, &tv))
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ externalErrorExitDesc ("Unexpected select() error");
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ externalErrorExitDesc ("Unexpected select() error");
+ Sleep (tv.tv_sec * 1000 + tv.tv_usec / 1000);
#endif
}
- curl_multi_perform (multi, &running);
- if (running == 0)
+
+ mret = curl_multi_perform (multi, &running);
+ if ((CURLM_OK != mret) && (CURLM_CALL_MULTI_PERFORM != mret))
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ fprintf (stderr, "curl_multi_perform() failed. Error: '%s'. ",
+ curl_multi_strerror (mret));
+ libcurlErrorExit ();
+ }
+ if (0 == running)
+ {
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "curl_multi_perform() failed: '%s' ",
+ curl_easy_strerror (msg->data.result));
+ libcurlErrorExit ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code ");
+ mhdErrorExit ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
if (multi != NULL)
- {
- curl_multi_remove_handle (multi, c);
- curl_easy_cleanup (c);
- curl_multi_cleanup (multi);
- }
+ mhdErrorExitDesc ("Request has been aborted by timeout");
+
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
{
- fprintf (stderr, "Got invalid response `%.*s'\n", (int) cbc.pos, cbc.buf);
- return 8192;
+ fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
+ (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
+ (unsigned) strlen ("/hello_world"));
+ mhdErrorExitDesc ("Wrong returned data length");
}
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
- return 16384;
+ {
+ fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
+ mhdErrorExitDesc ("Wrong returned data length");
+ }
return 0;
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_parse_cookies.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file test_parse_cookies.c
* @brief Testcase for HTTP cookie parsing
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -228,35 +230,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -290,5 +313,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_patch.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2020 Christian Grothoff
+ Copyright (C) 2016-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file test_patch.c
* @brief Testcase for libmicrohttpd PATCH operations
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -95,7 +97,7 @@
enum MHD_Result ret;
(void) version; (void) unused; /* Unused. Silent compiler warning. */
- if (0 != strcasecmp ("PATCH", method))
+ if (0 != strcmp ("PATCH", method))
return MHD_NO; /* unexpected method */
if ((*done) == 0)
{
@@ -113,7 +115,7 @@
*done = 1;
return MHD_YES;
}
- response = MHD_create_response_from_buffer (strlen (url), (void*) url,
+ response = MHD_create_response_from_buffer (strlen (url), (void *) url,
MHD_RESPMEM_MUST_COPY);
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
MHD_destroy_response (response);
@@ -439,35 +441,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- _exit (99);
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -507,5 +530,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_post.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file test_post.c
* @brief Testcase for libmicrohttpd POST operations using URL-encoding
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -135,7 +137,7 @@
enum MHD_Result ret;
(void) cls; (void) version; /* Unused. Silent compiler warning. */
- if (0 != strcasecmp ("POST", method))
+ if (0 != strcmp ("POST", method))
{
printf ("METHOD: %s\n", method);
return MHD_NO; /* unexpected method */
@@ -515,35 +517,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -576,7 +599,7 @@
(void) cls; (void) url; (void) version; /* Unused. Silent compiler warning. */
(void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */
- if (0 != strcasecmp ("POST", method))
+ if (0 != strcmp ("POST", method))
{
fprintf (stderr,
"Unexpected method `%s'\n", method);
@@ -759,8 +782,8 @@
if (CURLE_OK != (cc = curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE,
&response_code)))
{
- fprintf (stderr, "curl_easy_getinfo failed: '%s'\n", curl_easy_strerror (
- errornum));
+ fprintf (stderr, "curl_easy_getinfo failed: '%s'\n",
+ curl_easy_strerror (cc));
result = 65536;
}
@@ -813,5 +836,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_post_loop.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file daemontest_post_loop.c
* @brief Testcase for libmicrohttpd POST operations using URL-encoding
* @author Christian Grothoff (inspired by bug report #1296)
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -519,38 +521,65 @@
if ((CURLM_OK == curl_multi_timeout (multi, &ctimeout)) &&
(ctimeout < (long long) timeout) && (ctimeout >= 0))
timeout = ctimeout;
- if ( (c == NULL) || (running == 0) )
+ if ( (c == NULL) || (0 == running) )
timeout = 0; /* terminate quickly... */
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
{
- if (EINTR == errno)
- continue;
- fprintf (stderr,
- "select failed: %s\n",
- strerror (errno));
- break;
+#ifdef MHD_POSIX_SOCKETS
+ if (EINTR != errno)
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+#else
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
+#endif
}
while (CURLM_CALL_MULTI_PERFORM ==
curl_multi_perform (multi, &running))
;
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_easy_cleanup (c);
- c = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_easy_cleanup (c);
+ c = NULL;
+ break;
}
MHD_run (d);
}
@@ -661,5 +690,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_postform.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file test_postform.c
* @brief Testcase for libmicrohttpd POST operations using multipart/postform data
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -540,36 +542,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__,
- curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -618,5 +640,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_process_arguments.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2013 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file test_process_arguments.c
* @brief Testcase for HTTP URI arguments
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -227,35 +229,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -289,5 +312,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_put.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file daemontest_put.c
* @brief Testcase for libmicrohttpd PUT operations
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -95,7 +97,7 @@
enum MHD_Result ret;
(void) version; (void) unused; /* Unused. Silent compiler warning. */
- if (0 != strcasecmp ("PUT", method))
+ if (0 != strcmp ("PUT", method))
return MHD_NO; /* unexpected method */
if ((*done) == 0)
{
@@ -113,7 +115,7 @@
*done = 1;
return MHD_YES;
}
- response = MHD_create_response_from_buffer (strlen (url), (void*) url,
+ response = MHD_create_response_from_buffer (strlen (url), (void *) url,
MHD_RESPMEM_MUST_COPY);
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
MHD_destroy_response (response);
@@ -483,35 +485,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- _exit (99);
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__, curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -551,5 +574,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_put_chunked.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -23,6 +24,7 @@
* @brief Testcase for libmicrohttpd PUT operations with chunked encoding
* for the upload data
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -478,36 +480,56 @@
{
#ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
#endif
}
curl_multi_perform (multi, &running);
- if (running == 0)
+ if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (msg == NULL)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if (msg->data.result != CURLE_OK)
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__,
- curl_easy_strerror (msg->data.result));
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ abort ();
+ }
+ }
}
+ if (! curl_fine)
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
}
MHD_run (d);
}
@@ -544,5 +566,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_quiesce.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2013, 2015 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -21,6 +22,7 @@
* @file test_quiesce.c
* @brief Testcase for libmicrohttpd quiescing
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -282,7 +284,7 @@
return 2;
}
if (0 != pthread_create (&thrd, NULL, &ServeOneRequest,
- (void*) (intptr_t) fd))
+ (void *) (intptr_t) fd))
{
fprintf (stderr, "pthread_create failed\n");
curl_easy_cleanup (c);
@@ -301,7 +303,7 @@
return 2;
}
- if (0 != pthread_join (thrd, (void**) &thrdRet))
+ if (0 != pthread_join (thrd, (void **) &thrdRet))
{
fprintf (stderr, "pthread_join failed\n");
curl_easy_cleanup (c);
@@ -336,7 +338,7 @@
/* at this point, the forked server quit, and the new
* server has quiesced, so new requests should fail
*/
- if (CURLE_OK == (errornum = curl_easy_perform (c)))
+ if (CURLE_OK == curl_easy_perform (c))
{
fprintf (stderr, "curl_easy_perform should fail\n");
curl_easy_cleanup (c);
@@ -454,44 +456,63 @@
tv.tv_usec = 1000;
if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
{
-#ifdef MHD_POSIX_SOCKETS
+ #ifdef MHD_POSIX_SOCKETS
if (EINTR != errno)
- abort ();
-#else
- if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 !=
- ws.
- fd_count)
- || (0 != es.fd_count) )
- abort ();
- Sleep (1000);
-#endif
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) errno, __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ #else
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ {
+ fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
+ (int) WSAGetLastError (), __LINE__);
+ fflush (stderr);
+ exit (99);
+ }
+ Sleep (1);
+ #endif
}
curl_multi_perform (multi, &running);
if (0 == running)
{
- msg = curl_multi_info_read (multi, &running);
- if (NULL == msg)
- break;
- if (msg->msg == CURLMSG_DONE)
+ int pending;
+ int curl_fine = 0;
+ while (NULL != (msg = curl_multi_info_read (multi, &pending)))
{
- if ((i == 0) && (msg->data.result != CURLE_OK) )
- printf ("%s failed at %s:%d: `%s'\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__,
- curl_easy_strerror (msg->data.result));
- else if ( (i == 1) &&
- (msg->data.result == CURLE_OK) )
- printf ("%s should have failed at %s:%d\n",
- "curl_multi_perform",
- __FILE__,
- __LINE__);
- curl_multi_remove_handle (multi, c);
- curl_multi_cleanup (multi);
- curl_easy_cleanup (c);
- c = NULL;
- multi = NULL;
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result == CURLE_OK)
+ curl_fine = 1;
+ else if (i == 0)
+ {
+ fprintf (stderr,
+ "%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ }
+ }
}
+ if ((i == 0) && (! curl_fine))
+ {
+ fprintf (stderr, "libcurl haven't returned OK code\n");
+ abort ();
+ }
+ else if ((i == 1) && (curl_fine))
+ {
+ fprintf (stderr, "libcurl returned OK code, while it shouldn't\n");
+ abort ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (c);
+ c = NULL;
+ multi = NULL;
+ break;
}
MHD_run (d);
}
@@ -575,5 +596,5 @@
"Error (code: %u)\n",
errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_quiesce_stream.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2016 Christian Grothoff
+ Copyright (C) 2016-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -169,7 +170,7 @@
/* Second call: create response */
response
- = MHD_create_response_from_callback (-1,
+ = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
32 * 1024,
&http_ContentReaderCallback,
*con_cls,
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_termination.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2009 Christian Grothoff
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file daemontest_termination.c
* @brief Testcase for libmicrohttpd tolerating client not closing immediately
* @author hollosig
+ * @author Karlson2k (Evgeny Grin)
*/
#include "platform.h"
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_timeout.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007 Christian Grothoff
+ Copyright (C) 2016-2019 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file test_timeout.c
* @brief Testcase for libmicrohttpd PUT operations
* @author Matthias Wachs
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -30,18 +32,58 @@
#include <microhttpd.h>
#include <stdlib.h>
#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#ifdef HAVE_TIME_H
#include <time.h>
+#endif /* HAVE_TIME_H */
#include "mhd_has_in_name.h"
-#ifndef WINDOWS
-#include <unistd.h>
+
+/**
+ * Pause execution for specified number of milliseconds.
+ * @param ms the number of milliseconds to sleep
+ */
+void
+_MHD_sleep (uint32_t ms)
+{
+#if defined(_WIN32)
+ Sleep (ms);
+#elif defined(HAVE_NANOSLEEP)
+ struct timespec slp = {ms / 1000, (ms % 1000) * 1000000};
+ struct timespec rmn;
+ int num_retries = 0;
+ while (0 != nanosleep (&slp, &rmn))
+ {
+ if (num_retries++ > 8)
+ break;
+ slp = rmn;
+ }
+#elif defined(HAVE_USLEEP)
+ uint64_t us = ms * 1000;
+ do
+ {
+ uint64_t this_sleep;
+ if (999999 < us)
+ this_sleep = 999999;
+ else
+ this_sleep = us;
+ /* Ignore return value as it could be void */
+ usleep (this_sleep);
+ us -= this_sleep;
+ } while (us > 0);
+#else
+ fprintf (stderr, "No sleep function available on this system.\n");
#endif
+}
+
static int oneone;
-static int withTimeout = 1;
+static int withTimeout = 0;
-static int withoutTimeout = 1;
+static int withoutTimeout = 0;
struct CBC
{
@@ -65,21 +107,40 @@
case MHD_REQUEST_TERMINATED_COMPLETED_OK:
if (test == &withoutTimeout)
{
- withoutTimeout = 0;
+ withoutTimeout = 1;
+ }
+ else
+ {
+ fprintf (stderr, "Connection completed without errors while "
+ "timeout is expected.\n");
}
break;
case MHD_REQUEST_TERMINATED_WITH_ERROR:
+ fprintf (stderr, "Connection terminated with error.\n");
+ exit (4);
+ break;
case MHD_REQUEST_TERMINATED_READ_ERROR:
+ fprintf (stderr, "Connection terminated with read error.\n");
+ exit (4);
break;
case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED:
if (test == &withTimeout)
{
- withTimeout = 0;
+ withTimeout = 1;
+ }
+ else
+ {
+ fprintf (stderr, "Connection terminated with timeout while expected "
+ "to be successfully completed.\n");
}
break;
case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN:
+ fprintf (stderr, "Connection terminated by daemon shutdown.\n");
+ exit (4);
break;
case MHD_REQUEST_TERMINATED_CLIENT_ABORT:
+ fprintf (stderr, "Connection terminated by client.\n");
+ exit (4);
break;
}
}
@@ -104,6 +165,7 @@
putBuffer_fail (void *stream, size_t size, size_t nmemb, void *ptr)
{
(void) stream; (void) size; (void) nmemb; (void) ptr; /* Unused. Silent compiler warning. */
+ _MHD_sleep (100); /* Avoid busy-waiting */
return 0;
}
@@ -191,7 +253,7 @@
NULL, NULL, &ahc_echo, &done_flag,
MHD_OPTION_CONNECTION_TIMEOUT, 2,
MHD_OPTION_NOTIFY_COMPLETED, &termination_cb,
- &withTimeout,
+ &withoutTimeout,
MHD_OPTION_END);
if (d == NULL)
return 1;
@@ -225,14 +287,22 @@
* setting NOSIGNAL results in really weird
* crashes on my system! */
curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+ withoutTimeout = 0;
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
+ fprintf (stderr, "curl_easy_perform failed: '%s'\n",
+ curl_easy_strerror (errornum));
curl_easy_cleanup (c);
MHD_stop_daemon (d);
return 2;
}
curl_easy_cleanup (c);
MHD_stop_daemon (d);
+ if (0 == withoutTimeout)
+ {
+ fprintf (stderr, "Request wasn't processed successfully.\n");
+ return 2;
+ }
if (cbc.pos != strlen ("/hello_world"))
return 4;
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
@@ -269,7 +339,7 @@
NULL, NULL, &ahc_echo, &done_flag,
MHD_OPTION_CONNECTION_TIMEOUT, 2,
MHD_OPTION_NOTIFY_COMPLETED, &termination_cb,
- &withoutTimeout,
+ &withTimeout,
MHD_OPTION_END);
if (d == NULL)
return 16;
@@ -303,13 +373,24 @@
* setting NOSIGNAL results in really weird
* crashes on my system! */
curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
+ withTimeout = 0;
if (CURLE_OK != (errornum = curl_easy_perform (c)))
{
curl_easy_cleanup (c);
MHD_stop_daemon (d);
if (errornum == CURLE_GOT_NOTHING)
- /* mhd had the timeout */
- return 0;
+ {
+ if (0 != withTimeout)
+ {
+ /* mhd had the timeout */
+ return 0;
+ }
+ else
+ {
+ fprintf (stderr, "Timeout wasn't detected.\n");
+ return 8;
+ }
+ }
else
/* curl had the timeout first */
return 32;
@@ -338,8 +419,5 @@
"Error during test execution (code: %u)\n",
errorCount);
curl_global_cleanup ();
- if ((withTimeout == 0) && (withoutTimeout == 0))
- return 0;
- else
- return errorCount; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_toolarge.c
^
|
@@ -0,0 +1,1624 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
+ Copyright (C) 2007, 2009, 2011 Christian Grothoff
+
+ 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 2, 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_toolarge.c
+ * @brief Testcase for handling of data larger then buffers.
+ * @details Testcases for handling of various situations when data cannot fit
+ * the buffers. Address sanitizers and debug asserts should increase
+ * number of problems detected by this test (detect actual overrun).
+ * Tests start with valid sizes to ensure that normal data is processed
+ * correctly and then sizes are monotonically increased to ensure that
+ * overflow is handled correctly at all stages in all codepaths.
+ * Tests with valid sizes are repeated several times to ensure that
+ * tests are failed because of overflow, but because of second run.
+ * @author Karlson2k (Evgeny Grin)
+ * @author Christian Grothoff
+ */
+#include "mhd_options.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "test_helpers.h"
+#include "mhd_sockets.h" /* only macros used */
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif /* HAVE_STRINGS_H */
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#ifndef WINDOWS
+#include <unistd.h>
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
+#if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
+#undef MHD_CPU_COUNT
+#endif
+#if ! defined(MHD_CPU_COUNT)
+#define MHD_CPU_COUNT 2
+#endif
+#if MHD_CPU_COUNT > 32
+#undef MHD_CPU_COUNT
+/* Limit to reasonable value */
+#define MHD_CPU_COUNT 32
+#endif /* MHD_CPU_COUNT > 32 */
+
+
+#if defined(HAVE___FUNC__)
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func(NULL, __func__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, __func__, __LINE__)
+#define libcurlErrorExit(ignore) \
+ _libcurlErrorExit_func(NULL, __func__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, __func__, __LINE__)
+#define mhdErrorExit(ignore) \
+ _mhdErrorExit_func(NULL, __func__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+ _mhdErrorExit_func(errDesc, __func__, __LINE__)
+#elif defined(HAVE___FUNCTION__)
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define libcurlErrorExit(ignore) \
+ _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define mhdErrorExit(ignore) \
+ _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+ _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#else
+#define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, NULL, __LINE__)
+#define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, NULL, __LINE__)
+#define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
+#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
+#endif
+
+
+_MHD_NORETURN static void
+_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "System or external library call failed");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+ fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+ fflush (stderr);
+ exit (99);
+}
+
+
+static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
+
+_MHD_NORETURN static void
+_libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "CURL library call failed");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+ if (0 != libcurl_errbuf[0])
+ fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf);
+
+ fflush (stderr);
+ exit (99);
+}
+
+
+_MHD_NORETURN static void
+_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "MHD unexpected error");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+
+ fflush (stderr);
+ exit (8);
+}
+
+
+/* Could be increased to facilitate debugging */
+#define TIMEOUTS_VAL 5
+
+#define EXPECTED_URI_BASE_PATH "/a"
+
+#define URL_SCHEME_HOST "http:/" "/127.0.0.1"
+
+#define N1_HEADER_NAME "n"
+#define N1_HEADER_VALUE "1"
+#define N1_HEADER N1_HEADER_NAME ": " N1_HEADER_VALUE
+#define N1_HEADER_CRLF N1_HEADER "\r\n"
+
+#define BUFFER_SIZE 1024
+
+/* The size of the test element that must pass the test */
+#ifndef MHD_ASAN_POISON_ACTIVE
+#define TEST_OK_SIZE (BUFFER_SIZE - 384)
+#else /* MHD_ASAN_POISON_ACTIVE */
+#define TEST_OK_SIZE (BUFFER_SIZE - 384 - 80)
+#endif /* MHD_ASAN_POISON_ACTIVE */
+
+/* The size of the test element where tests are started */
+#define TEST_START_SIZE (TEST_OK_SIZE - 16)
+
+/* The size of the test element that must definitely fail */
+#define TEST_FAIL_SIZE (BUFFER_SIZE + 32)
+
+/* Special value for request many headers test.
+ * MHD uses the same buffer to store headers strings and pointers to the strings
+ * so allocation is multiplied for small request header. */
+/* The size of the test element that must pass the test */
+#define TEST_RQ_N1_OK_SIZE 50
+
+/* The size of the test element where tests are started */
+#define TEST_RQ_N1_START_SIZE (TEST_RQ_N1_OK_SIZE - 32)
+
+/* Global parameters */
+static int verbose; /**< Be verbose */
+static int oneone; /**< If false use HTTP/1.0 for requests*/
+static int global_port; /**< MHD daemons listen port number */
+static int large_req_method; /**< Large request method */
+static int large_req_url; /**< Large request URL */
+static int large_req_header_name; /**< Large request single header name */
+static int large_req_header_value; /**< Large request single header value */
+static int large_req_headers; /**< Large request headers */
+static int large_rsp_header_name; /**< Large response single header name */
+static int large_rsp_header_value; /**< Large response single header value */
+static int large_rsp_headers; /**< Large response headers */
+static int response_timeout_val = TIMEOUTS_VAL;
+
+/* Current test parameters */
+/* * Moved to local variables * */
+
+/* Static helper variables */
+/* * None for this test * */
+
+static void
+test_global_init (void)
+{
+ libcurl_errbuf[0] = 0;
+
+ if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+ externalErrorExit ();
+}
+
+
+static void
+test_global_cleanup (void)
+{
+ curl_global_cleanup ();
+}
+
+
+struct headers_check_result
+{
+ unsigned int num_n1_headers;
+ unsigned int large_header_name_size;
+ unsigned int large_header_value_size;
+ int large_header_valid;
+};
+
+size_t
+lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
+ void *userdata)
+{
+ const size_t data_size = size * nitems;
+ struct headers_check_result *check_res =
+ (struct headers_check_result *) userdata;
+
+ if ((6 == data_size) && (0 == memcmp (N1_HEADER_CRLF, buffer, 6)))
+ check_res->num_n1_headers++;
+ else if ((5 <= data_size) && ('0' == buffer[0]))
+ {
+ const char *const col_ptr = strstr (buffer, ": ");
+ if (0 != check_res->large_header_value_size)
+ mhdErrorExitDesc ("Expected only one large header, " \
+ "but found two large headers in the reply");
+ check_res->large_header_valid = 0;
+ if (NULL != col_ptr)
+ {
+ const char *const name = buffer;
+ const size_t name_len = col_ptr - buffer;
+ const size_t val_pos = name_len + 2;
+ const size_t val_len = data_size - val_pos - 2; /* 2 = strlen("\r\n") */
+ const char *const value = buffer + val_pos;
+ size_t i;
+ check_res->large_header_name_size = name_len;
+ check_res->large_header_value_size = val_len;
+ check_res->large_header_valid = 1; /* To be reset if any problem found */
+ for (i = 1; i < name_len; i++)
+ if ('a' + (char) (i % ('z' - 'a' + 1)) != name[i])
+ {
+ fprintf (stderr, "Wrong sequence in reply header name " \
+ "at position %u. Expected '%c', got '%c'\n",
+ (unsigned int) i,
+ 'a' + (char) (i % ('z' - 'a' + 1)),
+ name[i]);
+ check_res->large_header_valid = 0;
+ break;
+ }
+ for (i = 0; i < val_len; i++)
+ if ('Z' - (char) (i % ('Z' - 'A' + 1)) != value[i])
+ {
+ fprintf (stderr, "Wrong sequence in reply header value " \
+ "at position %u. Expected '%c', got '%c'\n",
+ (unsigned int) i,
+ 'Z' - (char) (i % ('Z' - 'A' + 1)),
+ value[i]);
+ check_res->large_header_valid = 0;
+ break;
+ }
+ }
+ }
+
+ return data_size;
+}
+
+
+struct lcurl_data_cb_param
+{
+ char *buf;
+ size_t pos;
+ size_t size;
+};
+
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+ struct lcurl_data_cb_param *cbc = ctx;
+
+ if (cbc->pos + size * nmemb > cbc->size)
+ externalErrorExit (); /* overflow */
+ memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+ cbc->pos += size * nmemb;
+ return size * nmemb;
+}
+
+
+struct check_uri_cls
+{
+ const char *volatile uri;
+};
+
+static void *
+check_uri_cb (void *cls,
+ const char *uri,
+ struct MHD_Connection *con)
+{
+ struct check_uri_cls *param = (struct check_uri_cls *) cls;
+ (void) con;
+
+ if (0 != strcmp (param->uri,
+ uri))
+ {
+ fprintf (stderr,
+ "Wrong URI: `%s', line: %d\n",
+ uri, __LINE__);
+ exit (22);
+ }
+ return NULL;
+}
+
+
+struct mhd_header_checker_param
+{
+ unsigned int num_n1_headers;
+ unsigned int large_header_name_size;
+ unsigned int large_header_value_size;
+ int large_header_valid;
+};
+
+enum MHD_Result
+headerCheckerInterator (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ size_t key_size,
+ const char *value,
+ size_t value_size)
+{
+ struct mhd_header_checker_param *const param =
+ (struct mhd_header_checker_param *) cls;
+
+ if (NULL == param)
+ mhdErrorExitDesc ("cls parameter is NULL");
+
+ if (MHD_HEADER_KIND != kind)
+ return MHD_YES; /* Continue iteration */
+
+ if (0 == key_size)
+ mhdErrorExitDesc ("Zero key length");
+
+ if ((1 == key_size) && (1 == value_size) &&
+ ('n' == key[0]) && ('1' == value[0]))
+ param->num_n1_headers++;
+ else if ('0' == key[0])
+ { /* Found 'large' header */
+ size_t i;
+ param->large_header_name_size = key_size;
+ param->large_header_value_size = value_size;
+ param->large_header_valid = 1;
+ for (i = 1; i < key_size; i++)
+ if ('a' + (char) (i % ('z' - 'a' + 1)) != key[i])
+ {
+ fprintf (stderr, "Wrong sequence in request header name " \
+ "at position %u. Expected '%c', got '%c'\n",
+ (unsigned int) i,
+ 'a' + (char) (i % ('z' - 'a' + 1)),
+ key[i]);
+ param->large_header_valid = 0;
+ break;
+ }
+ for (i = 0; i < value_size; i++)
+ if ('Z' - (char) (i % ('Z' - 'A' + 1)) != value[i])
+ {
+ fprintf (stderr, "Wrong sequence in request header value " \
+ "at position %u. Expected '%c', got '%c'\n",
+ (unsigned int) i,
+ 'Z' - (char) (i % ('Z' - 'A' + 1)),
+ value[i]);
+ param->large_header_valid = 0;
+ break;
+ }
+ }
+ return MHD_YES;
+}
+
+
+struct ahc_cls_type
+{
+ const char *volatile rp_data;
+ volatile size_t rp_data_size;
+ volatile size_t rp_num_n1_hdrs;
+ volatile size_t rp_large_hdr_name_size;
+ volatile size_t rp_large_hdr_value_size;
+ struct mhd_header_checker_param header_check_param;
+ const char *volatile rq_method;
+ const char *volatile rq_url;
+};
+
+
+static enum MHD_Result
+ahcCheck (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data, size_t *upload_data_size,
+ void **con_cls)
+{
+ static int ptr;
+ struct MHD_Response *response;
+ enum MHD_Result ret;
+ struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
+ size_t i;
+
+ if (NULL == param)
+ mhdErrorExitDesc ("cls parameter is NULL");
+
+ if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
+ mhdErrorExitDesc ("Unexpected HTTP version");
+
+ if (0 != strcmp (url, param->rq_url))
+ mhdErrorExitDesc ("Unexpected URI");
+
+ if (NULL != upload_data)
+ mhdErrorExitDesc ("'upload_data' is not NULL");
+
+ if (NULL == upload_data_size)
+ mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
+
+ if (0 != *upload_data_size)
+ mhdErrorExitDesc ("'*upload_data_size' value is not zero");
+
+ if (0 != strcmp (param->rq_method, method))
+ mhdErrorExitDesc ("Unexpected request method");
+
+ if (&ptr != *con_cls)
+ {
+ *con_cls = &ptr;
+ return MHD_YES;
+ }
+ *con_cls = NULL;
+
+ if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND,
+ &headerCheckerInterator,
+ ¶m->header_check_param))
+ mhdErrorExitDesc ("Wrong number of headers in the request");
+
+ response = MHD_create_response_from_buffer (param->rp_data_size,
+ (void *) param->rp_data,
+ MHD_RESPMEM_MUST_COPY);
+ if (NULL == response)
+ mhdErrorExitDesc ("Failed to create response");
+
+ for (i = 0; i < param->rp_num_n1_hdrs; i++)
+ if (MHD_YES != MHD_add_response_header (response,
+ N1_HEADER_NAME,
+ N1_HEADER_VALUE))
+ mhdErrorExitDesc ("Cannot add header");
+
+ if (0 != param->rp_large_hdr_name_size)
+ {
+ const size_t large_hdr_name_size = param->rp_large_hdr_name_size;
+ char *large_hrd_name;
+ const size_t large_hdr_value_size = param->rp_large_hdr_value_size;
+ char *large_hrd_value;
+
+ large_hrd_name = malloc (large_hdr_name_size + 1);
+ if (NULL == large_hrd_name)
+ externalErrorExit ();
+ if (0 != large_hdr_value_size)
+ large_hrd_value = malloc (large_hdr_value_size + 1);
+ else
+ large_hrd_value = NULL;
+
+ if ((0 != large_hdr_value_size) && (NULL == large_hrd_value))
+ externalErrorExit ();
+ large_hrd_name[0] = '0'; /* Name starts with zero for unique identification */
+ for (i = 1; i < large_hdr_name_size; i++)
+ large_hrd_name[i] = 'a' + i % ('z' - 'a' + 1);
+ large_hrd_name[large_hdr_name_size] = 0;
+ for (i = 0; i < large_hdr_value_size; i++)
+ large_hrd_value[i] = 'Z' - i % ('Z' - 'A' + 1);
+ if (NULL != large_hrd_value)
+ large_hrd_value[large_hdr_value_size] = 0;
+ if (MHD_YES != MHD_add_response_header (response,
+ large_hrd_name,
+ large_hrd_value))
+ mhdErrorExitDesc ("Cannot add large header");
+
+ if (NULL != large_hrd_value)
+ free (large_hrd_value);
+ free (large_hrd_name);
+ }
+
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ if (MHD_YES != ret)
+ mhdErrorExitDesc ("Failed to queue response");
+
+ return ret;
+}
+
+
+static CURL *
+curlEasyInitForTest (const char *queryPath, const char *method,
+ int port,
+ struct lcurl_data_cb_param *dcbp,
+ struct headers_check_result *hdr_chk_result,
+ struct curl_slist *headers)
+{
+ CURL *c;
+
+ c = curl_easy_init ();
+ if (NULL == c)
+ libcurlErrorExitDesc ("curl_easy_init() failed");
+
+ if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, queryPath)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+ ©Buffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, dcbp)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
+ (long) response_timeout_val)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
+ (long) response_timeout_val)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
+ libcurl_errbuf)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION,
+ lcurl_hdr_callback)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA,
+ hdr_chk_result)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
+ (oneone) ?
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_1)) :
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_0)))
+ libcurlErrorExitDesc ("curl_easy_setopt() failed");
+
+ if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST, method))
+ libcurlErrorExitDesc ("curl_easy_setopt() failed");
+
+ if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, headers))
+ libcurlErrorExitDesc ("curl_easy_setopt() failed");
+
+ return c;
+}
+
+
+static CURLcode
+performQueryExternal (struct MHD_Daemon *d, CURL *c)
+{
+ CURLM *multi;
+ time_t start;
+ struct timeval tv;
+ CURLcode ret;
+
+ ret = CURLE_FAILED_INIT; /* will be replaced with real result */
+ multi = NULL;
+ multi = curl_multi_init ();
+ if (multi == NULL)
+ libcurlErrorExitDesc ("curl_multi_init() failed");
+ if (CURLM_OK != curl_multi_add_handle (multi, c))
+ libcurlErrorExitDesc ("curl_multi_add_handle() failed");
+
+ start = time (NULL);
+ while (time (NULL) - start <= TIMEOUTS_VAL)
+ {
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ MHD_socket maxMhdSk;
+ int maxCurlSk;
+ int running;
+
+ maxMhdSk = MHD_INVALID_SOCKET;
+ maxCurlSk = -1;
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ if (NULL != multi)
+ {
+ curl_multi_perform (multi, &running);
+ if (0 == running)
+ {
+ struct CURLMsg *msg;
+ int msgLeft;
+ int totalMsgs = 0;
+ do
+ {
+ msg = curl_multi_info_read (multi, &msgLeft);
+ if (NULL == msg)
+ libcurlErrorExitDesc ("curl_multi_info_read() failed");
+ totalMsgs++;
+ if (CURLMSG_DONE == msg->msg)
+ ret = msg->data.result;
+ } while (msgLeft > 0);
+ if (1 != totalMsgs)
+ {
+ fprintf (stderr,
+ "curl_multi_info_read returned wrong "
+ "number of results (%d).\n",
+ totalMsgs);
+ externalErrorExit ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ multi = NULL;
+ }
+ else
+ {
+ if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
+ libcurlErrorExitDesc ("curl_multi_fdset() failed");
+ }
+ }
+ if (NULL == multi)
+ { /* libcurl has finished, check whether MHD still needs to perform cleanup */
+ unsigned long long to;
+ if ((MHD_YES != MHD_get_timeout (d, &to)) || (0 != to))
+ break; /* MHD finished as well */
+ }
+ if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
+ mhdErrorExitDesc ("MHD_get_fdset() failed");
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000;
+#ifdef MHD_POSIX_SOCKETS
+ if (maxMhdSk > maxCurlSk)
+ maxCurlSk = maxMhdSk;
+#endif /* MHD_POSIX_SOCKETS */
+ if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
+ {
+#ifdef MHD_POSIX_SOCKETS
+ if (EINTR != errno)
+ externalErrorExitDesc ("Unexpected select() error");
+#else
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ externalErrorExitDesc ("Unexpected select() error");
+ Sleep (1);
+#endif
+ }
+ if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
+ mhdErrorExitDesc ("MHD_run_from_select() failed");
+ }
+
+ return ret;
+}
+
+
+struct curlQueryParams
+{
+ /* Destination path for CURL query */
+ const char *queryPath;
+
+ /* Custom query method, NULL for default */
+ const char *method;
+
+ /* Destination port for CURL query */
+ int queryPort;
+
+ /* List of additional request headers */
+ struct curl_slist *headers;
+
+ /* CURL query result error flag */
+ volatile int queryError;
+
+ /* Response HTTP code, zero if no response */
+ volatile int responseCode;
+};
+
+
+/* Returns zero for successful response and non-zero for failed response */
+static int
+doCurlQueryInThread (struct MHD_Daemon *d,
+ struct curlQueryParams *p,
+ struct headers_check_result *hdr_res,
+ const char *expected_data,
+ size_t expected_data_size)
+{
+ const union MHD_DaemonInfo *dinfo;
+ CURL *c;
+ struct lcurl_data_cb_param dcbp;
+ CURLcode errornum;
+ int use_external_poll;
+ long resp_code;
+
+ dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
+ if (NULL == dinfo)
+ mhdErrorExitDesc ("MHD_get_daemon_info() failed");
+ use_external_poll = (0 == (dinfo->flags
+ & MHD_USE_INTERNAL_POLLING_THREAD));
+
+ if (NULL == p->queryPath)
+ abort ();
+
+ if (0 == p->queryPort)
+ abort ();
+
+ /* Test must not fail due to test's internal buffer shortage */
+ dcbp.size = TEST_FAIL_SIZE * 2 + 1;
+ dcbp.buf = malloc (dcbp.size);
+ if (NULL == dcbp.buf)
+ externalErrorExit ();
+ dcbp.pos = 0;
+
+ memset (hdr_res, 0, sizeof(*hdr_res));
+
+ c = curlEasyInitForTest (p->queryPath, p->method, p->queryPort,
+ &dcbp, hdr_res, p->headers);
+
+ if (! use_external_poll)
+ errornum = curl_easy_perform (c);
+ else
+ errornum = performQueryExternal (d, c);
+
+ if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &resp_code))
+ libcurlErrorExitDesc ("curl_easy_getinfo() failed");
+
+ p->responseCode = (int) resp_code;
+ if ((CURLE_OK == errornum) && (200 != resp_code))
+ {
+ fprintf (stderr,
+ "Got reply with unexpected status code: %d\n",
+ p->responseCode);
+ mhdErrorExit ();
+ }
+
+ if (CURLE_OK != errornum)
+ {
+ if ((CURLE_GOT_NOTHING != errornum) && (CURLE_RECV_ERROR != errornum)
+ && (CURLE_HTTP_RETURNED_ERROR != errornum))
+ {
+ if (CURLE_OPERATION_TIMEDOUT == errornum)
+ mhdErrorExitDesc ("Request was aborted due to timeout");
+ fprintf (stderr, "libcurl returned expected error: %s\n",
+ curl_easy_strerror (errornum));
+ mhdErrorExitDesc ("Request failed due to unexpected error");
+ }
+ p->queryError = 1;
+ if ((0 != resp_code) &&
+ ((499 < resp_code) || (400 > resp_code))) /* TODO: add all expected error codes */
+ {
+ fprintf (stderr,
+ "Got reply with unexpected status code: %ld\n",
+ resp_code);
+ mhdErrorExit ();
+ }
+ }
+ else
+ {
+ if (dcbp.pos != expected_data_size)
+ mhdErrorExit ("libcurl reports wrong size of MHD reply body data");
+ else if (0 != memcmp (expected_data, dcbp.buf,
+ expected_data_size))
+ mhdErrorExit ("libcurl reports wrong MHD reply body data");
+ else
+ p->queryError = 0;
+ }
+
+ curl_easy_cleanup (c);
+ free (dcbp.buf);
+
+ return p->queryError;
+}
+
+
+/* Perform test queries, shut down MHD daemon, and free parameters */
+static int
+performTestQueries (struct MHD_Daemon *d, int d_port,
+ struct ahc_cls_type *ahc_param,
+ struct check_uri_cls *uri_cb_param)
+{
+ struct curlQueryParams qParam;
+ int ret = 0; /* Return value */
+ struct headers_check_result rp_headers_check;
+ char *buf;
+ size_t i;
+ size_t first_failed_at = 0;
+
+
+ buf = malloc (TEST_FAIL_SIZE + 1 + strlen (URL_SCHEME_HOST));
+ if (NULL == buf)
+ externalErrorExit ();
+
+ /* Common parameters, to be individually overridden by specific test cases */
+ qParam.queryPort = d_port;
+ qParam.method = NULL; /* Use libcurl default: GET */
+ qParam.queryPath = URL_SCHEME_HOST EXPECTED_URI_BASE_PATH;
+ qParam.headers = NULL; /* No additional headers */
+ uri_cb_param->uri = EXPECTED_URI_BASE_PATH;
+ ahc_param->rq_url = EXPECTED_URI_BASE_PATH;
+ ahc_param->rq_method = "GET"; /* Default expected method */
+
+ ahc_param->rp_data = "~";
+ ahc_param->rp_data_size = 1;
+ ahc_param->rp_large_hdr_name_size = 0;
+ ahc_param->rp_large_hdr_value_size = 0;
+ ahc_param->rp_num_n1_hdrs = 0;
+
+ if (large_req_method)
+ {
+ for (i = 0; i < TEST_START_SIZE; i++)
+ buf[i] = 'A' + i % ('Z' - 'A' + 1);
+ for (; i <= TEST_FAIL_SIZE; i++)
+ {
+ buf[i] = 0;
+
+ qParam.method = buf;
+ ahc_param->rq_method = buf;
+
+ memset (&ahc_param->header_check_param, 0,
+ sizeof (ahc_param->header_check_param));
+ if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
+ ahc_param->rp_data,
+ ahc_param->rp_data_size))
+ {
+ (void) qParam.responseCode; /* TODO: check for the right response code */
+ if (TEST_OK_SIZE >= i)
+ {
+ fprintf (stderr,
+ "Request failed when running with the valid value size.\n");
+ ret = 1; /* Failed too early */
+ }
+ if (0 == first_failed_at)
+ {
+ if (verbose)
+ fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
+ first_failed_at = i;
+ }
+ }
+ else
+ {
+ if (TEST_FAIL_SIZE == i)
+ {
+ fprintf (stderr, "Request succeed with the largest size.\n");
+ ret = 1; /* Succeed with largest value */
+ }
+ }
+ if (0 != ahc_param->header_check_param.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected request headers");
+ if (0 != ahc_param->header_check_param.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large request header");
+ if (0 != rp_headers_check.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected reply headers");
+ if (0 != rp_headers_check.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large reply header");
+
+ buf[i] = 'A' + i % ('Z' - 'A' + 1);
+ }
+ }
+ else if (large_req_url)
+ {
+ const size_t base_size = strlen (URL_SCHEME_HOST);
+ char *const url = buf + base_size;
+
+ memcpy (buf, URL_SCHEME_HOST, base_size);
+ url[0] = '/';
+ for (i = 1; i < TEST_START_SIZE; i++)
+ url[i] = 'a' + i % ('z' - 'a' + 1);
+ for (; i <= TEST_FAIL_SIZE; i++)
+ {
+ url[i] = 0;
+
+ qParam.queryPath = buf;
+ uri_cb_param->uri = url;
+ ahc_param->rq_url = url;
+
+ memset (&ahc_param->header_check_param, 0,
+ sizeof (ahc_param->header_check_param));
+ if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
+ ahc_param->rp_data,
+ ahc_param->rp_data_size))
+ {
+ (void) qParam.responseCode; /* TODO: check for the right response code */
+ if (TEST_OK_SIZE >= i)
+ {
+ fprintf (stderr,
+ "Request failed when running with the valid value size.\n");
+ ret = 1; /* Failed too early */
+ }
+ if (0 == first_failed_at)
+ {
+ if (verbose)
+ fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
+ first_failed_at = i;
+ }
+ }
+ else
+ {
+ if (TEST_FAIL_SIZE == i)
+ {
+ fprintf (stderr, "Request succeed with the largest size.\n");
+ ret = 1; /* Succeed with largest value */
+ }
+ }
+ if (0 != ahc_param->header_check_param.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected request headers");
+ if (0 != ahc_param->header_check_param.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large request header");
+ if (0 != rp_headers_check.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected reply headers");
+ if (0 != rp_headers_check.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large reply header");
+
+ url[i] = 'a' + i % ('z' - 'a' + 1);
+ }
+ }
+ else if (large_req_header_name)
+ {
+ buf[0] = '0'; /* Name starts with zero for unique identification */
+ for (i = 1; i < TEST_START_SIZE; i++)
+ buf[i] = 'a' + i % ('z' - 'a' + 1);
+ for (; i <= TEST_FAIL_SIZE; i++)
+ {
+ struct curl_slist *curl_headers;
+ curl_headers = NULL;
+
+ memcpy (buf + i, ": Z", 3); /* Note: strlen(": Z") is less than strlen(URL_SCHEME_HOST) */
+ buf[i + 3] = 0;
+
+ curl_headers = curl_slist_append (curl_headers, buf);
+ if (NULL == curl_headers)
+ externalErrorExit ();
+
+ qParam.headers = curl_headers;
+
+ memset (&ahc_param->header_check_param, 0,
+ sizeof (ahc_param->header_check_param));
+ if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
+ ahc_param->rp_data,
+ ahc_param->rp_data_size))
+ {
+ (void) qParam.responseCode; /* TODO: check for the right response code */
+ if (0 != ahc_param->header_check_param.large_header_name_size)
+ { /* If large header was processed, it must be valid */
+ if (i != ahc_param->header_check_param.large_header_name_size)
+ mhdErrorExitDesc ("Detected wrong large request header name size");
+ if (1 != ahc_param->header_check_param.large_header_value_size)
+ mhdErrorExitDesc ("Detected wrong large request header value size");
+ if (0 == ahc_param->header_check_param.large_header_valid)
+ mhdErrorExitDesc ("Detected wrong large request header");
+ }
+ if (TEST_OK_SIZE >= i)
+ {
+ fprintf (stderr,
+ "Request failed when running with the valid value size.\n");
+ ret = 1; /* Failed too early */
+ }
+ if (0 == first_failed_at)
+ {
+ if (verbose)
+ fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
+ first_failed_at = i;
+ }
+ }
+ else
+ {
+ if (i != ahc_param->header_check_param.large_header_name_size)
+ mhdErrorExitDesc ("Detected wrong large request header name size");
+ if (1 != ahc_param->header_check_param.large_header_value_size)
+ mhdErrorExitDesc ("Detected wrong large request header value size");
+ if (0 == ahc_param->header_check_param.large_header_valid)
+ mhdErrorExitDesc ("Detected wrong large request header");
+ if (TEST_FAIL_SIZE == i)
+ {
+ fprintf (stderr, "Request succeed with the largest size.\n");
+ ret = 1; /* Succeed with largest value */
+ }
+ }
+ if (0 != ahc_param->header_check_param.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected request headers");
+ if (0 != rp_headers_check.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected reply headers");
+ if (0 != rp_headers_check.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large reply header");
+
+ curl_slist_free_all (curl_headers);
+ buf[i] = 'a' + i % ('z' - 'a' + 1);
+ }
+ }
+ else if (large_req_header_value)
+ {
+ char *const hdr_value = buf + 3;
+ /* Name starts with zero for unique identification */
+ memcpy (buf, "0: ", 3); /* Note: strlen(": Z") is less than strlen(URL_SCHEME_HOST) */
+ for (i = 0; i < TEST_START_SIZE; i++)
+ hdr_value[i] = 'Z' - i % ('Z' - 'A' + 1);
+ for (; i <= TEST_FAIL_SIZE; i++)
+ {
+ struct curl_slist *curl_headers;
+ curl_headers = NULL;
+
+ hdr_value[i] = 0;
+
+ curl_headers = curl_slist_append (curl_headers, buf);
+ if (NULL == curl_headers)
+ externalErrorExit ();
+
+ qParam.headers = curl_headers;
+
+ memset (&ahc_param->header_check_param, 0,
+ sizeof (ahc_param->header_check_param));
+ if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
+ ahc_param->rp_data,
+ ahc_param->rp_data_size))
+ {
+ (void) qParam.responseCode; /* TODO: check for the right response code */
+ if (0 != ahc_param->header_check_param.large_header_name_size)
+ { /* If large header was processed, it must be valid */
+ if (1 != ahc_param->header_check_param.large_header_name_size)
+ mhdErrorExitDesc ("Detected wrong large request header name size");
+ if (i != ahc_param->header_check_param.large_header_value_size)
+ mhdErrorExitDesc ("Detected wrong large request header value size");
+ if (0 == ahc_param->header_check_param.large_header_valid)
+ mhdErrorExitDesc ("Detected wrong large request header");
+ }
+ if (TEST_OK_SIZE >= i)
+ {
+ fprintf (stderr,
+ "Request failed when running with the valid value size.\n");
+ ret = 1; /* Failed too early */
+ }
+ if (0 == first_failed_at)
+ {
+ if (verbose)
+ fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
+ first_failed_at = i;
+ }
+ }
+ else
+ {
+ if (1 != ahc_param->header_check_param.large_header_name_size)
+ mhdErrorExitDesc ("Detected wrong large request header name size");
+ if (i != ahc_param->header_check_param.large_header_value_size)
+ mhdErrorExitDesc ("Detected wrong large request header value size");
+ if (0 == ahc_param->header_check_param.large_header_valid)
+ mhdErrorExitDesc ("Detected wrong large request header");
+ if (TEST_FAIL_SIZE == i)
+ {
+ fprintf (stderr, "Request succeed with the largest size.\n");
+ ret = 1; /* Succeed with largest value */
+ }
+ }
+ if (0 != ahc_param->header_check_param.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected request headers");
+ if (0 != rp_headers_check.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected reply headers");
+ if (0 != rp_headers_check.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large reply header");
+
+ curl_slist_free_all (curl_headers);
+ hdr_value[i] = 'Z' - i % ('Z' - 'A' + 1);
+ }
+ }
+ else if (large_req_headers)
+ {
+ unsigned int num_hdrs = 0;
+ struct curl_slist *curl_headers;
+ const size_t hdr_size = strlen (N1_HEADER_CRLF);
+
+ curl_headers = NULL;
+
+ for (i = 0; i < TEST_RQ_N1_START_SIZE; i += hdr_size)
+ {
+ curl_headers = curl_slist_append (curl_headers, N1_HEADER);
+ if (NULL == curl_headers)
+ externalErrorExit ();
+ num_hdrs++;
+ }
+ for (; i <= TEST_FAIL_SIZE; i += hdr_size)
+ {
+ qParam.headers = curl_headers;
+ ahc_param->header_check_param.num_n1_headers = num_hdrs;
+
+ memset (&ahc_param->header_check_param, 0,
+ sizeof (ahc_param->header_check_param));
+ if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
+ ahc_param->rp_data,
+ ahc_param->rp_data_size))
+ {
+ (void) qParam.responseCode; /* TODO: check for the right response code */
+ if (0 != ahc_param->header_check_param.num_n1_headers)
+ { /* If headers were processed, they must be valid */
+ if (num_hdrs != ahc_param->header_check_param.num_n1_headers)
+ mhdErrorExitDesc ("Detected wrong number of request headers");
+ }
+ if (TEST_RQ_N1_OK_SIZE >= i)
+ {
+ fprintf (stderr,
+ "Request failed when running with the valid value size.\n");
+ ret = 1; /* Failed too early */
+ }
+ if (0 == first_failed_at)
+ {
+ if (verbose)
+ fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
+ first_failed_at = i;
+ }
+ }
+ else
+ {
+ if (num_hdrs != ahc_param->header_check_param.num_n1_headers)
+ mhdErrorExitDesc ("Detected wrong number of request headers");
+ if (TEST_FAIL_SIZE == i)
+ {
+ fprintf (stderr, "Request succeed with the largest size.\n");
+ ret = 1; /* Succeed with largest value */
+ }
+ }
+ if (0 != ahc_param->header_check_param.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large request header");
+ if (0 != rp_headers_check.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected reply headers");
+ if (0 != rp_headers_check.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large reply header");
+
+ curl_headers = curl_slist_append (curl_headers, N1_HEADER);
+ if (NULL == curl_headers)
+ externalErrorExit ();
+ num_hdrs++;
+ }
+ curl_slist_free_all (curl_headers);
+ }
+ else if (large_rsp_header_name)
+ {
+ for (i = TEST_START_SIZE; i <= TEST_FAIL_SIZE; i++)
+ {
+ ahc_param->rp_large_hdr_name_size = i;
+ ahc_param->rp_large_hdr_value_size = 1;
+
+ memset (&ahc_param->header_check_param, 0,
+ sizeof (ahc_param->header_check_param));
+ if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
+ ahc_param->rp_data,
+ ahc_param->rp_data_size))
+ {
+ (void) qParam.responseCode; /* TODO: check for the right response code */
+ if (0 != rp_headers_check.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large reply header");
+ if (TEST_OK_SIZE >= i)
+ {
+ fprintf (stderr,
+ "Request failed when running with the valid value size.\n");
+ ret = 1; /* Failed too early */
+ }
+ if (0 == first_failed_at)
+ {
+ if (verbose)
+ fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
+ first_failed_at = i;
+ }
+ }
+ else
+ {
+ if (i != rp_headers_check.large_header_name_size)
+ mhdErrorExitDesc ("Detected wrong large reply header name size");
+ if (1 != rp_headers_check.large_header_value_size)
+ mhdErrorExitDesc ("Detected wrong large reply header value size");
+ if (0 == rp_headers_check.large_header_valid)
+ mhdErrorExitDesc ("Detected wrong large reply header");
+ if (TEST_FAIL_SIZE == i)
+ {
+ fprintf (stderr, "Request succeed with the largest size.\n");
+ ret = 1; /* Succeed with largest value */
+ }
+ }
+ if (0 != ahc_param->header_check_param.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected request headers");
+ if (0 != ahc_param->header_check_param.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large request header");
+ if (0 != rp_headers_check.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected reply headers");
+ }
+ }
+ else if (large_rsp_header_value)
+ {
+ for (i = TEST_START_SIZE; i <= TEST_FAIL_SIZE; i++)
+ {
+ ahc_param->rp_large_hdr_name_size = 1;
+ ahc_param->rp_large_hdr_value_size = i;
+
+ memset (&ahc_param->header_check_param, 0,
+ sizeof (ahc_param->header_check_param));
+ if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
+ ahc_param->rp_data,
+ ahc_param->rp_data_size))
+ {
+ (void) qParam.responseCode; /* TODO: check for the right response code */
+ if (0 != rp_headers_check.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large reply header");
+ if (TEST_OK_SIZE >= i)
+ {
+ fprintf (stderr,
+ "Request failed when running with the valid value size.\n");
+ ret = 1; /* Failed too early */
+ }
+ if (0 == first_failed_at)
+ {
+ if (verbose)
+ fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
+ first_failed_at = i;
+ }
+ }
+ else
+ {
+ if (1 != rp_headers_check.large_header_name_size)
+ mhdErrorExitDesc ("Detected wrong large reply header name size");
+ if (i != rp_headers_check.large_header_value_size)
+ mhdErrorExitDesc ("Detected wrong large reply header value size");
+ if (0 == rp_headers_check.large_header_valid)
+ mhdErrorExitDesc ("Detected wrong large reply header");
+ if (TEST_FAIL_SIZE == i)
+ {
+ fprintf (stderr, "Request succeed with the largest size.\n");
+ ret = 1; /* Succeed with largest value */
+ }
+ }
+ if (0 != ahc_param->header_check_param.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected request headers");
+ if (0 != ahc_param->header_check_param.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large request header");
+ if (0 != rp_headers_check.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected reply headers");
+ }
+ }
+ else if (large_rsp_headers)
+ {
+ unsigned int num_hrds;
+ const size_t hdr_size = strlen (N1_HEADER_CRLF);
+
+ for (num_hrds = TEST_START_SIZE / hdr_size;
+ num_hrds * hdr_size <= TEST_FAIL_SIZE; num_hrds++)
+ {
+ i = num_hrds * hdr_size;
+ ahc_param->rp_num_n1_hdrs = num_hrds;
+
+ memset (&ahc_param->header_check_param, 0,
+ sizeof (ahc_param->header_check_param));
+ if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
+ ahc_param->rp_data,
+ ahc_param->rp_data_size))
+ {
+ (void) qParam.responseCode; /* TODO: check for the right response code */
+ if (0 != rp_headers_check.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected reply headers");
+ if (TEST_OK_SIZE >= i)
+ {
+ fprintf (stderr,
+ "Request failed when running with the valid value size.\n");
+ ret = 1; /* Failed too early */
+ }
+ if (0 == first_failed_at)
+ {
+ if (verbose)
+ fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
+ first_failed_at = i;
+ }
+ }
+ else
+ {
+ if (num_hrds != rp_headers_check.num_n1_headers)
+ mhdErrorExitDesc ("Detected wrong number of reply headers");
+ if (TEST_FAIL_SIZE == i)
+ {
+ fprintf (stderr, "Request succeed with the largest size.\n");
+ ret = 1; /* Succeed with largest value */
+ }
+ }
+ if (0 != ahc_param->header_check_param.num_n1_headers)
+ mhdErrorExitDesc ("Detected unexpected request headers");
+ if (0 != ahc_param->header_check_param.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large request header");
+ if (0 != rp_headers_check.large_header_name_size)
+ mhdErrorExitDesc ("Detected unexpected large reply header");
+ }
+ }
+ else
+ externalErrorExitDesc ("No valid test test was selected");
+
+ MHD_stop_daemon (d);
+ free (buf);
+ free (uri_cb_param);
+ free (ahc_param);
+
+ return ret;
+}
+
+
+enum testMhdThreadsType
+{
+ testMhdThreadExternal = 0,
+ testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD,
+ testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
+ | MHD_USE_INTERNAL_POLLING_THREAD,
+ testMhdThreadInternalPool
+};
+
+enum testMhdPollType
+{
+ testMhdPollBySelect = 0,
+ testMhdPollByPoll = MHD_USE_POLL,
+ testMhdPollByEpoll = MHD_USE_EPOLL,
+ testMhdPollAuto = MHD_USE_AUTO
+};
+
+/* Get number of threads for thread pool depending
+ * on used poll function and test type. */
+static unsigned int
+testNumThreadsForPool (enum testMhdPollType pollType)
+{
+ int numThreads = MHD_CPU_COUNT;
+ (void) pollType; /* Don't care about pollType for this test */
+ return numThreads; /* No practical limit for non-cleanup test */
+}
+
+
+static struct MHD_Daemon *
+startTestMhdDaemon (enum testMhdThreadsType thrType,
+ enum testMhdPollType pollType, int *pport,
+ struct ahc_cls_type **ahc_param,
+ struct check_uri_cls **uri_cb_param)
+{
+ struct MHD_Daemon *d;
+ const union MHD_DaemonInfo *dinfo;
+
+ if ((NULL == ahc_param) || (NULL == uri_cb_param))
+ abort ();
+
+ *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type));
+ if (NULL == *ahc_param)
+ externalErrorExit ();
+ *uri_cb_param =
+ (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls));
+ if (NULL == *uri_cb_param)
+ externalErrorExit ();
+
+ if ( (0 == *pport) &&
+ (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
+ {
+ *pport = 4100;
+ if (large_req_method)
+ *pport += 1;
+ if (large_req_url)
+ *pport += 2;
+ if (large_req_header_name)
+ *pport += 3;
+ if (large_req_header_value)
+ *pport += 4;
+ if (large_req_headers)
+ *pport += 5;
+ if (large_rsp_header_name)
+ *pport += 6;
+ if (large_rsp_header_value)
+ *pport += 7;
+ if (large_rsp_headers)
+ *pport += 8;
+ if (! oneone)
+ *pport += 16;
+ }
+
+ if (testMhdThreadInternalPool != thrType)
+ d = MHD_start_daemon (((int) thrType) | ((int) pollType)
+ | (verbose ? MHD_USE_ERROR_LOG : 0),
+ *pport, NULL, NULL,
+ &ahcCheck, *ahc_param,
+ MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
+ *uri_cb_param,
+ MHD_OPTION_CONNECTION_MEMORY_LIMIT,
+ (size_t) BUFFER_SIZE,
+ MHD_OPTION_END);
+ else
+ d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | ((int) pollType)
+ | (verbose ? MHD_USE_ERROR_LOG : 0),
+ *pport, NULL, NULL,
+ &ahcCheck, *ahc_param,
+ MHD_OPTION_THREAD_POOL_SIZE,
+ testNumThreadsForPool (pollType),
+ MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
+ *uri_cb_param,
+ MHD_OPTION_CONNECTION_MEMORY_LIMIT,
+ (size_t) BUFFER_SIZE,
+ MHD_OPTION_END);
+
+ if (NULL == d)
+ {
+ fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno);
+ abort ();
+ }
+
+ if (0 == *pport)
+ {
+ dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
+ if ((NULL == dinfo) || (0 == dinfo->port) )
+ {
+ fprintf (stderr, "MHD_get_daemon_info() failed.\n");
+ abort ();
+ }
+ *pport = (int) dinfo->port;
+ if (0 == global_port)
+ global_port = *pport; /* Reuse the same port for all tests */
+ }
+
+ return d;
+}
+
+
+/* Test runners */
+
+
+static int
+testExternalGet (void)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+
+ d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port,
+ &ahc_param, &uri_cb_param);
+
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param);
+}
+
+
+static int
+testInternalGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+
+ d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port,
+ &ahc_param, &uri_cb_param);
+
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param);
+}
+
+
+static int
+testMultithreadedGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+
+ d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port,
+ &ahc_param, &uri_cb_param);
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param);
+}
+
+
+static int
+testMultithreadedPoolGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+
+ d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port,
+ &ahc_param, &uri_cb_param);
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param);
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+ unsigned int errorCount = 0;
+ unsigned int test_result = 0;
+ verbose = 0;
+
+ if ((NULL == argv) || (0 == argv[0]))
+ return 99;
+ oneone = ! has_in_name (argv[0], "10");
+ large_req_method = has_in_name (argv[0], "_method") ? 1 : 0;
+ large_req_url = has_in_name (argv[0], "_url") ? 1 : 0;
+ large_req_header_name = has_in_name (argv[0], "_request_header_name") ?
+ 1 : 0;
+ large_req_header_value = has_in_name (argv[0], "_request_header_value") ?
+ 1 : 0;
+ large_req_headers = has_in_name (argv[0], "_request_headers") ? 1 : 0;
+ large_rsp_header_name = has_in_name (argv[0], "_reply_header_name") ?
+ 1 : 0;
+ large_rsp_header_value = has_in_name (argv[0], "_reply_header_value") ?
+ 1 : 0;
+ large_rsp_headers = has_in_name (argv[0], "_reply_headers") ? 1 : 0;
+ if (large_req_method + large_req_url + large_req_header_name
+ + large_req_header_value + large_req_headers + large_rsp_header_name
+ + large_rsp_header_value + large_rsp_headers != 1)
+ return 99;
+ verbose = ! has_param (argc, argv, "-q") || has_param (argc, argv, "--quiet");
+
+ test_global_init ();
+
+ /* Could be set to non-zero value to enforce using specific port
+ * in the test */
+ global_port = 0;
+ test_result = testExternalGet ();
+ if (test_result)
+ fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
+ else if (verbose)
+ printf ("PASSED: testExternalGet ().\n");
+ errorCount += test_result;
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
+ {
+ test_result = testInternalGet (testMhdPollAuto);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+#ifdef _MHD_HEAVY_TESTS
+ /* Actually tests are not heavy, but took too long to complete while
+ * not really provide any additional results. */
+ test_result = testInternalGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ test_result = testMultithreadedPoolGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr,
+ "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ test_result = testMultithreadedGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr,
+ "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
+ {
+ test_result = testInternalGet (testMhdPollByPoll);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
+ errorCount += test_result;
+ }
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
+ {
+ test_result = testInternalGet (testMhdPollByEpoll);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
+ errorCount += test_result;
+ }
+#else
+ /* Mute compiler warnings */
+ (void) testMultithreadedGet;
+ (void) testMultithreadedPoolGet;
+#endif /* _MHD_HEAVY_TESTS */
+ }
+ if (0 != errorCount)
+ fprintf (stderr,
+ "Error (code: %u)\n",
+ errorCount);
+ else if (verbose)
+ printf ("All tests passed.\n");
+
+ test_global_cleanup ();
+
+ return (errorCount == 0) ? 0 : 1; /* 0 == pass */
+}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_tricky.c
^
|
@@ -0,0 +1,1174 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
+ Copyright (C) 2007, 2009, 2011 Christian Grothoff
+
+ 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 2, 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_toolarge.c
+ * @brief Testcase for handling of untypical data.
+ * @author Karlson2k (Evgeny Grin)
+ * @author Christian Grothoff
+ */
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "test_helpers.h"
+#include "mhd_sockets.h" /* only macros used */
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif /* HAVE_STRINGS_H */
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#ifndef WINDOWS
+#include <unistd.h>
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
+#ifndef CURL_VERSION_BITS
+#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
+#endif /* ! CURL_VERSION_BITS */
+#ifndef CURL_AT_LEAST_VERSION
+#define CURL_AT_LEAST_VERSION(x,y,z) \
+ (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
+#endif /* ! CURL_AT_LEAST_VERSION */
+
+#if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
+#undef MHD_CPU_COUNT
+#endif
+#if ! defined(MHD_CPU_COUNT)
+#define MHD_CPU_COUNT 2
+#endif
+#if MHD_CPU_COUNT > 32
+#undef MHD_CPU_COUNT
+/* Limit to reasonable value */
+#define MHD_CPU_COUNT 32
+#endif /* MHD_CPU_COUNT > 32 */
+
+
+#if defined(HAVE___FUNC__)
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func(NULL, __func__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, __func__, __LINE__)
+#define libcurlErrorExit(ignore) \
+ _libcurlErrorExit_func(NULL, __func__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, __func__, __LINE__)
+#define mhdErrorExit(ignore) \
+ _mhdErrorExit_func(NULL, __func__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+ _mhdErrorExit_func(errDesc, __func__, __LINE__)
+#elif defined(HAVE___FUNCTION__)
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define libcurlErrorExit(ignore) \
+ _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define mhdErrorExit(ignore) \
+ _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+ _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#else
+#define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func(errDesc, NULL, __LINE__)
+#define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+ _libcurlErrorExit_func(errDesc, NULL, __LINE__)
+#define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
+#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
+#endif
+
+
+_MHD_NORETURN static void
+_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "System or external library call failed");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+ fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+ fflush (stderr);
+ exit (99);
+}
+
+
+static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
+
+_MHD_NORETURN static void
+_libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "CURL library call failed");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+ if (0 != libcurl_errbuf[0])
+ fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf);
+
+ fflush (stderr);
+ exit (99);
+}
+
+
+_MHD_NORETURN static void
+_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "MHD unexpected error");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+
+ fflush (stderr);
+ exit (8);
+}
+
+
+/* Could be increased to facilitate debugging */
+#define TIMEOUTS_VAL 5
+
+#define EXPECTED_URI_BASE_PATH "/a"
+
+#define EXPECTED_URI_BASE_PATH_TRICKY "/one\rtwo"
+
+#define URL_SCHEME "http:/" "/"
+
+#define URL_HOST "127.0.0.1"
+
+#define URL_SCHEME_HOST URL_SCHEME URL_HOST
+
+#define HEADER1_NAME "First"
+#define HEADER1_VALUE "1st"
+#define HEADER1 HEADER1_NAME ": " HEADER1_VALUE
+#define HEADER2_NAME "Second"
+#define HEADER2CR_VALUE "2\rnd"
+#define HEADER2CR HEADER2_NAME ": " HEADER2CR_VALUE
+/* Use headers when it would be properly supported by MHD
+#define HEADER3CR_NAME "Thi\rrd"
+#define HEADER3CR_VALUE "3r\rd"
+#define HEADER3CR HEADER3CR_NAME ": " HEADER3CR_VALUE
+*/
+#define HEADER4_NAME "Normal"
+#define HEADER4_VALUE "it's fine"
+#define HEADER4 HEADER4_NAME ": " HEADER4_VALUE
+
+/* Global parameters */
+static int verbose; /**< Be verbose */
+static int oneone; /**< If false use HTTP/1.0 for requests*/
+static int global_port; /**< MHD daemons listen port number */
+static int response_timeout_val = TIMEOUTS_VAL;
+
+static int tricky_url; /**< Tricky request URL */
+static int tricky_header2; /**< Tricky request header2 */
+
+/* Current test parameters */
+/* * Moved to local variables * */
+
+/* Static helper variables */
+/* * None for this test * */
+
+static void
+test_global_init (void)
+{
+ libcurl_errbuf[0] = 0;
+
+ if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+ externalErrorExit ();
+}
+
+
+static void
+test_global_cleanup (void)
+{
+ curl_global_cleanup ();
+}
+
+
+struct headers_check_result
+{
+ int dummy; /* no checks in this test */
+};
+
+
+size_t
+lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
+ void *userdata)
+{
+ const size_t data_size = size * nitems;
+ struct headers_check_result *check_res =
+ (struct headers_check_result *) userdata;
+
+ /* no checks in this test */
+ (void) check_res; (void) buffer;
+
+ return data_size;
+}
+
+
+struct lcurl_data_cb_param
+{
+ char *buf;
+ size_t pos;
+ size_t size;
+};
+
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+ struct lcurl_data_cb_param *cbc = ctx;
+
+ if (cbc->pos + size * nmemb > cbc->size)
+ externalErrorExit (); /* overflow */
+ memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+ cbc->pos += size * nmemb;
+ return size * nmemb;
+}
+
+
+struct check_uri_cls
+{
+ const char *volatile uri;
+};
+
+static void *
+check_uri_cb (void *cls,
+ const char *uri,
+ struct MHD_Connection *con)
+{
+ struct check_uri_cls *param = (struct check_uri_cls *) cls;
+ (void) con;
+
+ if (0 != strcmp (param->uri,
+ uri))
+ {
+ fprintf (stderr,
+ "Wrong URI: `%s', line: %d\n",
+ uri, __LINE__);
+ exit (22);
+ }
+ return NULL;
+}
+
+
+struct mhd_header_checker_param
+{
+ int found_header1;
+ int found_header2;
+ int found_header4;
+};
+
+enum MHD_Result
+headerCheckerInterator (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ size_t key_size,
+ const char *value,
+ size_t value_size)
+{
+ struct mhd_header_checker_param *const param =
+ (struct mhd_header_checker_param *) cls;
+
+ if (NULL == param)
+ mhdErrorExitDesc ("cls parameter is NULL");
+
+ if (MHD_HEADER_KIND != kind)
+ return MHD_YES; /* Continue iteration */
+
+ if (0 == key_size)
+ mhdErrorExitDesc ("Zero key length");
+
+ if ((strlen (HEADER1_NAME) == key_size) &&
+ (0 == memcmp (key, HEADER1_NAME, key_size)))
+ {
+ if ((strlen (HEADER1_VALUE) == value_size) &&
+ (0 == memcmp (value, HEADER1_VALUE, value_size)))
+ param->found_header1 = 1;
+ else
+ fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
+ (int) value_size, value, HEADER1_VALUE);
+ }
+ else if ((strlen (HEADER2_NAME) == key_size) &&
+ (0 == memcmp (key, HEADER2_NAME, key_size)))
+ {
+ if ((strlen (HEADER2CR_VALUE) == value_size) &&
+ (0 == memcmp (value, HEADER2CR_VALUE, value_size)))
+ param->found_header2 = 1;
+ else
+ fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
+ (int) value_size, value, HEADER2CR_VALUE);
+ }
+ else if ((strlen (HEADER4_NAME) == key_size) &&
+ (0 == memcmp (key, HEADER4_NAME, key_size)))
+ {
+ if ((strlen (HEADER4_VALUE) == value_size) &&
+ (0 == memcmp (value, HEADER4_VALUE, value_size)))
+ param->found_header4 = 1;
+ else
+ fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
+ (int) value_size, value, HEADER4_VALUE);
+ }
+ return MHD_YES;
+}
+
+
+struct ahc_cls_type
+{
+ const char *volatile rp_data;
+ volatile size_t rp_data_size;
+ struct mhd_header_checker_param header_check_param;
+ const char *volatile rq_method;
+ const char *volatile rq_url;
+};
+
+
+static enum MHD_Result
+ahcCheck (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data, size_t *upload_data_size,
+ void **con_cls)
+{
+ static int ptr;
+ struct MHD_Response *response;
+ enum MHD_Result ret;
+ struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
+
+ if (NULL == param)
+ mhdErrorExitDesc ("cls parameter is NULL");
+
+ if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
+ mhdErrorExitDesc ("Unexpected HTTP version");
+
+ if (0 != strcmp (url, param->rq_url))
+ mhdErrorExitDesc ("Unexpected URI");
+
+ if (NULL != upload_data)
+ mhdErrorExitDesc ("'upload_data' is not NULL");
+
+ if (NULL == upload_data_size)
+ mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
+
+ if (0 != *upload_data_size)
+ mhdErrorExitDesc ("'*upload_data_size' value is not zero");
+
+ if (0 != strcmp (param->rq_method, method))
+ mhdErrorExitDesc ("Unexpected request method");
+
+ if (&ptr != *con_cls)
+ {
+ *con_cls = &ptr;
+ return MHD_YES;
+ }
+ *con_cls = NULL;
+
+ if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND,
+ &headerCheckerInterator,
+ ¶m->header_check_param))
+ mhdErrorExitDesc ("Wrong number of headers in the request");
+
+ response = MHD_create_response_from_buffer (param->rp_data_size,
+ (void *) param->rp_data,
+ MHD_RESPMEM_MUST_COPY);
+ if (NULL == response)
+ mhdErrorExitDesc ("Failed to create response");
+
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ if (MHD_YES != ret)
+ mhdErrorExitDesc ("Failed to queue response");
+
+ return ret;
+}
+
+
+struct curlQueryParams
+{
+ /* Destination path for CURL query */
+ const char *queryPath;
+
+#if CURL_AT_LEAST_VERSION (7, 62, 0)
+ CURLU *url;
+#endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */
+
+ /* Custom query method, NULL for default */
+ const char *method;
+
+ /* Destination port for CURL query */
+ int queryPort;
+
+ /* List of additional request headers */
+ struct curl_slist *headers;
+
+ /* CURL query result error flag */
+ volatile int queryError;
+
+ /* Response HTTP code, zero if no response */
+ volatile int responseCode;
+};
+
+
+static CURL *
+curlEasyInitForTest (struct curlQueryParams *p,
+ struct lcurl_data_cb_param *dcbp,
+ struct headers_check_result *hdr_chk_result)
+{
+ CURL *c;
+
+ c = curl_easy_init ();
+ if (NULL == c)
+ libcurlErrorExitDesc ("curl_easy_init() failed");
+
+ if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, p->queryPath)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) p->queryPort)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+ ©Buffer)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, dcbp)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
+ (long) response_timeout_val)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
+ (long) response_timeout_val)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
+ libcurl_errbuf)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION,
+ lcurl_hdr_callback)) ||
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA,
+ hdr_chk_result)) ||
+#if CURL_AT_LEAST_VERSION (7, 42, 0)
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_PATH_AS_IS,
+ (long) 1)) ||
+#endif /* CURL_AT_LEAST_VERSION(7, 42, 0) */
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
+ (oneone) ?
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_1)) :
+ (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+ CURL_HTTP_VERSION_1_0)))
+ libcurlErrorExitDesc ("curl_easy_setopt() failed");
+
+ if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST, p->method))
+ libcurlErrorExitDesc ("curl_easy_setopt() failed");
+
+ if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, p->headers))
+ libcurlErrorExitDesc ("curl_easy_setopt() failed");
+
+#if CURL_AT_LEAST_VERSION (7, 62, 0)
+ if (NULL != p->url)
+ {
+ if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CURLU, p->url))
+ libcurlErrorExitDesc ("curl_easy_setopt() failed");
+ }
+#endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */
+ return c;
+}
+
+
+static CURLcode
+performQueryExternal (struct MHD_Daemon *d, CURL *c)
+{
+ CURLM *multi;
+ time_t start;
+ struct timeval tv;
+ CURLcode ret;
+
+ ret = CURLE_FAILED_INIT; /* will be replaced with real result */
+ multi = NULL;
+ multi = curl_multi_init ();
+ if (multi == NULL)
+ libcurlErrorExitDesc ("curl_multi_init() failed");
+ if (CURLM_OK != curl_multi_add_handle (multi, c))
+ libcurlErrorExitDesc ("curl_multi_add_handle() failed");
+
+ start = time (NULL);
+ while (time (NULL) - start <= TIMEOUTS_VAL)
+ {
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ MHD_socket maxMhdSk;
+ int maxCurlSk;
+ int running;
+
+ maxMhdSk = MHD_INVALID_SOCKET;
+ maxCurlSk = -1;
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ if (NULL != multi)
+ {
+ curl_multi_perform (multi, &running);
+ if (0 == running)
+ {
+ struct CURLMsg *msg;
+ int msgLeft;
+ int totalMsgs = 0;
+ do
+ {
+ msg = curl_multi_info_read (multi, &msgLeft);
+ if (NULL == msg)
+ libcurlErrorExitDesc ("curl_multi_info_read() failed");
+ totalMsgs++;
+ if (CURLMSG_DONE == msg->msg)
+ ret = msg->data.result;
+ } while (msgLeft > 0);
+ if (1 != totalMsgs)
+ {
+ fprintf (stderr,
+ "curl_multi_info_read returned wrong "
+ "number of results (%d).\n",
+ totalMsgs);
+ externalErrorExit ();
+ }
+ curl_multi_remove_handle (multi, c);
+ curl_multi_cleanup (multi);
+ multi = NULL;
+ }
+ else
+ {
+ if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
+ libcurlErrorExitDesc ("curl_multi_fdset() failed");
+ }
+ }
+ if (NULL == multi)
+ { /* libcurl has finished, check whether MHD still needs to perform cleanup */
+ unsigned long long to;
+ if ((MHD_YES != MHD_get_timeout (d, &to)) || (0 != to))
+ break; /* MHD finished as well */
+ }
+ if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
+ mhdErrorExitDesc ("MHD_get_fdset() failed");
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000;
+#ifdef MHD_POSIX_SOCKETS
+ if (maxMhdSk > maxCurlSk)
+ maxCurlSk = maxMhdSk;
+#endif /* MHD_POSIX_SOCKETS */
+ if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
+ {
+#ifdef MHD_POSIX_SOCKETS
+ if (EINTR != errno)
+ externalErrorExitDesc ("Unexpected select() error");
+#else
+ if ((WSAEINVAL != WSAGetLastError ()) ||
+ (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+ externalErrorExitDesc ("Unexpected select() error");
+ Sleep (1);
+#endif
+ }
+ if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
+ mhdErrorExitDesc ("MHD_run_from_select() failed");
+ }
+
+ return ret;
+}
+
+
+/* Returns zero for successful response and non-zero for failed response */
+static int
+doCurlQueryInThread (struct MHD_Daemon *d,
+ struct curlQueryParams *p,
+ struct headers_check_result *hdr_res,
+ const char *expected_data,
+ size_t expected_data_size)
+{
+ const union MHD_DaemonInfo *dinfo;
+ CURL *c;
+ struct lcurl_data_cb_param dcbp;
+ CURLcode errornum;
+ int use_external_poll;
+ long resp_code;
+
+ dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
+ if (NULL == dinfo)
+ mhdErrorExitDesc ("MHD_get_daemon_info() failed");
+ use_external_poll = (0 == (dinfo->flags
+ & MHD_USE_INTERNAL_POLLING_THREAD));
+
+ if (NULL == p->queryPath
+#if CURL_AT_LEAST_VERSION (7, 62, 0)
+ && NULL == p->url
+#endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */
+ )
+ abort ();
+
+ if (0 == p->queryPort)
+ abort ();
+
+ /* Test must not fail due to test's internal buffer shortage */
+ dcbp.size = expected_data_size * 2 + 1;
+ dcbp.buf = malloc (dcbp.size);
+ if (NULL == dcbp.buf)
+ externalErrorExit ();
+ dcbp.pos = 0;
+
+ memset (hdr_res, 0, sizeof(*hdr_res));
+
+ c = curlEasyInitForTest (p,
+ &dcbp, hdr_res);
+
+ if (! use_external_poll)
+ errornum = curl_easy_perform (c);
+ else
+ errornum = performQueryExternal (d, c);
+
+ if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &resp_code))
+ libcurlErrorExitDesc ("curl_easy_getinfo() failed");
+
+ p->responseCode = (int) resp_code;
+ if ((CURLE_OK == errornum) && (200 != resp_code))
+ {
+ fprintf (stderr,
+ "Got reply with unexpected status code: %d\n",
+ p->responseCode);
+ mhdErrorExit ();
+ }
+
+ if (CURLE_OK != errornum)
+ {
+ if ((CURLE_GOT_NOTHING != errornum) && (CURLE_RECV_ERROR != errornum)
+ && (CURLE_HTTP_RETURNED_ERROR != errornum))
+ {
+ if (CURLE_OPERATION_TIMEDOUT == errornum)
+ mhdErrorExitDesc ("Request was aborted due to timeout");
+ fprintf (stderr, "libcurl returned expected error: %s\n",
+ curl_easy_strerror (errornum));
+ mhdErrorExitDesc ("Request failed due to unexpected error");
+ }
+ p->queryError = 1;
+ if ((0 != resp_code) &&
+ ((499 < resp_code) || (400 > resp_code))) /* TODO: add all expected error codes */
+ {
+ fprintf (stderr,
+ "Got reply with unexpected status code: %ld\n",
+ resp_code);
+ mhdErrorExit ();
+ }
+ }
+ else
+ {
+ if (dcbp.pos != expected_data_size)
+ mhdErrorExit ("libcurl reports wrong size of MHD reply body data");
+ else if (0 != memcmp (expected_data, dcbp.buf,
+ expected_data_size))
+ mhdErrorExit ("libcurl reports wrong MHD reply body data");
+ else
+ p->queryError = 0;
+ }
+
+ curl_easy_cleanup (c);
+ free (dcbp.buf);
+
+ return p->queryError;
+}
+
+
+/* Perform test queries, shut down MHD daemon, and free parameters */
+static int
+performTestQueries (struct MHD_Daemon *d, int d_port,
+ struct ahc_cls_type *ahc_param,
+ struct check_uri_cls *uri_cb_param)
+{
+ struct curlQueryParams qParam;
+ int ret = 0; /* Return value */
+ struct headers_check_result rp_headers_check;
+ struct curl_slist *curl_headers;
+ curl_headers = NULL;
+
+ /* Common parameters, to be individually overridden by specific test cases */
+ qParam.queryPort = d_port;
+ qParam.method = NULL; /* Use libcurl default: GET */
+ qParam.queryPath = URL_SCHEME_HOST EXPECTED_URI_BASE_PATH;
+#if CURL_AT_LEAST_VERSION (7, 62, 0)
+ qParam.url = NULL;
+#endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */
+ qParam.headers = NULL; /* No additional headers */
+ uri_cb_param->uri = EXPECTED_URI_BASE_PATH;
+ ahc_param->rq_url = EXPECTED_URI_BASE_PATH;
+ ahc_param->rq_method = "GET"; /* Default expected method */
+
+ ahc_param->rp_data = "~";
+ ahc_param->rp_data_size = 1;
+
+ curl_headers = curl_slist_append (curl_headers, HEADER1);
+ if (NULL == curl_headers)
+ externalErrorExit ();
+ curl_headers = curl_slist_append (curl_headers, HEADER4);
+ if (NULL == curl_headers)
+ externalErrorExit ();
+ qParam.headers = curl_headers;
+
+ memset (&ahc_param->header_check_param, 0,
+ sizeof (ahc_param->header_check_param));
+
+ if (tricky_url)
+ {
+#if CURL_AT_LEAST_VERSION (7, 62, 0)
+ CURLU *url;
+ url = curl_url ();
+ if (NULL == url)
+ externalErrorExit ();
+ qParam.url = url;
+
+ if ((CURLUE_OK != curl_url_set (qParam.url, CURLUPART_SCHEME, "http", 0)) ||
+ (CURLUE_OK != curl_url_set (qParam.url, CURLUPART_HOST, URL_HOST,
+ CURLU_PATH_AS_IS
+#ifdef CURLU_ALLOW_SPACE
+ | CURLU_ALLOW_SPACE
+#endif /* CURLU_ALLOW_SPACE */
+ )) ||
+ (CURLUE_OK != curl_url_set (qParam.url, CURLUPART_PATH,
+ EXPECTED_URI_BASE_PATH_TRICKY, 0)))
+ libcurlErrorExit ();
+
+ qParam.queryPath = NULL;
+ uri_cb_param->uri = EXPECTED_URI_BASE_PATH_TRICKY;
+ ahc_param->rq_url = EXPECTED_URI_BASE_PATH_TRICKY;
+
+ if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
+ ahc_param->rp_data,
+ ahc_param->rp_data_size))
+ {
+ /* TODO: Allow fail only if relevant MHD mode set */
+ if (0 == qParam.responseCode)
+ {
+ fprintf (stderr, "Request failed without any valid response.\n");
+ ret = 1;
+ }
+ else
+ {
+ if (verbose)
+ printf ("Request failed with %d response code.\n",
+ qParam.responseCode);
+ (void) qParam.responseCode; /* TODO: check for the right response code */
+ ret = 0;
+ }
+ }
+ else
+ {
+ if (200 != qParam.responseCode)
+ {
+ fprintf (stderr, "Request succeed with wrong response code: %d.\n",
+ qParam.responseCode);
+ ret = 1;
+ }
+ else
+ {
+ ret = 0;
+ if (verbose)
+ printf ("Request succeed.\n");
+ }
+
+ if (! ahc_param->header_check_param.found_header1)
+ mhdErrorExitDesc ("Required header1 was not detected in request");
+ if (! ahc_param->header_check_param.found_header4)
+ mhdErrorExitDesc ("Required header4 was not detected in request");
+ }
+ curl_url_cleanup (url);
+#else
+ fprintf (stderr, "This test requires libcurl version 7.62.0 or newer.\n");
+ abort ();
+#endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */
+ }
+ else if (tricky_header2)
+ {
+ /* Reset libcurl headers */
+ qParam.headers = NULL;
+ curl_slist_free_all (curl_headers);
+ curl_headers = NULL;
+
+ /* Set special libcurl headers */
+ curl_headers = curl_slist_append (curl_headers, HEADER1);
+ if (NULL == curl_headers)
+ externalErrorExit ();
+ curl_headers = curl_slist_append (curl_headers, HEADER2CR);
+ if (NULL == curl_headers)
+ externalErrorExit ();
+ curl_headers = curl_slist_append (curl_headers, HEADER4);
+ if (NULL == curl_headers)
+ externalErrorExit ();
+ qParam.headers = curl_headers;
+
+ if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
+ ahc_param->rp_data,
+ ahc_param->rp_data_size))
+ {
+ /* TODO: Allow fail only if relevant MHD mode set */
+ if (0 == qParam.responseCode)
+ {
+ fprintf (stderr, "Request failed without any valid response.\n");
+ ret = 1;
+ }
+ else
+ {
+ if (verbose)
+ printf ("Request failed with %d response code.\n",
+ qParam.responseCode);
+ (void) qParam.responseCode; /* TODO: check for the right response code */
+ ret = 0;
+ }
+ }
+ else
+ {
+ if (200 != qParam.responseCode)
+ {
+ fprintf (stderr, "Request succeed with wrong response code: %d.\n",
+ qParam.responseCode);
+ ret = 1;
+ }
+ else
+ {
+ ret = 0;
+ if (verbose)
+ printf ("Request succeed.\n");
+ }
+
+ if (! ahc_param->header_check_param.found_header1)
+ mhdErrorExitDesc ("Required header1 was not detected in request");
+ if (! ahc_param->header_check_param.found_header2)
+ mhdErrorExitDesc ("Required header2 was not detected in request");
+ if (! ahc_param->header_check_param.found_header4)
+ mhdErrorExitDesc ("Required header4 was not detected in request");
+ }
+ }
+ else
+ externalErrorExitDesc ("No valid test test was selected");
+
+ MHD_stop_daemon (d);
+ curl_slist_free_all (curl_headers);
+ free (uri_cb_param);
+ free (ahc_param);
+
+ return ret;
+}
+
+
+enum testMhdThreadsType
+{
+ testMhdThreadExternal = 0,
+ testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD,
+ testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
+ | MHD_USE_INTERNAL_POLLING_THREAD,
+ testMhdThreadInternalPool
+};
+
+enum testMhdPollType
+{
+ testMhdPollBySelect = 0,
+ testMhdPollByPoll = MHD_USE_POLL,
+ testMhdPollByEpoll = MHD_USE_EPOLL,
+ testMhdPollAuto = MHD_USE_AUTO
+};
+
+/* Get number of threads for thread pool depending
+ * on used poll function and test type. */
+static unsigned int
+testNumThreadsForPool (enum testMhdPollType pollType)
+{
+ int numThreads = MHD_CPU_COUNT;
+ (void) pollType; /* Don't care about pollType for this test */
+ return numThreads; /* No practical limit for non-cleanup test */
+}
+
+
+static struct MHD_Daemon *
+startTestMhdDaemon (enum testMhdThreadsType thrType,
+ enum testMhdPollType pollType, int *pport,
+ struct ahc_cls_type **ahc_param,
+ struct check_uri_cls **uri_cb_param)
+{
+ struct MHD_Daemon *d;
+ const union MHD_DaemonInfo *dinfo;
+
+ if ((NULL == ahc_param) || (NULL == uri_cb_param))
+ abort ();
+
+ *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type));
+ if (NULL == *ahc_param)
+ externalErrorExit ();
+ *uri_cb_param =
+ (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls));
+ if (NULL == *uri_cb_param)
+ externalErrorExit ();
+
+ if ( (0 == *pport) &&
+ (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
+ {
+ *pport = 4150;
+ if (tricky_url)
+ *pport += 1;
+ if (tricky_header2)
+ *pport += 2;
+ if (! oneone)
+ *pport += 16;
+ }
+
+ if (testMhdThreadInternalPool != thrType)
+ d = MHD_start_daemon (((int) thrType) | ((int) pollType)
+ | (verbose ? MHD_USE_ERROR_LOG : 0),
+ *pport, NULL, NULL,
+ &ahcCheck, *ahc_param,
+ MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
+ *uri_cb_param,
+ MHD_OPTION_END);
+ else
+ d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | ((int) pollType)
+ | (verbose ? MHD_USE_ERROR_LOG : 0),
+ *pport, NULL, NULL,
+ &ahcCheck, *ahc_param,
+ MHD_OPTION_THREAD_POOL_SIZE,
+ testNumThreadsForPool (pollType),
+ MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
+ *uri_cb_param,
+ MHD_OPTION_END);
+
+ if (NULL == d)
+ {
+ fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno);
+ abort ();
+ }
+
+ if (0 == *pport)
+ {
+ dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
+ if ((NULL == dinfo) || (0 == dinfo->port) )
+ {
+ fprintf (stderr, "MHD_get_daemon_info() failed.\n");
+ abort ();
+ }
+ *pport = (int) dinfo->port;
+ if (0 == global_port)
+ global_port = *pport; /* Reuse the same port for all tests */
+ }
+
+ return d;
+}
+
+
+/* Test runners */
+
+
+static int
+testExternalGet (void)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+
+ d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port,
+ &ahc_param, &uri_cb_param);
+
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param);
+}
+
+
+static int
+testInternalGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+
+ d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port,
+ &ahc_param, &uri_cb_param);
+
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param);
+}
+
+
+static int
+testMultithreadedGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+
+ d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port,
+ &ahc_param, &uri_cb_param);
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param);
+}
+
+
+static int
+testMultithreadedPoolGet (enum testMhdPollType pollType)
+{
+ struct MHD_Daemon *d;
+ int d_port = global_port; /* Daemon's port */
+ struct ahc_cls_type *ahc_param;
+ struct check_uri_cls *uri_cb_param;
+
+ d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port,
+ &ahc_param, &uri_cb_param);
+ return performTestQueries (d, d_port, ahc_param, uri_cb_param);
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+ unsigned int errorCount = 0;
+ unsigned int test_result = 0;
+ verbose = 0;
+
+ if ((NULL == argv) || (0 == argv[0]))
+ return 99;
+ oneone = ! has_in_name (argv[0], "10");
+ tricky_url = has_in_name (argv[0], "_url") ? 1 : 0;
+ tricky_header2 = has_in_name (argv[0], "_header2") ? 1 : 0;
+ if (1 != tricky_url + tricky_header2)
+ return 99;
+ verbose = ! has_param (argc, argv, "-q") || has_param (argc, argv, "--quiet");
+
+#if ! CURL_AT_LEAST_VERSION (7, 62, 0)
+ if (tricky_url)
+ {
+ fprintf (stderr, "This test requires libcurl version 7.62.0 or newer.\n");
+ return 77;
+ }
+#endif /* ! CURL_AT_LEAST_VERSION(7, 62, 0) */
+
+ test_global_init ();
+
+ /* Could be set to non-zero value to enforce using specific port
+ * in the test */
+ global_port = 0;
+ test_result = testExternalGet ();
+ if (test_result)
+ fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
+ else if (verbose)
+ printf ("PASSED: testExternalGet ().\n");
+ errorCount += test_result;
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
+ {
+ test_result = testInternalGet (testMhdPollAuto);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+#ifdef _MHD_HEAVY_TESTS
+ /* Actually tests are not heavy, but took too long to complete while
+ * not really provide any additional results. */
+ test_result = testInternalGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ test_result = testMultithreadedPoolGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr,
+ "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ test_result = testMultithreadedGet (testMhdPollBySelect);
+ if (test_result)
+ fprintf (stderr,
+ "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
+ errorCount += test_result;
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
+ {
+ test_result = testInternalGet (testMhdPollByPoll);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
+ errorCount += test_result;
+ }
+ if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
+ {
+ test_result = testInternalGet (testMhdPollByEpoll);
+ if (test_result)
+ fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n",
+ test_result);
+ else if (verbose)
+ printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
+ errorCount += test_result;
+ }
+#else
+ /* Mute compiler warnings */
+ (void) testMultithreadedGet;
+ (void) testMultithreadedPoolGet;
+#endif /* _MHD_HEAVY_TESTS */
+ }
+ if (0 != errorCount)
+ fprintf (stderr,
+ "Error (code: %u)\n",
+ errorCount);
+ else if (verbose)
+ printf ("All tests passed.\n");
+
+ test_global_cleanup ();
+
+ return (errorCount == 0) ? 0 : 1; /* 0 == pass */
+}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testcurl/test_urlparse.c
^
|
@@ -1,6 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007, 2009, 2011 Christian Grothoff
+ Copyright (C) 2014-2019 Evgeny Grin (Karlson2k)
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
@@ -22,6 +23,7 @@
* @file daemontest_urlparse.c
* @brief Testcase for libmicrohttpd url parsing
* @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
*/
#include "MHD_config.h"
@@ -216,5 +218,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testzzuf/Makefile.am
^
|
@@ -8,10 +8,6 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/include \
$(LIBCURL_CPPFLAGS)
-
-if HEAVY_TESTS
-AM_CPPFLAGS += -D_MHD_HEAVY_TESTS=1
-endif
EXTRA_DIST = README socat.c
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testzzuf/test_get.c
^
|
@@ -329,5 +329,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testzzuf/test_get_chunked.c
^
|
@@ -360,5 +360,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testzzuf/test_post.c
^
|
@@ -412,5 +412,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testzzuf/test_post_form.c
^
|
@@ -428,5 +428,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testzzuf/test_put.c
^
|
@@ -376,5 +376,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testzzuf/test_put_chunked.c
^
|
@@ -391,5 +391,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/src/testzzuf/test_put_large.c
^
|
@@ -399,5 +399,5 @@
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
- return errorCount != 0; /* 0 == pass */
+ return (0 == errorCount) ? 0 : 1; /* 0 == pass */
}
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS-Any-Version/.gitignore
^
|
@@ -0,0 +1,6 @@
+/Output
+/libmicrohttpd
+/hellobrowser
+/largepost
+/simplepost
+/.vs
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS-Any-Version/hellobrowser.vcxproj
^
|
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug-dll|Win32">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-dll|x64">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|Win32">
+ <Configuration>Debug-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|x64">
+ <Configuration>Debug-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|Win32">
+ <Configuration>Release-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|x64">
+ <Configuration>Release-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|Win32">
+ <Configuration>Release-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|x64">
+ <Configuration>Release-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <Import Project="$(MhdW32Common)\hellobrowser-files.vcxproj" />
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>16.0</VCProjectVersion>
+ <ProjectGuid>{310F39BD-A2D6-44FF-8344-37ADD0524CBD}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>hellobrowser</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries Condition="$(Configuration.StartsWith('Debug'))">true</UseDebugLibraries>
+ <UseDebugLibraries Condition="! $(Configuration.StartsWith('Debug'))">false</UseDebugLibraries>
+ <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
+ <WholeProgramOptimization Condition="! $(Configuration.StartsWith('Debug'))">true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <Import Project="$(MhdW32Common)common-build-settings.vcxproj" />
+ <Import Project="$(MhdW32Common)apps-build-settings.vcxproj" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <ClCompile />
+ <Link />
+ <ProjectReference />
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS-Any-Version/hellobrowser.vcxproj.filters
^
|
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <Import Project="$(MhdW32Common)hellobrowser-filters.vcxproj" />
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS-Any-Version/largepost.vcxproj
^
|
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug-dll|Win32">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-dll|x64">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|Win32">
+ <Configuration>Debug-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|x64">
+ <Configuration>Debug-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|Win32">
+ <Configuration>Release-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|x64">
+ <Configuration>Release-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|Win32">
+ <Configuration>Release-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|x64">
+ <Configuration>Release-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <Import Project="$(MhdW32Common)\largepost-files.vcxproj" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{77A27E6D-9A39-40B8-961B-40E63DB7FA65}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>largepost</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries Condition="$(Configuration.StartsWith('Debug'))">true</UseDebugLibraries>
+ <UseDebugLibraries Condition="! $(Configuration.StartsWith('Debug'))">false</UseDebugLibraries>
+ <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
+ <WholeProgramOptimization Condition="! $(Configuration.StartsWith('Debug'))">true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <Import Project="$(MhdW32Common)common-build-settings.vcxproj" />
+ <Import Project="$(MhdW32Common)apps-build-settings.vcxproj" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <ClCompile />
+ <Link />
+ <ProjectReference />
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS-Any-Version/libmicrohttpd.sln
^
|
@@ -0,0 +1,102 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+MinimumVisualStudioVersion = 10.0.40219.1
+
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hellobrowser", "hellobrowser.vcxproj", "{310F39BD-A2D6-44FF-8344-37ADD0524CBD}"
+ ProjectSection(ProjectDependencies) = postProject
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A} = {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmicrohttpd", "libmicrohttpd.vcxproj", "{9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simplepost", "simplepost.vcxproj", "{294D5317-E983-4682-8DB5-678EA4645E11}"
+ ProjectSection(ProjectDependencies) = postProject
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A} = {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "largepost", "largepost.vcxproj", "{77A27E6D-9A39-40B8-961B-40E63DB7FA65}"
+ ProjectSection(ProjectDependencies) = postProject
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A} = {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug-dll|Win32 = Debug-dll|Win32
+ Debug-dll|x64 = Debug-dll|x64
+ Debug-static|Win32 = Debug-static|Win32
+ Debug-static|x64 = Debug-static|x64
+ Release-dll|Win32 = Release-dll|Win32
+ Release-dll|x64 = Release-dll|x64
+ Release-static|Win32 = Release-static|Win32
+ Release-static|x64 = Release-static|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-dll|Win32.ActiveCfg = Debug-dll|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-dll|Win32.Build.0 = Debug-dll|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-dll|x64.ActiveCfg = Debug-dll|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-dll|x64.Build.0 = Debug-dll|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-static|Win32.ActiveCfg = Debug-static|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-static|Win32.Build.0 = Debug-static|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-static|x64.ActiveCfg = Debug-static|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-static|x64.Build.0 = Debug-static|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-dll|Win32.ActiveCfg = Release-dll|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-dll|Win32.Build.0 = Release-dll|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-dll|x64.ActiveCfg = Release-dll|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-dll|x64.Build.0 = Release-dll|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-static|Win32.ActiveCfg = Release-static|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-static|Win32.Build.0 = Release-static|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-static|x64.ActiveCfg = Release-static|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-static|x64.Build.0 = Release-static|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-dll|Win32.ActiveCfg = Debug-dll|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-dll|Win32.Build.0 = Debug-dll|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-dll|x64.ActiveCfg = Debug-dll|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-dll|x64.Build.0 = Debug-dll|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-static|Win32.ActiveCfg = Debug-static|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-static|Win32.Build.0 = Debug-static|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-static|x64.ActiveCfg = Debug-static|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-static|x64.Build.0 = Debug-static|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-dll|Win32.ActiveCfg = Release-dll|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-dll|Win32.Build.0 = Release-dll|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-dll|x64.ActiveCfg = Release-dll|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-dll|x64.Build.0 = Release-dll|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-static|Win32.ActiveCfg = Release-static|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-static|Win32.Build.0 = Release-static|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-static|x64.ActiveCfg = Release-static|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-static|x64.Build.0 = Release-static|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-dll|Win32.ActiveCfg = Debug-dll|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-dll|Win32.Build.0 = Debug-dll|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-dll|x64.ActiveCfg = Debug-dll|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-dll|x64.Build.0 = Debug-dll|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-static|Win32.ActiveCfg = Debug-static|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-static|Win32.Build.0 = Debug-static|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-static|x64.ActiveCfg = Debug-static|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-static|x64.Build.0 = Debug-static|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-dll|Win32.ActiveCfg = Release-dll|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-dll|Win32.Build.0 = Release-dll|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-dll|x64.ActiveCfg = Release-dll|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-dll|x64.Build.0 = Release-dll|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-static|Win32.ActiveCfg = Release-static|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-static|Win32.Build.0 = Release-static|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-static|x64.ActiveCfg = Release-static|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-static|x64.Build.0 = Release-static|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-dll|Win32.ActiveCfg = Debug-dll|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-dll|Win32.Build.0 = Debug-dll|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-dll|x64.ActiveCfg = Debug-dll|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-dll|x64.Build.0 = Debug-dll|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-static|Win32.ActiveCfg = Debug-static|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-static|Win32.Build.0 = Debug-static|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-static|x64.ActiveCfg = Debug-static|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-static|x64.Build.0 = Debug-static|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-dll|Win32.ActiveCfg = Release-dll|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-dll|Win32.Build.0 = Release-dll|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-dll|x64.ActiveCfg = Release-dll|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-dll|x64.Build.0 = Release-dll|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-static|Win32.ActiveCfg = Release-static|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-static|Win32.Build.0 = Release-static|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-static|x64.ActiveCfg = Release-static|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-static|x64.Build.0 = Release-static|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS-Any-Version/libmicrohttpd.vcxproj
^
|
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug-dll|Win32">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-dll|x64">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|Win32">
+ <Configuration>Debug-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|x64">
+ <Configuration>Debug-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|Win32">
+ <Configuration>Release-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|x64">
+ <Configuration>Release-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|Win32">
+ <Configuration>Release-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|x64">
+ <Configuration>Release-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <Import Project="$(MhdW32Common)\libmicrohttpd-files.vcxproj" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>libmicrohttpd</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType Condition="$(Configuration.EndsWith('-static'))">StaticLibrary</ConfigurationType>
+ <ConfigurationType Condition="! $(Configuration.EndsWith('-static'))">DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries Condition="$(Configuration.StartsWith('Debug'))">true</UseDebugLibraries>
+ <UseDebugLibraries Condition="! $(Configuration.StartsWith('Debug'))">false</UseDebugLibraries>
+ <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
+ <WholeProgramOptimization Condition="! $(Configuration.StartsWith('Debug'))">true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <Import Project="$(MhdW32Common)common-build-settings.vcxproj" />
+ <Import Project="$(MhdW32Common)libmicrohttpd-build-settings.vcxproj" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <ClCompile />
+ <Link />
+ <Lib />
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS-Any-Version/libmicrohttpd.vcxproj.filters
^
|
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <Import Project="$(MhdW32Common)libmicrohttpd-filters.vcxproj" />
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS-Any-Version/simplepost.vcxproj
^
|
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug-dll|Win32">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-dll|x64">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|Win32">
+ <Configuration>Debug-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|x64">
+ <Configuration>Debug-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|Win32">
+ <Configuration>Release-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|x64">
+ <Configuration>Release-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|Win32">
+ <Configuration>Release-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|x64">
+ <Configuration>Release-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <Import Project="$(MhdW32Common)\simplepost-files.vcxproj" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{294D5317-E983-4682-8DB5-678EA4645E11}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>simplepost</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries Condition="$(Configuration.StartsWith('Debug'))">true</UseDebugLibraries>
+ <UseDebugLibraries Condition="! $(Configuration.StartsWith('Debug'))">false</UseDebugLibraries>
+ <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
+ <WholeProgramOptimization Condition="! $(Configuration.StartsWith('Debug'))">true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <Import Project="$(MhdW32Common)common-build-settings.vcxproj" />
+ <Import Project="$(MhdW32Common)apps-build-settings.vcxproj" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <ClCompile />
+ <Link />
+ <ProjectReference />
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS2022/.gitignore
^
|
@@ -0,0 +1,6 @@
+/Output
+/libmicrohttpd
+/hellobrowser
+/largepost
+/simplepost
+/.vs
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS2022/hellobrowser.vcxproj
^
|
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug-dll|Win32">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-dll|x64">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|Win32">
+ <Configuration>Debug-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|x64">
+ <Configuration>Debug-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|Win32">
+ <Configuration>Release-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|x64">
+ <Configuration>Release-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|Win32">
+ <Configuration>Release-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|x64">
+ <Configuration>Release-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <Import Project="$(MhdW32Common)\hellobrowser-files.vcxproj" />
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>16.0</VCProjectVersion>
+ <ProjectGuid>{310F39BD-A2D6-44FF-8344-37ADD0524CBD}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>hellobrowser</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries Condition="$(Configuration.StartsWith('Debug'))">true</UseDebugLibraries>
+ <UseDebugLibraries Condition="! $(Configuration.StartsWith('Debug'))">false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization Condition="! $(Configuration.StartsWith('Debug'))">true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <Import Project="$(MhdW32Common)common-build-settings.vcxproj" />
+ <Import Project="$(MhdW32Common)apps-build-settings.vcxproj" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <ClCompile />
+ <Link />
+ <ProjectReference />
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS2022/hellobrowser.vcxproj.filters
^
|
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <Import Project="$(MhdW32Common)hellobrowser-filters.vcxproj" />
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS2022/largepost.vcxproj
^
|
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug-dll|Win32">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-dll|x64">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|Win32">
+ <Configuration>Debug-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|x64">
+ <Configuration>Debug-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|Win32">
+ <Configuration>Release-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|x64">
+ <Configuration>Release-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|Win32">
+ <Configuration>Release-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|x64">
+ <Configuration>Release-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <Import Project="$(MhdW32Common)\largepost-files.vcxproj" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{77A27E6D-9A39-40B8-961B-40E63DB7FA65}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>largepost</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries Condition="$(Configuration.StartsWith('Debug'))">true</UseDebugLibraries>
+ <UseDebugLibraries Condition="! $(Configuration.StartsWith('Debug'))">false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization Condition="! $(Configuration.StartsWith('Debug'))">true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <Import Project="$(MhdW32Common)common-build-settings.vcxproj" />
+ <Import Project="$(MhdW32Common)apps-build-settings.vcxproj" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <ClCompile />
+ <Link />
+ <ProjectReference />
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS2022/libmicrohttpd.sln
^
|
@@ -0,0 +1,104 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31815.197
+MinimumVisualStudioVersion = 10.0.40219.1
+
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hellobrowser", "hellobrowser.vcxproj", "{310F39BD-A2D6-44FF-8344-37ADD0524CBD}"
+ ProjectSection(ProjectDependencies) = postProject
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A} = {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmicrohttpd", "libmicrohttpd.vcxproj", "{9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simplepost", "simplepost.vcxproj", "{294D5317-E983-4682-8DB5-678EA4645E11}"
+ ProjectSection(ProjectDependencies) = postProject
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A} = {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "largepost", "largepost.vcxproj", "{77A27E6D-9A39-40B8-961B-40E63DB7FA65}"
+ ProjectSection(ProjectDependencies) = postProject
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A} = {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug-dll|Win32 = Debug-dll|Win32
+ Debug-dll|x64 = Debug-dll|x64
+ Debug-static|Win32 = Debug-static|Win32
+ Debug-static|x64 = Debug-static|x64
+ Release-dll|Win32 = Release-dll|Win32
+ Release-dll|x64 = Release-dll|x64
+ Release-static|Win32 = Release-static|Win32
+ Release-static|x64 = Release-static|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-dll|Win32.ActiveCfg = Debug-dll|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-dll|Win32.Build.0 = Debug-dll|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-dll|x64.ActiveCfg = Debug-dll|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-dll|x64.Build.0 = Debug-dll|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-static|Win32.ActiveCfg = Debug-static|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-static|Win32.Build.0 = Debug-static|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-static|x64.ActiveCfg = Debug-static|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Debug-static|x64.Build.0 = Debug-static|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-dll|Win32.ActiveCfg = Release-dll|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-dll|Win32.Build.0 = Release-dll|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-dll|x64.ActiveCfg = Release-dll|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-dll|x64.Build.0 = Release-dll|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-static|Win32.ActiveCfg = Release-static|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-static|Win32.Build.0 = Release-static|Win32
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-static|x64.ActiveCfg = Release-static|x64
+ {9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}.Release-static|x64.Build.0 = Release-static|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-dll|Win32.ActiveCfg = Debug-dll|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-dll|Win32.Build.0 = Debug-dll|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-dll|x64.ActiveCfg = Debug-dll|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-dll|x64.Build.0 = Debug-dll|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-static|Win32.ActiveCfg = Debug-static|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-static|Win32.Build.0 = Debug-static|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-static|x64.ActiveCfg = Debug-static|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Debug-static|x64.Build.0 = Debug-static|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-dll|Win32.ActiveCfg = Release-dll|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-dll|Win32.Build.0 = Release-dll|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-dll|x64.ActiveCfg = Release-dll|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-dll|x64.Build.0 = Release-dll|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-static|Win32.ActiveCfg = Release-static|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-static|Win32.Build.0 = Release-static|Win32
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-static|x64.ActiveCfg = Release-static|x64
+ {310F39BD-A2D6-44FF-8344-37ADD0524CBD}.Release-static|x64.Build.0 = Release-static|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-dll|Win32.ActiveCfg = Debug-dll|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-dll|Win32.Build.0 = Debug-dll|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-dll|x64.ActiveCfg = Debug-dll|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-dll|x64.Build.0 = Debug-dll|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-static|Win32.ActiveCfg = Debug-static|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-static|Win32.Build.0 = Debug-static|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-static|x64.ActiveCfg = Debug-static|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Debug-static|x64.Build.0 = Debug-static|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-dll|Win32.ActiveCfg = Release-dll|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-dll|Win32.Build.0 = Release-dll|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-dll|x64.ActiveCfg = Release-dll|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-dll|x64.Build.0 = Release-dll|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-static|Win32.ActiveCfg = Release-static|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-static|Win32.Build.0 = Release-static|Win32
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-static|x64.ActiveCfg = Release-static|x64
+ {294D5317-E983-4682-8DB5-678EA4645E11}.Release-static|x64.Build.0 = Release-static|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-dll|Win32.ActiveCfg = Debug-dll|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-dll|Win32.Build.0 = Debug-dll|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-dll|x64.ActiveCfg = Debug-dll|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-dll|x64.Build.0 = Debug-dll|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-static|Win32.ActiveCfg = Debug-static|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-static|Win32.Build.0 = Debug-static|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-static|x64.ActiveCfg = Debug-static|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Debug-static|x64.Build.0 = Debug-static|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-dll|Win32.ActiveCfg = Release-dll|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-dll|Win32.Build.0 = Release-dll|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-dll|x64.ActiveCfg = Release-dll|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-dll|x64.Build.0 = Release-dll|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-static|Win32.ActiveCfg = Release-static|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-static|Win32.Build.0 = Release-static|Win32
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-static|x64.ActiveCfg = Release-static|x64
+ {77A27E6D-9A39-40B8-961B-40E63DB7FA65}.Release-static|x64.Build.0 = Release-static|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS2022/libmicrohttpd.vcxproj
^
|
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug-dll|Win32">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-dll|x64">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|Win32">
+ <Configuration>Debug-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|x64">
+ <Configuration>Debug-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|Win32">
+ <Configuration>Release-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|x64">
+ <Configuration>Release-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|Win32">
+ <Configuration>Release-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|x64">
+ <Configuration>Release-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <Import Project="$(MhdW32Common)\libmicrohttpd-files.vcxproj" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{9CFB0342-A9E7-483E-BEE5-A1DE22584C5A}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>libmicrohttpd</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType Condition="$(Configuration.EndsWith('-static'))">StaticLibrary</ConfigurationType>
+ <ConfigurationType Condition="! $(Configuration.EndsWith('-static'))">DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries Condition="$(Configuration.StartsWith('Debug'))">true</UseDebugLibraries>
+ <UseDebugLibraries Condition="! $(Configuration.StartsWith('Debug'))">false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization Condition="! $(Configuration.StartsWith('Debug'))">true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <Import Project="$(MhdW32Common)common-build-settings.vcxproj" />
+ <Import Project="$(MhdW32Common)libmicrohttpd-build-settings.vcxproj" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <ClCompile />
+ <Link />
+ <Lib />
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS2022/libmicrohttpd.vcxproj.filters
^
|
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <Import Project="$(MhdW32Common)libmicrohttpd-filters.vcxproj" />
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Added |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/VS2022/simplepost.vcxproj
^
|
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(SolutionDir)..\common\vs_dirs.props" />
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug-dll|Win32">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-dll|x64">
+ <Configuration>Debug-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|Win32">
+ <Configuration>Debug-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug-static|x64">
+ <Configuration>Debug-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|Win32">
+ <Configuration>Release-dll</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-dll|x64">
+ <Configuration>Release-dll</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|Win32">
+ <Configuration>Release-static</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release-static|x64">
+ <Configuration>Release-static</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <Import Project="$(MhdW32Common)\simplepost-files.vcxproj" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{294D5317-E983-4682-8DB5-678EA4645E11}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>simplepost</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries Condition="$(Configuration.StartsWith('Debug'))">true</UseDebugLibraries>
+ <UseDebugLibraries Condition="! $(Configuration.StartsWith('Debug'))">false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization Condition="! $(Configuration.StartsWith('Debug'))">true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <Import Project="$(MhdW32Common)common-build-settings.vcxproj" />
+ <Import Project="$(MhdW32Common)apps-build-settings.vcxproj" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <ClCompile />
+ <Link />
+ <ProjectReference />
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/common/MHD_config.h
^
|
@@ -9,13 +9,13 @@
/* Define if MS VC compiler is used */
#define MSVC 1
-/* Define that MS VC does not support VLAs */
#ifndef __clang__
+/* Define that MS VC does not support VLAs */
+#ifndef __STDC_NO_VLA__
#define __STDC_NO_VLA__ 1
-#endif
-
+#endif /* ! __STDC_NO_VLA__ */
+#else
/* If clang is used then variable-length arrays are supported. */
-#ifdef __clang__
#define HAVE_C_VARARRAYS 1
#endif
@@ -33,6 +33,31 @@
#define MHD_HAVE___BUILTIN_BSWAP64 1
#endif /* __clang__ */
+/* The size of `size_t', as computed by sizeof. */
+#if defined(_M_X64) || defined(_M_AMD64) || defined(_M_ARM64) || defined(_WIN64)
+#define SIZEOF_SIZE_T 8
+#else /* ! _WIN64 */
+#define SIZEOF_SIZE_T 4
+#endif /* ! _WIN64 */
+
+/* The size of `tv_sec' member of `struct timeval', as computed by sizeof */
+#define SIZEOF_STRUCT_TIMEVAL_TV_SEC 4
+
+/* The size of `uint64_t', as computed by sizeof. */
+#define SIZEOF_UINT64_T 8
+
+/* The size of `unsigned int', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_INT 4
+
+/* The size of `unsigned long long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG_LONG 8
+
+/* Define to supported 'noreturn' function declaration */
+#if defined(_STDC_VERSION__) && (__STDC_VERSION__ + 0) >= 201112L
+#define _MHD_NORETURN _Noreturn
+#else /* before C11 */
+#define _MHD_NORETURN __declspec(noreturn)
+#endif /* before C11 */
/* *** MHD configuration *** */
/* Undef to disable feature */
@@ -65,7 +90,7 @@
#ifndef _WIN32_WINNT
/* MHD supports Windows XP and later W32 systems*/
-#define _WIN32_WINNT 0x0501
+#define _WIN32_WINNT 0x0600
#endif /* _WIN32_WINNT */
/* winsock poll is available only on Vista and later */
@@ -128,6 +153,14 @@
/* Define to 1 if your compiler supports __func__ magic-macro. */
#define HAVE___FUNC__ 1
+#if _MSC_VER + 0 >= 1900 /* VS 2015 and later */
+#if defined(_STDC_VERSION__) && (__STDC_VERSION__ + 0) >= 201112L
+/* Define to 1 if your compiler supports 'alignof()' */
+#define HAVE_C_ALIGNOF 1
+/* Define to 1 if you have the <stdalign.h> header file. */
+#define HAVE_STDALIGN_H 1
+#endif /* C11 */
+#endif /* VS 2015 and later */
/* *** Headers information *** */
/* Not really important as not used by code currently */
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/common/common-build-settings.vcxproj
^
|
@@ -32,6 +32,7 @@
<TreatSpecificWarningsAsErrors>4013</TreatSpecificWarningsAsErrors>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <LanguageStandard_C Condition="'%(ClCompile.LanguageStandard_C)' != ''">stdc17</LanguageStandard_C>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/common/libmicrohttpd-files.vcxproj
^
|
@@ -21,7 +21,9 @@
<ClCompile Include="$(MhdSrc)microhttpd\mhd_send.c" />
<ClCompile Include="$(MhdSrc)microhttpd\mhd_sockets.c" />
<ClCompile Include="$(MhdSrc)microhttpd\mhd_itc.c" />
- <ClCompile Include="$(MhdSrc)microhttpd\mhd_compat.c" />
+ <ClCompile Include="$(MhdSrc)microhttpd\mhd_compat.c">
+ <ExcludedFromBuild Condition="'$(PlatformToolsetVersion)'>='140'">true</ExcludedFromBuild>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(MhdSrc)include\autoinit_funcs.h" />
@@ -35,6 +37,7 @@
<ClInclude Include="$(MhdSrc)microhttpd\sha256.h" />
<ClInclude Include="$(MhdSrc)microhttpd\memorypool.h" />
<ClInclude Include="$(MhdSrc)microhttpd\mhd_assert.h" />
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_align.h" />
<ClInclude Include="$(MhdSrc)microhttpd\mhd_bithelpers.h" />
<ClInclude Include="$(MhdSrc)microhttpd\mhd_byteorder.h" />
<ClInclude Include="$(MhdSrc)microhttpd\mhd_limits.h" />
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/common/libmicrohttpd-filters.vcxproj
^
|
@@ -5,11 +5,15 @@
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
- <Filter Include="Header Files">
+ <Filter Include="Internal Headers">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
- <Filter Include="Resource Files">
+ <Filter Include="Public Headers">
+ <UniqueIdentifier>{ec88d605-3383-4989-8e25-bc8efa824720}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
@@ -19,20 +23,91 @@
</Filter>
</ItemGroup>
<ItemGroup>
- <ClInclude Include="$(MhdSrc)include\autoinit_funcs.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="$(MhdSrc)include\microhttpd.h">
- <Filter>Header Files</Filter>
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="$(MhdSrc)include\autoinit_funcs.h">
+ <Filter>Internal Headers</Filter>
</ClInclude>
<ClInclude Include="$(MhdSrc)include\platform.h">
- <Filter>Header Files</Filter>
+ <Filter>Internal Headers</Filter>
</ClInclude>
<ClInclude Include="$(MhdSrc)include\mhd_options.h">
- <Filter>Header Files</Filter>
+ <Filter>Internal Headers</Filter>
</ClInclude>
<ClInclude Include="$(MhdW32Common)MHD_config.h">
- <Filter>Header Files</Filter>
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\base64.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\connection.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\internal.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\md5.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\sha256.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\memorypool.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\response.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\tsearch.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_assert.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_align.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_limits.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_bithelpers.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_byteorder.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_mono_clock.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\sysfdsetsize.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_str.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_threads.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_locks.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_sockets.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_itc_types.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_itc.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_send.h">
+ <Filter>Internal Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MhdSrc)microhttpd\mhd_compat.h">
+ <Filter>Internal Headers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
@@ -60,9 +135,6 @@
<ClCompile Include="$(MhdSrc)microhttpd\sha256.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClInclude Include="$(MhdSrc)microhttpd\sha256.h">
- <Filter>Source Files</Filter>
- </ClInclude>
<ClCompile Include="$(MhdSrc)microhttpd\memorypool.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -81,87 +153,24 @@
<ClCompile Include="$(MhdSrc)microhttpd\mhd_mono_clock.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClInclude Include="$(MhdSrc)microhttpd\base64.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\connection.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\internal.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\md5.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\memorypool.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\response.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\tsearch.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_assert.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_limits.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_bithelpers.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_byteorder.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_mono_clock.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\sysfdsetsize.h">
- <Filter>Source Files</Filter>
- </ClInclude>
<ClCompile Include="$(MhdSrc)microhttpd\sysfdsetsize.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_str.h">
- <Filter>Source Files</Filter>
- </ClInclude>
<ClCompile Include="$(MhdSrc)microhttpd\mhd_str.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_threads.h">
- <Filter>Source Files</Filter>
- </ClInclude>
<ClCompile Include="$(MhdSrc)microhttpd\mhd_threads.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_locks.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_sockets.h">
- <Filter>Source Files</Filter>
- </ClInclude>
<ClCompile Include="$(MhdSrc)microhttpd\mhd_sockets.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_itc_types.h">
- <Filter>Source Files</Filter>
- </ClInclude>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_itc.h">
- <Filter>Source Files</Filter>
- </ClInclude>
<ClCompile Include="$(MhdSrc)microhttpd\mhd_itc.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_send.h">
- <Filter>Source Files</Filter>
- </ClInclude>
<ClCompile Include="$(MhdSrc)microhttpd\mhd_send.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClInclude Include="$(MhdSrc)microhttpd\mhd_compat.h">
- <Filter>Source Files</Filter>
- </ClInclude>
<ClCompile Include="$(MhdSrc)microhttpd\mhd_compat.c">
<Filter>Source Files</Filter>
</ClCompile>
|
[-]
[+]
|
Changed |
_service:tar_git:libmicrohttpd-0.9.75.tar.gz/libmicrohttpd/w32/common/microhttpd_dll_res_vc.rc.in
^
|
@@ -31,7 +31,7 @@
VALUE "OriginalFilename", "libmicrohttpd-dll.dll\0"
#endif
VALUE "CompanyName", "Free Software Foundation\0"
- VALUE "LegalCopyright", "Copyright (C) 2007-2020 Christian Grothoff, Evgeny Grin and project contributors\0"
+ VALUE "LegalCopyright", "Copyright (C) 2007-2021 Christian Grothoff, Evgeny Grin, and project contributors\0"
VALUE "Comments", "http://www.gnu.org/software/libmicrohttpd/\0"
END
END
|