Change llhttp to libcurl

pull/169/head
SChernykh 2 years ago
parent 05b0973a23
commit a35d4b2701

@ -1,10 +1,6 @@
name: C/C++ CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
on: [push, pull_request]
jobs:
build-ubuntu:
@ -22,7 +18,7 @@ jobs:
run: |
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install -y git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev ${{ matrix.config.c }} ${{ matrix.config.cpp }}
sudo apt install -y git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev libcurl4-openssl-dev libidn2-0-dev ${{ matrix.config.c }} ${{ matrix.config.cpp }}
- name: Checkout repository
uses: actions/checkout@v2
@ -34,7 +30,7 @@ jobs:
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
make -j2
make -j$(nproc)
- name: Build tests
run: |
@ -42,7 +38,7 @@ jobs:
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cpp }}
make -j2
make -j$(nproc)
- name: Run tests
run: cd tests/build && ./p2pool_tests
@ -53,6 +49,68 @@ jobs:
name: p2pool-${{ matrix.config.os }}
path: build/p2pool
build-ubuntu-static-libs:
runs-on: ubuntu-latest
steps:
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y git build-essential cmake autoconf libgss-dev
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts
make -j$(nproc)
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake .. -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
run: |
cd external/src/libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j$(nproc)
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -DSTATIC_LIBS=ON
make -j$(nproc)
- name: Build tests
run: |
cd tests
mkdir build
cd build
cmake .. -DSTATIC_LIBS=ON
make -j$(nproc)
- name: Run tests
run: cd tests/build && ./p2pool_tests
- name: Archive binary
uses: actions/upload-artifact@v2
with:
name: p2pool-ubuntu-static-libs
path: build/p2pool
build-ubuntu-aarch64:
runs-on: ubuntu-latest
@ -61,19 +119,26 @@ jobs:
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y git build-essential cmake gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
sudo apt install -y git build-essential cmake autoconf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --host=aarch64-linux-gnu --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts
make -j$(nproc)
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
@ -115,22 +180,45 @@ jobs:
uses: eine/setup-msys2@v2
with:
update: true
install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-libuv
install: mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake make autoconf libtool automake
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts
make -j$(nproc)
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake .. -G "Unix Makefiles" -DBUILD_TESTING=OFF
make -j$(nproc)
- name: Build libzmq
run: |
cd external/src/libzmq
mkdir build
cd build
cmake .. -G "Unix Makefiles" -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF -DZMQ_HAVE_IPC=OFF
make -j$(nproc)
- name: Build p2pool
run: |
mkdir build
cd build
cmake .. -G "Unix Makefiles"
make -j2
cmake .. -G "Unix Makefiles" -DSTATIC_LIBS=ON
make -j$(nproc)
- name: Build tests
run: |
cd tests
mkdir build
cd build
cmake .. -G "Unix Makefiles"
make -j2
cmake .. -G "Unix Makefiles" -DSTATIC_LIBS=ON
make -j$(nproc)
- name: Run tests
run: |
@ -204,14 +292,21 @@ jobs:
submodules: recursive
- name: Install dependencies
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake autoconf libtool automake
- name: Build libcurl
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts
make -j3
- name: Build libuv
run: |
cd external/src/libuv
mkdir build
cd build
cmake ..
cmake .. -DBUILD_TESTING=OFF
make -j3
- name: Build libzmq
@ -219,7 +314,7 @@ jobs:
cd external/src/libzmq
mkdir build
cd build
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF
cmake .. -DWITH_TLS=OFF -DWITH_LIBSODIUM=OFF -DWITH_LIBBSD=OFF -DBUILD_TESTS=OFF
make -j3
- name: Build p2pool

@ -13,10 +13,7 @@ name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '44 11 * * 0'
@ -40,7 +37,7 @@ jobs:
steps:
- name: Install dependencies
run: |
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev libcurl4-openssl-dev libidn2-0-dev
- name: Checkout repository
uses: actions/checkout@v2

@ -1,10 +1,6 @@
name: cppcheck
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
on: [push, pull_request]
jobs:
cppcheck-ubuntu:

@ -10,9 +10,7 @@ name: Microsoft C++ Code Analysis
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
schedule:
- cron: '40 10 * * 0'

3
.gitmodules vendored

@ -19,3 +19,6 @@
[submodule "external/src/robin-hood-hashing"]
path = external/src/robin-hood-hashing
url = https://github.com/SChernykh/robin-hood-hashing
[submodule "external/src/curl"]
path = external/src/curl
url = https://github.com/SChernykh/curl

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 3.12)
project(p2pool)
option(STATIC_BINARY "Build static binary" OFF)
@ -7,9 +7,7 @@ option(WITH_RANDOMX "Include the RandomX library in the build. If this is turned
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.6.0")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
endif()
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT p2pool)
if (WITH_RANDOMX)
add_definitions(-DWITH_RANDOMX)
@ -21,7 +19,6 @@ include(cmake/flags.cmake)
set(HEADERS
external/src/cryptonote/crypto-ops.h
external/src/llhttp/llhttp.h
src/block_cache.h
src/block_template.h
src/common.h
@ -52,9 +49,6 @@ set(HEADERS
set(SOURCES
external/src/cryptonote/crypto-ops-data.c
external/src/cryptonote/crypto-ops.c
external/src/llhttp/api.c
external/src/llhttp/http.c
external/src/llhttp/llhttp.c
src/block_cache.cpp
src/block_template.cpp
src/console_commands.cpp
@ -86,10 +80,10 @@ endif()
include_directories(src)
include_directories(external/src)
include_directories(external/src/cryptonote)
include_directories(external/src/curl/include)
include_directories(external/src/libuv/include)
include_directories(external/src/cppzmq)
include_directories(external/src/libzmq/include)
include_directories(external/src/llhttp)
if (WITH_RANDOMX)
include_directories(external/src/RandomX/src)
endif()
@ -98,6 +92,7 @@ include_directories(external/src/robin-hood-hashing/src/include)
if (WIN32)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
add_definitions(-DCURL_STATICLIB)
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(LIBS ${LIBS} pthread)
elseif (NOT APPLE)
@ -109,11 +104,20 @@ if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
find_library(ZMQ_LIBRARY NAMES libzmq-v142-mt-s-4_3_5 PATHS "external/lib/libzmq/Release")
find_library(UV_LIBRARY_DEBUG NAMES uv_a PATHS "external/lib/libuv/Debug")
find_library(UV_LIBRARY NAMES uv_a PATHS "external/lib/libuv/Release")
find_library(CURL_LIBRARY_DEBUG NAMES libcurld PATHS "external/lib/libcurl/Debug")
find_library(CURL_LIBRARY NAMES libcurl PATHS "external/lib/libcurl/Release")
elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
find_library(ZMQ_LIBRARY_DEBUG NAMES zmq libzmq.a)
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
find_library(UV_LIBRARY_DEBUG NAMES uv libuv.a)
find_library(UV_LIBRARY NAMES uv libuv.a)
if (WIN32)
find_library(CURL_LIBRARY_DEBUG NAMES libcurl.a PATHS "external/src/curl/lib/.libs" NO_DEFAULT_PATH)
find_library(CURL_LIBRARY NAMES libcurl.a PATHS "external/src/curl/lib/.libs" NO_DEFAULT_PATH)
else()
find_library(CURL_LIBRARY_DEBUG NAMES curl)
find_library(CURL_LIBRARY NAMES curl)
endif()
find_library(SODIUM_LIBRARY sodium)
endif()
@ -132,6 +136,13 @@ if (SODIUM_LIBRARY)
set(LIBS ${LIBS} ${SODIUM_LIBRARY})
endif()
if(APPLE)
find_library(FOUNDATION_LIB Foundation)
find_library(CORE_FOUNDATION_LIB CoreFoundation)
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
set(LIBS ${LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
endif()
add_definitions(/DZMQ_STATIC)
include(CheckSymbolExists)
@ -159,23 +170,35 @@ endif()
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
if (STATIC_BINARY OR STATIC_LIBS)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
if (WIN32)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} "${CMAKE_PROJECT_NAME}.exe")
else()
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} ${CMAKE_PROJECT_NAME})
endif()
if (WITH_RANDOMX)
set(STATIC_LIBS randomx)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
if (WIN32)
set(STATIC_LIBS ${STATIC_LIBS} ws2_32 iphlpapi userenv psapi wldap32)
elseif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(STATIC_LIBS ${STATIC_LIBS} pthread)
elseif (NOT APPLE)
elseif (APPLE)
find_library(FOUNDATION_LIB Foundation)
find_library(CORE_FOUNDATION_LIB CoreFoundation)
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
set(STATIC_LIBS ${STATIC_LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
else()
set(STATIC_LIBS ${STATIC_LIBS} pthread dl)
endif()
target_link_libraries(${CMAKE_PROJECT_NAME}
"${CMAKE_SOURCE_DIR}/external/src/libzmq/build/lib/libzmq.a"
"${CMAKE_SOURCE_DIR}/external/src/libuv/build/libuv_a.a"
"${CMAKE_SOURCE_DIR}/external/src/curl/lib/.libs/libcurl.a"
${STATIC_LIBS}
)
else()
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} ${LIBS})
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} debug ${CURL_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} optimized ${CURL_LIBRARY} ${LIBS})
endif()

@ -200,7 +200,7 @@ Please see the relevant instructions for your platform:
Run the following commands to install the necessary prerequisites, clone this repo, and build P2Pool locally on Ubuntu 20.04:
```
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev
sudo apt update && sudo apt install git build-essential cmake libuv1-dev libzmq3-dev libsodium-dev libpgm-dev libnorm-dev libgss-dev libcurl4-openssl-dev libidn2-0-dev
git clone --recursive https://github.com/SChernykh/p2pool
cd p2pool
mkdir build && cd build

@ -39,7 +39,6 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${GENERAL_FLAGS} ${WARNING_FLAGS} ${SECURITY_FLAGS} /Ob1 /Ot /Zi /MT")
elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(GENERAL_FLAGS "-pthread")
set(WARNING_FLAGS "-Wall -Wextra -Wno-undefined-internal -Wunreachable-code-aggressive -Wmissing-prototypes -Wmissing-variable-declarations -Werror")

@ -4,7 +4,7 @@
../external/src/libuv/include/
../external/src/cppzmq/
../external/src/libzmq/include/
../external/src/llhttp/
../external/src/curl/include
../external/src/RandomX/src/
../external/src/rapidjson/include
../external/src/robin-hood-hashing/src/include

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
external/src/curl vendored

@ -0,0 +1 @@
Subproject commit 2bd75e5686b2e6ff3824c4dfb2b6ec86b60f454c

@ -1,22 +0,0 @@
This software is licensed under the MIT License.
Copyright Fedor Indutny, 2018.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -1,379 +0,0 @@
#ifdef _MSC_VER
#pragma warning(disable : 4100 4668 4710 4711 4820)
#elif defined __clang__
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#elif defined __GNUC__
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wcast-qual"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "llhttp.h"
#define CALLBACK_MAYBE(PARSER, NAME) \
do { \
const llhttp_settings_t* settings; \
settings = (const llhttp_settings_t*) (PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) { \
err = 0; \
break; \
} \
err = settings->NAME((PARSER)); \
} while (0)
#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
do { \
const llhttp_settings_t* settings; \
settings = (const llhttp_settings_t*) (PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) { \
err = 0; \
break; \
} \
err = settings->NAME((PARSER), (START), (LEN)); \
if (err == -1) { \
err = HPE_USER; \
llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \
} \
} while (0)
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
const llhttp_settings_t* settings) {
llhttp__internal_init(parser);
parser->type = type;
parser->settings = (void*) settings;
}
#if defined(__wasm__)
extern int wasm_on_message_begin(llhttp_t * p);
extern int wasm_on_url(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_status(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_headers_complete(llhttp_t * p, int status_code,
uint8_t upgrade, int should_keep_alive);
extern int wasm_on_body(llhttp_t* p, const char* at, size_t length);
extern int wasm_on_message_complete(llhttp_t * p);
static int wasm_on_headers_complete_wrap(llhttp_t* p) {
return wasm_on_headers_complete(p, p->status_code, p->upgrade,
llhttp_should_keep_alive(p));
}
const llhttp_settings_t wasm_settings = {
wasm_on_message_begin,
wasm_on_url,
wasm_on_status,
wasm_on_header_field,
wasm_on_header_value,
wasm_on_headers_complete_wrap,
wasm_on_body,
wasm_on_message_complete,
NULL,
NULL,
};
llhttp_t* llhttp_alloc(llhttp_type_t type) {
llhttp_t* parser = malloc(sizeof(llhttp_t));
llhttp_init(parser, type, &wasm_settings);
return parser;
}
void llhttp_free(llhttp_t* parser) {
free(parser);
}
/* Some getters required to get stuff from the parser */
uint8_t llhttp_get_type(llhttp_t* parser) {
return parser->type;
}
uint8_t llhttp_get_http_major(llhttp_t* parser) {
return parser->http_major;
}
uint8_t llhttp_get_http_minor(llhttp_t* parser) {
return parser->http_minor;
}
uint8_t llhttp_get_method(llhttp_t* parser) {
return parser->method;
}
int llhttp_get_status_code(llhttp_t* parser) {
return parser->status_code;
}
uint8_t llhttp_get_upgrade(llhttp_t* parser) {
return parser->upgrade;
}
#endif // defined(__wasm__)
void llhttp_reset(llhttp_t* parser) {
llhttp_type_t type = parser->type;
const llhttp_settings_t* settings = parser->settings;
void* data = parser->data;
uint8_t lenient_flags = parser->lenient_flags;
llhttp__internal_init(parser);
parser->type = type;
parser->settings = (void*) settings;
parser->data = data;
parser->lenient_flags = lenient_flags;
}
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
return llhttp__internal_execute(parser, data, data + len);
}
void llhttp_settings_init(llhttp_settings_t* settings) {
memset(settings, 0, sizeof(*settings));
}
llhttp_errno_t llhttp_finish(llhttp_t* parser) {
int err;
/* We're in an error state. Don't bother doing anything. */
if (parser->error != 0) {
return 0;
}
switch (parser->finish) {
case HTTP_FINISH_SAFE_WITH_CB:
CALLBACK_MAYBE(parser, on_message_complete);
if (err != HPE_OK) return err;
/* FALLTHROUGH */
case HTTP_FINISH_SAFE:
return HPE_OK;
case HTTP_FINISH_UNSAFE:
parser->reason = "Invalid EOF state";
return HPE_INVALID_EOF_STATE;
default:
abort();
}
}
void llhttp_pause(llhttp_t* parser) {
if (parser->error != HPE_OK) {
return;
}
parser->error = HPE_PAUSED;
parser->reason = "Paused";
}
void llhttp_resume(llhttp_t* parser) {
if (parser->error != HPE_PAUSED) {
return;
}
parser->error = 0;
}
void llhttp_resume_after_upgrade(llhttp_t* parser) {
if (parser->error != HPE_PAUSED_UPGRADE) {
return;
}
parser->error = 0;
}
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
return parser->error;
}
const char* llhttp_get_error_reason(const llhttp_t* parser) {
return parser->reason;
}
void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
parser->reason = reason;
}
const char* llhttp_get_error_pos(const llhttp_t* parser) {
return parser->error_pos;
}
const char* llhttp_errno_name(llhttp_errno_t err) {
#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
switch (err) {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
default: abort();
}
#undef HTTP_ERRNO_GEN
}
const char* llhttp_method_name(llhttp_method_t method) {
#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;
switch (method) {
HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN)
default: abort();
}
#undef HTTP_METHOD_GEN
}
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_HEADERS;
} else {
parser->lenient_flags &= ~LENIENT_HEADERS;
}
}
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;
} else {
parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;
}
}
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_KEEP_ALIVE;
} else {
parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;
}
}
/* Callbacks */
int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_message_begin);
return err;
}
int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);
return err;
}
int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_url_complete);
return err;
}
int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);
return err;
}
int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_status_complete);
return err;
}
int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);
return err;
}
int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_header_field_complete);
return err;
}
int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);
return err;
}
int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_header_value_complete);
return err;
}
int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_headers_complete);
return err;
}
int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_message_complete);
return err;
}
int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
int err;
SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);
return err;
}
int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_chunk_header);
return err;
}
int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_chunk_complete);
return err;
}
/* Private */
void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
const char* msg) {
if (p == endp) {
fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
s->flags, msg);
} else {
fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
s->type, s->flags, *p, msg);
}
}

@ -1,158 +0,0 @@
#ifdef _MSC_VER
#pragma warning(disable : 4100 4668 4710 4711 4820)
#elif defined __clang__
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#elif defined __GNUC__
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#include <stdio.h>
#ifndef LLHTTP__TEST
# include "llhttp.h"
#else
# define llhttp_t llparse_t
#endif /* */
int llhttp_message_needs_eof(const llhttp_t* parser);
int llhttp_should_keep_alive(const llhttp_t* parser);
int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
const char* endp) {
/* Set this here so that on_headers_complete() callbacks can see it */
if ((parser->flags & F_UPGRADE) &&
(parser->flags & F_CONNECTION_UPGRADE)) {
/* For responses, "Upgrade: foo" and "Connection: upgrade" are
* mandatory only when it is a 101 Switching Protocols response,
* otherwise it is purely informational, to announce support.
*/
parser->upgrade =
(parser->type == HTTP_REQUEST || parser->status_code == 101);
} else {
parser->upgrade = (parser->method == HTTP_CONNECT);
}
return 0;
}
/* Return values:
* 0 - No body, `restart`, message_complete
* 1 - CONNECT request, `restart`, message_complete, and pause
* 2 - chunk_size_start
* 3 - body_identity
* 4 - body_identity_eof
* 5 - invalid transfer-encoding for request
*/
int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
const char* endp) {
int hasBody;
hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
if (parser->upgrade && (parser->method == HTTP_CONNECT ||
(parser->flags & F_SKIPBODY) || !hasBody)) {
/* Exit, the rest of the message is in a different protocol. */
return 1;
}
if (parser->flags & F_SKIPBODY) {
return 0;
} else if (parser->flags & F_CHUNKED) {
/* chunked encoding - ignore Content-Length header, prepare for a chunk */
return 2;
} else if (parser->flags & F_TRANSFER_ENCODING) {
if (parser->type == HTTP_REQUEST &&
(parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0) {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field
* is present in a request and the chunked transfer coding is not
* the final encoding, the message body length cannot be determined
* reliably; the server MUST respond with the 400 (Bad Request)
* status code and then close the connection.
*/
return 5;
} else {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field is present in a response and
* the chunked transfer coding is not the final encoding, the
* message body length is determined by reading the connection until
* it is closed by the server.
*/
return 4;
}
} else {
if (!(parser->flags & F_CONTENT_LENGTH)) {
if (!llhttp_message_needs_eof(parser)) {
/* Assume content-length 0 - read the next */
return 0;
} else {
/* Read body until EOF */
return 4;
}
} else if (parser->content_length == 0) {
/* Content-Length header given but zero: Content-Length: 0\r\n */
return 0;
} else {
/* Content-Length header given and non-zero */
return 3;
}
}
}
int llhttp__after_message_complete(llhttp_t* parser, const char* p,
const char* endp) {
int should_keep_alive;
should_keep_alive = llhttp_should_keep_alive(parser);
parser->finish = HTTP_FINISH_SAFE;
parser->flags = 0;
/* NOTE: this is ignored in loose parsing mode */
return should_keep_alive;
}
int llhttp_message_needs_eof(const llhttp_t* parser) {
if (parser->type == HTTP_REQUEST) {
return 0;
}
/* See RFC 2616 section 4.4 */
if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
parser->status_code == 204 || /* No Content */
parser->status_code == 304 || /* Not Modified */
(parser->flags & F_SKIPBODY)) { /* response to a HEAD request */
return 0;
}
/* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */
if ((parser->flags & F_TRANSFER_ENCODING) &&
(parser->flags & F_CHUNKED) == 0) {
return 1;
}
if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
return 0;
}
return 1;
}
int llhttp_should_keep_alive(const llhttp_t* parser) {
if (parser->http_major > 0 && parser->http_minor > 0) {
/* HTTP/1.1 */
if (parser->flags & F_CONNECTION_CLOSE) {
return 0;
}
} else {
/* HTTP/1.0 or earlier */
if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
return 0;
}
}
return !llhttp_message_needs_eof(parser);
}

File diff suppressed because it is too large Load Diff

@ -1,564 +0,0 @@
#ifndef INCLUDE_LLHTTP_H_
#define INCLUDE_LLHTTP_H_
#define LLHTTP_VERSION_MAJOR 6
#define LLHTTP_VERSION_MINOR 0
#define LLHTTP_VERSION_PATCH 4
#ifndef LLHTTP_STRICT_MODE
# define LLHTTP_STRICT_MODE 0
#endif
#ifndef INCLUDE_LLHTTP_ITSELF_H_
#define INCLUDE_LLHTTP_ITSELF_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct llhttp__internal_s llhttp__internal_t;
struct llhttp__internal_s {
int32_t _index;
void* _span_pos0;
void* _span_cb0;
int32_t error;
const char* reason;
const char* error_pos;
void* data;
void* _current;
uint64_t content_length;
uint8_t type;
uint8_t method;
uint8_t http_major;
uint8_t http_minor;
uint8_t header_state;
uint8_t lenient_flags;
uint8_t upgrade;
uint8_t finish;
uint16_t flags;
uint16_t status_code;
void* settings;
};
int llhttp__internal_init(llhttp__internal_t* s);
int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
#ifndef LLLLHTTP_C_HEADERS_
#define LLLLHTTP_C_HEADERS_
#ifdef __cplusplus
extern "C" {
#endif
enum llhttp_errno {
HPE_OK = 0,
HPE_INTERNAL = 1,
HPE_STRICT = 2,
HPE_LF_EXPECTED = 3,
HPE_UNEXPECTED_CONTENT_LENGTH = 4,
HPE_CLOSED_CONNECTION = 5,
HPE_INVALID_METHOD = 6,
HPE_INVALID_URL = 7,
HPE_INVALID_CONSTANT = 8,
HPE_INVALID_VERSION = 9,
HPE_INVALID_HEADER_TOKEN = 10,
HPE_INVALID_CONTENT_LENGTH = 11,
HPE_INVALID_CHUNK_SIZE = 12,
HPE_INVALID_STATUS = 13,
HPE_INVALID_EOF_STATE = 14,
HPE_INVALID_TRANSFER_ENCODING = 15,
HPE_CB_MESSAGE_BEGIN = 16,
HPE_CB_HEADERS_COMPLETE = 17,
HPE_CB_MESSAGE_COMPLETE = 18,
HPE_CB_CHUNK_HEADER = 19,
HPE_CB_CHUNK_COMPLETE = 20,
HPE_PAUSED = 21,
HPE_PAUSED_UPGRADE = 22,
HPE_PAUSED_H2_UPGRADE = 23,
HPE_USER = 24
};
typedef enum llhttp_errno llhttp_errno_t;
enum llhttp_flags {
F_CONNECTION_KEEP_ALIVE = 0x1,
F_CONNECTION_CLOSE = 0x2,
F_CONNECTION_UPGRADE = 0x4,
F_CHUNKED = 0x8,
F_UPGRADE = 0x10,
F_CONTENT_LENGTH = 0x20,
F_SKIPBODY = 0x40,
F_TRAILING = 0x80,
F_TRANSFER_ENCODING = 0x200
};
typedef enum llhttp_flags llhttp_flags_t;
enum llhttp_lenient_flags {
LENIENT_HEADERS = 0x1,
LENIENT_CHUNKED_LENGTH = 0x2,
LENIENT_KEEP_ALIVE = 0x4
};
typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;
enum llhttp_type {
HTTP_BOTH = 0,
HTTP_REQUEST = 1,
HTTP_RESPONSE = 2
};
typedef enum llhttp_type llhttp_type_t;
enum llhttp_finish {
HTTP_FINISH_SAFE = 0,
HTTP_FINISH_SAFE_WITH_CB = 1,
HTTP_FINISH_UNSAFE = 2
};
typedef enum llhttp_finish llhttp_finish_t;
enum llhttp_method {
HTTP_DELETE = 0,
HTTP_GET = 1,
HTTP_HEAD = 2,
HTTP_POST = 3,
HTTP_PUT = 4,
HTTP_CONNECT = 5,
HTTP_OPTIONS = 6,
HTTP_TRACE = 7,
HTTP_COPY = 8,
HTTP_LOCK = 9,
HTTP_MKCOL = 10,
HTTP_MOVE = 11,
HTTP_PROPFIND = 12,
HTTP_PROPPATCH = 13,
HTTP_SEARCH = 14,
HTTP_UNLOCK = 15,
HTTP_BIND = 16,
HTTP_REBIND = 17,
HTTP_UNBIND = 18,
HTTP_ACL = 19,
HTTP_REPORT = 20,
HTTP_MKACTIVITY = 21,
HTTP_CHECKOUT = 22,
HTTP_MERGE = 23,
HTTP_MSEARCH = 24,
HTTP_NOTIFY = 25,
HTTP_SUBSCRIBE = 26,
HTTP_UNSUBSCRIBE = 27,
HTTP_PATCH = 28,
HTTP_PURGE = 29,
HTTP_MKCALENDAR = 30,
HTTP_LINK = 31,
HTTP_UNLINK = 32,
HTTP_SOURCE = 33,
HTTP_PRI = 34,
HTTP_DESCRIBE = 35,
HTTP_ANNOUNCE = 36,
HTTP_SETUP = 37,
HTTP_PLAY = 38,
HTTP_PAUSE = 39,
HTTP_TEARDOWN = 40,
HTTP_GET_PARAMETER = 41,
HTTP_SET_PARAMETER = 42,
HTTP_REDIRECT = 43,
HTTP_RECORD = 44,
HTTP_FLUSH = 45
};
typedef enum llhttp_method llhttp_method_t;
#define HTTP_ERRNO_MAP(XX) \
XX(0, OK, OK) \
XX(1, INTERNAL, INTERNAL) \
XX(2, STRICT, STRICT) \
XX(3, LF_EXPECTED, LF_EXPECTED) \
XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
XX(6, INVALID_METHOD, INVALID_METHOD) \
XX(7, INVALID_URL, INVALID_URL) \
XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
XX(9, INVALID_VERSION, INVALID_VERSION) \
XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
XX(13, INVALID_STATUS, INVALID_STATUS) \
XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
XX(21, PAUSED, PAUSED) \
XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \
XX(24, USER, USER) \
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
XX(30, MKCALENDAR, MKCALENDAR) \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
XX(33, SOURCE, SOURCE) \
#define RTSP_METHOD_MAP(XX) \
XX(1, GET, GET) \
XX(3, POST, POST) \
XX(6, OPTIONS, OPTIONS) \
XX(35, DESCRIBE, DESCRIBE) \
XX(36, ANNOUNCE, ANNOUNCE) \
XX(37, SETUP, SETUP) \
XX(38, PLAY, PLAY) \
XX(39, PAUSE, PAUSE) \
XX(40, TEARDOWN, TEARDOWN) \
XX(41, GET_PARAMETER, GET_PARAMETER) \
XX(42, SET_PARAMETER, SET_PARAMETER) \
XX(43, REDIRECT, REDIRECT) \
XX(44, RECORD, RECORD) \
XX(45, FLUSH, FLUSH) \
#define HTTP_ALL_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
XX(30, MKCALENDAR, MKCALENDAR) \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
XX(33, SOURCE, SOURCE) \
XX(34, PRI, PRI) \
XX(35, DESCRIBE, DESCRIBE) \
XX(36, ANNOUNCE, ANNOUNCE) \
XX(37, SETUP, SETUP) \
XX(38, PLAY, PLAY) \
XX(39, PAUSE, PAUSE) \
XX(40, TEARDOWN, TEARDOWN) \
XX(41, GET_PARAMETER, GET_PARAMETER) \
XX(42, SET_PARAMETER, SET_PARAMETER) \
XX(43, REDIRECT, REDIRECT) \
XX(44, RECORD, RECORD) \
XX(45, FLUSH, FLUSH) \
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LLLLHTTP_C_HEADERS_ */
#ifndef INCLUDE_LLHTTP_API_H_
#define INCLUDE_LLHTTP_API_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#if defined(__wasm__)
#define LLHTTP_EXPORT __attribute__((visibility("default")))
#else
#define LLHTTP_EXPORT
#endif
typedef llhttp__internal_t llhttp_t;
typedef struct llhttp_settings_s llhttp_settings_t;
typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
typedef int (*llhttp_cb)(llhttp_t*);
struct llhttp_settings_s {
/* Possible return values 0, -1, `HPE_PAUSED` */
llhttp_cb on_message_begin;
/* Possible return values 0, -1, HPE_USER */
llhttp_data_cb on_url;
llhttp_data_cb on_status;
llhttp_data_cb on_header_field;
llhttp_data_cb on_header_value;
/* Possible return values:
* 0 - Proceed normally
* 1 - Assume that request/response has no body, and proceed to parsing the
* next message
* 2 - Assume absence of body (as above) and make `llhttp_execute()` return
* `HPE_PAUSED_UPGRADE`
* -1 - Error
* `HPE_PAUSED`
*/
llhttp_cb on_headers_complete;
/* Possible return values 0, -1, HPE_USER */
llhttp_data_cb on_body;
/* Possible return values 0, -1, `HPE_PAUSED` */
llhttp_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
* Possible return values 0, -1, `HPE_PAUSED`
*/
llhttp_cb on_chunk_header;
llhttp_cb on_chunk_complete;
/* Information-only callbacks, return value is ignored */
llhttp_cb on_url_complete;
llhttp_cb on_status_complete;
llhttp_cb on_header_field_complete;
llhttp_cb on_header_value_complete;
};
/* Initialize the parser with specific type and user settings.
*
* NOTE: lifetime of `settings` has to be at least the same as the lifetime of
* the `parser` here. In practice, `settings` has to be either a static
* variable or be allocated with `malloc`, `new`, etc.
*/
LLHTTP_EXPORT
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
const llhttp_settings_t* settings);
#if defined(__wasm__)
LLHTTP_EXPORT
llhttp_t* llhttp_alloc(llhttp_type_t type);
LLHTTP_EXPORT
void llhttp_free(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_type(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_http_major(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_http_minor(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_method(llhttp_t* parser);
LLHTTP_EXPORT
int llhttp_get_status_code(llhttp_t* parser);
LLHTTP_EXPORT
uint8_t llhttp_get_upgrade(llhttp_t* parser);
#endif // defined(__wasm__)
/* Reset an already initialized parser back to the start state, preserving the
* existing parser type, callback settings, user data, and lenient flags.
*/
LLHTTP_EXPORT
void llhttp_reset(llhttp_t* parser);
/* Initialize the settings object */
LLHTTP_EXPORT
void llhttp_settings_init(llhttp_settings_t* settings);
/* Parse full or partial request/response, invoking user callbacks along the
* way.
*
* If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
* interrupts, and such errno is returned from `llhttp_execute()`. If
* `HPE_PAUSED` was used as a errno, the execution can be resumed with
* `llhttp_resume()` call.
*
* In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
* is returned after fully parsing the request/response. If the user wishes to
* continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
*
* NOTE: if this function ever returns a non-pause type error, it will continue
* to return the same error upon each successive call up until `llhttp_init()`
* is called.
*/
LLHTTP_EXPORT
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
/* This method should be called when the other side has no further bytes to
* send (e.g. shutdown of readable side of the TCP connection.)
*
* Requests without `Content-Length` and other messages might require treating
* all incoming bytes as the part of the body, up to the last byte of the
* connection. This method will invoke `on_message_complete()` callback if the
* request was terminated safely. Otherwise a error code would be returned.
*/
LLHTTP_EXPORT
llhttp_errno_t llhttp_finish(llhttp_t* parser);
/* Returns `1` if the incoming message is parsed until the last byte, and has
* to be completed by calling `llhttp_finish()` on EOF
*/
LLHTTP_EXPORT
int llhttp_message_needs_eof(const llhttp_t* parser);
/* Returns `1` if there might be any other messages following the last that was
* successfully parsed.
*/
LLHTTP_EXPORT
int llhttp_should_keep_alive(const llhttp_t* parser);
/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
* appropriate error reason.
*
* Important: do not call this from user callbacks! User callbacks must return
* `HPE_PAUSED` if pausing is required.
*/
LLHTTP_EXPORT
void llhttp_pause(llhttp_t* parser);
/* Might be called to resume the execution after the pause in user's callback.
* See `llhttp_execute()` above for details.
*
* Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
*/
LLHTTP_EXPORT
void llhttp_resume(llhttp_t* parser);
/* Might be called to resume the execution after the pause in user's callback.
* See `llhttp_execute()` above for details.
*
* Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
*/
LLHTTP_EXPORT
void llhttp_resume_after_upgrade(llhttp_t* parser);
/* Returns the latest return error */
LLHTTP_EXPORT
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
/* Returns the verbal explanation of the latest returned error.
*
* Note: User callback should set error reason when returning the error. See
* `llhttp_set_error_reason()` for details.
*/
LLHTTP_EXPORT
const char* llhttp_get_error_reason(const llhttp_t* parser);
/* Assign verbal description to the returned error. Must be called in user
* callbacks right before returning the errno.
*
* Note: `HPE_USER` error code might be useful in user callbacks.
*/
LLHTTP_EXPORT
void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
/* Returns the pointer to the last parsed byte before the returned error. The
* pointer is relative to the `data` argument of `llhttp_execute()`.
*
* Note: this method might be useful for counting the number of parsed bytes.
*/
LLHTTP_EXPORT
const char* llhttp_get_error_pos(const llhttp_t* parser);
/* Returns textual name of error code */
LLHTTP_EXPORT
const char* llhttp_errno_name(llhttp_errno_t err);
/* Returns textual name of HTTP method */
LLHTTP_EXPORT
const char* llhttp_method_name(llhttp_method_t method);
/* Enables/disables lenient header value parsing (disabled by default).
*
* Lenient parsing disables header value token checks, extending llhttp's
* protocol support to highly non-compliant clients/server. No
* `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
* lenient parsing is "on".
*
* **(USE AT YOUR OWN RISK)**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and
* `Content-Length` headers (disabled by default).
*
* Normally `llhttp` would error when `Transfer-Encoding` is present in
* conjunction with `Content-Length`. This error is important to prevent HTTP
* request smuggling, but may be less desirable for small number of cases
* involving legacy servers.
*
* **(USE AT YOUR OWN RISK)**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0
* requests responses.
*
* Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
* the HTTP request/response after the request/response with `Connection: close`
* and `Content-Length`. This is important to prevent cache poisoning attacks,
* but might interact badly with outdated and insecure clients. With this flag
* the extra request/response will be parsed normally.
*
* **(USE AT YOUR OWN RISK)**
*/
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_API_H_ */
#endif /* INCLUDE_LLHTTP_H_ */

