Added proper command-line option support using getopt_long.
Now -nvh option works.
This commit is contained in:
parent
856d230595
commit
d3a3dc5943
|
@ -31,6 +31,7 @@
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -43,6 +44,8 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
@ -53,16 +56,31 @@
|
||||||
|
|
||||||
namespace spdylay {
|
namespace spdylay {
|
||||||
|
|
||||||
std::string target_path;
|
struct Config {
|
||||||
int32_t target_stream_id;
|
bool null_out;
|
||||||
bool debug = false;
|
bool remote_name;
|
||||||
|
bool verbose;
|
||||||
|
Config():null_out(false), remote_name(false), verbose(false) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Request {
|
||||||
|
uri::UriStruct us;
|
||||||
|
Request(const uri::UriStruct& us):us(us) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<int32_t, Request*> stream2req;
|
||||||
|
std::map<std::string, Request*> path2req;
|
||||||
|
size_t numreq, complete;
|
||||||
|
|
||||||
|
Config config;
|
||||||
extern bool ssl_debug;
|
extern bool ssl_debug;
|
||||||
|
|
||||||
void on_data_chunk_recv_callback
|
void on_data_chunk_recv_callback
|
||||||
(spdylay_session *session, uint8_t flags, int32_t stream_id,
|
(spdylay_session *session, uint8_t flags, int32_t stream_id,
|
||||||
const uint8_t *data, size_t len, void *user_data)
|
const uint8_t *data, size_t len, void *user_data)
|
||||||
{
|
{
|
||||||
if(!debug && stream_id == target_stream_id) {
|
std::map<int32_t, Request*>::iterator itr = stream2req.find(stream_id);
|
||||||
|
if(itr != stream2req.end()) {
|
||||||
std::cout.write(reinterpret_cast<const char*>(data), len);
|
std::cout.write(reinterpret_cast<const char*>(data), len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,8 +90,10 @@ void check_stream_id(spdylay_frame_type type, spdylay_frame *frame)
|
||||||
if(type == SPDYLAY_SYN_STREAM) {
|
if(type == SPDYLAY_SYN_STREAM) {
|
||||||
for(int i = 0; frame->syn_stream.nv[i]; i += 2) {
|
for(int i = 0; frame->syn_stream.nv[i]; i += 2) {
|
||||||
if(strcmp("url", frame->syn_stream.nv[i]) == 0) {
|
if(strcmp("url", frame->syn_stream.nv[i]) == 0) {
|
||||||
if(target_path == frame->syn_stream.nv[i+1]) {
|
std::map<std::string, Request*>::iterator itr = path2req.find
|
||||||
target_stream_id = frame->syn_stream.stream_id;
|
(frame->syn_stream.nv[i+1]);
|
||||||
|
if(itr != path2req.end()) {
|
||||||
|
stream2req[frame->syn_stream.stream_id] = (*itr).second;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -100,17 +120,23 @@ void on_stream_close_callback
|
||||||
(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code,
|
(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
if(target_stream_id == stream_id) {
|
std::map<int32_t, Request*>::iterator itr = stream2req.find(stream_id);
|
||||||
|
if(itr != stream2req.end()) {
|
||||||
|
++complete;
|
||||||
|
if(complete == numreq) {
|
||||||
spdylay_submit_goaway(session);
|
spdylay_submit_goaway(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int communicate(const std::string& host, uint16_t port,
|
int communicate(const std::string& host, uint16_t port,
|
||||||
const std::string& path,
|
std::vector<Request>& reqvec,
|
||||||
const spdylay_session_callbacks *callbacks)
|
const spdylay_session_callbacks *callbacks)
|
||||||
{
|
{
|
||||||
target_path = path;
|
numreq = reqvec.size();
|
||||||
ssl_debug = debug;
|
complete = 0;
|
||||||
|
path2req.clear();
|
||||||
|
stream2req.clear();
|
||||||
int fd = connect_to(host, port);
|
int fd = connect_to(host, port);
|
||||||
if(fd == -1) {
|
if(fd == -1) {
|
||||||
std::cerr << "Could not connect to the host" << std::endl;
|
std::cerr << "Could not connect to the host" << std::endl;
|
||||||
|
@ -138,8 +164,13 @@ int communicate(const std::string& host, uint16_t port,
|
||||||
perror("epoll_create");
|
perror("epoll_create");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
sc.submit_request(path, 3);
|
for(int i = 0, n = reqvec.size(); i < n; ++i) {
|
||||||
|
uri::UriStruct& us = reqvec[i].us;
|
||||||
|
std::string path = us.dir+us.file+us.query;
|
||||||
|
int r = sc.submit_request(path, 3);
|
||||||
|
assert(r == 0);
|
||||||
|
path2req[path] = &reqvec[i];
|
||||||
|
}
|
||||||
ctl_epollev(epollfd, EPOLL_CTL_ADD, &sc);
|
ctl_epollev(epollfd, EPOLL_CTL_ADD, &sc);
|
||||||
static const size_t MAX_EVENTS = 1;
|
static const size_t MAX_EVENTS = 1;
|
||||||
epoll_event events[MAX_EVENTS];
|
epoll_event events[MAX_EVENTS];
|
||||||
|
@ -177,48 +208,100 @@ int communicate(const std::string& host, uint16_t port,
|
||||||
return ok ? 0 : -1;
|
return ok ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int run(const std::string& host, uint16_t port, const std::string& path)
|
int run(char **uris, int n)
|
||||||
{
|
{
|
||||||
spdylay_session_callbacks callbacks;
|
spdylay_session_callbacks callbacks;
|
||||||
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
|
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
|
||||||
callbacks.send_callback = send_callback;
|
callbacks.send_callback = send_callback;
|
||||||
callbacks.recv_callback = recv_callback;
|
callbacks.recv_callback = recv_callback;
|
||||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||||
if(debug) {
|
if(config.verbose) {
|
||||||
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;
|
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;
|
||||||
callbacks.on_data_recv_callback = on_data_recv_callback;
|
callbacks.on_data_recv_callback = on_data_recv_callback;
|
||||||
callbacks.on_ctrl_send_callback = on_ctrl_send_callback3;
|
callbacks.on_ctrl_send_callback = on_ctrl_send_callback3;
|
||||||
} else {
|
} else {
|
||||||
callbacks.on_ctrl_send_callback = on_ctrl_send_callback2;
|
callbacks.on_ctrl_send_callback = on_ctrl_send_callback2;
|
||||||
|
}
|
||||||
|
if(!config.null_out) {
|
||||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||||
}
|
}
|
||||||
return communicate(host, port, path, &callbacks);
|
ssl_debug = config.verbose;
|
||||||
|
std::vector<Request> reqvec;
|
||||||
|
std::string prev_host;
|
||||||
|
uint16_t prev_port = 0;
|
||||||
|
for(int i = 0; i < n; ++i) {
|
||||||
|
uri::UriStruct us;
|
||||||
|
if(uri::parse(us, uris[i])) {
|
||||||
|
if(prev_host != us.host || prev_port != us.port) {
|
||||||
|
if(!reqvec.empty()) {
|
||||||
|
communicate(prev_host, prev_port, reqvec, &callbacks);
|
||||||
|
reqvec.clear();
|
||||||
|
}
|
||||||
|
prev_host = us.host;
|
||||||
|
prev_port = us.port;
|
||||||
|
}
|
||||||
|
reqvec.push_back(Request(us));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!reqvec.empty()) {
|
||||||
|
communicate(prev_host, prev_port, reqvec, &callbacks);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace spdylay
|
void print_usage(std::ostream& out)
|
||||||
|
|
||||||
void print_usage(const char *prog)
|
|
||||||
{
|
{
|
||||||
std::cerr << "Usage: " << prog << " [-d] URI" << std::endl;
|
out << "Usage: spdycat [-Onv] [URI...]" << std::endl;
|
||||||
std::cerr << " Get the resource identified by URI via spdy/2 protocol and\n"
|
}
|
||||||
<< " output the resource.\n"
|
|
||||||
<< "\n"
|
void print_help(std::ostream& out)
|
||||||
<< " -d: Output debug information instead of output the resource."
|
{
|
||||||
|
print_usage(out);
|
||||||
|
out << "\n"
|
||||||
|
<< "OPTIONS:\n"
|
||||||
|
<< " -v, --verbose Print debug information such as reception/\n"
|
||||||
|
<< " transmission of frames and name/value pairs.\n"
|
||||||
|
<< " -n, --null-out Discard downloaded data.\n"
|
||||||
|
<< " -O, --remote-name Save download data in the current directory.\n"
|
||||||
|
<< " The filename is dereived from URI. If URI\n"
|
||||||
|
<< " ends with '/', 'index.html' is used as a\n"
|
||||||
|
<< " filename. Not implemented yet.\n"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
std::string uri;
|
while(1) {
|
||||||
if(argc == 2) {
|
static option long_options[] = {
|
||||||
spdylay::debug = false;
|
{"verbose", no_argument, 0, 'v' },
|
||||||
uri = argv[1];
|
{"null-out", no_argument, 0, 'n' },
|
||||||
} else if(argc == 3 && strcmp(argv[1], "-d") == 0) {
|
{"remote-name", no_argument, 0, 'O' },
|
||||||
spdylay::debug = true;
|
{"help", no_argument, 0, 'h' },
|
||||||
uri = argv[2];
|
{0, 0, 0, 0 }
|
||||||
} else {
|
};
|
||||||
print_usage(basename(argv[0]));
|
int option_index = 0;
|
||||||
|
int c = getopt_long(argc, argv, "Onhv", long_options, &option_index);
|
||||||
|
if(c == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch(c) {
|
||||||
|
case 'O':
|
||||||
|
config.remote_name = true;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
print_help(std::cout);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
case 'n':
|
||||||
|
config.null_out = true;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
config.verbose = true;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
memset(&act, 0, sizeof(struct sigaction));
|
memset(&act, 0, sizeof(struct sigaction));
|
||||||
|
@ -226,11 +309,14 @@ int main(int argc, char **argv)
|
||||||
sigaction(SIGPIPE, &act, 0);
|
sigaction(SIGPIPE, &act, 0);
|
||||||
SSL_load_error_strings();
|
SSL_load_error_strings();
|
||||||
SSL_library_init();
|
SSL_library_init();
|
||||||
spdylay::uri::UriStruct us;
|
reset_timer();
|
||||||
if(!spdylay::uri::parse(us, uri)) {
|
run(argv+optind, argc-optind);
|
||||||
std::cerr << "Could not parse URI" << std::endl;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
spdylay::run(us.host, us.port, us.dir+us.file+us.query);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace spdylay
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
return spdylay::main(argc, argv);
|
||||||
|
}
|
||||||
|
|
|
@ -204,7 +204,8 @@ ssize_t recv_callback(spdylay_session *session,
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *ctrl_names[] = {
|
namespace {
|
||||||
|
const char *ctrl_names[] = {
|
||||||
"SYN_STREAM",
|
"SYN_STREAM",
|
||||||
"SYN_REPLY",
|
"SYN_REPLY",
|
||||||
"RST_STREAM",
|
"RST_STREAM",
|
||||||
|
@ -214,6 +215,7 @@ static const char *ctrl_names[] = {
|
||||||
"GOAWAY",
|
"GOAWAY",
|
||||||
"HEADERS"
|
"HEADERS"
|
||||||
};
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void print_nv(char **nv)
|
void print_nv(char **nv)
|
||||||
{
|
{
|
||||||
|
@ -223,14 +225,26 @@ void print_nv(char **nv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_ctrl_recv_callback
|
void print_timer()
|
||||||
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
|
|
||||||
void *user_data)
|
|
||||||
{
|
{
|
||||||
printf("recv %s frame ", ctrl_names[type-1]);
|
timespec ts;
|
||||||
|
get_timer(&ts);
|
||||||
|
printf("[%3ld.%03ld]", ts.tv_sec, ts.tv_nsec/1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_frame(spdylay_frame_type type, spdylay_frame *frame)
|
||||||
|
{
|
||||||
|
printf("%s frame ", ctrl_names[type-1]);
|
||||||
switch(type) {
|
switch(type) {
|
||||||
|
case SPDYLAY_SYN_STREAM:
|
||||||
|
printf("(stream_id=%d, assoc_stream_id=%d, flags=%u, length=%d, pri=%u)\n",
|
||||||
|
frame->syn_stream.stream_id, frame->syn_stream.assoc_stream_id,
|
||||||
|
frame->syn_stream.hd.flags,
|
||||||
|
frame->syn_stream.hd.length, frame->syn_stream.pri);
|
||||||
|
print_nv(frame->syn_stream.nv);
|
||||||
|
break;
|
||||||
case SPDYLAY_SYN_REPLY:
|
case SPDYLAY_SYN_REPLY:
|
||||||
printf("(stream_id=%d, flags=%d, length=%d)\n",
|
printf("(stream_id=%d, flags=%u, length=%d)\n",
|
||||||
frame->syn_reply.stream_id, frame->syn_reply.hd.flags,
|
frame->syn_reply.stream_id, frame->syn_reply.hd.flags,
|
||||||
frame->syn_reply.hd.length);
|
frame->syn_reply.hd.length);
|
||||||
print_nv(frame->syn_reply.nv);
|
print_nv(frame->syn_reply.nv);
|
||||||
|
@ -238,40 +252,6 @@ void on_ctrl_recv_callback
|
||||||
case SPDYLAY_PING:
|
case SPDYLAY_PING:
|
||||||
printf("(unique_id=%d)\n", frame->ping.unique_id);
|
printf("(unique_id=%d)\n", frame->ping.unique_id);
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
printf("\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_data_recv_callback
|
|
||||||
(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
printf("recv DATA frame (stream_id=%d, flags=%d, length=%d)\n",
|
|
||||||
stream_id, flags, length);
|
|
||||||
// if(flags & SPDYLAY_FLAG_FIN) {
|
|
||||||
// spdylay_submit_ping(session);
|
|
||||||
// }
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_ctrl_send_callback
|
|
||||||
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
printf("send %s frame ", ctrl_names[type-1]);
|
|
||||||
switch(type) {
|
|
||||||
case SPDYLAY_SYN_STREAM:
|
|
||||||
printf("(stream_id=%d, flags=%d, length=%d)\n",
|
|
||||||
frame->syn_stream.stream_id, frame->syn_stream.hd.flags,
|
|
||||||
frame->syn_stream.hd.length);
|
|
||||||
print_nv(frame->syn_stream.nv);
|
|
||||||
break;
|
|
||||||
case SPDYLAY_PING:
|
|
||||||
printf("(unique_id=%d)\n", frame->ping.unique_id);
|
|
||||||
break;
|
|
||||||
case SPDYLAY_GOAWAY:
|
case SPDYLAY_GOAWAY:
|
||||||
printf("(last_good_stream_id=%d)\n", frame->goaway.last_good_stream_id);
|
printf("(last_good_stream_id=%d)\n", frame->goaway.last_good_stream_id);
|
||||||
break;
|
break;
|
||||||
|
@ -279,6 +259,35 @@ void on_ctrl_send_callback
|
||||||
printf("\n");
|
printf("\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_ctrl_recv_callback
|
||||||
|
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
print_timer();
|
||||||
|
printf(" recv ");
|
||||||
|
print_frame(type, frame);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_data_recv_callback
|
||||||
|
(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
print_timer();
|
||||||
|
printf(" recv DATA frame (stream_id=%d, flags=%d, length=%d)\n",
|
||||||
|
stream_id, flags, length);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_ctrl_send_callback
|
||||||
|
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
print_timer();
|
||||||
|
printf(" send ");
|
||||||
|
print_frame(type, frame);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +315,9 @@ int select_next_proto_cb(SSL* ssl,
|
||||||
*out = (unsigned char*)in+1;
|
*out = (unsigned char*)in+1;
|
||||||
*outlen = in[0];
|
*outlen = in[0];
|
||||||
if(ssl_debug) {
|
if(ssl_debug) {
|
||||||
std::cout << "NPN select next proto: server offers:" << std::endl;
|
print_timer();
|
||||||
|
std::cout << " NPN select next protocol: the remote server offers:"
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
for(unsigned int i = 0; i < inlen; i += in[i]+1) {
|
for(unsigned int i = 0; i < inlen; i += in[i]+1) {
|
||||||
if(ssl_debug) {
|
if(ssl_debug) {
|
||||||
|
@ -345,4 +356,24 @@ int ssl_handshake(SSL *ssl, int fd)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
timespec basets;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void reset_timer()
|
||||||
|
{
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &basets);
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_timer(timespec* ts)
|
||||||
|
{
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, ts);
|
||||||
|
ts->tv_nsec -= basets.tv_nsec;
|
||||||
|
ts->tv_sec -= basets.tv_sec;
|
||||||
|
if(ts->tv_nsec < 0) {
|
||||||
|
ts->tv_nsec += 1000000000;
|
||||||
|
--ts->tv_sec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace spdylay
|
} // namespace spdylay
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
@ -93,6 +94,10 @@ void setup_ssl_ctx(SSL_CTX *ssl_ctx);
|
||||||
|
|
||||||
int ssl_handshake(SSL *ssl, int fd);
|
int ssl_handshake(SSL *ssl, int fd);
|
||||||
|
|
||||||
|
void reset_timer();
|
||||||
|
|
||||||
|
void get_timer(timespec *ts);
|
||||||
|
|
||||||
} // namespace spdylay
|
} // namespace spdylay
|
||||||
|
|
||||||
#endif // SPDYLAY_SSL_H
|
#endif // SPDYLAY_SSL_H
|
||||||
|
|
Loading…
Reference in New Issue