Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • phoenix/public/h2o
1 result
Show changes
Commits on Source (903)
Showing
with 1628 additions and 455 deletions
......@@ -23,6 +23,8 @@ jobs:
command: make -f misc/docker-ci/check.mk ossl1.1.1 BUILD_ARGS=-j6 TEST_ENV='TEST_JOBS=4 TEST_PLATFORM=github-actions'
- name: OpenSSL 3.0 (Ubuntu 22.04 / DTrace)
command: make -f misc/docker-ci/check.mk ossl3.0 BUILD_ARGS=-j6 TEST_ENV='TEST_JOBS=4 TEST_PLATFORM=github-actions'
- name: boringssl
command: make -f misc/docker-ci/check.mk boringssl BUILD_ARGS=-j6 TEST_ENV='TEST_JOBS=4 TEST_PLATFORM=github-actions'
- name: ASan (Ubuntu 20.04)
command: make -f misc/docker-ci/check.mk asan BUILD_ARGS=-j6 TEST_ENV='TEST_JOBS=4 TEST_PLATFORM=github-actions'
- name: Coverage
......
......@@ -15,5 +15,3 @@ xcuserdata
misc/test-ca/demoCA/newcerts/*.pem
tmp/
/doc/workdir/
include/h2o/gitrev.h
/misc/mruby_config.rb.lock
......@@ -21,27 +21,20 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
CMAKE_POLICY(SET CMP0003 NEW)
IF (NOT CMAKE_VERSION VERSION_LESS 3.0)
CMAKE_POLICY(SET CMP0042 NEW)
ENDIF()
PROJECT(h2o)
SET(VERSION_MAJOR "2")
SET(VERSION_MINOR "3")
SET(VERSION_PATCH "0")
SET(VERSION_PRERELEASE "-DEV")
SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${VERSION_PRERELEASE}")
SET(LIBRARY_VERSION_MAJOR "0")
SET(LIBRARY_VERSION_MINOR "16")
SET(LIBRARY_VERSION_PATCH "0")
SET(LIBRARY_VERSION "${LIBRARY_VERSION_MAJOR}.${LIBRARY_VERSION_MINOR}.${LIBRARY_VERSION_PATCH}${VERSION_PRERELEASE}")
SET(LIBRARY_SOVERSION "${LIBRARY_VERSION_MAJOR}.${LIBRARY_VERSION_MINOR}")
INCLUDE(GNUInstallDirs)
INCLUDE(CheckCSourceCompiles)
INCLUDE(CMakePushCheckState)
INCLUDE(ExternalProject)
INCLUDE(deps/picotls/cmake/boringssl-adjust.cmake)
INCLUDE(deps/picotls/cmake/dtrace-utils.cmake)
INCLUDE(deps/picotls/cmake/fusion.cmake)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/include/h2o/version.h)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libh2o.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libh2o.pc @ONLY)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libh2o-evloop.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libh2o-evloop.pc @ONLY)
......@@ -67,6 +60,7 @@ IF ((CMAKE_SYSTEM_NAME STREQUAL "Darwin") AND NOT (DEFINED OPENSSL_ROOT_DIR OR D
ENDIF ()
ENDIF ()
FIND_PACKAGE(OpenSSL REQUIRED)
BORINGSSL_ADJUST()
FIND_PACKAGE(ZLIB REQUIRED)
CHECK_C_SOURCE_COMPILES("
......@@ -143,15 +137,7 @@ IF (WITH_DTRACE)
MESSAGE(STATUS "Enabling USDT support")
ENDIF ()
CMAKE_PUSH_CHECK_STATE()
SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -mavx2 -maes -mpclmul -mvaes -mvpclmulqdq")
CHECK_C_SOURCE_COMPILES("int main(void) {}" CC_HAS_AESNI256)
CMAKE_POP_CHECK_STATE()
IF (CC_HAS_AESNI256)
SET(WITH_FUSION_DEFAULT "ON")
ELSE ()
SET(WITH_FUSION_DEFAULT "OFF")
ENDIF ()
CHECK_FUSION_PREREQUISITES()
OPTION(WITH_FUSION "build with fusion AES-GCM engine" ${WITH_FUSION_DEFAULT})
# KTLS
......@@ -174,6 +160,15 @@ ELSE ()
ENDIF()
OPTION(WITH_KTLS "use Kernel TLS" ${WITH_KTLS_DEFAULT})
# aegis
FIND_PACKAGE(aegis)
IF (aegis_FOUND)
SET(WITH_AEGIS_DEFAULT "ON")
ELSE ()
SET(WITH_AEGIS_DEFAULT "OFF")
ENDIF ()
OPTION(WITH_AEGIS "enable AEGIS (requires libaegis)" ${WITH_AEGIS_DEFAULT})
INCLUDE_DIRECTORIES(
include
deps/cloexec
......@@ -430,7 +425,10 @@ SET(LIB_SOURCE_FILES
lib/handler/expires.c
lib/handler/fastcgi.c
lib/handler/file.c
lib/handler/h2olog.c
lib/handler/headers.c
lib/handler/headers_util.c
lib/handler/http2_debug_state.c
lib/handler/mimemap.c
lib/handler/proxy.c
lib/handler/connect.c
......@@ -440,12 +438,10 @@ SET(LIB_SOURCE_FILES
lib/handler/self_trace.c
lib/handler/server_timing.c
lib/handler/status.c
lib/handler/headers_util.c
lib/handler/status/events.c
lib/handler/status/memory.c
lib/handler/status/requests.c
lib/handler/status/ssl.c
lib/handler/http2_debug_state.c
lib/handler/status/durations.c
lib/handler/configurator/access_log.c
lib/handler/configurator/compress.c
......@@ -453,7 +449,10 @@ SET(LIB_SOURCE_FILES
lib/handler/configurator/expires.c
lib/handler/configurator/fastcgi.c
lib/handler/configurator/file.c
lib/handler/configurator/h2olog.c
lib/handler/configurator/headers.c
lib/handler/configurator/headers_util.c
lib/handler/configurator/http2_debug_state.c
lib/handler/configurator/proxy.c
lib/handler/configurator/redirect.c
lib/handler/configurator/reproxy.c
......@@ -461,8 +460,6 @@ SET(LIB_SOURCE_FILES
lib/handler/configurator/self_trace.c
lib/handler/configurator/server_timing.c
lib/handler/configurator/status.c
lib/handler/configurator/http2_debug_state.c
lib/handler/configurator/headers_util.c
lib/http1.c
......@@ -613,6 +610,20 @@ ELSE ()
ADD_LIBRARY(libh2o-evloop ${LIB_SOURCE_FILES})
ENDIF (WSLAY_FOUND)
EXECUTE_PROCESS(
COMMAND sed -n -E "/#define {1,}H2O_VERSION_BASE .*-DEV\"$/s//YES/p" ${CMAKE_CURRENT_SOURCE_DIR}/include/h2o/version.h
OUTPUT_VARIABLE IS_PRERELEASE
OUTPUT_STRIP_TRAILING_WHITESPACE)
EXECUTE_PROCESS(
COMMAND sed -n -E "/#define {1,}H2O_LIBRARY_VERSION {1,}\"(.*)\"$/s//\\1/p" ${CMAKE_CURRENT_SOURCE_DIR}/include/h2o/version.h
OUTPUT_VARIABLE LIBRARY_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
EXECUTE_PROCESS(
COMMAND sed -n -E "/#define {1,}H2O_LIBRARY_VERSION {1,}\"(.*)\\.[0-9]{1,}\"$/s//\\1/p" ${CMAKE_CURRENT_SOURCE_DIR}/include/h2o/version.h
OUTPUT_VARIABLE LIBRARY_SOVERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
MESSAGE(STATUS "extracted version numbers from ${CMAKE_CURRENT_SOURCE_DIR}/include/h2o/version.h:\n\tprerelease=${IS_PRERELEASE}\n\tlibrary version=${LIBRARY_VERSION}\n\tlibrary soversion=${LIBRARY_SOVERSION}")
SET_TARGET_PROPERTIES(libh2o PROPERTIES
OUTPUT_NAME h2o
VERSION ${LIBRARY_VERSION}
......@@ -745,7 +756,7 @@ IF (WITH_MRUBY)
ADD_CUSTOM_TARGET(mruby
# deps/mruby/tasks/toolchains/clang.rake looks for CC, CXX and LD.
# There are no C++ files in deps/mruby, use the C compiler for linking.
MRUBY_TOOLCHAIN=${MRUBY_TOOLCHAIN} CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} MRUBY_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/misc/mruby_config.rb MRUBY_BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR}/mruby MRUBY_ADDITIONAL_CONFIG=${MRUBY_ADDITIONAL_CONFIG} ruby minirake
MRUBY_TOOLCHAIN=${MRUBY_TOOLCHAIN} CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} MRUBY_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/misc/mruby_config.rb MRUBY_BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR}/mruby MRUBY_ADDITIONAL_CONFIG=${MRUBY_ADDITIONAL_CONFIG} INSTALL_DIR=${CMAKE_CURRENT_BINARY_DIR}/mruby-bin ruby minirake
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/deps/mruby
BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/mruby/host/lib/libmruby.a"
"${CMAKE_CURRENT_BINARY_DIR}/mruby/host/mrbgems/mruby-onig-regexp/onigmo-6.2.0/.libs/libonigmo.a"
......@@ -770,6 +781,9 @@ ENDIF ()
IF (WITH_KTLS)
SET(STANDALONE_COMPILE_FLAGS "${STANDALONE_COMPILE_FLAGS} -DH2O_USE_KTLS=1")
ENDIF ()
IF (WITH_AEGIS)
SET(STANDALONE_COMPILE_FLAGS "${STANDALONE_COMPILE_FLAGS} -DPTLS_HAVE_AEGIS=1")
ENDIF ()
ADD_EXECUTABLE(h2o ${STANDALONE_SOURCE_FILES})
SET_TARGET_PROPERTIES(h2o PROPERTIES COMPILE_FLAGS "${STANDALONE_COMPILE_FLAGS}")
TARGET_INCLUDE_DIRECTORIES(h2o PUBLIC ${OPENSSL_INCLUDE_DIR})
......@@ -783,6 +797,10 @@ IF (WITH_MRUBY)
"m")
ADD_DEPENDENCIES(h2o mruby)
ENDIF (WITH_MRUBY)
IF (WITH_AEGIS)
TARGET_INCLUDE_DIRECTORIES(h2o PUBLIC ${aegis_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(h2o ${aegis_LIBRARIES})
ENDIF ()
TARGET_LINK_LIBRARIES(h2o ${EXTRA_LIBS})
INSTALL(TARGETS h2o
......@@ -791,6 +809,8 @@ INSTALL(TARGETS h2o
IF (NOT WITHOUT_LIBS)
INSTALL(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
INSTALL(DIRECTORY deps/picotls/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
INSTALL(DIRECTORY deps/quicly/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
IF (LIBUV_FOUND)
INSTALL(FILES "${CMAKE_BINARY_DIR}/libh2o.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
ENDIF ()
......@@ -873,9 +893,9 @@ ADD_CUSTOM_TARGET(doc-publish ${CMAKE_MAKE_PROGRAM} -f ../misc/doc.mk publish BI
GET_FILENAME_COMPONENT(EXTERNALPROJECT_SSL_ROOT_DIR ${OPENSSL_INCLUDE_DIR} PATH)
IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/misc/h2get/CMakeLists.txt AND RUBY AND BISON)
IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/misc/h2get/CMakeLists.txt AND RUBY AND BISON AND NOT (OPENSSL_VERSION VERSION_LESS "1.1.1"))
ExternalProject_Add(h2get
CONFIGURE_COMMAND ${CMAKE_COMMAND} -DH2GET_SSL_ROOT_DIR=${EXTERNALPROJECT_SSL_ROOT_DIR} "-DCMAKE_C_FLAGS=-include ${CMAKE_SOURCE_DIR}/include/h2o/openssl_backport.h" ${CMAKE_CURRENT_SOURCE_DIR}/misc/h2get
CONFIGURE_COMMAND ${CMAKE_COMMAND} -DH2GET_SSL_ROOT_DIR=${EXTERNALPROJECT_SSL_ROOT_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/misc/h2get
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/misc/h2get
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/h2get_bin
BUILD_COMMAND ${CMAKE_MAKE_PROGRAM}
......@@ -1044,7 +1064,7 @@ IF (WITH_H2OLOG)
CXX_STANDARD 11
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
COMPILE_FLAGS "-DH2O_USE_LIBUV=0"
COMPILE_FLAGS "-DH2O_USE_LIBUV=0 -DBORINGSSL_NO_CXX"
)
TARGET_LINK_LIBRARIES(h2olog "${LIBBCC_LIBRARIES}")
......@@ -1057,11 +1077,11 @@ IF (WITH_H2OLOG)
ENDIF (WITH_H2OLOG)
# gitrev
IF ("${VERSION_PRERELEASE}" STREQUAL "-DEV")
IF (IS_PRERELEASE)
# To ask generate_gitrev.pl whether or not a gitrev is available,
# since only the script knows how to calculate the gitrev.
EXECUTE_PROCESS(
COMMAND perl ${CMAKE_CURRENT_SOURCE_DIR}/misc/generate_gitrev.pl
COMMAND perl ${CMAKE_CURRENT_SOURCE_DIR}/misc/generate_gitrev.pl ${CMAKE_CURRENT_BINARY_DIR}/h2o-gitrev.h
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GITREV_RESULT_CODE
OUTPUT_QUIET
......@@ -1071,7 +1091,7 @@ IF ("${VERSION_PRERELEASE}" STREQUAL "-DEV")
IF (GITREV_RESULT_CODE EQUAL 0)
ADD_DEFINITIONS("-DH2O_HAS_GITREV_H")
ADD_CUSTOM_TARGET(gitrev ALL
COMMAND perl ${CMAKE_CURRENT_SOURCE_DIR}/misc/generate_gitrev.pl
COMMAND perl ${CMAKE_CURRENT_SOURCE_DIR}/misc/generate_gitrev.pl ${CMAKE_CURRENT_BINARY_DIR}/h2o-gitrev.h
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
ADD_DEPENDENCIES(libh2o gitrev)
ADD_DEPENDENCIES(libh2o-evloop gitrev)
......
......@@ -5,3 +5,17 @@ The h2o project team welcomes security reports and is committed to providing pro
## Security advisories
Remediation of security vulnerabilities is prioritized by the project team. The project team endeavors to coordinate remediation with third-party stakeholders, and is committed to transparency in the disclosure process. The h2o team announces security issues via [Github Release notes](https://github.com/h2o/h2o/releases) as well as [the h2o website](h2o.examp1e.net) on a best-effort basis.
## Vulnerability Disclosure Policy
Once the report has been acknowledged by the h2o maintainer team, the timeline window to public disclosure will start.
* Timeline window to public disclosure is 90 days long.
* The h2o maintainer team will remediate the vulnerability before the 90 day window closes.
* There will be a 14-day grace period AFTER the 90 day window, in which the h2o maintainer team can negotiate to make the report publicly available.
Example: The 90 day due date falls on a holiday for the h2o maintainers. The h2o maintainers can negotiate with the reporter to move the disclosure to 4 days after the 90 day due date.
* The exact time (in UTC) and date of public disclosure will be agreed upon by the h2o maintainers and the reporter.
......@@ -106,24 +106,9 @@ struct freelist {
struct freelist *next;
};
static uint64_t ullog2(uint64_t x)
static int ullog2(uint64_t x)
{
static const uint64_t debruijn_magic = 0x022fdd63cc95386dULL;
static const uint64_t magic_table[] = {
0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12,
};
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
x |= (x >> 32);
return (magic_table[((x & ~(x>>1))*debruijn_magic)>>58]);
return 63 - __builtin_clzll(x);
}
struct gkc_tuple {
......@@ -265,11 +250,7 @@ static uint64_t band(struct gkc_summary *s, uint64_t delta)
diff = 1 + (s->epsilon * s->nr_elems * 2) - delta;
if (diff == 1) {
return 0;
} else {
return ullog2(diff)/ullog2(2);
}
return ullog2(diff);
}
static void gkc_compress(struct gkc_summary *s)
......
This diff is collapsed.
......@@ -26,6 +26,10 @@
#include <sys/un.h>
#include <openssl/engine.h>
#ifdef __FreeBSD__
#include <pthread_np.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
......@@ -54,6 +58,8 @@ typedef struct st_neverbleed_iobuf_t {
char *start;
char *end;
size_t capacity;
struct st_neverbleed_iobuf_t *next;
unsigned processing : 1;
} neverbleed_iobuf_t;
/**
......@@ -69,6 +75,24 @@ int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char
*/
int neverbleed_setuidgid(neverbleed_t *nb, const char *user, int change_socket_ownership);
/**
* builds a digestsign request
*/
void neverbleed_start_digestsign(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const EVP_MD *md, const void *input, size_t len,
int rsa_pss);
/**
* parses a digestsign response
*/
void neverbleed_finish_digestsign(neverbleed_iobuf_t *buf, void **digest, size_t *digest_len);
/**
* builds a RSA decrypt request
*/
void neverbleed_start_decrypt(neverbleed_iobuf_t *buf, EVP_PKEY *pkey, const void *input, size_t len);
/**
* parses a decrypt response
*/
void neverbleed_finish_decrypt(neverbleed_iobuf_t *buf, void **digest, size_t *digest_len);
#if NEVERBLEED_HAS_PTHREAD_SETAFFINITY_NP
/**
* set the cpu affinity for the neverbleed thread (returns 0 if successful)
......@@ -82,18 +106,29 @@ int neverbleed_setaffinity(neverbleed_t *nb, NEVERBLEED_CPU_SET_T *cpuset);
*/
extern void (*neverbleed_post_fork_cb)(void);
/**
* An optional callback used for replacing `expbuf_send`; i.e., the logic that sends the request and receives the response. The
* callback returns a boolean indicating if it handled the task. It may return false to delagate the task back to the default logic.
* An optional callback used for replacing `iobuf_transaction`; i.e., the logic that sends the request and receives the response.
*
* If `responseless` equals `1`, the ownership of stack-allocated `req` is given to the callback. In this case, `req` must be free'd using `neverbleed_iobuf_dispose`
*/
extern void (*neverbleed_transaction_cb)(neverbleed_iobuf_t *);
extern void (*neverbleed_transaction_cb)(neverbleed_iobuf_t *req, int responseless);
typedef void (*neverbleed_cb)(int);
int neverbleed_get_fd(neverbleed_t *nb);
static size_t neverbleed_iobuf_size(neverbleed_iobuf_t *buf);
void neverbleed_iobuf_dispose(neverbleed_iobuf_t *buf);
void neverbleed_transaction_read(neverbleed_t *nb, neverbleed_iobuf_t *buf);
void neverbleed_transaction_write(neverbleed_t *nb, neverbleed_iobuf_t *buf);
/**
* if set to a non-zero value, RSA operations are offloaded
*/
extern enum neverbleed_offload_type {
NEVERBLEED_OFFLOAD_OFF = 0,
NEVERBLEED_OFFLOAD_QAT_ON,
NEVERBLEED_OFFLOAD_QAT_AUTO,
} neverbleed_offload;
/* inline function definitions */
inline size_t neverbleed_iobuf_size(neverbleed_iobuf_t *buf)
......
......@@ -29,8 +29,8 @@
#include <openssl/opensslconf.h>
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_EC) \
&& (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2090100fL)
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_EC) && \
(!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2090100fL)
#define NEVERBLEED_TEST_ECDSA
#endif
......
picohttpparser.* ident
name: CI
on:
push:
branches: [ "master" ]
pull_request:
workflow_dispatch:
jobs:
build:
strategy:
fail-fast: false
matrix:
cc:
- gcc
- clang
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: make test
run: make test CC=${{ matrix.cc }}
test-bin
xcuserdata
*.xccheckout
.DS_Store
language: c
compiler:
- gcc
- clang
script:
- make test
......@@ -25,14 +25,16 @@
CC?=gcc
PROVE?=prove
CFLAGS=-Wall -fsanitize=address,undefined
TEST_ENV="UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1"
all:
test: test-bin
$(PROVE) -v ./test-bin
env $(TEST_ENV) $(PROVE) -v ./test-bin
test-bin: picohttpparser.c picotest/picotest.c test.c
$(CC) -Wall $(CFLAGS) $(LDFLAGS) -o $@ $^
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
clean:
rm -f test-bin
......
......@@ -36,8 +36,6 @@
#endif
#include "picohttpparser.h"
/* $Id$ */
#if __GNUC__ >= 3
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
......@@ -73,9 +71,9 @@
#define ADVANCE_TOKEN(tok, toklen) \
do { \
const char *tok_start = buf; \
static const char ALIGNED(16) ranges2[] = "\000\040\177\177"; \
static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
int found2; \
buf = findchar_fast(buf, buf_end, ranges2, sizeof(ranges2) - 1, &found2); \
buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
if (!found2) { \
CHECK_EOF(); \
} \
......@@ -138,15 +136,11 @@ static const char *get_token_to_eol(const char *buf, const char *buf_end, const
const char *token_start = buf;
#ifdef __SSE4_2__
static const char ranges1[] = "\0\010"
/* allow HT */
"\012\037"
/* allow SP and up to but not including DEL */
"\177\177"
/* allow chars w. MSB set */
;
static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */
"\012\037" /* allow SP and up to but not including DEL */
"\177\177"; /* allow chars w. MSB set */
int found;
buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
if (found)
goto FOUND_CTL;
#else
......@@ -247,6 +241,41 @@ static const char *is_complete(const char *buf, const char *buf_end, size_t last
*valp_ += res_; \
} while (0)
/* returned pointer is always within [buf, buf_end), or null */
static const char *parse_token(const char *buf, const char *buf_end, const char **token, size_t *token_len, char next_char,
int *ret)
{
/* We use pcmpestri to detect non-token characters. This instruction can take no more than eight character ranges (8*2*8=128
* bits that is the size of a SSE register). Due to this restriction, characters `|` and `~` are handled in the slow loop. */
static const char ALIGNED(16) ranges[] = "\x00 " /* control chars and up to SP */
"\"\"" /* 0x22 */
"()" /* 0x28,0x29 */
",," /* 0x2c */
"//" /* 0x2f */
":@" /* 0x3a-0x40 */
"[]" /* 0x5b-0x5d */
"{\xff"; /* 0x7b-0xff */
const char *buf_start = buf;
int found;
buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found);
if (!found) {
CHECK_EOF();
}
while (1) {
if (*buf == next_char) {
break;
} else if (!token_char_map[(unsigned char)*buf]) {
*ret = -1;
return NULL;
}
++buf;
CHECK_EOF();
}
*token = buf_start;
*token_len = buf - buf_start;
return buf;
}
/* returned pointer is always within [buf, buf_end), or null */
static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
{
......@@ -286,31 +315,10 @@ static const char *parse_headers(const char *buf, const char *buf_end, struct ph
if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
/* parsing name, but do not discard SP before colon, see
* http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
headers[*num_headers].name = buf;
static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */
"\"\"" /* 0x22 */
"()" /* 0x28,0x29 */
",," /* 0x2c */
"//" /* 0x2f */
":@" /* 0x3a-0x40 */
"[]" /* 0x5b-0x5d */
"{\377"; /* 0x7b-0xff */
int found;
buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
if (!found) {
CHECK_EOF();
}
while (1) {
if (*buf == ':') {
break;
} else if (!token_char_map[(unsigned char)*buf]) {
*ret = -1;
return NULL;
}
++buf;
CHECK_EOF();
if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, &headers[*num_headers].name_len, ':', ret)) == NULL) {
return NULL;
}
if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) {
if (headers[*num_headers].name_len == 0) {
*ret = -1;
return NULL;
}
......@@ -358,10 +366,18 @@ static const char *parse_request(const char *buf, const char *buf_end, const cha
}
/* parse request line */
ADVANCE_TOKEN(*method, *method_len);
++buf;
if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) {
return NULL;
}
do {
++buf;
CHECK_EOF();
} while (*buf == ' ');
ADVANCE_TOKEN(*path, *path_len);
++buf;
do {
++buf;
CHECK_EOF();
} while (*buf == ' ');
if (*method_len == 0 || *path_len == 0) {
*ret = -1;
return NULL;
......@@ -418,10 +434,14 @@ static const char *parse_response(const char *buf, const char *buf_end, int *min
return NULL;
}
/* skip space */
if (*buf++ != ' ') {
if (*buf != ' ') {
*ret = -1;
return NULL;
}
do {
++buf;
CHECK_EOF();
} while (*buf == ' ');
/* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */
if (buf_end - buf < 4) {
*ret = -2;
......@@ -429,13 +449,22 @@ static const char *parse_response(const char *buf, const char *buf_end, int *min
}
PARSE_INT_3(status);
/* skip space */
if (*buf++ != ' ') {
*ret = -1;
/* get message including preceding space */
if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
return NULL;
}
/* get message */
if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
if (*msg_len == 0) {
/* ok */
} else if (**msg == ' ') {
/* Remove preceding space. Successful return from `get_token_to_eol` guarantees that we would hit something other than SP
* before running past the end of the given buffer. */
do {
++*msg;
--*msg_len;
} while (**msg == ' ');
} else {
/* garbage found after status code */
*ret = -1;
return NULL;
}
......@@ -516,6 +545,8 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
size_t dst = 0, src = 0, bufsz = *_bufsz;
ssize_t ret = -2; /* incomplete */
decoder->_total_read += bufsz;
while (1) {
switch (decoder->_state) {
case CHUNKED_IN_CHUNK_SIZE:
......@@ -528,6 +559,18 @@ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_
ret = -1;
goto Exit;
}
/* the only characters that may appear after the chunk size are BWS, semicolon, or CRLF */
switch (buf[src]) {
case ' ':
case '\011':
case ';':
case '\012':
case '\015':
break;
default:
ret = -1;
goto Exit;
}
break;
}
if (decoder->_hex_count == sizeof(size_t) * 2) {
......@@ -623,6 +666,12 @@ Exit:
if (dst != src)
memmove(buf + dst, buf + src, bufsz - src);
*_bufsz = dst;
/* if incomplete but the overhead of the chunked encoding is >=100KB and >80%, signal an error */
if (ret == -2) {
decoder->_total_overhead += bufsz - dst;
if (decoder->_total_overhead >= 100 * 1024 && decoder->_total_read - decoder->_total_overhead < decoder->_total_read / 4)
ret = -1;
}
return ret;
}
......
......@@ -27,14 +27,13 @@
#ifndef picohttpparser_h
#define picohttpparser_h
#include <stdint.h>
#include <sys/types.h>
#ifdef _MSC_VER
#define ssize_t intptr_t
#endif
/* $Id$ */
#ifdef __cplusplus
extern "C" {
#endif
......@@ -66,6 +65,8 @@ struct phr_chunked_decoder {
char consume_trailer; /* if trailing headers should be consumed */
char _hex_count;
char _state;
uint64_t _total_read;
uint64_t _total_overhead;
};
/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
......@@ -74,8 +75,8 @@ struct phr_chunked_decoder {
* repeatedly call the function while it returns -2 (incomplete) every time
* supplying newly arrived data. If the end of the chunked-encoded data is
* found, the function returns a non-negative number indicating the number of
* octets left undecoded at the tail of the supplied buffer. Returns -1 on
* error.
* octets left undecoded, that starts from the offset returned by `*bufsz`.
* Returns -1 on error.
*/
ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
......
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
E98BADCF24BBFCA10040C7D4 /* picohttpparser.c in Sources */ = {isa = PBXBuildFile; fileRef = E98BADCD24BBFCA10040C7D4 /* picohttpparser.c */; };
E98BADD024BBFCA10040C7D4 /* test.c in Sources */ = {isa = PBXBuildFile; fileRef = E98BADCE24BBFCA10040C7D4 /* test.c */; };
E98BADD424BBFCB40040C7D4 /* picotest.c in Sources */ = {isa = PBXBuildFile; fileRef = E98BADD224BBFCB40040C7D4 /* picotest.c */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
E98BADC024BBFC4E0040C7D4 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
E98BADC224BBFC4E0040C7D4 /* test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test; sourceTree = BUILT_PRODUCTS_DIR; };
E98BADCC24BBFCA10040C7D4 /* picohttpparser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = picohttpparser.h; sourceTree = "<group>"; };
E98BADCD24BBFCA10040C7D4 /* picohttpparser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = picohttpparser.c; sourceTree = "<group>"; };
E98BADCE24BBFCA10040C7D4 /* test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test.c; sourceTree = "<group>"; };
E98BADD224BBFCB40040C7D4 /* picotest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = picotest.c; sourceTree = "<group>"; };
E98BADD324BBFCB40040C7D4 /* picotest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = picotest.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
E98BADBF24BBFC4E0040C7D4 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
E98BADB924BBFC4E0040C7D4 = {
isa = PBXGroup;
children = (
E98BADD124BBFCA50040C7D4 /* picotest */,
E98BADCD24BBFCA10040C7D4 /* picohttpparser.c */,
E98BADCC24BBFCA10040C7D4 /* picohttpparser.h */,
E98BADCE24BBFCA10040C7D4 /* test.c */,
E98BADC324BBFC4E0040C7D4 /* Products */,
);
sourceTree = "<group>";
};
E98BADC324BBFC4E0040C7D4 /* Products */ = {
isa = PBXGroup;
children = (
E98BADC224BBFC4E0040C7D4 /* test */,
);
name = Products;
sourceTree = "<group>";
};
E98BADD124BBFCA50040C7D4 /* picotest */ = {
isa = PBXGroup;
children = (
E98BADD224BBFCB40040C7D4 /* picotest.c */,
E98BADD324BBFCB40040C7D4 /* picotest.h */,
);
path = picotest;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
E98BADC124BBFC4E0040C7D4 /* test */ = {
isa = PBXNativeTarget;
buildConfigurationList = E98BADC924BBFC4E0040C7D4 /* Build configuration list for PBXNativeTarget "test" */;
buildPhases = (
E98BADBE24BBFC4E0040C7D4 /* Sources */,
E98BADBF24BBFC4E0040C7D4 /* Frameworks */,
E98BADC024BBFC4E0040C7D4 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = test;
productName = picohttpparser;
productReference = E98BADC224BBFC4E0040C7D4 /* test */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
E98BADBA24BBFC4E0040C7D4 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1150;
ORGANIZATIONNAME = H2O;
TargetAttributes = {
E98BADC124BBFC4E0040C7D4 = {
CreatedOnToolsVersion = 11.5;
};
};
};
buildConfigurationList = E98BADBD24BBFC4E0040C7D4 /* Build configuration list for PBXProject "picohttpparser" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = E98BADB924BBFC4E0040C7D4;
productRefGroup = E98BADC324BBFC4E0040C7D4 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
E98BADC124BBFC4E0040C7D4 /* test */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
E98BADBE24BBFC4E0040C7D4 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E98BADD424BBFCB40040C7D4 /* picotest.c in Sources */,
E98BADCF24BBFCA10040C7D4 /* picohttpparser.c in Sources */,
E98BADD024BBFCA10040C7D4 /* test.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
E98BADC724BBFC4E0040C7D4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
E98BADC824BBFC4E0040C7D4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
};
name = Release;
};
E98BADCA24BBFC4E0040C7D4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
E98BADCB24BBFC4E0040C7D4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
E98BADBD24BBFC4E0040C7D4 /* Build configuration list for PBXProject "picohttpparser" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E98BADC724BBFC4E0040C7D4 /* Debug */,
E98BADC824BBFC4E0040C7D4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E98BADC924BBFC4E0040C7D4 /* Build configuration list for PBXNativeTarget "test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E98BADCA24BBFC4E0040C7D4 /* Debug */,
E98BADCB24BBFC4E0040C7D4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = E98BADBA24BBFC4E0040C7D4 /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1150"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E98BADC124BBFC4E0040C7D4"
BuildableName = "test"
BlueprintName = "test"
ReferencedContainer = "container:picohttpparser.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E98BADC124BBFC4E0040C7D4"
BuildableName = "test"
BlueprintName = "test"
ReferencedContainer = "container:picohttpparser.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
<AdditionalOption
key = "DYLD_INSERT_LIBRARIES"
value = "/usr/lib/libgmalloc.dylib"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocGuardEdges"
value = ""
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocScribble"
value = ""
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E98BADC124BBFC4E0040C7D4"
BuildableName = "test"
BlueprintName = "test"
ReferencedContainer = "container:picohttpparser.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
......@@ -24,10 +24,13 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include "picotest/picotest.h"
#include "picohttpparser.h"
......@@ -36,6 +39,8 @@ static int bufis(const char *s, size_t l, const char *t)
return strlen(t) == l && memcmp(s, t, l) == 0;
}
static char *inputbuf; /* point to the end of the buffer */
static void test_request(void)
{
const char *method;
......@@ -48,10 +53,12 @@ static void test_request(void)
#define PARSE(s, last_len, exp, comment) \
do { \
size_t slen = sizeof(s) - 1; \
note(comment); \
num_headers = sizeof(headers) / sizeof(headers[0]); \
ok(phr_parse_request(s, sizeof(s) - 1, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, \
last_len) == (exp == 0 ? strlen(s) : exp)); \
memcpy(inputbuf - slen, s, slen); \
ok(phr_parse_request(inputbuf - slen, slen, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, \
last_len) == (exp == 0 ? (int)slen : exp)); \
} while (0)
PARSE("GET / HTTP/1.0\r\n\r\n", 0, 0, "simple");
......@@ -122,6 +129,7 @@ static void test_request(void)
PARSE("G\0T / HTTP/1.0\r\n\r\n", 0, -1, "NUL in method");
PARSE("G\tT / HTTP/1.0\r\n\r\n", 0, -1, "tab in method");
PARSE(":GET / HTTP/1.0\r\n\r\n", 0, -1, "invalid method");
PARSE("GET /\x7fhello HTTP/1.0\r\n\r\n", 0, -1, "DEL in uri-path");
PARSE("GET / HTTP/1.0\r\na\0b: c\r\n\r\n", 0, -1, "NUL in header name");
PARSE("GET / HTTP/1.0\r\nab: c\0d\r\n\r\n", 0, -1, "NUL in header value");
......@@ -146,6 +154,8 @@ static void test_request(void)
PARSE("GET / HTTP/1.0\r\nfoo: a \t \r\n\r\n", 0, 0, "exclude leading and trailing spaces in header value");
ok(bufis(headers[0].value, headers[0].value_len, "a"));
PARSE("GET / HTTP/1.0\r\n\r\n", 0, 0, "accept multiple spaces between tokens");
#undef PARSE
}
......@@ -160,10 +170,12 @@ static void test_response(void)
#define PARSE(s, last_len, exp, comment) \
do { \
size_t slen = sizeof(s) - 1; \
note(comment); \
num_headers = sizeof(headers) / sizeof(headers[0]); \
ok(phr_parse_response(s, strlen(s), &minor_version, &status, &msg, &msg_len, headers, &num_headers, last_len) == \
(exp == 0 ? strlen(s) : exp)); \
memcpy(inputbuf - slen, s, slen); \
ok(phr_parse_response(inputbuf - slen, slen, &minor_version, &status, &msg, &msg_len, headers, &num_headers, last_len) == \
(exp == 0 ? (int)slen : exp)); \
} while (0)
PARSE("HTTP/1.0 200 OK\r\n\r\n", 0, 0, "simple");
......@@ -236,9 +248,17 @@ static void test_response(void)
PARSE("HTTP/1.2z 200 OK\r\n\r\n", 0, -1, "invalid http version 2");
PARSE("HTTP/1.1 OK\r\n\r\n", 0, -1, "no status code");
PARSE("HTTP/1.1 200\r\n\r\n", 0, 0, "accept missing trailing whitespace in status-line");
ok(bufis(msg, msg_len, ""));
PARSE("HTTP/1.1 200X\r\n\r\n", 0, -1, "garbage after status 1");
PARSE("HTTP/1.1 200X \r\n\r\n", 0, -1, "garbage after status 2");
PARSE("HTTP/1.1 200X OK\r\n\r\n", 0, -1, "garbage after status 3");
PARSE("HTTP/1.1 200 OK\r\nbar: \t b\t \t\r\n\r\n", 0, 0, "exclude leading and trailing spaces in header value");
ok(bufis(headers[0].value, headers[0].value_len, "b"));
PARSE("HTTP/1.1 200 OK\r\n\r\n", 0, 0, "accept multiple spaces between tokens");
#undef PARSE
}
......@@ -253,7 +273,7 @@ static void test_headers(void)
do { \
note(comment); \
num_headers = sizeof(headers) / sizeof(headers[0]); \
ok(phr_parse_headers(s, strlen(s), headers, &num_headers, last_len) == (exp == 0 ? strlen(s) : exp)); \
ok(phr_parse_headers(s, strlen(s), headers, &num_headers, last_len) == (exp == 0 ? (int)strlen(s) : exp)); \
} while (0)
PARSE("Host: example.com\r\nCookie: \r\n\r\n", 0, 0, "simple");
......@@ -390,6 +410,7 @@ static void test_chunked(void)
chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
chunked_test_runners[i](__LINE__, 0, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
chunked_test_runners[i](__LINE__, 0, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
chunked_test_runners[i](__LINE__, 0, "6 ; comment\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
chunked_test_runners[i](__LINE__, 0, "6\r\nhello \r\n5\r\nworld\r\n0\r\na: b\r\nc: d\r\n\r\n", "hello world",
sizeof("a: b\r\nc: d\r\n\r\n") - 1);
chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
......@@ -401,6 +422,7 @@ static void test_chunked(void)
test_chunked_failure(__LINE__, "6\r\nhello \r\nffffffffffffffff\r\nabcdefg", -2);
test_chunked_failure(__LINE__, "6\r\nhello \r\nfffffffffffffffff\r\nabcdefg", -1);
}
test_chunked_failure(__LINE__, "1x\r\na\r\n0\r\n", -1);
}
static void test_chunked_consume_trailer(void)
......@@ -417,12 +439,91 @@ static void test_chunked_consume_trailer(void)
}
}
int main(int argc, char **argv)
static void test_chunked_leftdata(void)
{
#define NEXT_REQ "GET / HTTP/1.1\r\n\r\n"
struct phr_chunked_decoder dec = {0};
dec.consume_trailer = 1;
char buf[] = "5\r\nabcde\r\n0\r\n\r\n" NEXT_REQ;
size_t bufsz = sizeof(buf) - 1;
ssize_t ret = phr_decode_chunked(&dec, buf, &bufsz);
ok(ret >= 0);
ok(bufsz == 5);
ok(memcmp(buf, "abcde", 5) == 0);
ok(ret == sizeof(NEXT_REQ) - 1);
ok(memcmp(buf + bufsz, NEXT_REQ, sizeof(NEXT_REQ) - 1) == 0);
#undef NEXT_REQ
}
static ssize_t do_test_chunked_overhead(size_t chunk_len, size_t chunk_count, const char *extra)
{
struct phr_chunked_decoder dec = {0};
char buf[1024];
size_t bufsz;
ssize_t ret;
for (size_t i = 0; i < chunk_count; ++i) {
/* build and feed the chunk header */
bufsz = (size_t)sprintf(buf, "%zx%s\r\n", chunk_len, extra);
if ((ret = phr_decode_chunked(&dec, buf, &bufsz)) != -2)
goto Exit;
assert(bufsz == 0);
/* build and feed the chunk boby */
memset(buf, 'A', chunk_len);
bufsz = chunk_len;
if ((ret = phr_decode_chunked(&dec, buf, &bufsz)) != -2)
goto Exit;
assert(bufsz == chunk_len);
/* build and feed the chunk end (CRLF) */
strcpy(buf, "\r\n");
bufsz = 2;
if ((ret = phr_decode_chunked(&dec, buf, &bufsz)) != -2)
goto Exit;
assert(bufsz == 0);
}
/* build and feed the end chunk */
strcpy(buf, "0\r\n\r\n");
bufsz = 5;
ret = phr_decode_chunked(&dec, buf, &bufsz);
assert(bufsz == 0);
Exit:
return ret;
}
static void test_chunked_overhead(void)
{
ok(do_test_chunked_overhead(100, 10000, "") == 2 /* consume trailer is not set */);
ok(do_test_chunked_overhead(10, 100000, "") == 2 /* consume trailer is not set */);
ok(do_test_chunked_overhead(1, 1000000, "") == -1);
ok(do_test_chunked_overhead(10, 100000, "; tiny=1") == 2 /* consume trailer is not set */);
ok(do_test_chunked_overhead(10, 100000, "; large=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == -1);
}
int main(void)
{
long pagesize = sysconf(_SC_PAGESIZE);
assert(pagesize >= 1);
inputbuf = mmap(NULL, pagesize * 3, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
assert(inputbuf != MAP_FAILED);
inputbuf += pagesize * 2;
ok(mprotect(inputbuf - pagesize, pagesize, PROT_READ | PROT_WRITE) == 0);
subtest("request", test_request);
subtest("response", test_response);
subtest("headers", test_headers);
subtest("chunked", test_chunked);
subtest("chunked-consume-trailer", test_chunked_consume_trailer);
subtest("chunked-leftdata", test_chunked_leftdata);
subtest("chunked-overhead", test_chunked_overhead);
munmap(inputbuf - pagesize * 2, pagesize * 3);
return done_testing();
}