@ -232,6 +232,7 @@ void ConsoleCommands::run()
if (std::cin.eof()) {
LOGINFO(1, "EOF, stopping");
do_exit(m_pool, nullptr);
return;
}

@ -18,235 +18,318 @@
#include "common.h"
#include "uv_util.h"
#include "json_rpc_request.h"
#include "llhttp.h"
#include <string>
#include <curl/curl.h>
static constexpr char log_category_prefix[] = "JSONRPCRequest ";
namespace p2pool {
namespace JSONRPCRequest {
JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
: m_socket{}
, m_connect{}
, m_write{}
struct CurlContext
{
CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
~CurlContext();
static int socket_func(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp)
{
CurlContext* ctx = reinterpret_cast<CurlContext*>(socketp ? socketp : userp);
return ctx->on_socket(easy, s, action);
}
static int timer_func(CURLM* multi, long timeout_ms, void* ctx)
{
return reinterpret_cast<CurlContext*>(ctx)->on_timer(multi, timeout_ms);
}
static size_t write_func(const void* buffer, size_t size, size_t count, void* ctx)
{
return reinterpret_cast<CurlContext*>(ctx)->on_write(buffer, size, count);
}
int on_socket(CURL* easy, curl_socket_t s, int action);
int on_timer(CURLM* multi, long timeout_ms);
static void on_timeout(uv_handle_t* req);
size_t on_write(const void* buffer, size_t size, size_t count);
static void curl_perform(uv_poll_t* req, int status, int events);
void check_multi_info();
static void on_close(uv_handle_t* h);
uv_poll_t m_pollHandle;
curl_socket_t m_socket;
CallbackBase* m_callback;
CallbackBase* m_closeCallback;
uv_loop_t* m_loop;
uv_timer_t m_timer;
uv_async_t m_async;
CURLM* m_multiHandle;
CURL* m_handle;
std::string m_url;
std::string m_req;
std::string m_auth;
std::vector<char> m_response;
std::string m_error;
};
CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
: m_pollHandle{}
, m_socket{}
, m_callback(cb)
, m_closeCallback(close_cb)
, m_contentLength(0)
, m_contentLengthHeader(false)
, m_readBufInUse(false)
, m_valid(true)
, m_loop(loop)
, m_timer{}
, m_async{}
, m_multiHandle(nullptr)
, m_handle(nullptr)
, m_req(req)
, m_auth(auth)
{
m_readBuf[0] = '\0';
{
char buf[log::Stream::BUF_SIZE + 1];
buf[0] = '\0';
uv_tcp_init(loop ? loop : uv_default_loop_checked(), &m_socket);
uv_tcp_nodelay(&m_socket, 1);
log::Stream s(buf);
s << "http://" << address << ':' << port;
sockaddr_storage addr;
if (uv_ip4_addr(address, port, reinterpret_cast<sockaddr_in*>(&addr)) != 0) {
const int err = uv_ip6_addr(address, port, reinterpret_cast<sockaddr_in6*>(&addr));
if (err) {
LOGERR(1, "invalid IP address " << address << " or port " << port);
m_valid = false;
return;
if (!m_req.empty() && (m_req.front() == '/')) {
s << m_req.c_str() << '\0';
m_req.clear();
}
else {
s << "/json_rpc\0";
}
}
m_socket.data = this;
m_connect.data = this;
m_write.data = this;
const char* uri = "/json_rpc";
m_url = buf;
}
size_t len = req ? strlen(req) : 0;
if (!len) {
LOGERR(1, "Empty JSONRPCRequest, fix the code!");
m_valid = false;
return;
int err = uv_timer_init(m_loop, &m_timer);
if (err) {
LOGERR(1, "uv_timer_init failed, error " << uv_err_name(err));
throw std::runtime_error("uv_timer_init failed");
}
m_timer.data = this;
if (req[0] == '/') {
uri = req;
len = 0;
err = uv_async_init(m_loop, &m_async, reinterpret_cast<uv_async_cb>(on_timeout));
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error("uv_async_init failed");
}
m_async.data = this;
m_multiHandle = curl_multi_init();
if (!m_multiHandle) {
constexpr char msg[] = "curl_multi_init() failed";
LOGERR(1, msg);
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error(msg);
}
m_request.reserve(std::max<size_t>(len + 128, log::Stream::BUF_SIZE + 1));
m_request.resize(log::Stream::BUF_SIZE + 1);
curl_multi_setopt(m_multiHandle, CURLMOPT_SOCKETFUNCTION, socket_func);
curl_multi_setopt(m_multiHandle, CURLMOPT_SOCKETDATA, this);
curl_multi_setopt(m_multiHandle, CURLMOPT_TIMERFUNCTION, timer_func);
curl_multi_setopt(m_multiHandle, CURLMOPT_TIMERDATA, this);
m_handle = curl_easy_init();
if (!m_handle) {
constexpr char msg[] = "curl_easy_init() failed";
LOGERR(1, msg);
curl_multi_cleanup(m_multiHandle);
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error(msg);
}
log::Stream s(m_request.data(), m_request.size());
s << "POST " << uri << " HTTP/1.1\nContent-Type: application/json\nContent-Length: " << len << "\n\n";
curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, write_func);
curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, this);
m_request.resize(s.m_pos);
m_request.insert(m_request.end(), req, req + len);
curl_easy_setopt(m_handle, CURLOPT_URL, m_url.c_str());
curl_easy_setopt(m_handle, CURLOPT_POSTFIELDS, m_req.c_str());
curl_easy_setopt(m_handle, CURLOPT_CONNECTTIMEOUT, 1);
curl_easy_setopt(m_handle, CURLOPT_TIMEOUT, 10);
m_response.reserve(sizeof(m_readBuf));
if (!m_auth.empty()) {
curl_easy_setopt(m_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_ONLY);
curl_easy_setopt(m_handle, CURLOPT_USERPWD, m_auth.c_str());
}
const int err = uv_tcp_connect(&m_connect, &m_socket, reinterpret_cast<const sockaddr*>(&addr), on_connect);
if (err) {
LOGERR(1, "failed to initiate tcp connection to " << address << ", error " << uv_err_name(err));
m_valid = false;
CURLMcode curl_err = curl_multi_add_handle(m_multiHandle, m_handle);
if (curl_err != CURLM_OK) {
LOGERR(1, "curl_multi_add_handle failed, error " << curl_multi_strerror(curl_err));
curl_easy_cleanup(m_handle);
curl_multi_cleanup(m_multiHandle);
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
throw std::runtime_error("curl_multi_add_handle failed");
}
}
void JSONRPCRequest::on_connect(uv_connect_t* req, int status)
CurlContext::~CurlContext()
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(req->data);
if (status != 0) {
pThis->m_error = uv_err_name(status);
LOGERR(1, "failed to connect, error " << pThis->m_error);
pThis->close();
return;
if (m_error.empty() && !m_response.empty()) {
(*m_callback)(m_response.data(), m_response.size());
}
delete m_callback;
uv_buf_t buf[1];
buf[0].base = pThis->m_request.data();
buf[0].len = static_cast<uint32_t>(pThis->m_request.size());
uv_write(&pThis->m_write, reinterpret_cast<uv_stream_t*>(&pThis->m_socket), buf, 1, on_write);
(*m_closeCallback)(m_error.c_str(), m_error.length());
delete m_closeCallback;
}
void JSONRPCRequest::on_write(uv_write_t* handle, int status)
int CurlContext::on_socket(CURL* /*easy*/, curl_socket_t s, int action)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(handle->data);
switch (action) {
case CURL_POLL_IN:
case CURL_POLL_OUT:
case CURL_POLL_INOUT:
{
if (!m_socket) {
m_socket = s;
curl_multi_assign(m_multiHandle, s, this);
}
else if (m_socket != s) {
LOGERR(1, "This code can't work with multiple parallel requests. Fix the code!");
}
if (status != 0) {
pThis->m_error = uv_err_name(status);
LOGERR(1, "failed to send request, error " << pThis->m_error);
pThis->close();
return;
int events = 0;
if (action != CURL_POLL_IN) events |= UV_WRITABLE;
if (action != CURL_POLL_OUT) events |= UV_READABLE;
if (!m_pollHandle.data) {
uv_poll_init_socket(m_loop, &m_pollHandle, s);
m_pollHandle.data = this;
}
uv_poll_start(&m_pollHandle, events, curl_perform);
}
break;
case CURL_POLL_REMOVE:
default:
curl_multi_assign(m_multiHandle, s, nullptr);
uv_poll_stop(&m_pollHandle);
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), on_close);
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), on_close);
uv_close(reinterpret_cast<uv_handle_t*>(&m_pollHandle), on_close);
break;
}
uv_read_start(reinterpret_cast<uv_stream_t*>(&pThis->m_socket), on_alloc, on_read);
return 0;
}
void JSONRPCRequest::on_alloc(uv_handle_t* handle, size_t /*suggested_size*/, uv_buf_t* buf)
int CurlContext::on_timer(CURLM* /*multi*/, long timeout_ms)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(handle->data);
if (timeout_ms < 0) {
uv_timer_stop(&m_timer);
return 0;
}
if (pThis->m_readBufInUse) {
LOGERR(1, "read buffer is already in use");
if (timeout_ms == 0) {
// 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop
uv_async_send(&m_async);
return 0;
}
buf->len = sizeof(pThis->m_readBuf);
buf->base = pThis->m_readBuf;
pThis->m_readBufInUse = true;
uv_timer_start(&m_timer, reinterpret_cast<uv_timer_cb>(on_timeout), timeout_ms, 0);
return 0;
}
void JSONRPCRequest::on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
void CurlContext::on_timeout(uv_handle_t* req)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(stream->data);
pThis->m_readBufInUse = false;
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
if (nread > 0) {
pThis->on_read(buf->base, nread);
}
else if (nread < 0) {
if (nread != UV_EOF){
pThis->m_error = uv_err_name(static_cast<int>(nread));
LOGERR(1, "failed to read response, error " << pThis->m_error);
}
pThis->close();
}
int running_handles;
curl_multi_socket_action(ctx->m_multiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles);
ctx->check_multi_info();
}
void JSONRPCRequest::on_read(const char* data, size_t size)
size_t CurlContext::on_write(const void* buffer, size_t size, size_t count)
{
m_response.append(data, size);
const char* p = reinterpret_cast<const char*>(buffer);
m_response.insert(m_response.end(), p, p + size * count);
return count;
}
static constexpr char headers_end[] = "\r\n\r\n";
if (m_response.find(headers_end) == std::string::npos) {
return;
void CurlContext::curl_perform(uv_poll_t* req, int status, int events)
{
int flags = 0;
if (status < 0) {
flags |= CURL_CSELECT_ERR;
LOGERR(1, "uv_poll_start returned error " << uv_err_name(status));
}
else {
if (events & UV_READABLE) flags |= CURL_CSELECT_IN;
if (events & UV_WRITABLE) flags |= CURL_CSELECT_OUT;
}
llhttp_settings_t settings{};
settings.on_status = [](llhttp_t*, const char* at, size_t length)
{
if ((length == 2) && (!memcmp(at, "Ok", 2) || !memcmp(at, "OK", 2))) {
return 0;
}
return -1;
};
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
settings.on_header_field = [](llhttp_t* parser, const char* at, size_t length)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
static const char header[] = "Content-Length";
pThis->m_contentLengthHeader = ((length == sizeof(header) - 1) && (memcmp(at, header, length) == 0));
return 0;
};
int running_handles;
curl_multi_socket_action(ctx->m_multiHandle, ctx->m_socket, flags, &running_handles);
ctx->check_multi_info();
}
settings.on_header_value = [](llhttp_t* parser, const char* at, size_t length)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
if (pThis->m_contentLengthHeader) {
uint32_t k = 0;
for (const char* p = at; p < at + length; ++p) {
if ('0' <= *p && *p <= '9') {
k = k * 10 + (*p - '0');
}
else {
return -1;
}
}
if (!k) {
return -1;
void CurlContext::check_multi_info()
{
int pending;
while (CURLMsg* message = curl_multi_info_read(m_multiHandle, &pending)) {
if (message->msg == CURLMSG_DONE) {
if ((message->data.result != CURLE_OK) || m_response.empty()) {
m_error = m_response.empty() ? "empty response" : curl_easy_strerror(message->data.result);
}
pThis->m_contentLength = k;
}
return 0;
};
settings.on_body = [](llhttp_t* parser, const char* at, size_t length)
{
JSONRPCRequest* pThis = static_cast<JSONRPCRequest*>(parser->data);
if (pThis->m_contentLength && (length >= pThis->m_contentLength) && pThis->m_callback) {
(*pThis->m_callback)(at, length);
delete pThis->m_callback;
pThis->m_callback = nullptr;
}
return 0;
};
llhttp_t parser;
llhttp_init(&parser, HTTP_RESPONSE, &settings);
long http_code = 0;
curl_easy_getinfo(message->easy_handle, CURLINFO_RESPONSE_CODE, &http_code);
parser.data = this;
const llhttp_errno result = llhttp_execute(&parser, m_response.c_str(), m_response.length());
if (result != HPE_OK) {
m_error = "failed to parse response";
LOGERR(1, m_error << ", result = " << static_cast<int>(result));
close();
return;
}
if (http_code != 200) {
char buf[32] = {};
log::Stream s(buf);
s << "HTTP error " << static_cast<int>(http_code) << '\0';
m_error = buf;
}
if (!m_callback) {
close();
curl_multi_remove_handle(m_multiHandle, m_handle);
curl_easy_cleanup(m_handle);
curl_multi_cleanup(m_multiHandle);
return;
}
}
}
void JSONRPCRequest::close()
void CurlContext::on_close(uv_handle_t* h)
{
uv_handle_t* h = reinterpret_cast<uv_handle_t*>(&m_socket);
if (!uv_is_closing(h)) {
uv_close(h, on_close);
}
}
CurlContext* ctx = reinterpret_cast<CurlContext*>(h->data);
h->data = nullptr;
void JSONRPCRequest::on_close(uv_handle_t* handle)
{
JSONRPCRequest* req = static_cast<JSONRPCRequest*>(handle->data);
if (req->m_closeCallback) {
(*req->m_closeCallback)(req->m_error.c_str(), req->m_error.length());
if (ctx->m_timer.data || ctx->m_async.data || ctx->m_pollHandle.data) {
return;
}
delete req;
delete ctx;
}
JSONRPCRequest::~JSONRPCRequest()
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
{
delete m_callback;
delete m_closeCallback;
CallOnLoop(loop,
[=]()
{
try {
new CurlContext(address, port, req, auth, cb, close_cb, loop);
}
catch (const std::exception& e) {
const char* msg = e.what();
(*close_cb)(msg, strlen(msg));
}
});
}
} // namespace JSONRPCRequest
} // namespace p2pool

@ -18,78 +18,32 @@
#pragma once
namespace p2pool {
namespace JSONRPCRequest {
class JSONRPCRequest
struct CallbackBase
{
public:
template<typename T>
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb)
{
// It will be deleted in one of the tcp callbacks eventually
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), nullptr, nullptr);
if (!r->m_valid) {
delete r;
}
}
virtual ~CallbackBase() {}
virtual void operator()(const char* data, size_t size) = 0;
};
template<typename T, typename U>
static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
{
// It will be deleted in one of the tcp callbacks eventually
CallbackBase* close_callback = new Callback<U>(std::move(close_cb));
JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback<T>(std::move(cb)), close_callback, loop);
if (!r->m_valid) {
constexpr char err[] = "internal error";
(*close_callback)(err, sizeof(err) - 1);
delete r;
}
}
template<typename T>
struct Callback : public CallbackBase
{
explicit FORCEINLINE Callback(T&& cb) : m_cb(std::move(cb)) {}
void operator()(const char* data, size_t size) override { m_cb(data, size); }
private:
struct CallbackBase
{
virtual ~CallbackBase() {}
virtual void operator()(const char* data, size_t size) = 0;
};
template<typename T>
struct Callback : public CallbackBase
{
explicit FORCEINLINE Callback(T&& cb) : m_cb(std::move(cb)) {}
void operator()(const char* data, size_t size) override { m_cb(data, size); }
private:
Callback& operator=(Callback&&) = delete;
T m_cb;
};
JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
~JSONRPCRequest();
static void on_connect(uv_connect_t* req, int status);
static void on_write(uv_write_t* handle, int status);
static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
void on_read(const char* data, size_t size);
// cppcheck-suppress functionConst
void close();
static void on_close(uv_handle_t* handle);
uv_tcp_t m_socket;
uv_connect_t m_connect;
uv_write_t m_write;
Callback& operator=(Callback&&) = delete;
T m_cb;
};
CallbackBase* m_callback;
CallbackBase* m_closeCallback;
uint32_t m_contentLength;
bool m_contentLengthHeader;
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
std::vector<char> m_request;
std::string m_response;
char m_readBuf[65536];
bool m_readBufInUse;
bool m_valid;
std::string m_error;
};
template<typename T, typename U>
FORCEINLINE void call(const std::string& address, int port, const std::string& req, const std::string& auth, T&& cb, U&& close_cb, uv_loop_t* loop = uv_default_loop_checked())
{
Call(address, port, req, auth, new Callback<T>(std::move(cb)), new Callback<U>(std::move(close_cb)), loop);
}
} // namespace JSONRPCRequest
} // namespace p2pool

