mirror of
https://github.com/curl/curl.git
synced 2026-08-25 04:53:31 +03:00
tests/http/clients: move to tests/client
To have all the tests binaries directly under the tests directory. There seems to be no issue adding non-http test clients to this subdir. Closes #17637
This commit is contained in:
parent
260ec730c2
commit
9b7c488f17
21 changed files with 16 additions and 20 deletions
|
|
@ -22,8 +22,6 @@
|
|||
#
|
||||
###########################################################################
|
||||
|
||||
SUBDIRS = clients
|
||||
|
||||
TESTENV = \
|
||||
testenv/__init__.py \
|
||||
testenv/caddy.py \
|
||||
|
|
@ -75,7 +73,4 @@ clean-local:
|
|||
check: clients
|
||||
|
||||
clients:
|
||||
@(cd clients; $(MAKE) check)
|
||||
|
||||
checksrc:
|
||||
cd clients && $(MAKE) checksrc
|
||||
@(cd ../client; $(MAKE) check)
|
||||
|
|
|
|||
6
tests/http/clients/.gitignore
vendored
6
tests/http/clients/.gitignore
vendored
|
|
@ -1,6 +0,0 @@
|
|||
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
|
||||
clients
|
||||
clients.c
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
#
|
||||
# This software is licensed as described in the file COPYING, which
|
||||
# you should have received as part of this distribution. The terms
|
||||
# are also available at https://curl.se/docs/copyright.html.
|
||||
#
|
||||
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
# copies of the Software, and permit persons to whom the Software is
|
||||
# furnished to do so, under the terms of the COPYING file.
|
||||
#
|
||||
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
# KIND, either express or implied.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
|
||||
# Get BUNDLE, BUNDLE_SRC, CURLX_SRCS, FIRSTFILES, TESTFILES variables
|
||||
curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
|
||||
include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
|
||||
|
||||
set(_bundle_extra "")
|
||||
if(LIB_SELECTED STREQUAL LIB_SHARED)
|
||||
list(APPEND _bundle_extra ${CURLX_SRCS}) # Not exported from the libcurl shared build. Build a copy.
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${BUNDLE_SRC}"
|
||||
COMMAND ${PERL_EXECUTABLE} "${PROJECT_SOURCE_DIR}/scripts/mk-unity.pl"
|
||||
--include ${_bundle_extra} --test ${TESTFILES}
|
||||
${CURL_MK_UNITY_OPTION} --srcdir "${CMAKE_CURRENT_SOURCE_DIR}" > "${BUNDLE_SRC}"
|
||||
DEPENDS
|
||||
"${PROJECT_SOURCE_DIR}/scripts/mk-unity.pl" "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.inc"
|
||||
${FIRSTFILES} ${_bundle_extra} ${TESTFILES}
|
||||
VERBATIM)
|
||||
|
||||
add_executable(http-clients EXCLUDE_FROM_ALL "${BUNDLE_SRC}")
|
||||
add_dependencies(testdeps http-clients)
|
||||
target_include_directories(http-clients PRIVATE
|
||||
"${PROJECT_BINARY_DIR}/lib" # for "curl_config.h"
|
||||
"${PROJECT_SOURCE_DIR}/lib" # for "curl_setup.h"
|
||||
"${PROJECT_SOURCE_DIR}/lib/curlx" # for curlx
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}" # for "first.h"
|
||||
)
|
||||
target_link_libraries(http-clients ${LIB_SELECTED} ${CURL_LIBS})
|
||||
set_property(TARGET http-clients APPEND PROPERTY COMPILE_DEFINITIONS "CURL_NO_OLDIES"
|
||||
"$<$<BOOL:MSVC>:_CRT_SECURE_NO_DEPRECATE>")
|
||||
if(LIB_SELECTED STREQUAL LIB_STATIC AND WIN32)
|
||||
set_property(TARGET http-clients APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
|
||||
endif()
|
||||
set_target_properties(http-clients PROPERTIES OUTPUT_NAME "${BUNDLE}" PROJECT_LABEL "Test ${BUNDLE}" UNITY_BUILD OFF)
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
#
|
||||
# This software is licensed as described in the file COPYING, which
|
||||
# you should have received as part of this distribution. The terms
|
||||
# are also available at https://curl.se/docs/copyright.html.
|
||||
#
|
||||
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
# copies of the Software, and permit persons to whom the Software is
|
||||
# furnished to do so, under the terms of the COPYING file.
|
||||
#
|
||||
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
# KIND, either express or implied.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
AUTOMAKE_OPTIONS = foreign nostdinc
|
||||
|
||||
# Specify our include paths here, and do it relative to $(top_srcdir) and
|
||||
# $(top_builddir), to ensure that these paths which belong to the library
|
||||
# being currently built and tested are searched before the library which
|
||||
# might possibly already be installed in the system.
|
||||
#
|
||||
# $(top_srcdir)/include is for libcurl's external include files
|
||||
# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file
|
||||
# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include \
|
||||
-I$(top_builddir)/lib \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/lib/curlx \
|
||||
-I$(srcdir)
|
||||
|
||||
# Get BUNDLE, BUNDLE_SRC, CURLX_SRCS, FIRSTFILES, TESTFILES variables
|
||||
include Makefile.inc
|
||||
|
||||
EXTRA_DIST = CMakeLists.txt $(FIRSTFILES) $(TESTFILES)
|
||||
|
||||
CFLAGS += @CURL_CFLAG_EXTRAS@
|
||||
|
||||
# Prevent LIBS from being used for all link targets
|
||||
LIBS = $(BLANK_AT_MAKETIME)
|
||||
|
||||
LIBDIR = $(top_builddir)/lib
|
||||
|
||||
if USE_CPPFLAG_CURL_STATICLIB
|
||||
AM_CPPFLAGS += -DCURL_STATICLIB
|
||||
endif
|
||||
if DOING_NATIVE_WINDOWS
|
||||
AM_CPPFLAGS += -DWIN32_LEAN_AND_MEAN
|
||||
endif
|
||||
|
||||
AM_CPPFLAGS += -DCURL_NO_OLDIES
|
||||
|
||||
bundle_extra =
|
||||
if USE_CPPFLAG_CURL_STATICLIB
|
||||
else
|
||||
# These are part of the libcurl static lib. Add them here when linking shared.
|
||||
bundle_extra += $(CURLX_SRCS)
|
||||
endif
|
||||
$(BUNDLE_SRC): $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRSTFILES) $(bundle_extra) $(TESTFILES)
|
||||
@PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(bundle_extra) --test $(TESTFILES) > $(BUNDLE_SRC)
|
||||
|
||||
noinst_PROGRAMS = $(BUNDLE)
|
||||
nodist_clients_SOURCES = $(BUNDLE_SRC)
|
||||
clients_SOURCES =
|
||||
clients_LDADD = $(LIBDIR)/libcurl.la @LIBCURL_PC_LIBS_PRIVATE@
|
||||
clients_CFLAGS = $(AM_CFLAGS)
|
||||
CLEANFILES = $(BUNDLE_SRC)
|
||||
|
||||
CHECKSRC = $(CS_$(V))
|
||||
CS_0 = @echo " RUN " $@;
|
||||
CS_1 =
|
||||
CS_ = $(CS_0)
|
||||
|
||||
# ignore generated C files since they play by slightly different rules!
|
||||
checksrc:
|
||||
$(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
|
||||
-W$(srcdir)/$(BUNDLE_SRC) \
|
||||
$(srcdir)/*.[ch])
|
||||
|
||||
if NOT_CURL_CI
|
||||
all-local: checksrc
|
||||
endif
|
||||
|
||||
clean-local:
|
||||
rm -f $(BUNDLE)
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
#
|
||||
# This software is licensed as described in the file COPYING, which
|
||||
# you should have received as part of this distribution. The terms
|
||||
# are also available at https://curl.se/docs/copyright.html.
|
||||
#
|
||||
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
# copies of the Software, and permit persons to whom the Software is
|
||||
# furnished to do so, under the terms of the COPYING file.
|
||||
#
|
||||
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
# KIND, either express or implied.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
# Shared between CMakeLists.txt and Makefile.am
|
||||
|
||||
BUNDLE = clients
|
||||
BUNDLE_SRC = clients.c
|
||||
|
||||
# Files referenced from the bundle source
|
||||
FIRSTFILES = first.c first.h
|
||||
|
||||
CURLX_SRCS = \
|
||||
../../../lib/curlx/multibyte.c
|
||||
|
||||
# All test clients
|
||||
TESTFILES = \
|
||||
h2_pausing.c \
|
||||
h2_serverpush.c \
|
||||
h2_upgrade_extreme.c \
|
||||
hx_download.c \
|
||||
hx_upload.c \
|
||||
tls_session_reuse.c \
|
||||
upload_pausing.c \
|
||||
ws_data.c \
|
||||
ws_pingpong.c
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
entry_func_t entry_func;
|
||||
char *entry_name;
|
||||
size_t tmp;
|
||||
|
||||
if(argc < 2) {
|
||||
fprintf(stderr, "Pass clientname as first argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
entry_name = argv[1];
|
||||
entry_func = NULL;
|
||||
for(tmp = 0; tmp < CURL_ARRAYSIZE(s_entries); ++tmp) {
|
||||
if(strcmp(entry_name, s_entries[tmp].name) == 0) {
|
||||
entry_func = s_entries[tmp].ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!entry_func) {
|
||||
fprintf(stderr, "Test '%s' not found.\n", entry_name);
|
||||
return 99;
|
||||
}
|
||||
|
||||
return entry_func(argc - 1, argv + 1);
|
||||
}
|
||||
|
|
@ -1,275 +0,0 @@
|
|||
#ifndef HEADER_CLIENT_FIRST_H
|
||||
#define HEADER_CLIENT_FIRST_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
typedef int (*entry_func_t)(int, char **);
|
||||
|
||||
struct entry_s {
|
||||
const char *name;
|
||||
entry_func_t ptr;
|
||||
};
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <stdio.h> /* for fprintf() */
|
||||
#include <stdlib.h> /* for calloc(), free(), strtol() */
|
||||
#include <string.h> /* for strchr(), strcmp() */
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h> /* for usleep() */
|
||||
#include <unistd.h> /* for usleep() */
|
||||
#endif
|
||||
|
||||
#ifdef __TANDEM
|
||||
#include <cextdecs.h(PROCESS_DELAY_)> /* for usleep() logic */
|
||||
#endif
|
||||
|
||||
#define ERR() \
|
||||
do { \
|
||||
fprintf(stderr, "something unexpected went wrong - bailing out!\n"); \
|
||||
return 2; \
|
||||
} while(0)
|
||||
|
||||
static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type)
|
||||
{
|
||||
/*
|
||||
* This is the trace look that is similar to what libcurl makes on its
|
||||
* own.
|
||||
*/
|
||||
static const char * const s_infotype[] = {
|
||||
"* ", "< ", "> ", "{ ", "} ", "{ ", "} "
|
||||
};
|
||||
if(idsbuf && *idsbuf)
|
||||
fprintf(log, "%s%s", idsbuf, s_infotype[type]);
|
||||
else
|
||||
fputs(s_infotype[type], log);
|
||||
}
|
||||
|
||||
#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] "
|
||||
#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \
|
||||
CURL_FORMAT_CURL_OFF_T "] "
|
||||
/*
|
||||
** callback for CURLOPT_DEBUGFUNCTION
|
||||
*/
|
||||
static int debug_cb(CURL *handle, curl_infotype type,
|
||||
char *data, size_t size, void *userdata)
|
||||
{
|
||||
FILE *output = stderr;
|
||||
static int newl = 0;
|
||||
static int traced_data = 0;
|
||||
char idsbuf[60];
|
||||
curl_off_t xfer_id, conn_id;
|
||||
|
||||
(void)handle; /* not used */
|
||||
(void)userdata;
|
||||
|
||||
if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) {
|
||||
if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) &&
|
||||
conn_id >= 0) {
|
||||
curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, xfer_id,
|
||||
conn_id);
|
||||
}
|
||||
else {
|
||||
curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
idsbuf[0] = 0;
|
||||
|
||||
switch(type) {
|
||||
case CURLINFO_HEADER_OUT:
|
||||
if(size > 0) {
|
||||
size_t st = 0;
|
||||
size_t i;
|
||||
for(i = 0; i < size - 1; i++) {
|
||||
if(data[i] == '\n') { /* LF */
|
||||
if(!newl) {
|
||||
log_line_start(output, idsbuf, type);
|
||||
}
|
||||
(void)fwrite(data + st, i - st + 1, 1, output);
|
||||
st = i + 1;
|
||||
newl = 0;
|
||||
}
|
||||
}
|
||||
if(!newl)
|
||||
log_line_start(output, idsbuf, type);
|
||||
(void)fwrite(data + st, i - st + 1, 1, output);
|
||||
}
|
||||
newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
|
||||
traced_data = 0;
|
||||
break;
|
||||
case CURLINFO_TEXT:
|
||||
case CURLINFO_HEADER_IN:
|
||||
if(!newl)
|
||||
log_line_start(output, idsbuf, type);
|
||||
(void)fwrite(data, size, 1, output);
|
||||
newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
|
||||
traced_data = 0;
|
||||
break;
|
||||
case CURLINFO_DATA_OUT:
|
||||
case CURLINFO_DATA_IN:
|
||||
case CURLINFO_SSL_DATA_IN:
|
||||
case CURLINFO_SSL_DATA_OUT:
|
||||
if(!traced_data) {
|
||||
if(!newl)
|
||||
log_line_start(output, idsbuf, type);
|
||||
fprintf(output, "[%ld bytes data]\n", (long)size);
|
||||
newl = 0;
|
||||
traced_data = 1;
|
||||
}
|
||||
break;
|
||||
default: /* nada */
|
||||
newl = 0;
|
||||
traced_data = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump(const char *text, unsigned char *ptr, size_t size, char nohex)
|
||||
{
|
||||
size_t i;
|
||||
size_t c;
|
||||
|
||||
unsigned int width = 0x10;
|
||||
|
||||
if(nohex)
|
||||
/* without the hex output, we can fit more on screen */
|
||||
width = 0x40;
|
||||
|
||||
fprintf(stderr, "%s, %lu bytes (0x%lx)\n",
|
||||
text, (unsigned long)size, (unsigned long)size);
|
||||
|
||||
for(i = 0; i < size; i += width) {
|
||||
|
||||
fprintf(stderr, "%4.4lx: ", (unsigned long)i);
|
||||
|
||||
if(!nohex) {
|
||||
/* hex not disabled, show it */
|
||||
for(c = 0; c < width; c++)
|
||||
if(i + c < size)
|
||||
fprintf(stderr, "%02x ", ptr[i + c]);
|
||||
else
|
||||
fputs(" ", stderr);
|
||||
}
|
||||
|
||||
for(c = 0; (c < width) && (i + c < size); c++) {
|
||||
/* check for 0D0A; if found, skip past and start a new line of output */
|
||||
if(nohex && (i + c + 1 < size) && ptr[i + c] == 0x0D &&
|
||||
ptr[i + c + 1] == 0x0A) {
|
||||
i += (c + 2 - width);
|
||||
break;
|
||||
}
|
||||
fprintf(stderr, "%c",
|
||||
(ptr[i + c] >= 0x20) && (ptr[i + c] < 0x80) ? ptr[i + c] : '.');
|
||||
/* check again for 0D0A, to avoid an extra \n if it's at width */
|
||||
if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D &&
|
||||
ptr[i + c + 2] == 0x0A) {
|
||||
i += (c + 3 - width);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fputc('\n', stderr); /* newline */
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_WEBSOCKETS
|
||||
/* just close the connection */
|
||||
static void websocket_close(CURL *curl)
|
||||
{
|
||||
size_t sent;
|
||||
CURLcode result =
|
||||
curl_ws_send(curl, "", 0, &sent, 0, CURLWS_CLOSE);
|
||||
fprintf(stderr,
|
||||
"ws: curl_ws_send returned %u, sent %u\n", (int)result, (int)sent);
|
||||
}
|
||||
#endif /* CURL_DISABLE_WEBSOCKETS */
|
||||
|
||||
static int coptind;
|
||||
static char *coptarg;
|
||||
|
||||
static int cgetopt(int argc, char * const argv[], const char *optstring)
|
||||
{
|
||||
static int optpos = 1;
|
||||
int coptopt;
|
||||
char *arg;
|
||||
|
||||
if(coptind == 0) { /* Reset? */
|
||||
coptind = !!argc;
|
||||
optpos = 1;
|
||||
}
|
||||
|
||||
arg = argv[coptind];
|
||||
if(arg && strcmp(arg, "--") == 0) {
|
||||
coptind++;
|
||||
return -1;
|
||||
}
|
||||
else if(!arg || arg[0] != '-') {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
const char *opt = strchr(optstring, arg[optpos]);
|
||||
coptopt = arg[optpos];
|
||||
if(!opt) {
|
||||
if(!arg[++optpos]) {
|
||||
coptind++;
|
||||
optpos = 1;
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
else if(opt[1] == ':') {
|
||||
if(arg[optpos + 1]) {
|
||||
coptarg = arg + optpos + 1;
|
||||
coptind++;
|
||||
optpos = 1;
|
||||
return coptopt;
|
||||
}
|
||||
else if(argv[coptind + 1]) {
|
||||
coptarg = argv[coptind + 1];
|
||||
coptind += 2;
|
||||
optpos = 1;
|
||||
return coptopt;
|
||||
}
|
||||
else {
|
||||
if(!arg[++optpos]) {
|
||||
coptind++;
|
||||
optpos = 1;
|
||||
}
|
||||
return *optstring == ':' ? ':' : '?';
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!arg[++optpos]) {
|
||||
coptind++;
|
||||
optpos = 1;
|
||||
}
|
||||
return coptopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HEADER_CLIENT_FIRST_H */
|
||||
|
|
@ -1,282 +0,0 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
/* This is based on the PoC client of issue #11982
|
||||
*/
|
||||
#include <assert.h>
|
||||
|
||||
static void usage_h2_pausing(const char *msg)
|
||||
{
|
||||
if(msg)
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
fprintf(stderr,
|
||||
"usage: [options] url\n"
|
||||
" pause downloads with following options:\n"
|
||||
" -V http_version (http/1.1, h2, h3) http version to use\n"
|
||||
);
|
||||
}
|
||||
|
||||
struct handle
|
||||
{
|
||||
size_t idx;
|
||||
int paused;
|
||||
int resumed;
|
||||
int errored;
|
||||
int fail_write;
|
||||
CURL *h;
|
||||
};
|
||||
|
||||
static size_t cb(char *data, size_t size, size_t nmemb, void *clientp)
|
||||
{
|
||||
size_t realsize = size * nmemb;
|
||||
struct handle *handle = (struct handle *) clientp;
|
||||
curl_off_t totalsize;
|
||||
|
||||
(void)data;
|
||||
if(curl_easy_getinfo(handle->h, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T,
|
||||
&totalsize) == CURLE_OK)
|
||||
curl_mfprintf(stderr, "INFO: [%d] write, "
|
||||
"Content-Length %" CURL_FORMAT_CURL_OFF_T "\n",
|
||||
(int)handle->idx, totalsize);
|
||||
|
||||
if(!handle->resumed) {
|
||||
++handle->paused;
|
||||
fprintf(stderr, "INFO: [%d] write, PAUSING %d time on %lu bytes\n",
|
||||
(int)handle->idx, handle->paused, (long)realsize);
|
||||
assert(handle->paused == 1);
|
||||
return CURL_WRITEFUNC_PAUSE;
|
||||
}
|
||||
if(handle->fail_write) {
|
||||
++handle->errored;
|
||||
fprintf(stderr, "INFO: [%d] FAIL write of %lu bytes, %d time\n",
|
||||
(int)handle->idx, (long)realsize, handle->errored);
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
}
|
||||
fprintf(stderr, "INFO: [%d] write, accepting %lu bytes\n",
|
||||
(int)handle->idx, (long)realsize);
|
||||
return realsize;
|
||||
}
|
||||
|
||||
static int test_h2_pausing(int argc, char *argv[])
|
||||
{
|
||||
struct handle handles[2];
|
||||
CURLM *multi_handle;
|
||||
int still_running = 1, msgs_left, numfds;
|
||||
size_t i;
|
||||
CURLMsg *msg;
|
||||
int rounds = 0;
|
||||
int rc = 0;
|
||||
CURLU *cu;
|
||||
struct curl_slist *resolve = NULL;
|
||||
char resolve_buf[1024];
|
||||
char *url, *host = NULL, *port = NULL;
|
||||
int all_paused = 0;
|
||||
int resume_round = -1;
|
||||
int http_version = CURL_HTTP_VERSION_2_0;
|
||||
int ch;
|
||||
|
||||
while((ch = cgetopt(argc, argv, "hV:")) != -1) {
|
||||
switch(ch) {
|
||||
case 'h':
|
||||
usage_h2_pausing(NULL);
|
||||
return 2;
|
||||
case 'V': {
|
||||
if(!strcmp("http/1.1", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_1_1;
|
||||
else if(!strcmp("h2", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_2_0;
|
||||
else if(!strcmp("h3", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_3ONLY;
|
||||
else {
|
||||
usage_h2_pausing("invalid http version");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
usage_h2_pausing("invalid option");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
argc -= coptind;
|
||||
argv += coptind;
|
||||
|
||||
if(argc != 1) {
|
||||
fprintf(stderr, "ERROR: need URL as argument\n");
|
||||
return 2;
|
||||
}
|
||||
url = argv[0];
|
||||
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
curl_global_trace("ids,time,http/2,http/3");
|
||||
|
||||
cu = curl_url();
|
||||
if(!cu) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
|
||||
fprintf(stderr, "not a URL: '%s'\n", url);
|
||||
return 1;
|
||||
}
|
||||
if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
|
||||
fprintf(stderr, "could not get host of '%s'\n", url);
|
||||
return 1;
|
||||
}
|
||||
if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
|
||||
fprintf(stderr, "could not get port of '%s'\n", url);
|
||||
return 1;
|
||||
}
|
||||
memset(&resolve, 0, sizeof(resolve));
|
||||
curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1, "%s:%s:127.0.0.1",
|
||||
host, port);
|
||||
resolve = curl_slist_append(resolve, resolve_buf);
|
||||
|
||||
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
|
||||
handles[i].idx = i;
|
||||
handles[i].paused = 0;
|
||||
handles[i].resumed = 0;
|
||||
handles[i].errored = 0;
|
||||
handles[i].fail_write = 1;
|
||||
handles[i].h = curl_easy_init();
|
||||
if(!handles[i].h ||
|
||||
curl_easy_setopt(handles[i].h, CURLOPT_WRITEFUNCTION, cb) != CURLE_OK ||
|
||||
curl_easy_setopt(handles[i].h, CURLOPT_WRITEDATA, &handles[i])
|
||||
!= CURLE_OK ||
|
||||
curl_easy_setopt(handles[i].h, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK ||
|
||||
curl_easy_setopt(handles[i].h, CURLOPT_VERBOSE, 1L) != CURLE_OK ||
|
||||
curl_easy_setopt(handles[i].h, CURLOPT_DEBUGFUNCTION, debug_cb)
|
||||
!= CURLE_OK ||
|
||||
curl_easy_setopt(handles[i].h, CURLOPT_SSL_VERIFYPEER, 0L) != CURLE_OK ||
|
||||
curl_easy_setopt(handles[i].h, CURLOPT_RESOLVE, resolve) != CURLE_OK ||
|
||||
curl_easy_setopt(handles[i].h, CURLOPT_PIPEWAIT, 1L) ||
|
||||
curl_easy_setopt(handles[i].h, CURLOPT_URL, url) != CURLE_OK) {
|
||||
ERR();
|
||||
}
|
||||
curl_easy_setopt(handles[i].h, CURLOPT_HTTP_VERSION, (long)http_version);
|
||||
}
|
||||
|
||||
multi_handle = curl_multi_init();
|
||||
if(!multi_handle)
|
||||
ERR();
|
||||
|
||||
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
|
||||
if(curl_multi_add_handle(multi_handle, handles[i].h) != CURLM_OK)
|
||||
ERR();
|
||||
}
|
||||
|
||||
for(rounds = 0;; rounds++) {
|
||||
fprintf(stderr, "INFO: multi_perform round %d\n", rounds);
|
||||
if(curl_multi_perform(multi_handle, &still_running) != CURLM_OK)
|
||||
ERR();
|
||||
|
||||
if(!still_running) {
|
||||
int as_expected = 1;
|
||||
fprintf(stderr, "INFO: no more handles running\n");
|
||||
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
|
||||
if(!handles[i].paused) {
|
||||
fprintf(stderr, "ERROR: [%d] NOT PAUSED\n", (int)i);
|
||||
as_expected = 0;
|
||||
}
|
||||
else if(handles[i].paused != 1) {
|
||||
fprintf(stderr, "ERROR: [%d] PAUSED %d times!\n",
|
||||
(int)i, handles[i].paused);
|
||||
as_expected = 0;
|
||||
}
|
||||
else if(!handles[i].resumed) {
|
||||
fprintf(stderr, "ERROR: [%d] NOT resumed!\n", (int)i);
|
||||
as_expected = 0;
|
||||
}
|
||||
else if(handles[i].errored != 1) {
|
||||
fprintf(stderr, "ERROR: [%d] NOT errored once, %d instead!\n",
|
||||
(int)i, handles[i].errored);
|
||||
as_expected = 0;
|
||||
}
|
||||
}
|
||||
if(!as_expected) {
|
||||
fprintf(stderr, "ERROR: handles not in expected state "
|
||||
"after %d rounds\n", rounds);
|
||||
rc = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(curl_multi_poll(multi_handle, NULL, 0, 100, &numfds) != CURLM_OK)
|
||||
ERR();
|
||||
|
||||
/* !checksrc! disable EQUALSNULL 1 */
|
||||
while((msg = curl_multi_info_read(multi_handle, &msgs_left)) != NULL) {
|
||||
if(msg->msg == CURLMSG_DONE) {
|
||||
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
|
||||
if(msg->easy_handle == handles[i].h) {
|
||||
if(handles[i].paused != 1 || !handles[i].resumed) {
|
||||
fprintf(stderr, "ERROR: [%d] done, pauses=%d, resumed=%d, "
|
||||
"result %d - wtf?\n", (int)i, handles[i].paused,
|
||||
handles[i].resumed, msg->data.result);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Successfully paused? */
|
||||
if(!all_paused) {
|
||||
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
|
||||
if(!handles[i].paused) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
all_paused = (i == CURL_ARRAYSIZE(handles));
|
||||
if(all_paused) {
|
||||
fprintf(stderr, "INFO: all transfers paused\n");
|
||||
/* give transfer some rounds to mess things up */
|
||||
resume_round = rounds + 2;
|
||||
}
|
||||
}
|
||||
if(resume_round > 0 && rounds == resume_round) {
|
||||
/* time to resume */
|
||||
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
|
||||
fprintf(stderr, "INFO: [%d] resumed\n", (int)i);
|
||||
handles[i].resumed = 1;
|
||||
curl_easy_pause(handles[i].h, CURLPAUSE_CONT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
|
||||
curl_multi_remove_handle(multi_handle, handles[i].h);
|
||||
curl_easy_cleanup(handles[i].h);
|
||||
}
|
||||
|
||||
curl_slist_free_all(resolve);
|
||||
curl_free(host);
|
||||
curl_free(port);
|
||||
curl_url_cleanup(cu);
|
||||
curl_multi_cleanup(multi_handle);
|
||||
curl_global_cleanup();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
static int my_trace(CURL *handle, curl_infotype type,
|
||||
char *data, size_t size, void *userp)
|
||||
{
|
||||
const char *text;
|
||||
(void)handle; /* prevent compiler warning */
|
||||
(void)userp;
|
||||
switch(type) {
|
||||
case CURLINFO_TEXT:
|
||||
fprintf(stderr, "== Info: %s", data);
|
||||
return 0;
|
||||
case CURLINFO_HEADER_OUT:
|
||||
text = "=> Send header";
|
||||
break;
|
||||
case CURLINFO_DATA_OUT:
|
||||
text = "=> Send data";
|
||||
break;
|
||||
case CURLINFO_SSL_DATA_OUT:
|
||||
text = "=> Send SSL data";
|
||||
break;
|
||||
case CURLINFO_HEADER_IN:
|
||||
text = "<= Recv header";
|
||||
break;
|
||||
case CURLINFO_DATA_IN:
|
||||
text = "<= Recv data";
|
||||
break;
|
||||
case CURLINFO_SSL_DATA_IN:
|
||||
text = "<= Recv SSL data";
|
||||
break;
|
||||
default: /* in case a new one is introduced to shock us */
|
||||
return 0;
|
||||
}
|
||||
|
||||
dump(text, (unsigned char *)data, size, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_h2_serverpush(CURL *hnd, const char *url)
|
||||
{
|
||||
FILE *out = fopen("download_0.data", "wb");
|
||||
if(!out)
|
||||
/* failed */
|
||||
return 1;
|
||||
|
||||
curl_easy_setopt(hnd, CURLOPT_URL, url);
|
||||
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
|
||||
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
|
||||
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out);
|
||||
|
||||
/* please be verbose */
|
||||
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace);
|
||||
|
||||
/* wait for pipe connection to confirm */
|
||||
curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
|
||||
|
||||
return 0; /* all is good */
|
||||
}
|
||||
|
||||
/* called when there's an incoming push */
|
||||
static int server_push_callback(CURL *parent,
|
||||
CURL *easy,
|
||||
size_t num_headers,
|
||||
struct curl_pushheaders *headers,
|
||||
void *userp)
|
||||
{
|
||||
char *headp;
|
||||
size_t i;
|
||||
int *transfers = (int *)userp;
|
||||
char filename[128];
|
||||
FILE *out;
|
||||
static unsigned int count = 0;
|
||||
int rv;
|
||||
|
||||
(void)parent; /* we have no use for this */
|
||||
curl_msnprintf(filename, sizeof(filename)-1, "push%u", count++);
|
||||
|
||||
/* here's a new stream, save it in a new file for each new push */
|
||||
out = fopen(filename, "wb");
|
||||
if(!out) {
|
||||
/* if we cannot save it, deny it */
|
||||
fprintf(stderr, "Failed to create output file for push\n");
|
||||
rv = CURL_PUSH_DENY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* write to this file */
|
||||
curl_easy_setopt(easy, CURLOPT_WRITEDATA, out);
|
||||
|
||||
fprintf(stderr, "**** push callback approves stream %u, got %lu headers!\n",
|
||||
count, (unsigned long)num_headers);
|
||||
|
||||
for(i = 0; i < num_headers; i++) {
|
||||
headp = curl_pushheader_bynum(headers, i);
|
||||
fprintf(stderr, "**** header %lu: %s\n", (unsigned long)i, headp);
|
||||
}
|
||||
|
||||
headp = curl_pushheader_byname(headers, ":path");
|
||||
if(headp) {
|
||||
fprintf(stderr, "**** The PATH is %s\n", headp /* skip :path + colon */);
|
||||
}
|
||||
|
||||
(*transfers)++; /* one more */
|
||||
rv = CURL_PUSH_OK;
|
||||
|
||||
out:
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Download a file over HTTP/2, take care of server push.
|
||||
*/
|
||||
static int test_h2_serverpush(int argc, char *argv[])
|
||||
{
|
||||
CURL *easy;
|
||||
CURLM *multi_handle;
|
||||
int transfers = 1; /* we start with one */
|
||||
struct CURLMsg *m;
|
||||
const char *url;
|
||||
|
||||
if(argc != 2) {
|
||||
fprintf(stderr, "need URL as argument\n");
|
||||
return 2;
|
||||
}
|
||||
url = argv[1];
|
||||
|
||||
multi_handle = curl_multi_init();
|
||||
curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
|
||||
curl_multi_setopt(multi_handle, CURLMOPT_PUSHFUNCTION, server_push_callback);
|
||||
curl_multi_setopt(multi_handle, CURLMOPT_PUSHDATA, &transfers);
|
||||
|
||||
easy = curl_easy_init();
|
||||
if(setup_h2_serverpush(easy, url)) {
|
||||
fprintf(stderr, "failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
curl_multi_add_handle(multi_handle, easy);
|
||||
do {
|
||||
int still_running; /* keep number of running handles */
|
||||
CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
|
||||
|
||||
if(still_running)
|
||||
/* wait for activity, timeout or "nothing" */
|
||||
mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
|
||||
|
||||
if(mc)
|
||||
break;
|
||||
|
||||
/*
|
||||
* A little caution when doing server push is that libcurl itself has
|
||||
* created and added one or more easy handles but we need to clean them up
|
||||
* when we are done.
|
||||
*/
|
||||
do {
|
||||
int msgq = 0;
|
||||
m = curl_multi_info_read(multi_handle, &msgq);
|
||||
if(m && (m->msg == CURLMSG_DONE)) {
|
||||
CURL *e = m->easy_handle;
|
||||
transfers--;
|
||||
curl_multi_remove_handle(multi_handle, e);
|
||||
curl_easy_cleanup(e);
|
||||
}
|
||||
} while(m);
|
||||
|
||||
} while(transfers); /* as long as we have transfers going */
|
||||
|
||||
curl_multi_cleanup(multi_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
static size_t write_h2ue_cb(char *ptr, size_t size, size_t nmemb, void *opaque)
|
||||
{
|
||||
(void)ptr;
|
||||
(void)opaque;
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static int test_h2_upgrade_extreme(int argc, char *argv[])
|
||||
{
|
||||
const char *url;
|
||||
CURLM *multi = NULL;
|
||||
CURL *easy;
|
||||
CURLMcode mc;
|
||||
int running_handles = 0, start_count, numfds;
|
||||
CURLMsg *msg;
|
||||
int msgs_in_queue;
|
||||
char range[128];
|
||||
int exitcode = 1;
|
||||
|
||||
if(argc != 2) {
|
||||
fprintf(stderr, "%s URL\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
url = argv[1];
|
||||
multi = curl_multi_init();
|
||||
if(!multi) {
|
||||
fprintf(stderr, "curl_multi_init failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
start_count = 200;
|
||||
do {
|
||||
if(start_count) {
|
||||
easy = curl_easy_init();
|
||||
if(!easy) {
|
||||
fprintf(stderr, "curl_easy_init failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb);
|
||||
curl_easy_setopt(easy, CURLOPT_URL, url);
|
||||
curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
|
||||
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_h2ue_cb);
|
||||
curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
|
||||
curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
|
||||
curl_msnprintf(range, sizeof(range),
|
||||
"%" CURL_FORMAT_CURL_OFF_TU "-"
|
||||
"%" CURL_FORMAT_CURL_OFF_TU,
|
||||
(curl_off_t)0,
|
||||
(curl_off_t)16384);
|
||||
curl_easy_setopt(easy, CURLOPT_RANGE, range);
|
||||
|
||||
mc = curl_multi_add_handle(multi, easy);
|
||||
if(mc != CURLM_OK) {
|
||||
fprintf(stderr, "curl_multi_add_handle: %s\n",
|
||||
curl_multi_strerror(mc));
|
||||
curl_easy_cleanup(easy);
|
||||
goto cleanup;
|
||||
}
|
||||
--start_count;
|
||||
}
|
||||
|
||||
mc = curl_multi_perform(multi, &running_handles);
|
||||
if(mc != CURLM_OK) {
|
||||
fprintf(stderr, "curl_multi_perform: %s\n",
|
||||
curl_multi_strerror(mc));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if(running_handles) {
|
||||
mc = curl_multi_poll(multi, NULL, 0, 1000000, &numfds);
|
||||
if(mc != CURLM_OK) {
|
||||
fprintf(stderr, "curl_multi_poll: %s\n",
|
||||
curl_multi_strerror(mc));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for finished handles and remove. */
|
||||
/* !checksrc! disable EQUALSNULL 1 */
|
||||
while((msg = curl_multi_info_read(multi, &msgs_in_queue)) != NULL) {
|
||||
if(msg->msg == CURLMSG_DONE) {
|
||||
long status = 0;
|
||||
curl_off_t xfer_id;
|
||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_XFER_ID, &xfer_id);
|
||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
|
||||
if(msg->data.result == CURLE_SEND_ERROR ||
|
||||
msg->data.result == CURLE_RECV_ERROR) {
|
||||
/* We get these if the server had a GOAWAY in transit on
|
||||
* re-using a connection */
|
||||
}
|
||||
else if(msg->data.result) {
|
||||
curl_mfprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
|
||||
": failed with %d\n", xfer_id, msg->data.result);
|
||||
goto cleanup;
|
||||
}
|
||||
else if(status != 206) {
|
||||
curl_mfprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
|
||||
": wrong http status %ld (expected 206)\n", xfer_id,
|
||||
status);
|
||||
goto cleanup;
|
||||
}
|
||||
curl_multi_remove_handle(multi, msg->easy_handle);
|
||||
curl_easy_cleanup(msg->easy_handle);
|
||||
curl_mfprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T" retiring "
|
||||
"(%d now running)\n", xfer_id, running_handles);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "running_handles=%d, yet_to_start=%d\n",
|
||||
running_handles, start_count);
|
||||
|
||||
} while(running_handles > 0 || start_count);
|
||||
|
||||
fprintf(stderr, "exiting\n");
|
||||
exitcode = 0;
|
||||
|
||||
cleanup:
|
||||
|
||||
if(multi) {
|
||||
CURL **list = curl_multi_get_handles(multi);
|
||||
if(list) {
|
||||
int i;
|
||||
for(i = 0; list[i]; i++) {
|
||||
curl_multi_remove_handle(multi, list[i]);
|
||||
curl_easy_cleanup(list[i]);
|
||||
}
|
||||
curl_free(list);
|
||||
}
|
||||
curl_multi_cleanup(multi);
|
||||
}
|
||||
|
||||
return exitcode;
|
||||
}
|
||||
|
|
@ -1,445 +0,0 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
static int verbose_d = 1;
|
||||
|
||||
struct transfer_d {
|
||||
int idx;
|
||||
CURL *easy;
|
||||
char filename[128];
|
||||
FILE *out;
|
||||
curl_off_t recv_size;
|
||||
curl_off_t fail_at;
|
||||
curl_off_t pause_at;
|
||||
curl_off_t abort_at;
|
||||
int started;
|
||||
int paused;
|
||||
int resumed;
|
||||
int done;
|
||||
CURLcode result;
|
||||
};
|
||||
|
||||
static size_t transfer_count_d = 1;
|
||||
static struct transfer_d *transfer_d;
|
||||
static int forbid_reuse_d = 0;
|
||||
|
||||
static struct transfer_d *get_transfer_for_easy_d(CURL *easy)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < transfer_count_d; ++i) {
|
||||
if(easy == transfer_d[i].easy)
|
||||
return &transfer_d[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t my_write_d_cb(char *buf, size_t nitems, size_t buflen,
|
||||
void *userdata)
|
||||
{
|
||||
struct transfer_d *t = userdata;
|
||||
size_t blen = (nitems * buflen);
|
||||
size_t nwritten;
|
||||
|
||||
fprintf(stderr, "[t-%d] RECV %ld bytes, total=%ld, pause_at=%ld\n",
|
||||
t->idx, (long)blen, (long)t->recv_size, (long)t->pause_at);
|
||||
if(!t->out) {
|
||||
curl_msnprintf(t->filename, sizeof(t->filename)-1, "download_%u.data",
|
||||
t->idx);
|
||||
t->out = fopen(t->filename, "wb");
|
||||
if(!t->out)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!t->resumed &&
|
||||
t->recv_size < t->pause_at &&
|
||||
((t->recv_size + (curl_off_t)blen) >= t->pause_at)) {
|
||||
fprintf(stderr, "[t-%d] PAUSE\n", t->idx);
|
||||
t->paused = 1;
|
||||
return CURL_WRITEFUNC_PAUSE;
|
||||
}
|
||||
|
||||
nwritten = fwrite(buf, nitems, buflen, t->out);
|
||||
if(nwritten < blen) {
|
||||
fprintf(stderr, "[t-%d] write failure\n", t->idx);
|
||||
return 0;
|
||||
}
|
||||
t->recv_size += (curl_off_t)nwritten;
|
||||
if(t->fail_at > 0 && t->recv_size >= t->fail_at) {
|
||||
fprintf(stderr, "[t-%d] FAIL by write callback at %ld bytes\n",
|
||||
t->idx, (long)t->recv_size);
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
}
|
||||
|
||||
return (size_t)nwritten;
|
||||
}
|
||||
|
||||
static int my_progress_d_cb(void *userdata,
|
||||
curl_off_t dltotal, curl_off_t dlnow,
|
||||
curl_off_t ultotal, curl_off_t ulnow)
|
||||
{
|
||||
struct transfer_d *t = userdata;
|
||||
(void)ultotal;
|
||||
(void)ulnow;
|
||||
(void)dltotal;
|
||||
if(t->abort_at > 0 && dlnow >= t->abort_at) {
|
||||
fprintf(stderr, "[t-%d] ABORT by progress_cb at %ld bytes\n",
|
||||
t->idx, (long)dlnow);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_hx_download(CURL *hnd, const char *url, struct transfer_d *t,
|
||||
long http_version, struct curl_slist *host,
|
||||
CURLSH *share, int use_earlydata,
|
||||
int fresh_connect)
|
||||
{
|
||||
curl_easy_setopt(hnd, CURLOPT_SHARE, share);
|
||||
curl_easy_setopt(hnd, CURLOPT_URL, url);
|
||||
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, http_version);
|
||||
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(hnd, CURLOPT_ACCEPT_ENCODING, "");
|
||||
curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, (long)(128 * 1024));
|
||||
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, my_write_d_cb);
|
||||
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, t);
|
||||
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 0L);
|
||||
curl_easy_setopt(hnd, CURLOPT_XFERINFOFUNCTION, my_progress_d_cb);
|
||||
curl_easy_setopt(hnd, CURLOPT_XFERINFODATA, t);
|
||||
if(use_earlydata)
|
||||
curl_easy_setopt(hnd, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_EARLYDATA);
|
||||
if(forbid_reuse_d)
|
||||
curl_easy_setopt(hnd, CURLOPT_FORBID_REUSE, 1L);
|
||||
if(host)
|
||||
curl_easy_setopt(hnd, CURLOPT_RESOLVE, host);
|
||||
if(fresh_connect)
|
||||
curl_easy_setopt(hnd, CURLOPT_FRESH_CONNECT, 1L);
|
||||
|
||||
/* please be verbose */
|
||||
if(verbose_d) {
|
||||
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, debug_cb);
|
||||
}
|
||||
|
||||
/* wait for pipe connection to confirm */
|
||||
curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
|
||||
|
||||
return 0; /* all is good */
|
||||
}
|
||||
|
||||
static void usage_hx_download(const char *msg)
|
||||
{
|
||||
if(msg)
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
fprintf(stderr,
|
||||
"usage: [options] url\n"
|
||||
" download a url with following options:\n"
|
||||
" -a abort paused transfer\n"
|
||||
" -m number max parallel downloads\n"
|
||||
" -e use TLS early data when possible\n"
|
||||
" -f forbid connection reuse\n"
|
||||
" -n number total downloads\n");
|
||||
fprintf(stderr,
|
||||
" -A number abort transfer after `number` response bytes\n"
|
||||
" -F number fail writing response after `number` response bytes\n"
|
||||
" -M number max concurrent connections to a host\n"
|
||||
" -P number pause transfer after `number` response bytes\n"
|
||||
" -r <host>:<port>:<addr> resolve information\n"
|
||||
" -T number max concurrent connections total\n"
|
||||
" -V http_version (http/1.1, h2, h3) http version to use\n"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Download a file over HTTP/2, take care of server push.
|
||||
*/
|
||||
static int test_hx_download(int argc, char *argv[])
|
||||
{
|
||||
CURLM *multi_handle;
|
||||
struct CURLMsg *m;
|
||||
CURLSH *share;
|
||||
const char *url;
|
||||
size_t i, n, max_parallel = 1;
|
||||
size_t active_transfers;
|
||||
size_t pause_offset = 0;
|
||||
size_t abort_offset = 0;
|
||||
size_t fail_offset = 0;
|
||||
int abort_paused = 0, use_earlydata = 0;
|
||||
struct transfer_d *t;
|
||||
int http_version = CURL_HTTP_VERSION_2_0;
|
||||
int ch;
|
||||
struct curl_slist *host = NULL;
|
||||
char *resolve = NULL;
|
||||
size_t max_host_conns = 0;
|
||||
size_t max_total_conns = 0;
|
||||
int fresh_connect = 0;
|
||||
int result = 0;
|
||||
|
||||
while((ch = cgetopt(argc, argv, "aefhm:n:xA:F:M:P:r:T:V:")) != -1) {
|
||||
switch(ch) {
|
||||
case 'h':
|
||||
usage_hx_download(NULL);
|
||||
result = 2;
|
||||
goto cleanup;
|
||||
case 'a':
|
||||
abort_paused = 1;
|
||||
break;
|
||||
case 'e':
|
||||
use_earlydata = 1;
|
||||
break;
|
||||
case 'f':
|
||||
forbid_reuse_d = 1;
|
||||
break;
|
||||
case 'm':
|
||||
max_parallel = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'n':
|
||||
transfer_count_d = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'x':
|
||||
fresh_connect = 1;
|
||||
break;
|
||||
case 'A':
|
||||
abort_offset = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'F':
|
||||
fail_offset = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'M':
|
||||
max_host_conns = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'P':
|
||||
pause_offset = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'r':
|
||||
free(resolve);
|
||||
resolve = strdup(coptarg);
|
||||
break;
|
||||
case 'T':
|
||||
max_total_conns = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'V': {
|
||||
if(!strcmp("http/1.1", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_1_1;
|
||||
else if(!strcmp("h2", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_2_0;
|
||||
else if(!strcmp("h3", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_3ONLY;
|
||||
else {
|
||||
usage_hx_download("invalid http version");
|
||||
result = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
usage_hx_download("invalid option");
|
||||
result = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
argc -= coptind;
|
||||
argv += coptind;
|
||||
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
curl_global_trace("ids,time,http/2,http/3");
|
||||
|
||||
if(argc != 1) {
|
||||
usage_hx_download("not enough arguments");
|
||||
result = 2;
|
||||
goto cleanup;
|
||||
}
|
||||
url = argv[0];
|
||||
|
||||
if(resolve)
|
||||
host = curl_slist_append(NULL, resolve);
|
||||
|
||||
share = curl_share_init();
|
||||
if(!share) {
|
||||
fprintf(stderr, "error allocating share\n");
|
||||
result = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
|
||||
/* curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); */
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
|
||||
|
||||
transfer_d = calloc(transfer_count_d, sizeof(*transfer_d));
|
||||
if(!transfer_d) {
|
||||
fprintf(stderr, "error allocating transfer structs\n");
|
||||
result = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
multi_handle = curl_multi_init();
|
||||
curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
|
||||
curl_multi_setopt(multi_handle, CURLMOPT_MAX_TOTAL_CONNECTIONS,
|
||||
(long)max_total_conns);
|
||||
curl_multi_setopt(multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS,
|
||||
(long)max_host_conns);
|
||||
|
||||
active_transfers = 0;
|
||||
for(i = 0; i < transfer_count_d; ++i) {
|
||||
t = &transfer_d[i];
|
||||
t->idx = (int)i;
|
||||
t->abort_at = (curl_off_t)abort_offset;
|
||||
t->fail_at = (curl_off_t)fail_offset;
|
||||
t->pause_at = (curl_off_t)pause_offset;
|
||||
}
|
||||
|
||||
n = (max_parallel < transfer_count_d) ? max_parallel : transfer_count_d;
|
||||
for(i = 0; i < n; ++i) {
|
||||
t = &transfer_d[i];
|
||||
t->easy = curl_easy_init();
|
||||
if(!t->easy ||
|
||||
setup_hx_download(t->easy, url, t, http_version, host, share,
|
||||
use_earlydata, fresh_connect)) {
|
||||
fprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
|
||||
result = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
curl_multi_add_handle(multi_handle, t->easy);
|
||||
t->started = 1;
|
||||
++active_transfers;
|
||||
fprintf(stderr, "[t-%d] STARTED\n", t->idx);
|
||||
}
|
||||
|
||||
do {
|
||||
int still_running; /* keep number of running handles */
|
||||
CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
|
||||
|
||||
if(still_running) {
|
||||
/* wait for activity, timeout or "nothing" */
|
||||
mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
|
||||
}
|
||||
|
||||
if(mc)
|
||||
break;
|
||||
|
||||
do {
|
||||
int msgq = 0;
|
||||
m = curl_multi_info_read(multi_handle, &msgq);
|
||||
if(m && (m->msg == CURLMSG_DONE)) {
|
||||
CURL *e = m->easy_handle;
|
||||
--active_transfers;
|
||||
curl_multi_remove_handle(multi_handle, e);
|
||||
t = get_transfer_for_easy_d(e);
|
||||
if(t) {
|
||||
t->done = 1;
|
||||
t->result = m->data.result;
|
||||
fprintf(stderr, "[t-%d] FINISHED with result %d\n",
|
||||
t->idx, t->result);
|
||||
if(use_earlydata) {
|
||||
curl_off_t sent;
|
||||
curl_easy_getinfo(e, CURLINFO_EARLYDATA_SENT_T, &sent);
|
||||
fprintf(stderr, "[t-%d] EarlyData: %ld\n", t->idx, (long)sent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
curl_easy_cleanup(e);
|
||||
fprintf(stderr, "unknown FINISHED???\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* nothing happening, maintenance */
|
||||
if(abort_paused) {
|
||||
/* abort paused transfers */
|
||||
for(i = 0; i < transfer_count_d; ++i) {
|
||||
t = &transfer_d[i];
|
||||
if(!t->done && t->paused && t->easy) {
|
||||
curl_multi_remove_handle(multi_handle, t->easy);
|
||||
t->done = 1;
|
||||
active_transfers--;
|
||||
fprintf(stderr, "[t-%d] ABORTED\n", t->idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* resume one paused transfer */
|
||||
for(i = 0; i < transfer_count_d; ++i) {
|
||||
t = &transfer_d[i];
|
||||
if(!t->done && t->paused) {
|
||||
t->resumed = 1;
|
||||
t->paused = 0;
|
||||
curl_easy_pause(t->easy, CURLPAUSE_CONT);
|
||||
fprintf(stderr, "[t-%d] RESUMED\n", t->idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(active_transfers < max_parallel) {
|
||||
for(i = 0; i < transfer_count_d; ++i) {
|
||||
t = &transfer_d[i];
|
||||
if(!t->started) {
|
||||
t->easy = curl_easy_init();
|
||||
if(!t->easy ||
|
||||
setup_hx_download(t->easy, url, t, http_version, host, share,
|
||||
use_earlydata, fresh_connect)) {
|
||||
fprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
|
||||
result = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
curl_multi_add_handle(multi_handle, t->easy);
|
||||
t->started = 1;
|
||||
++active_transfers;
|
||||
fprintf(stderr, "[t-%d] STARTED\n", t->idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* all started */
|
||||
if(i == transfer_count_d)
|
||||
break;
|
||||
}
|
||||
} while(m);
|
||||
|
||||
} while(active_transfers); /* as long as we have transfers going */
|
||||
|
||||
curl_multi_cleanup(multi_handle);
|
||||
|
||||
for(i = 0; i < transfer_count_d; ++i) {
|
||||
t = &transfer_d[i];
|
||||
if(t->out) {
|
||||
fclose(t->out);
|
||||
t->out = NULL;
|
||||
}
|
||||
if(t->easy) {
|
||||
curl_easy_cleanup(t->easy);
|
||||
t->easy = NULL;
|
||||
}
|
||||
if(t->result)
|
||||
result = t->result;
|
||||
}
|
||||
free(transfer_d);
|
||||
|
||||
curl_share_cleanup(share);
|
||||
curl_slist_free_all(host);
|
||||
cleanup:
|
||||
free(resolve);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1,496 +0,0 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
static int verbose_u = 1;
|
||||
|
||||
struct transfer_u {
|
||||
int idx;
|
||||
CURL *easy;
|
||||
const char *method;
|
||||
char filename[128];
|
||||
FILE *out;
|
||||
curl_off_t send_total;
|
||||
curl_off_t recv_size;
|
||||
curl_off_t send_size;
|
||||
curl_off_t fail_at;
|
||||
curl_off_t pause_at;
|
||||
curl_off_t abort_at;
|
||||
int started;
|
||||
int paused;
|
||||
int resumed;
|
||||
int done;
|
||||
};
|
||||
|
||||
static size_t transfer_count_u = 1;
|
||||
static struct transfer_u *transfer_u;
|
||||
static int forbid_reuse_u = 0;
|
||||
|
||||
static struct transfer_u *get_transfer_for_easy_u(CURL *easy)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < transfer_count_u; ++i) {
|
||||
if(easy == transfer_u[i].easy)
|
||||
return &transfer_u[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t my_write_u_cb(char *buf, size_t nitems, size_t buflen,
|
||||
void *userdata)
|
||||
{
|
||||
struct transfer_u *t = userdata;
|
||||
size_t blen = (nitems * buflen);
|
||||
size_t nwritten;
|
||||
|
||||
fprintf(stderr, "[t-%d] RECV %ld bytes, total=%ld, pause_at=%ld\n",
|
||||
t->idx, (long)blen, (long)t->recv_size, (long)t->pause_at);
|
||||
if(!t->out) {
|
||||
curl_msnprintf(t->filename, sizeof(t->filename)-1, "download_%u.data",
|
||||
t->idx);
|
||||
t->out = fopen(t->filename, "wb");
|
||||
if(!t->out)
|
||||
return 0;
|
||||
}
|
||||
|
||||
nwritten = fwrite(buf, nitems, buflen, t->out);
|
||||
if(nwritten < blen) {
|
||||
fprintf(stderr, "[t-%d] write failure\n", t->idx);
|
||||
return 0;
|
||||
}
|
||||
t->recv_size += (curl_off_t)nwritten;
|
||||
return (size_t)nwritten;
|
||||
}
|
||||
|
||||
static size_t my_read_cb(char *buf, size_t nitems, size_t buflen,
|
||||
void *userdata)
|
||||
{
|
||||
struct transfer_u *t = userdata;
|
||||
size_t blen = (nitems * buflen);
|
||||
size_t nread;
|
||||
|
||||
if(t->send_total <= t->send_size)
|
||||
nread = 0;
|
||||
else if((t->send_total - t->send_size) < (curl_off_t)blen)
|
||||
nread = (size_t)(t->send_total - t->send_size);
|
||||
else
|
||||
nread = blen;
|
||||
|
||||
fprintf(stderr, "[t-%d] SEND %ld bytes, total=%ld, pause_at=%ld\n",
|
||||
t->idx, (long)nread, (long)t->send_total, (long)t->pause_at);
|
||||
|
||||
if(!t->resumed &&
|
||||
t->send_size < t->pause_at &&
|
||||
((t->send_size + (curl_off_t)blen) >= t->pause_at)) {
|
||||
fprintf(stderr, "[t-%d] PAUSE\n", t->idx);
|
||||
t->paused = 1;
|
||||
return CURL_READFUNC_PAUSE;
|
||||
}
|
||||
|
||||
memset(buf, 'x', nread);
|
||||
t->send_size += (curl_off_t)nread;
|
||||
if(t->fail_at > 0 && t->send_size >= t->fail_at) {
|
||||
fprintf(stderr, "[t-%d] ABORT by read callback at %ld bytes\n",
|
||||
t->idx, (long)t->send_size);
|
||||
return CURL_READFUNC_ABORT;
|
||||
}
|
||||
return (size_t)nread;
|
||||
}
|
||||
|
||||
static int my_progress_u_cb(void *userdata,
|
||||
curl_off_t dltotal, curl_off_t dlnow,
|
||||
curl_off_t ultotal, curl_off_t ulnow)
|
||||
{
|
||||
struct transfer_u *t = userdata;
|
||||
(void)ultotal;
|
||||
(void)dlnow;
|
||||
(void)dltotal;
|
||||
if(t->abort_at > 0 && ulnow >= t->abort_at) {
|
||||
fprintf(stderr, "[t-%d] ABORT by progress_cb at %ld bytes sent\n",
|
||||
t->idx, (long)ulnow);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_hx_upload(CURL *hnd, const char *url, struct transfer_u *t,
|
||||
long http_version, struct curl_slist *host,
|
||||
CURLSH *share, int use_earlydata,
|
||||
int announce_length)
|
||||
{
|
||||
curl_easy_setopt(hnd, CURLOPT_SHARE, share);
|
||||
curl_easy_setopt(hnd, CURLOPT_URL, url);
|
||||
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, http_version);
|
||||
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, (long)(128 * 1024));
|
||||
curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE);
|
||||
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, my_write_u_cb);
|
||||
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, t);
|
||||
if(use_earlydata)
|
||||
curl_easy_setopt(hnd, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_EARLYDATA);
|
||||
|
||||
if(!t->method || !strcmp("PUT", t->method))
|
||||
curl_easy_setopt(hnd, CURLOPT_UPLOAD, 1L);
|
||||
else if(!strcmp("POST", t->method))
|
||||
curl_easy_setopt(hnd, CURLOPT_POST, 1L);
|
||||
else {
|
||||
fprintf(stderr, "unsupported method '%s'\n", t->method);
|
||||
return 1;
|
||||
}
|
||||
curl_easy_setopt(hnd, CURLOPT_READFUNCTION, my_read_cb);
|
||||
curl_easy_setopt(hnd, CURLOPT_READDATA, t);
|
||||
if(announce_length)
|
||||
curl_easy_setopt(hnd, CURLOPT_INFILESIZE_LARGE, t->send_total);
|
||||
|
||||
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 0L);
|
||||
curl_easy_setopt(hnd, CURLOPT_XFERINFOFUNCTION, my_progress_u_cb);
|
||||
curl_easy_setopt(hnd, CURLOPT_XFERINFODATA, t);
|
||||
if(forbid_reuse_u)
|
||||
curl_easy_setopt(hnd, CURLOPT_FORBID_REUSE, 1L);
|
||||
if(host)
|
||||
curl_easy_setopt(hnd, CURLOPT_RESOLVE, host);
|
||||
|
||||
/* please be verbose */
|
||||
if(verbose_u) {
|
||||
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, debug_cb);
|
||||
}
|
||||
|
||||
/* wait for pipe connection to confirm */
|
||||
curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
|
||||
|
||||
return 0; /* all is good */
|
||||
}
|
||||
|
||||
static void usage_hx_upload(const char *msg)
|
||||
{
|
||||
if(msg)
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
fprintf(stderr,
|
||||
"usage: [options] url\n"
|
||||
" upload to a url with following options:\n"
|
||||
" -a abort paused transfer\n"
|
||||
" -e use TLS earlydata\n"
|
||||
" -m number max parallel uploads\n"
|
||||
" -n number total uploads\n"
|
||||
" -A number abort transfer after `number` request body bytes\n"
|
||||
" -F number fail reading request body after `number` of bytes\n"
|
||||
" -P number pause transfer after `number` request body bytes\n"
|
||||
" -r <host>:<port>:<addr> resolve information\n"
|
||||
" -S number size to upload\n"
|
||||
" -V http_version (http/1.1, h2, h3) http version to use\n"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Download a file over HTTP/2, take care of server push.
|
||||
*/
|
||||
static int test_hx_upload(int argc, char *argv[])
|
||||
{
|
||||
CURLM *multi_handle;
|
||||
CURLSH *share;
|
||||
const char *url;
|
||||
const char *method = "PUT";
|
||||
size_t i, n, max_parallel = 1;
|
||||
size_t active_transfers;
|
||||
size_t pause_offset = 0;
|
||||
size_t abort_offset = 0;
|
||||
size_t fail_offset = 0;
|
||||
size_t send_total = (128 * 1024);
|
||||
int abort_paused = 0;
|
||||
int reuse_easy = 0;
|
||||
int use_earlydata = 0;
|
||||
int announce_length = 0;
|
||||
struct transfer_u *t;
|
||||
int http_version = CURL_HTTP_VERSION_2_0;
|
||||
struct curl_slist *host = NULL;
|
||||
const char *resolve = NULL;
|
||||
int ch;
|
||||
|
||||
while((ch = cgetopt(argc, argv, "aefhlm:n:A:F:M:P:r:RS:V:")) != -1) {
|
||||
switch(ch) {
|
||||
case 'h':
|
||||
usage_hx_upload(NULL);
|
||||
return 2;
|
||||
case 'a':
|
||||
abort_paused = 1;
|
||||
break;
|
||||
case 'e':
|
||||
use_earlydata = 1;
|
||||
break;
|
||||
case 'f':
|
||||
forbid_reuse_u = 1;
|
||||
break;
|
||||
case 'l':
|
||||
announce_length = 1;
|
||||
break;
|
||||
case 'm':
|
||||
max_parallel = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'n':
|
||||
transfer_count_u = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'A':
|
||||
abort_offset = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'F':
|
||||
fail_offset = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'M':
|
||||
method = coptarg;
|
||||
break;
|
||||
case 'P':
|
||||
pause_offset = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'r':
|
||||
resolve = coptarg;
|
||||
break;
|
||||
case 'R':
|
||||
reuse_easy = 1;
|
||||
break;
|
||||
case 'S':
|
||||
send_total = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'V': {
|
||||
if(!strcmp("http/1.1", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_1_1;
|
||||
else if(!strcmp("h2", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_2_0;
|
||||
else if(!strcmp("h3", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_3ONLY;
|
||||
else {
|
||||
usage_hx_upload("invalid http version");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
usage_hx_upload("invalid option");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
argc -= coptind;
|
||||
argv += coptind;
|
||||
|
||||
if(max_parallel > 1 && reuse_easy) {
|
||||
usage_hx_upload("cannot mix -R and -P");
|
||||
return 2;
|
||||
}
|
||||
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
curl_global_trace("ids,time,http/2,http/3");
|
||||
|
||||
if(argc != 1) {
|
||||
usage_hx_upload("not enough arguments");
|
||||
return 2;
|
||||
}
|
||||
url = argv[0];
|
||||
|
||||
if(resolve)
|
||||
host = curl_slist_append(NULL, resolve);
|
||||
|
||||
share = curl_share_init();
|
||||
if(!share) {
|
||||
fprintf(stderr, "error allocating share\n");
|
||||
return 1;
|
||||
}
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
|
||||
|
||||
transfer_u = calloc(transfer_count_u, sizeof(*transfer_u));
|
||||
if(!transfer_u) {
|
||||
fprintf(stderr, "error allocating transfer structs\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
active_transfers = 0;
|
||||
for(i = 0; i < transfer_count_u; ++i) {
|
||||
t = &transfer_u[i];
|
||||
t->idx = (int)i;
|
||||
t->method = method;
|
||||
t->send_total = (curl_off_t)send_total;
|
||||
t->abort_at = (curl_off_t)abort_offset;
|
||||
t->fail_at = (curl_off_t)fail_offset;
|
||||
t->pause_at = (curl_off_t)pause_offset;
|
||||
}
|
||||
|
||||
if(reuse_easy) {
|
||||
CURL *easy = curl_easy_init();
|
||||
CURLcode rc = CURLE_OK;
|
||||
if(!easy) {
|
||||
fprintf(stderr, "failed to init easy handle\n");
|
||||
return 1;
|
||||
}
|
||||
for(i = 0; i < transfer_count_u; ++i) {
|
||||
t = &transfer_u[i];
|
||||
t->easy = easy;
|
||||
if(setup_hx_upload(t->easy, url, t, http_version, host, share,
|
||||
use_earlydata, announce_length)) {
|
||||
fprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "[t-%d] STARTING\n", t->idx);
|
||||
rc = curl_easy_perform(easy);
|
||||
fprintf(stderr, "[t-%d] DONE -> %d\n", t->idx, rc);
|
||||
t->easy = NULL;
|
||||
curl_easy_reset(easy);
|
||||
}
|
||||
curl_easy_cleanup(easy);
|
||||
}
|
||||
else {
|
||||
multi_handle = curl_multi_init();
|
||||
curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
|
||||
|
||||
n = (max_parallel < transfer_count_u) ? max_parallel : transfer_count_u;
|
||||
for(i = 0; i < n; ++i) {
|
||||
t = &transfer_u[i];
|
||||
t->easy = curl_easy_init();
|
||||
if(!t->easy || setup_hx_upload(t->easy, url, t, http_version, host,
|
||||
share, use_earlydata, announce_length)) {
|
||||
fprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
|
||||
return 1;
|
||||
}
|
||||
curl_multi_add_handle(multi_handle, t->easy);
|
||||
t->started = 1;
|
||||
++active_transfers;
|
||||
fprintf(stderr, "[t-%d] STARTED\n", t->idx);
|
||||
}
|
||||
|
||||
do {
|
||||
int still_running; /* keep number of running handles */
|
||||
CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
|
||||
struct CURLMsg *m;
|
||||
|
||||
if(still_running) {
|
||||
/* wait for activity, timeout or "nothing" */
|
||||
mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
|
||||
}
|
||||
|
||||
if(mc)
|
||||
break;
|
||||
|
||||
do {
|
||||
int msgq = 0;
|
||||
m = curl_multi_info_read(multi_handle, &msgq);
|
||||
if(m && (m->msg == CURLMSG_DONE)) {
|
||||
CURL *e = m->easy_handle;
|
||||
--active_transfers;
|
||||
curl_multi_remove_handle(multi_handle, e);
|
||||
t = get_transfer_for_easy_u(e);
|
||||
if(t) {
|
||||
long res_status;
|
||||
curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &res_status);
|
||||
t->done = 1;
|
||||
fprintf(stderr, "[t-%d] FINISHED, result=%d, response=%ld\n",
|
||||
t->idx, m->data.result, res_status);
|
||||
if(use_earlydata) {
|
||||
curl_off_t sent;
|
||||
curl_easy_getinfo(e, CURLINFO_EARLYDATA_SENT_T, &sent);
|
||||
fprintf(stderr, "[t-%d] EarlyData: %ld\n", t->idx, (long)sent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
curl_easy_cleanup(e);
|
||||
fprintf(stderr, "unknown FINISHED???\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* nothing happening, maintenance */
|
||||
if(abort_paused) {
|
||||
/* abort paused transfers */
|
||||
for(i = 0; i < transfer_count_u; ++i) {
|
||||
t = &transfer_u[i];
|
||||
if(!t->done && t->paused && t->easy) {
|
||||
curl_multi_remove_handle(multi_handle, t->easy);
|
||||
t->done = 1;
|
||||
active_transfers--;
|
||||
fprintf(stderr, "[t-%d] ABORTED\n", t->idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* resume one paused transfer */
|
||||
for(i = 0; i < transfer_count_u; ++i) {
|
||||
t = &transfer_u[i];
|
||||
if(!t->done && t->paused) {
|
||||
t->resumed = 1;
|
||||
t->paused = 0;
|
||||
curl_easy_pause(t->easy, CURLPAUSE_CONT);
|
||||
fprintf(stderr, "[t-%d] RESUMED\n", t->idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(active_transfers < max_parallel) {
|
||||
for(i = 0; i < transfer_count_u; ++i) {
|
||||
t = &transfer_u[i];
|
||||
if(!t->started) {
|
||||
t->easy = curl_easy_init();
|
||||
if(!t->easy || setup_hx_upload(t->easy, url, t, http_version,
|
||||
host, share, use_earlydata,
|
||||
announce_length)) {
|
||||
fprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
|
||||
return 1;
|
||||
}
|
||||
curl_multi_add_handle(multi_handle, t->easy);
|
||||
t->started = 1;
|
||||
++active_transfers;
|
||||
fprintf(stderr, "[t-%d] STARTED\n", t->idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* all started */
|
||||
if(i == transfer_count_u)
|
||||
break;
|
||||
}
|
||||
} while(m);
|
||||
|
||||
} while(active_transfers); /* as long as we have transfers going */
|
||||
|
||||
curl_multi_cleanup(multi_handle);
|
||||
}
|
||||
|
||||
for(i = 0; i < transfer_count_u; ++i) {
|
||||
t = &transfer_u[i];
|
||||
if(t->out) {
|
||||
fclose(t->out);
|
||||
t->out = NULL;
|
||||
}
|
||||
if(t->easy) {
|
||||
curl_easy_cleanup(t->easy);
|
||||
t->easy = NULL;
|
||||
}
|
||||
}
|
||||
free(transfer_u);
|
||||
curl_share_cleanup(share);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
static size_t write_tse_cb(char *ptr, size_t size, size_t nmemb, void *opaque)
|
||||
{
|
||||
(void)ptr;
|
||||
(void)opaque;
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static int add_transfer(CURLM *multi, CURLSH *share,
|
||||
struct curl_slist *resolve,
|
||||
const char *url, long http_version)
|
||||
{
|
||||
CURL *easy;
|
||||
CURLMcode mc;
|
||||
|
||||
easy = curl_easy_init();
|
||||
if(!easy) {
|
||||
fprintf(stderr, "curl_easy_init failed\n");
|
||||
return 1;
|
||||
}
|
||||
curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb);
|
||||
curl_easy_setopt(easy, CURLOPT_URL, url);
|
||||
curl_easy_setopt(easy, CURLOPT_SHARE, share);
|
||||
curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, http_version);
|
||||
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_tse_cb);
|
||||
curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
|
||||
curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
if(resolve)
|
||||
curl_easy_setopt(easy, CURLOPT_RESOLVE, resolve);
|
||||
|
||||
|
||||
mc = curl_multi_add_handle(multi, easy);
|
||||
if(mc != CURLM_OK) {
|
||||
fprintf(stderr, "curl_multi_add_handle: %s\n",
|
||||
curl_multi_strerror(mc));
|
||||
curl_easy_cleanup(easy);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_tls_session_reuse(int argc, char *argv[])
|
||||
{
|
||||
const char *url;
|
||||
CURLM *multi = NULL;
|
||||
CURLMcode mc;
|
||||
int running_handles = 0, numfds;
|
||||
CURLMsg *msg;
|
||||
CURLSH *share = NULL;
|
||||
CURLU *cu;
|
||||
struct curl_slist *resolve = NULL;
|
||||
char resolve_buf[1024];
|
||||
int msgs_in_queue;
|
||||
int add_more, waits, ongoing = 0;
|
||||
char *host = NULL, *port = NULL;
|
||||
int http_version = CURL_HTTP_VERSION_1_1;
|
||||
int exitcode = 1;
|
||||
|
||||
if(argc != 3) {
|
||||
fprintf(stderr, "%s proto URL\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(!strcmp("h2", argv[1]))
|
||||
http_version = CURL_HTTP_VERSION_2;
|
||||
else if(!strcmp("h3", argv[1]))
|
||||
http_version = CURL_HTTP_VERSION_3ONLY;
|
||||
|
||||
url = argv[2];
|
||||
cu = curl_url();
|
||||
if(!cu) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
|
||||
fprintf(stderr, "not a URL: '%s'\n", url);
|
||||
goto cleanup;
|
||||
}
|
||||
if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
|
||||
fprintf(stderr, "could not get host of '%s'\n", url);
|
||||
goto cleanup;
|
||||
}
|
||||
if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
|
||||
fprintf(stderr, "could not get port of '%s'\n", url);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1, "%s:%s:127.0.0.1",
|
||||
host, port);
|
||||
resolve = curl_slist_append(resolve, resolve_buf);
|
||||
|
||||
multi = curl_multi_init();
|
||||
if(!multi) {
|
||||
fprintf(stderr, "curl_multi_init failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
share = curl_share_init();
|
||||
if(!share) {
|
||||
fprintf(stderr, "curl_share_init failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
|
||||
|
||||
|
||||
if(add_transfer(multi, share, resolve, url, http_version))
|
||||
goto cleanup;
|
||||
++ongoing;
|
||||
add_more = 6;
|
||||
waits = 3;
|
||||
do {
|
||||
mc = curl_multi_perform(multi, &running_handles);
|
||||
if(mc != CURLM_OK) {
|
||||
fprintf(stderr, "curl_multi_perform: %s\n",
|
||||
curl_multi_strerror(mc));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if(running_handles) {
|
||||
mc = curl_multi_poll(multi, NULL, 0, 1000000, &numfds);
|
||||
if(mc != CURLM_OK) {
|
||||
fprintf(stderr, "curl_multi_poll: %s\n",
|
||||
curl_multi_strerror(mc));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if(waits) {
|
||||
--waits;
|
||||
}
|
||||
else {
|
||||
while(add_more) {
|
||||
if(add_transfer(multi, share, resolve, url, http_version))
|
||||
goto cleanup;
|
||||
++ongoing;
|
||||
--add_more;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for finished handles and remove. */
|
||||
/* !checksrc! disable EQUALSNULL 1 */
|
||||
while((msg = curl_multi_info_read(multi, &msgs_in_queue)) != NULL) {
|
||||
if(msg->msg == CURLMSG_DONE) {
|
||||
long status = 0;
|
||||
curl_off_t xfer_id;
|
||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_XFER_ID, &xfer_id);
|
||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
|
||||
if(msg->data.result == CURLE_SEND_ERROR ||
|
||||
msg->data.result == CURLE_RECV_ERROR) {
|
||||
/* We get these if the server had a GOAWAY in transit on
|
||||
* re-using a connection */
|
||||
}
|
||||
else if(msg->data.result) {
|
||||
curl_mfprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
|
||||
": failed with %d\n", xfer_id, msg->data.result);
|
||||
goto cleanup;
|
||||
}
|
||||
else if(status != 200) {
|
||||
curl_mfprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
|
||||
": wrong http status %ld (expected 200)\n", xfer_id,
|
||||
status);
|
||||
goto cleanup;
|
||||
}
|
||||
curl_multi_remove_handle(multi, msg->easy_handle);
|
||||
curl_easy_cleanup(msg->easy_handle);
|
||||
--ongoing;
|
||||
curl_mfprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T" retiring "
|
||||
"(%d now running)\n", xfer_id, running_handles);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "running_handles=%d, yet_to_start=%d\n",
|
||||
running_handles, add_more);
|
||||
|
||||
} while(ongoing || add_more);
|
||||
|
||||
fprintf(stderr, "exiting\n");
|
||||
exitcode = 0;
|
||||
|
||||
cleanup:
|
||||
|
||||
if(multi) {
|
||||
CURL **list = curl_multi_get_handles(multi);
|
||||
if(list) {
|
||||
int i;
|
||||
for(i = 0; list[i]; i++) {
|
||||
curl_multi_remove_handle(multi, list[i]);
|
||||
curl_easy_cleanup(list[i]);
|
||||
}
|
||||
curl_free(list);
|
||||
}
|
||||
curl_multi_cleanup(multi);
|
||||
}
|
||||
curl_share_cleanup(share);
|
||||
curl_slist_free_all(resolve);
|
||||
curl_free(host);
|
||||
curl_free(port);
|
||||
curl_url_cleanup(cu);
|
||||
|
||||
return exitcode;
|
||||
}
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
/* This is based on the PoC client of issue #11769
|
||||
*/
|
||||
static size_t total_read = 0;
|
||||
|
||||
static size_t read_callback(char *ptr, size_t size, size_t nmemb,
|
||||
void *userdata)
|
||||
{
|
||||
static const size_t PAUSE_READ_AFTER = 1;
|
||||
|
||||
(void)size;
|
||||
(void)nmemb;
|
||||
(void)userdata;
|
||||
if(total_read >= PAUSE_READ_AFTER) {
|
||||
fprintf(stderr, "read_callback, return PAUSE\n");
|
||||
return CURL_READFUNC_PAUSE;
|
||||
}
|
||||
else {
|
||||
ptr[0] = '\n';
|
||||
++total_read;
|
||||
fprintf(stderr, "read_callback, return 1 byte\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int progress_callback(void *clientp,
|
||||
curl_off_t dltotal,
|
||||
curl_off_t dlnow,
|
||||
curl_off_t ultotal,
|
||||
curl_off_t ulnow)
|
||||
{
|
||||
(void)dltotal;
|
||||
(void)dlnow;
|
||||
(void)ultotal;
|
||||
(void)ulnow;
|
||||
(void)clientp;
|
||||
#if 0
|
||||
/* Used to unpause on progress, but keeping for now. */
|
||||
{
|
||||
CURL *curl = (CURL *)clientp;
|
||||
curl_easy_pause(curl, CURLPAUSE_CONT);
|
||||
/* curl_easy_pause(curl, CURLPAUSE_RECV_CONT); */
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage_upload_pausing(const char *msg)
|
||||
{
|
||||
if(msg)
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
fprintf(stderr,
|
||||
"usage: [options] url\n"
|
||||
" upload and pause, options:\n"
|
||||
" -V http_version (http/1.1, h2, h3) http version to use\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int test_upload_pausing(int argc, char *argv[])
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode rc = CURLE_OK;
|
||||
CURLU *cu;
|
||||
struct curl_slist *resolve = NULL;
|
||||
char resolve_buf[1024];
|
||||
char *url, *host = NULL, *port = NULL;
|
||||
long http_version = CURL_HTTP_VERSION_1_1;
|
||||
int ch;
|
||||
|
||||
while((ch = cgetopt(argc, argv, "V:")) != -1) {
|
||||
switch(ch) {
|
||||
case 'V': {
|
||||
if(!strcmp("http/1.1", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_1_1;
|
||||
else if(!strcmp("h2", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_2_0;
|
||||
else if(!strcmp("h3", coptarg))
|
||||
http_version = CURL_HTTP_VERSION_3ONLY;
|
||||
else {
|
||||
usage_upload_pausing("invalid http version");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
usage_upload_pausing("invalid option");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
argc -= coptind;
|
||||
argv += coptind;
|
||||
|
||||
if(argc != 1) {
|
||||
usage_upload_pausing("not enough arguments");
|
||||
return 2;
|
||||
}
|
||||
url = argv[0];
|
||||
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
curl_global_trace("ids,time");
|
||||
|
||||
cu = curl_url();
|
||||
if(!cu) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
|
||||
fprintf(stderr, "not a URL: '%s'\n", url);
|
||||
return 1;
|
||||
}
|
||||
if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
|
||||
fprintf(stderr, "could not get host of '%s'\n", url);
|
||||
return 1;
|
||||
}
|
||||
if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
|
||||
fprintf(stderr, "could not get port of '%s'\n", url);
|
||||
return 1;
|
||||
}
|
||||
memset(&resolve, 0, sizeof(resolve));
|
||||
curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1, "%s:%s:127.0.0.1",
|
||||
host, port);
|
||||
resolve = curl_slist_append(resolve, resolve_buf);
|
||||
|
||||
curl = curl_easy_init();
|
||||
if(!curl) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
/* We want to use our own read function. */
|
||||
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
|
||||
|
||||
/* It will help us to continue the read function. */
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, curl);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
|
||||
|
||||
/* It will help us to ensure that keepalive does not help. */
|
||||
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_TCP_KEEPCNT, 1L);
|
||||
|
||||
/* Enable uploading. */
|
||||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
|
||||
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
|
||||
if(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L) != CURLE_OK ||
|
||||
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, debug_cb)
|
||||
!= CURLE_OK ||
|
||||
curl_easy_setopt(curl, CURLOPT_RESOLVE, resolve) != CURLE_OK)
|
||||
ERR();
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, http_version);
|
||||
|
||||
rc = curl_easy_perform(curl);
|
||||
|
||||
if(curl) {
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
curl_slist_free_all(resolve);
|
||||
curl_free(host);
|
||||
curl_free(port);
|
||||
curl_url_cleanup(cu);
|
||||
curl_global_cleanup();
|
||||
|
||||
return (int)rc;
|
||||
}
|
||||
|
|
@ -1,260 +0,0 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#ifndef CURL_DISABLE_WEBSOCKETS
|
||||
|
||||
static CURLcode check_recv(const struct curl_ws_frame *frame,
|
||||
size_t r_offset, size_t nread, size_t exp_len)
|
||||
{
|
||||
if(!frame)
|
||||
return CURLE_OK;
|
||||
|
||||
if(frame->flags & CURLWS_CLOSE) {
|
||||
fprintf(stderr, "recv_data: unexpected CLOSE frame from server, "
|
||||
"got %ld bytes, offset=%ld, rflags %x\n",
|
||||
(long)nread, (long)r_offset, frame->flags);
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
if(!r_offset && !(frame->flags & CURLWS_BINARY)) {
|
||||
fprintf(stderr, "recv_data: wrong frame, got %ld bytes, offset=%ld, "
|
||||
"rflags %x\n",
|
||||
(long)nread, (long)r_offset, frame->flags);
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
if(frame->offset != (curl_off_t)r_offset) {
|
||||
fprintf(stderr, "recv_data: frame offset, expected %ld, got %ld\n",
|
||||
(long)r_offset, (long)frame->offset);
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
if(frame->bytesleft != (curl_off_t)(exp_len - r_offset - nread)) {
|
||||
fprintf(stderr, "recv_data: frame bytesleft, expected %ld, got %ld\n",
|
||||
(long)(exp_len - r_offset - nread), (long)frame->bytesleft);
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
if(r_offset + nread > exp_len) {
|
||||
fprintf(stderr, "recv_data: data length, expected %ld, now at %ld\n",
|
||||
(long)exp_len, (long)(r_offset + nread));
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode data_echo(CURL *curl, size_t count,
|
||||
size_t plen_min, size_t plen_max)
|
||||
{
|
||||
CURLcode r = CURLE_OK;
|
||||
const struct curl_ws_frame *frame;
|
||||
size_t len;
|
||||
char *send_buf = NULL, *recv_buf = NULL;
|
||||
size_t i, scount = count, rcount = count;
|
||||
int rblock, sblock;
|
||||
|
||||
send_buf = calloc(1, plen_max + 1);
|
||||
recv_buf = calloc(1, plen_max + 1);
|
||||
if(!send_buf || !recv_buf) {
|
||||
r = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for(i = 0; i < plen_max; ++i) {
|
||||
send_buf[i] = (char)('0' + ((int)i % 10));
|
||||
}
|
||||
|
||||
for(len = plen_min; len <= plen_max; ++len) {
|
||||
size_t nwritten, nread, slen = len, rlen = len;
|
||||
char *sbuf = send_buf, *rbuf = recv_buf;
|
||||
|
||||
memset(recv_buf, 0, plen_max);
|
||||
while(slen || rlen || scount || rcount) {
|
||||
sblock = rblock = 1;
|
||||
if(slen) {
|
||||
r = curl_ws_send(curl, sbuf, slen, &nwritten, 0, CURLWS_BINARY);
|
||||
sblock = (r == CURLE_AGAIN);
|
||||
if(!r || (r == CURLE_AGAIN)) {
|
||||
fprintf(stderr, "curl_ws_send(len=%ld) -> %d, %ld (%ld/%ld)\n",
|
||||
(long)slen, r, (long)nwritten,
|
||||
(long)(len - slen), (long)len);
|
||||
sbuf += nwritten;
|
||||
slen -= nwritten;
|
||||
}
|
||||
else
|
||||
goto out;
|
||||
}
|
||||
if(!slen && scount) { /* go again? */
|
||||
scount--;
|
||||
sbuf = send_buf;
|
||||
slen = len;
|
||||
}
|
||||
|
||||
if(rlen) {
|
||||
size_t max_recv = (64 * 1024);
|
||||
r = curl_ws_recv(curl, rbuf, (rlen > max_recv) ? max_recv : rlen,
|
||||
&nread, &frame);
|
||||
if(!r || (r == CURLE_AGAIN)) {
|
||||
rblock = (r == CURLE_AGAIN);
|
||||
fprintf(stderr, "curl_ws_recv(len=%ld) -> %d, %ld (%ld/%ld) \n",
|
||||
(long)rlen, r, (long)nread, (long)(len - rlen), (long)len);
|
||||
if(!r) {
|
||||
r = check_recv(frame, len - rlen, nread, len);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
rbuf += nread;
|
||||
rlen -= nread;
|
||||
}
|
||||
else
|
||||
goto out;
|
||||
}
|
||||
if(!rlen && rcount) { /* go again? */
|
||||
rcount--;
|
||||
rbuf = recv_buf;
|
||||
rlen = len;
|
||||
}
|
||||
|
||||
if(rblock && sblock) {
|
||||
fprintf(stderr, "EAGAIN, sleep, try again\n");
|
||||
#ifdef _WIN32
|
||||
Sleep(100);
|
||||
#elif defined(__TANDEM)
|
||||
/* NonStop only defines usleep when building for a threading model */
|
||||
# if defined(_PUT_MODEL_) || defined(_KLT_MODEL_)
|
||||
usleep(100*1000);
|
||||
# else
|
||||
PROCESS_DELAY_(100*1000);
|
||||
# endif
|
||||
#else
|
||||
usleep(100*1000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(memcmp(send_buf, recv_buf, len)) {
|
||||
fprintf(stderr, "recv_data: data differs\n");
|
||||
dump("expected:", (unsigned char *)send_buf, len, 0);
|
||||
dump("received:", (unsigned char *)recv_buf, len, 0);
|
||||
r = CURLE_RECV_ERROR;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if(!r)
|
||||
websocket_close(curl);
|
||||
free(send_buf);
|
||||
free(recv_buf);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void usage_ws_data(const char *msg)
|
||||
{
|
||||
if(msg)
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
fprintf(stderr,
|
||||
"usage: [options] url\n"
|
||||
" -m number minimum frame size\n"
|
||||
" -M number maximum frame size\n"
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int test_ws_data(int argc, char *argv[])
|
||||
{
|
||||
#ifndef CURL_DISABLE_WEBSOCKETS
|
||||
CURL *curl;
|
||||
CURLcode res = CURLE_OK;
|
||||
const char *url;
|
||||
size_t plen_min = 0, plen_max = 0, count = 1;
|
||||
int ch;
|
||||
|
||||
while((ch = cgetopt(argc, argv, "c:hm:M:")) != -1) {
|
||||
switch(ch) {
|
||||
case 'h':
|
||||
usage_ws_data(NULL);
|
||||
res = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto cleanup;
|
||||
case 'c':
|
||||
count = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'm':
|
||||
plen_min = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
case 'M':
|
||||
plen_max = (size_t)strtol(coptarg, NULL, 10);
|
||||
break;
|
||||
default:
|
||||
usage_ws_data("invalid option");
|
||||
res = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
argc -= coptind;
|
||||
argv += coptind;
|
||||
|
||||
if(!plen_max)
|
||||
plen_max = plen_min;
|
||||
|
||||
if(plen_max < plen_min) {
|
||||
fprintf(stderr, "maxlen must be >= minlen, got %ld-%ld\n",
|
||||
(long)plen_min, (long)plen_max);
|
||||
res = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if(argc != 1) {
|
||||
usage_ws_data(NULL);
|
||||
res = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto cleanup;
|
||||
}
|
||||
url = argv[0];
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
curl = curl_easy_init();
|
||||
if(curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
|
||||
/* use the callback style */
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "ws-data");
|
||||
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */
|
||||
res = curl_easy_perform(curl);
|
||||
fprintf(stderr, "curl_easy_perform() returned %u\n", (int)res);
|
||||
if(res == CURLE_OK)
|
||||
res = data_echo(curl, count, plen_min, plen_max);
|
||||
|
||||
/* always cleanup */
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
curl_global_cleanup();
|
||||
return (int)res;
|
||||
|
||||
#else /* !CURL_DISABLE_WEBSOCKETS */
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
fprintf(stderr, "WebSockets not enabled in libcurl\n");
|
||||
return 1;
|
||||
#endif /* CURL_DISABLE_WEBSOCKETS */
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#ifndef CURL_DISABLE_WEBSOCKETS
|
||||
|
||||
static CURLcode ping(CURL *curl, const char *send_payload)
|
||||
{
|
||||
size_t sent;
|
||||
CURLcode result =
|
||||
curl_ws_send(curl, send_payload, strlen(send_payload), &sent, 0,
|
||||
CURLWS_PING);
|
||||
fprintf(stderr,
|
||||
"ws: curl_ws_send returned %u, sent %u\n", (int)result, (int)sent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode recv_pong(CURL *curl, const char *expected_payload)
|
||||
{
|
||||
size_t rlen;
|
||||
const struct curl_ws_frame *meta;
|
||||
char buffer[256];
|
||||
CURLcode result = curl_ws_recv(curl, buffer, sizeof(buffer), &rlen, &meta);
|
||||
if(result) {
|
||||
fprintf(stderr, "ws: curl_ws_recv returned %u, received %ld\n",
|
||||
(int)result, (long)rlen);
|
||||
return result;
|
||||
}
|
||||
|
||||
if(!(meta->flags & CURLWS_PONG)) {
|
||||
fprintf(stderr, "recv_pong: wrong frame, got %d bytes rflags %x\n",
|
||||
(int)rlen, meta->flags);
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
|
||||
fprintf(stderr, "ws: got PONG back\n");
|
||||
if(rlen == strlen(expected_payload) &&
|
||||
!memcmp(expected_payload, buffer, rlen)) {
|
||||
fprintf(stderr, "ws: got the same payload back\n");
|
||||
return CURLE_OK;
|
||||
}
|
||||
fprintf(stderr, "ws: did NOT get the same payload back\n");
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
|
||||
static CURLcode pingpong(CURL *curl, const char *payload)
|
||||
{
|
||||
CURLcode res;
|
||||
int i;
|
||||
|
||||
res = ping(curl, payload);
|
||||
if(res)
|
||||
return res;
|
||||
for(i = 0; i < 10; ++i) {
|
||||
fprintf(stderr, "Receive pong\n");
|
||||
res = recv_pong(curl, payload);
|
||||
if(res == CURLE_AGAIN) {
|
||||
#ifdef _WIN32
|
||||
Sleep(100);
|
||||
#elif defined(__TANDEM)
|
||||
/* NonStop only defines usleep when building for a threading model */
|
||||
# if defined(_PUT_MODEL_) || defined(_KLT_MODEL_)
|
||||
usleep(100*1000);
|
||||
# else
|
||||
PROCESS_DELAY_(100*1000);
|
||||
# endif
|
||||
#else
|
||||
usleep(100*1000);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
websocket_close(curl);
|
||||
return res;
|
||||
}
|
||||
websocket_close(curl);
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int test_ws_pingpong(int argc, char *argv[])
|
||||
{
|
||||
#ifndef CURL_DISABLE_WEBSOCKETS
|
||||
CURL *curl;
|
||||
CURLcode res = CURLE_OK;
|
||||
const char *url, *payload;
|
||||
|
||||
if(argc != 3) {
|
||||
fprintf(stderr, "usage: ws-pingpong url payload\n");
|
||||
return 2;
|
||||
}
|
||||
url = argv[1];
|
||||
payload = argv[2];
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
curl = curl_easy_init();
|
||||
if(curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
|
||||
/* use the callback style */
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "ws-pingpong");
|
||||
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */
|
||||
res = curl_easy_perform(curl);
|
||||
fprintf(stderr, "curl_easy_perform() returned %u\n", (int)res);
|
||||
if(res == CURLE_OK)
|
||||
res = pingpong(curl, payload);
|
||||
|
||||
/* always cleanup */
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
curl_global_cleanup();
|
||||
return (int)res;
|
||||
|
||||
#else /* !CURL_DISABLE_WEBSOCKETS */
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
fprintf(stderr, "WebSockets not enabled in libcurl\n");
|
||||
return 1;
|
||||
#endif /* CURL_DISABLE_WEBSOCKETS */
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ class LocalClient:
|
|||
timeout: Optional[float] = None,
|
||||
run_env: Optional[Dict[str,str]] = None):
|
||||
self.name = name
|
||||
self.path = os.path.join(env.build_dir, 'tests/http/clients/clients')
|
||||
self.path = os.path.join(env.build_dir, 'tests/client/clients')
|
||||
self.env = env
|
||||
self._run_env = run_env
|
||||
self._timeout = timeout if timeout else env.test_timeout
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue