Added proper command-line option support using getopt_long.

Now -nvh option works.
This commit is contained in:
Tatsuhiro Tsujikawa 2012-01-31 00:46:46 +09:00
parent 856d230595
commit d3a3dc5943
3 changed files with 204 additions and 82 deletions

View File

@ -31,6 +31,7 @@
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <signal.h>
#include <getopt.h>
#include <cassert>
#include <cstdio>
@ -43,6 +44,8 @@
#include <set>
#include <iomanip>
#include <fstream>
#include <map>
#include <vector>
#include <openssl/ssl.h>
#include <openssl/err.h>
@ -53,16 +56,31 @@
namespace spdylay {
std::string target_path;
int32_t target_stream_id;
bool debug = false;
struct Config {
bool null_out;
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;
void on_data_chunk_recv_callback
(spdylay_session *session, uint8_t flags, int32_t stream_id,
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);
}
}
@ -72,8 +90,10 @@ void check_stream_id(spdylay_frame_type type, spdylay_frame *frame)
if(type == SPDYLAY_SYN_STREAM) {
for(int i = 0; frame->syn_stream.nv[i]; i += 2) {
if(strcmp("url", frame->syn_stream.nv[i]) == 0) {
if(target_path == frame->syn_stream.nv[i+1]) {
target_stream_id = frame->syn_stream.stream_id;
std::map<std::string, Request*>::iterator itr = path2req.find
(frame->syn_stream.nv[i+1]);
if(itr != path2req.end()) {
stream2req[frame->syn_stream.stream_id] = (*itr).second;
}
break;
}
@ -100,17 +120,23 @@ void on_stream_close_callback
(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code,
void *user_data)
{
if(target_stream_id == stream_id) {
spdylay_submit_goaway(session);
std::map<int32_t, Request*>::iterator itr = stream2req.find(stream_id);
if(itr != stream2req.end()) {
++complete;
if(complete == numreq) {
spdylay_submit_goaway(session);
}
}
}
int communicate(const std::string& host, uint16_t port,
const std::string& path,
std::vector<Request>& reqvec,
const spdylay_session_callbacks *callbacks)
{
target_path = path;
ssl_debug = debug;
numreq = reqvec.size();
complete = 0;
path2req.clear();
stream2req.clear();
int fd = connect_to(host, port);
if(fd == -1) {
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");
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);
static const size_t MAX_EVENTS = 1;
epoll_event events[MAX_EVENTS];
@ -177,48 +208,100 @@ int communicate(const std::string& host, uint16_t port,
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;
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
callbacks.send_callback = send_callback;
callbacks.recv_callback = recv_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_data_recv_callback = on_data_recv_callback;
callbacks.on_ctrl_send_callback = on_ctrl_send_callback3;
} else {
callbacks.on_ctrl_send_callback = on_ctrl_send_callback2;
}
if(!config.null_out) {
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(const char *prog)
void print_usage(std::ostream& out)
{
std::cerr << "Usage: " << prog << " [-d] URI" << std::endl;
std::cerr << " Get the resource identified by URI via spdy/2 protocol and\n"
<< " output the resource.\n"
<< "\n"
<< " -d: Output debug information instead of output the resource."
<< std::endl;
out << "Usage: spdycat [-Onv] [URI...]" << std::endl;
}
void print_help(std::ostream& out)
{
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;
}
int main(int argc, char **argv)
{
std::string uri;
if(argc == 2) {
spdylay::debug = false;
uri = argv[1];
} else if(argc == 3 && strcmp(argv[1], "-d") == 0) {
spdylay::debug = true;
uri = argv[2];
} else {
print_usage(basename(argv[0]));
exit(EXIT_FAILURE);
while(1) {
static option long_options[] = {
{"verbose", no_argument, 0, 'v' },
{"null-out", no_argument, 0, 'n' },
{"remote-name", no_argument, 0, 'O' },
{"help", no_argument, 0, 'h' },
{0, 0, 0, 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);
default:
break;
}
}
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
@ -226,11 +309,14 @@ int main(int argc, char **argv)
sigaction(SIGPIPE, &act, 0);
SSL_load_error_strings();
SSL_library_init();
spdylay::uri::UriStruct us;
if(!spdylay::uri::parse(us, uri)) {
std::cerr << "Could not parse URI" << std::endl;
exit(EXIT_FAILURE);
}
spdylay::run(us.host, us.port, us.dir+us.file+us.query);
reset_timer();
run(argv+optind, argc-optind);
return 0;
}
} // namespace spdylay
int main(int argc, char **argv)
{
return spdylay::main(argc, argv);
}

View File

@ -204,7 +204,8 @@ ssize_t recv_callback(spdylay_session *session,
return r;
}
static const char *ctrl_names[] = {
namespace {
const char *ctrl_names[] = {
"SYN_STREAM",
"SYN_REPLY",
"RST_STREAM",
@ -214,6 +215,7 @@ static const char *ctrl_names[] = {
"GOAWAY",
"HEADERS"
};
} // namespace
void print_nv(char **nv)
{
@ -223,14 +225,26 @@ void print_nv(char **nv)
}
}
void on_ctrl_recv_callback
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data)
void print_timer()
{
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) {
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:
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.hd.length);
print_nv(frame->syn_reply.nv);
@ -238,40 +252,6 @@ void on_ctrl_recv_callback
case SPDYLAY_PING:
printf("(unique_id=%d)\n", frame->ping.unique_id);
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:
printf("(last_good_stream_id=%d)\n", frame->goaway.last_good_stream_id);
break;
@ -279,6 +259,35 @@ void on_ctrl_send_callback
printf("\n");
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);
}
@ -306,7 +315,9 @@ int select_next_proto_cb(SSL* ssl,
*out = (unsigned char*)in+1;
*outlen = in[0];
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) {
if(ssl_debug) {
@ -345,4 +356,24 @@ int ssl_handshake(SSL *ssl, int fd)
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

View File

@ -27,6 +27,7 @@
#include <stdint.h>
#include <cstdlib>
#include <time.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
@ -93,6 +94,10 @@ void setup_ssl_ctx(SSL_CTX *ssl_ctx);
int ssl_handshake(SSL *ssl, int fd);
void reset_timer();
void get_timer(timespec *ts);
} // namespace spdylay
#endif // SPDYLAY_SSL_H