@ -46,6 +46,7 @@ void p2pool_usage()
"--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n"
"--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d\n"
"--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\n"
"--rpc-login Specify username[:password] required for Monero RPC server\n"
"--help Show this help message\n\n"
"Example command line:\n\n"
"%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n",

@ -555,7 +555,7 @@ void P2PServer::load_monerod_peer_list()
{
const Params& params = m_pool->params();
JSONRPCRequest::call(params.m_host.c_str(), params.m_rpcPort, "/get_peer_list",
JSONRPCRequest::call(params.m_host, params.m_rpcPort, "/get_peer_list", params.m_rpcLogin,
[this](const char* data, size_t size)
{
#define ERR_STR "/get_peer_list RPC request returned invalid JSON "

@ -38,6 +38,7 @@
#include "keccak.h"
#include <thread>
#include <fstream>
#include <curl/curl.h>
constexpr char log_category_prefix[] = "P2Pool ";
constexpr int BLOCK_HEADERS_REQUIRED = 720;
@ -88,7 +89,13 @@ p2pool::p2pool(int argc, char* argv[])
LOGWARN(1, "Mining to a stagenet wallet address");
}
int err = uv_async_init(uv_default_loop_checked(), &m_submitBlockAsync, on_submit_block);
int err = static_cast<int>(curl_global_init(CURL_GLOBAL_ALL));
if (err != CURLE_OK) {
LOGERR(1, "Failed to initialize curl, error " << err);
throw std::exception();
}
err = uv_async_init(uv_default_loop_checked(), &m_submitBlockAsync, on_submit_block);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
throw std::exception();
@ -176,6 +183,8 @@ p2pool::~p2pool()
delete m_mempool;
delete m_params;
delete m_consoleCommands;
curl_global_cleanup();
}
bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result)
@ -311,11 +320,11 @@ void p2pool::handle_miner_data(MinerData& data)
}
for (uint64_t h : missing_heights) {
char buf[log::Stream::BUF_SIZE + 1];
char buf[log::Stream::BUF_SIZE + 1] = {};
log::Stream s(buf);
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << h << "}}\0";
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin,
[this, h](const char* data, size_t size)
{
ChainMain block;
@ -462,7 +471,10 @@ void p2pool::on_stop(uv_async_t* async)
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_blockTemplateAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_stopAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&pool->m_restartZMQAsync), nullptr);
uv_stop(uv_default_loop());
uv_loop_t* loop = uv_default_loop_checked();
delete GetLoopUserData(loop, false);
uv_stop(loop);
}
void p2pool::submit_block() const
@ -522,7 +534,7 @@ void p2pool::submit_block() const
}
request.append("\"]}");
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, request.c_str(),
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, request, m_params->m_rpcLogin,
[height, diff, template_id, nonce, extra_nonce, is_external](const char* data, size_t size)
{
rapidjson::Document doc;
@ -617,7 +629,7 @@ void p2pool::download_block_headers(uint64_t current_height)
const uint64_t seed_height = get_seed_height(current_height);
const uint64_t prev_seed_height = (seed_height > SEEDHASH_EPOCH_BLOCKS) ? (seed_height - SEEDHASH_EPOCH_BLOCKS) : 0;
char buf[log::Stream::BUF_SIZE + 1];
char buf[log::Stream::BUF_SIZE + 1] = {};
log::Stream s(buf);
// First download 2 RandomX seeds
@ -626,7 +638,7 @@ void p2pool::download_block_headers(uint64_t current_height)
s.m_pos = 0;
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_header_by_height\",\"params\":{\"height\":" << height << "}}\0";
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin,
[this, prev_seed_height, height](const char* data, size_t size)
{
ChainMain block;
@ -655,7 +667,7 @@ void p2pool::download_block_headers(uint64_t current_height)
s.m_pos = 0;
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_block_headers_range\",\"params\":{\"start_height\":" << start_height << ",\"end_height\":" << current_height - 1 << "}}\0";
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, buf,
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, buf, m_params->m_rpcLogin,
[this, start_height, current_height](const char* data, size_t size)
{
if (parse_block_headers_range(data, size) == current_height - start_height) {
@ -763,7 +775,7 @@ void p2pool::stratum_on_block()
void p2pool::get_info()
{
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}",
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}", m_params->m_rpcLogin,
[this](const char* data, size_t size)
{
parse_get_info_rpc(data, size);
@ -869,7 +881,7 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
void p2pool::get_version()
{
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_version\"}",
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_version\"}", m_params->m_rpcLogin,
[this](const char* data, size_t size)
{
parse_get_version_rpc(data, size);
@ -933,7 +945,7 @@ void p2pool::get_miner_data()
{
m_getMinerDataPending = true;
JSONRPCRequest::call(m_params->m_host.c_str(), m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}",
JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}", m_params->m_rpcLogin,
[this](const char* data, size_t size)
{
parse_get_miner_data_rpc(data, size);
@ -1473,6 +1485,11 @@ int p2pool::run()
return 1;
}
// Init default loop user data before running it
uv_loop_t* loop = uv_default_loop_checked();
loop->data = nullptr;
GetLoopUserData(loop);
try {
get_info();
load_found_blocks();

@ -130,6 +130,11 @@ Params::Params(int argc, char* argv[])
ok = true;
}
if ((strcmp(argv[i], "--rpc-login") == 0) && (i + 1 < argc)) {
m_rpcLogin = argv[++i];
ok = true;
}
if (!ok) {
fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]);
p2pool_usage();

@ -49,6 +49,7 @@ struct Params
uint32_t m_minerThreads = 0;
bool m_mini = false;
bool m_autoDiff = true;
std::string m_rpcLogin;
};
} // namespace p2pool

@ -370,6 +370,7 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*
RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
: m_pool(pool)
, m_loop{}
, m_loopThread{}
{
int err = uv_loop_init(&m_loop);
@ -378,6 +379,9 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
panic();
}
// Init loop user data before running it
GetLoopUserData(&m_loop);
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr);
m_shutdownAsync.data = this;
@ -426,17 +430,17 @@ bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t h
const uint8_t* data = reinterpret_cast<const uint8_t*>(data_ptr);
const uint8_t major_version = data[0];
char buf[log::Stream::BUF_SIZE + 1];
char buf[log::Stream::BUF_SIZE + 1] = {};
log::Stream s(buf);
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"calc_pow\",\"params\":{\"major_version\":" << major_version <<
",\"height\":" << height <<
",\"block_blob\":\"" << log::hex_buf(data, size) << '"' <<
",\"seed_hash\":\"\"}}";
",\"seed_hash\":\"\"}}\0";
volatile int result = 0;
volatile bool done = false;
JSONRPCRequest::call(m_pool->params().m_host.c_str(), m_pool->params().m_rpcPort, buf,
JSONRPCRequest::call(m_pool->params().m_host, m_pool->params().m_rpcPort, buf, m_pool->params().m_rpcLogin,
[&result, &h](const char* data, size_t size)
{
rapidjson::Document doc;

@ -122,6 +122,8 @@ private:
RandomX_Hasher_RPC* server = reinterpret_cast<RandomX_Hasher_RPC*>(async->data);
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_kickTheLoopAsync), nullptr);
delete GetLoopUserData(&server->m_loop, false);
}
};

@ -177,6 +177,8 @@ protected:
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_dropConnectionsAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&server->m_shutdownAsync), nullptr);
delete GetLoopUserData(&server->m_loop, false);
}
};

