h2load: Add -d option to upload data to server
This commit is contained in:
parent
faa2c4467a
commit
ed79637737
|
@ -28,6 +28,8 @@
|
|||
#include <signal.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
@ -57,16 +59,27 @@
|
|||
#include "util.h"
|
||||
#include "template.h"
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY (0)
|
||||
#endif // O_BINARY
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace h2load {
|
||||
|
||||
Config::Config()
|
||||
: addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
||||
: data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
||||
max_concurrent_streams(-1), window_bits(16), connection_window_bits(16),
|
||||
no_tls_proto(PROTO_HTTP2), port(0), default_port(0), verbose(false) {}
|
||||
no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0),
|
||||
verbose(false) {}
|
||||
|
||||
Config::~Config() { freeaddrinfo(addrs); }
|
||||
Config::~Config() {
|
||||
freeaddrinfo(addrs);
|
||||
|
||||
if (data_fd != -1) {
|
||||
close(data_fd);
|
||||
}
|
||||
}
|
||||
|
||||
Config config;
|
||||
|
||||
|
@ -95,7 +108,7 @@ void debug_nextproto_error() {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
RequestStat::RequestStat() : completed(false) {}
|
||||
RequestStat::RequestStat() : data_offset(0), completed(false) {}
|
||||
|
||||
Stats::Stats(size_t req_todo)
|
||||
: req_todo(0), req_started(0), req_done(0), req_success(0),
|
||||
|
@ -987,6 +1000,9 @@ Options:
|
|||
#endif // !HAVE_SPDYLAY
|
||||
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||
Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||
-d, --data=<FILE>
|
||||
Post FILE to server. The request method is changed to
|
||||
POST.
|
||||
-v, --verbose
|
||||
Output debug information.
|
||||
--version Display version information and exit.
|
||||
|
@ -995,11 +1011,13 @@ Options:
|
|||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::string datafile;
|
||||
while (1) {
|
||||
static int flag = 0;
|
||||
static option long_options[] = {
|
||||
{"requests", required_argument, nullptr, 'n'},
|
||||
{"clients", required_argument, nullptr, 'c'},
|
||||
{"data", required_argument, nullptr, 'd'},
|
||||
{"threads", required_argument, nullptr, 't'},
|
||||
{"max-concurrent-streams", required_argument, nullptr, 'm'},
|
||||
{"window-bits", required_argument, nullptr, 'w'},
|
||||
|
@ -1012,7 +1030,7 @@ int main(int argc, char **argv) {
|
|||
{"version", no_argument, &flag, 1},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
int option_index = 0;
|
||||
auto c = getopt_long(argc, argv, "hvW:c:m:n:p:t:w:H:i:", long_options,
|
||||
auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:", long_options,
|
||||
&option_index);
|
||||
if (c == -1) {
|
||||
break;
|
||||
|
@ -1024,6 +1042,9 @@ int main(int argc, char **argv) {
|
|||
case 'c':
|
||||
config.nclients = strtoul(optarg, nullptr, 10);
|
||||
break;
|
||||
case 'd':
|
||||
datafile = optarg;
|
||||
break;
|
||||
case 't':
|
||||
#ifdef NOTHREADS
|
||||
std::cerr << "-t: WARNING: Threading disabled at build time, "
|
||||
|
@ -1162,6 +1183,20 @@ int main(int argc, char **argv) {
|
|||
<< "cores." << std::endl;
|
||||
}
|
||||
|
||||
if (!datafile.empty()) {
|
||||
config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY);
|
||||
if (config.data_fd == -1) {
|
||||
std::cerr << "-d: Could not open file " << datafile << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
struct stat data_stat;
|
||||
if (fstat(config.data_fd, &data_stat) == -1) {
|
||||
std::cerr << "-d: Could not stat file " << datafile << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
config.data_length = data_stat.st_size;
|
||||
}
|
||||
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(struct sigaction));
|
||||
act.sa_handler = SIG_IGN;
|
||||
|
@ -1243,7 +1278,7 @@ int main(int argc, char **argv) {
|
|||
} else {
|
||||
shared_nva.emplace_back(":authority", config.host);
|
||||
}
|
||||
shared_nva.emplace_back(":method", "GET");
|
||||
shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST");
|
||||
|
||||
// list overridalbe headers
|
||||
auto override_hdrs =
|
||||
|
|
|
@ -60,6 +60,8 @@ struct Config {
|
|||
std::string scheme;
|
||||
std::string host;
|
||||
std::string ifile;
|
||||
// length of upload data
|
||||
int64_t data_length;
|
||||
addrinfo *addrs;
|
||||
size_t nreqs;
|
||||
size_t nclients;
|
||||
|
@ -69,6 +71,8 @@ struct Config {
|
|||
size_t window_bits;
|
||||
size_t connection_window_bits;
|
||||
enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto;
|
||||
// file descriptor for upload data
|
||||
int data_fd;
|
||||
uint16_t port;
|
||||
uint16_t default_port;
|
||||
bool verbose;
|
||||
|
@ -83,6 +87,8 @@ struct RequestStat {
|
|||
std::chrono::steady_clock::time_point request_time;
|
||||
// time point when stream was closed
|
||||
std::chrono::steady_clock::time_point stream_close_time;
|
||||
// upload data length sent so far
|
||||
int64_t data_offset;
|
||||
// true if stream was successfully closed. This means stream was
|
||||
// not reset, but it does not mean HTTP level error (e.g., 404).
|
||||
bool completed;
|
||||
|
|
|
@ -110,6 +110,36 @@ int before_frame_send_callback(nghttp2_session *session,
|
|||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data) {
|
||||
auto client = static_cast<Client *>(user_data);
|
||||
auto config = client->worker->config;
|
||||
auto req_stat = static_cast<RequestStat *>(
|
||||
nghttp2_session_get_stream_user_data(session, stream_id));
|
||||
|
||||
ssize_t nread;
|
||||
while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) ==
|
||||
-1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
|
||||
if (nread == -1) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
req_stat->data_offset += nread;
|
||||
|
||||
if (nread == 0 || req_stat->data_offset == config->data_length) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
|
||||
size_t length, int flags, void *user_data) {
|
||||
|
@ -188,8 +218,11 @@ void Http2Session::submit_request(RequestStat *req_stat) {
|
|||
client_->reqidx = 0;
|
||||
}
|
||||
|
||||
auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(),
|
||||
nva.size(), nullptr, req_stat);
|
||||
nghttp2_data_provider prd{{0}, file_read_callback};
|
||||
|
||||
auto stream_id =
|
||||
nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(),
|
||||
config->data_fd == -1 ? nullptr : &prd, req_stat);
|
||||
assert(stream_id > 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,35 @@ ssize_t send_callback(spdylay_session *session, const uint8_t *data,
|
|||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
ssize_t file_read_callback(spdylay_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, int *eof,
|
||||
spdylay_data_source *source, void *user_data) {
|
||||
auto client = static_cast<Client *>(user_data);
|
||||
auto config = client->worker->config;
|
||||
auto req_stat = static_cast<RequestStat *>(
|
||||
spdylay_session_get_stream_user_data(session, stream_id));
|
||||
|
||||
ssize_t nread;
|
||||
while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) ==
|
||||
-1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
|
||||
if (nread == -1) {
|
||||
return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
req_stat->data_offset += nread;
|
||||
|
||||
if (nread == 0 || req_stat->data_offset == config->data_length) {
|
||||
*eof = 1;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void SpdySession::on_connect() {
|
||||
spdylay_session_callbacks callbacks = {0};
|
||||
callbacks.send_callback = send_callback;
|
||||
|
@ -150,7 +179,10 @@ void SpdySession::submit_request(RequestStat *req_stat) {
|
|||
client_->reqidx = 0;
|
||||
}
|
||||
|
||||
spdylay_submit_request(session_, 0, nv.data(), nullptr, req_stat);
|
||||
spdylay_data_provider prd{{0}, file_read_callback};
|
||||
|
||||
spdylay_submit_request(session_, 0, nv.data(),
|
||||
config->data_fd == -1 ? nullptr : &prd, req_stat);
|
||||
}
|
||||
|
||||
int SpdySession::on_read(const uint8_t *data, size_t len) {
|
||||
|
|
Loading…
Reference in New Issue