diff --git a/examples/spdycat.cc b/examples/spdycat.cc index 04ef47f9..205e3962 100644 --- a/examples/spdycat.cc +++ b/examples/spdycat.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,8 @@ #include #include #include +#include +#include #include #include @@ -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 stream2req; +std::map 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::iterator itr = stream2req.find(stream_id); + if(itr != stream2req.end()) { std::cout.write(reinterpret_cast(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::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::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& 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 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); +} diff --git a/examples/spdylay_ssl.cc b/examples/spdylay_ssl.cc index 3b7ea78a..99d94b0c 100644 --- a/examples/spdylay_ssl.cc +++ b/examples/spdylay_ssl.cc @@ -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 diff --git a/examples/spdylay_ssl.h b/examples/spdylay_ssl.h index fb7d7e1a..5582f965 100644 --- a/examples/spdylay_ssl.h +++ b/examples/spdylay_ssl.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -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