@ -27,6 +27,7 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
, m_loopThread{}
, m_finished(0)
, m_listenPort(-1)
, m_loop{}
, m_loopStopped{false}
, m_numConnections{ 0 }
, m_numIncomingConnections{ 0 }
@ -37,6 +38,9 @@ TCPServer<READ_BUF_SIZE, WRITE_BUF_SIZE>::TCPServer(allocate_client_callback all
panic();
}
// Init loop user data before running it
GetLoopUserData(&m_loop);
err = uv_async_init(&m_loop, &m_dropConnectionsAsync, on_drop_connections);
if (err) {
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));

@ -423,4 +423,16 @@ NOINLINE uint64_t bsr_reference(uint64_t x)
return bsr8_table.data[y >> 24] - n0 - n1 - n2;
}
UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create)
{
UV_LoopUserData* data = reinterpret_cast<UV_LoopUserData*>(loop->data);
if (!data && create) {
data = new UV_LoopUserData(loop);
loop->data = data;
}
return data;
}
} // namespace p2pool

@ -61,4 +61,93 @@ void uv_mutex_init_checked(uv_mutex_t* mutex);
void uv_rwlock_init_checked(uv_rwlock_t* lock);
uv_loop_t* uv_default_loop_checked();
struct UV_LoopCallbackBase
{
virtual ~UV_LoopCallbackBase() {}
virtual void operator()() = 0;
};
template<typename T>
struct UV_LoopCallback : public UV_LoopCallbackBase
{
explicit FORCEINLINE UV_LoopCallback(T&& cb) : m_cb(std::move(cb)) {}
void operator()() override { m_cb(); }
private:
UV_LoopCallback& operator=(UV_LoopCallback&&) = delete;
T m_cb;
};
struct UV_LoopUserData
{
uv_loop_t* m_loop;
uv_async_t* m_async;
uv_mutex_t m_callbacksLock;
std::vector<UV_LoopCallbackBase*> m_callbacks;
std::vector<UV_LoopCallbackBase*> m_callbacksToRun;
explicit UV_LoopUserData(uv_loop_t* loop)
: m_loop(loop)
, m_async(new uv_async_t{})
, m_callbacksLock{}
, m_callbacks{}
, m_callbacksToRun{}
{
uv_async_init(m_loop, m_async, async_cb);
m_async->data = this;
uv_mutex_init_checked(&m_callbacksLock);
m_callbacks.reserve(2);
m_callbacksToRun.reserve(2);
}
~UV_LoopUserData()
{
m_loop->data = nullptr;
uv_mutex_destroy(&m_callbacksLock);
uv_close(reinterpret_cast<uv_handle_t*>(m_async), [](uv_handle_t* h) { delete reinterpret_cast<uv_async_t*>(h); });
for (const UV_LoopCallbackBase* cb : m_callbacks) {
delete cb;
}
}
static void async_cb(uv_async_t* h)
{
UV_LoopUserData* data = reinterpret_cast<UV_LoopUserData*>(h->data);
data->m_callbacksToRun.clear();
{
MutexLock lock(data->m_callbacksLock);
std::swap(data->m_callbacks, data->m_callbacksToRun);
}
for (UV_LoopCallbackBase* cb : data->m_callbacksToRun) {
(*cb)();
delete cb;
}
}
UV_LoopUserData(const UV_LoopUserData&) = delete;
UV_LoopUserData& operator=(const UV_LoopUserData&) = delete;
};
UV_LoopUserData* GetLoopUserData(uv_loop_t* loop, bool create = true);
template<typename T>
void CallOnLoop(uv_loop_t* loop, T&& callback)
{
UV_LoopUserData* data = GetLoopUserData(loop);
UV_LoopCallbackBase* cb = new UV_LoopCallback<T>(std::move(callback));
{
MutexLock lock(data->m_callbacksLock);
data->m_callbacks.push_back(cb);
}
uv_async_send(data->m_async);
}
} // namespace p2pool

@ -32,9 +32,6 @@ set(SOURCES
src/wallet_tests.cpp
../external/src/cryptonote/crypto-ops-data.c
../external/src/cryptonote/crypto-ops.c
../external/src/llhttp/api.c
../external/src/llhttp/http.c
../external/src/llhttp/llhttp.c
../src/block_cache.cpp
../src/block_template.cpp
../src/console_commands.cpp
@ -61,10 +58,10 @@ set(SOURCES
include_directories(../src)
include_directories(../external/src)
include_directories(../external/src/cryptonote)
include_directories(../external/src/curl/include)
include_directories(../external/src/libuv/include)
include_directories(../external/src/cppzmq)
include_directories(../external/src/libzmq/include)
include_directories(../external/src/llhttp)
include_directories(../external/src/RandomX/src)
include_directories(../external/src/rapidjson/include)
include_directories(../external/src/robin-hood-hashing/src/include)
@ -72,7 +69,8 @@ include_directories(src)
include_directories(googletest/googletest/include)
if (WIN32)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi)
set(LIBS ${LIBS} ws2_32 iphlpapi userenv psapi wldap32)
add_definitions(-DCURL_STATICLIB)
elseif (NOT APPLE)
set(LIBS ${LIBS} pthread gss dl)
endif()
@ -82,17 +80,28 @@ if (STATIC_LIBS)
set(ZMQ_LIBRARY_DEBUG "${CMAKE_SOURCE_DIR}/../external/src/libzmq/build/lib/libzmq.a")
set(UV_LIBRARY_DEBUG "${CMAKE_SOURCE_DIR}/../external/src/libuv/build/libuv_a.a")
set(UV_LIBRARY "${CMAKE_SOURCE_DIR}/../external/src/libuv/build/libuv_a.a")
set(CURL_LIBRARY_DEBUG "${CMAKE_SOURCE_DIR}/../external/src/curl/lib/.libs/libcurl.a")
set(CURL_LIBRARY "${CMAKE_SOURCE_DIR}/../external/src/curl/lib/.libs/libcurl.a")
else()
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
find_library(ZMQ_LIBRARY_DEBUG NAMES libzmq-v142-mt-sgd-4_3_5 PATHS "../external/lib/libzmq/Debug")
find_library(ZMQ_LIBRARY NAMES libzmq-v142-mt-s-4_3_5 PATHS "../external/lib/libzmq/Release")
find_library(UV_LIBRARY_DEBUG NAMES uv_a PATHS "../external/lib/libuv/Debug")
find_library(UV_LIBRARY NAMES uv_a PATHS "../external/lib/libuv/Release")
find_library(CURL_LIBRARY_DEBUG NAMES libcurld PATHS "../external/lib/libcurl/Debug")
find_library(CURL_LIBRARY NAMES libcurl PATHS "../external/lib/libcurl/Release")
elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
find_library(ZMQ_LIBRARY_DEBUG NAMES zmq libzmq.a)
find_library(ZMQ_LIBRARY NAMES zmq libzmq.a)
find_library(UV_LIBRARY_DEBUG NAMES uv libuv.a)
find_library(UV_LIBRARY NAMES uv libuv.a)
if (WIN32)
find_library(CURL_LIBRARY_DEBUG NAMES libcurl.a PATHS "../external/src/curl/lib/.libs" NO_DEFAULT_PATH)
find_library(CURL_LIBRARY NAMES libcurl.a PATHS "../external/src/curl/lib/.libs" NO_DEFAULT_PATH)
else()
find_library(CURL_LIBRARY_DEBUG NAMES curl)
find_library(CURL_LIBRARY NAMES curl)
endif()
find_library(SODIUM_LIBRARY sodium)
endif()
@ -112,6 +121,13 @@ if (NORM_LIBRARY)
set(LIBS ${LIBS} ${NORM_LIBRARY})
endif()
if(APPLE)
find_library(FOUNDATION_LIB Foundation)
find_library(CORE_FOUNDATION_LIB CoreFoundation)
find_library(SYSTEM_CONFIGURATION_LIB SystemConfiguration)
set(LIBS ${LIBS} ${FOUNDATION_LIB} ${CORE_FOUNDATION_LIB} ${SYSTEM_CONFIGURATION_LIB})
endif()
add_definitions(/DZMQ_STATIC /DP2POOL_LOG_DISABLE)
include(CheckCXXSourceCompiles)
@ -128,7 +144,7 @@ if (HAVE_BITSCANREVERSE64)
endif()
add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES})
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} ${LIBS})
target_link_libraries(${CMAKE_PROJECT_NAME} debug ${ZMQ_LIBRARY_DEBUG} debug ${UV_LIBRARY_DEBUG} debug ${CURL_LIBRARY_DEBUG} optimized ${ZMQ_LIBRARY} optimized ${UV_LIBRARY} optimized ${CURL_LIBRARY} ${LIBS})
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/crypto_tests.txt" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/mainnet_test2_block.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/src/sidechain_dump.dat" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)

Loading…
Cancel
Save