1
0
mirror of https://github.com/moparisthebest/spdylay synced 2024-12-22 15:48:53 -05:00

Added -s, --stat option to print statistics

Print, for each stream, time delta from SSL/TLS handshake and each
SYN_STREAM. The time deltas are measured after SYN_REPLY received and
all data received.
For example, the following output:

  SYN_REPLY: X(Y)

means SYN_REPLY was received X ms after SSL/TLS handshake and Y ms
after the corresponding SYN_STREAM was sent.
This commit is contained in:
Tatsuhiro Tsujikawa 2012-05-19 21:49:00 +09:00
parent 991ded912d
commit e2332abc1d

View File

@ -66,22 +66,46 @@ struct Config {
bool remote_name; bool remote_name;
bool verbose; bool verbose;
bool get_assets; bool get_assets;
bool stat;
int spdy_version; int spdy_version;
int timeout; int timeout;
std::string certfile; std::string certfile;
std::string keyfile; std::string keyfile;
int window_bits; int window_bits;
Config():null_out(false), remote_name(false), verbose(false), Config():null_out(false), remote_name(false), verbose(false),
get_assets(false), spdy_version(-1), timeout(-1), window_bits(-1) get_assets(false), stat(false),
spdy_version(-1), timeout(-1), window_bits(-1)
{} {}
}; };
struct RequestStat {
timeval on_syn_stream_time;
timeval on_syn_reply_time;
timeval on_complete_time;
RequestStat()
{
on_syn_stream_time.tv_sec = -1;
on_syn_stream_time.tv_usec = -1;
on_syn_reply_time.tv_sec = -1;
on_syn_reply_time.tv_usec = -1;
on_complete_time.tv_sec = -1;
on_complete_time.tv_usec = -1;
}
};
void record_time(timeval *tv)
{
gettimeofday(tv, 0);
}
struct Request { struct Request {
uri::UriStruct us; uri::UriStruct us;
spdylay_gzip *inflater; spdylay_gzip *inflater;
HtmlParser *html_parser; HtmlParser *html_parser;
// Recursion level: 0: first entity, 1: entity linked from first entity // Recursion level: 0: first entity, 1: entity linked from first entity
int level; int level;
RequestStat stat;
std::string status;
Request(const uri::UriStruct& us, int level = 0) Request(const uri::UriStruct& us, int level = 0)
: us(us), inflater(0), html_parser(0), level(level) : us(us), inflater(0), html_parser(0), level(level)
{} {}
@ -114,6 +138,30 @@ struct Request {
fin); fin);
return rv; return rv;
} }
void record_syn_stream_time()
{
record_time(&stat.on_syn_stream_time);
}
void record_syn_reply_time()
{
record_time(&stat.on_syn_reply_time);
}
void record_complete_time()
{
record_time(&stat.on_complete_time);
}
};
struct SessionStat {
timeval on_handshake_time;
SessionStat()
{
on_handshake_time.tv_sec = -1;
on_handshake_time.tv_usec = -1;
}
}; };
struct SpdySession { struct SpdySession {
@ -127,6 +175,7 @@ struct SpdySession {
size_t complete; size_t complete;
std::string hostport; std::string hostport;
Spdylay *sc; Spdylay *sc;
SessionStat stat;
SpdySession():complete(0) {} SpdySession():complete(0) {}
~SpdySession() ~SpdySession()
{ {
@ -165,6 +214,10 @@ struct SpdySession {
return true; return true;
} }
} }
void record_handshake_time()
{
record_time(&stat.on_handshake_time);
}
}; };
Config config; Config config;
@ -251,6 +304,7 @@ void check_stream_id(spdylay_session *session,
Request *req = (Request*)spdylay_session_get_stream_user_data(session, Request *req = (Request*)spdylay_session_get_stream_user_data(session,
stream_id); stream_id);
spdySession->streams[stream_id] = req; spdySession->streams[stream_id] = req;
req->record_syn_stream_time();
} }
void on_ctrl_send_callback2 void on_ctrl_send_callback2
@ -260,16 +314,9 @@ void on_ctrl_send_callback2
if(type == SPDYLAY_SYN_STREAM) { if(type == SPDYLAY_SYN_STREAM) {
check_stream_id(session, type, frame, user_data); check_stream_id(session, type, frame, user_data);
} }
} if(config.verbose) {
on_ctrl_send_callback(session, type, frame, user_data);
void on_ctrl_send_callback3
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data)
{
if(type == SPDYLAY_SYN_STREAM) {
check_stream_id(session, type, frame, user_data);
} }
on_ctrl_send_callback(session, type, frame, user_data);
} }
void check_response_header void check_response_header
@ -294,6 +341,8 @@ void check_response_header
for(size_t i = 0; nv[i]; i += 2) { for(size_t i = 0; nv[i]; i += 2) {
if(strcmp("content-encoding", nv[i]) == 0) { if(strcmp("content-encoding", nv[i]) == 0) {
gzip = util::strieq("gzip", nv[i+1]) || util::strieq("deflate", nv[i+1]); gzip = util::strieq("gzip", nv[i+1]) || util::strieq("deflate", nv[i+1]);
} else if(strcmp(":status", nv[i]) == 0) {
req->status = nv[i+1];
} }
} }
if(gzip) { if(gzip) {
@ -312,15 +361,16 @@ void on_ctrl_recv_callback2
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, (spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data) void *user_data)
{ {
if(type == SPDYLAY_SYN_REPLY) {
Request *req = (Request*)spdylay_session_get_stream_user_data
(session, frame->syn_reply.stream_id);
assert(req);
req->record_syn_reply_time();
}
check_response_header(session, type, frame, user_data); check_response_header(session, type, frame, user_data);
} if(config.verbose) {
on_ctrl_recv_callback(session, type, frame, user_data);
void on_ctrl_recv_callback3 }
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data)
{
check_response_header(session, type, frame, user_data);
on_ctrl_recv_callback(session, type, frame, user_data);
} }
void on_stream_close_callback void on_stream_close_callback
@ -332,6 +382,7 @@ void on_stream_close_callback
spdySession->streams.find(stream_id); spdySession->streams.find(stream_id);
if(itr != spdySession->streams.end()) { if(itr != spdySession->streams.end()) {
update_html_parser(spdySession, (*itr).second, 0, 0, 1); update_html_parser(spdySession, (*itr).second, 0, 0, 1);
(*itr).second->record_complete_time();
++spdySession->complete; ++spdySession->complete;
if(spdySession->all_requests_processed()) { if(spdySession->all_requests_processed()) {
spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK); spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
@ -339,6 +390,52 @@ void on_stream_close_callback
} }
} }
int64_t time_delta(const timeval& a, const timeval& b)
{
int64_t res;
res = a.tv_usec - b.tv_usec;
int off = 0;
if(res < 0) {
res += 1000000;
off = -1;
}
res += (a.tv_sec + off - b.tv_sec)*1000000;
return res/1000;
}
void print_stats(const SpdySession& spdySession)
{
std::cout << "***** Statistics *****" << std::endl;
for(size_t i = 0; i < spdySession.reqvec.size(); ++i) {
const Request *req = spdySession.reqvec[i];
std::cout << "#" << i+1 << ": " << uri::construct(req->us) << std::endl;
std::cout << " Status: " << req->status << std::endl;
std::cout << " Delta (ms) from SSL/TLS handshake(SYN_STREAM):"
<< std::endl;
if(req->stat.on_syn_reply_time.tv_sec >= 0) {
std::cout << " SYN_REPLY: "
<< time_delta(req->stat.on_syn_reply_time,
spdySession.stat.on_handshake_time)
<< "("
<< time_delta(req->stat.on_syn_reply_time,
req->stat.on_syn_stream_time)
<< ")"
<< std::endl;
}
if(req->stat.on_complete_time.tv_sec >= 0) {
std::cout << " Completed: "
<< time_delta(req->stat.on_complete_time,
spdySession.stat.on_handshake_time)
<< "("
<< time_delta(req->stat.on_complete_time,
req->stat.on_syn_stream_time)
<< ")"
<< std::endl;
}
std::cout << std::endl;
}
}
int communicate(const std::string& host, uint16_t port, int communicate(const std::string& host, uint16_t port,
SpdySession& spdySession, SpdySession& spdySession,
const spdylay_session_callbacks *callbacks) const spdylay_session_callbacks *callbacks)
@ -386,6 +483,7 @@ int communicate(const std::string& host, uint16_t port,
if(ssl_handshake(ssl, fd) == -1) { if(ssl_handshake(ssl, fd) == -1) {
return -1; return -1;
} }
spdySession.record_handshake_time();
make_non_block(fd); make_non_block(fd);
set_tcp_nodelay(fd); set_tcp_nodelay(fd);
int spdy_version = spdylay_npn_get_version( int spdy_version = spdylay_npn_get_version(
@ -459,6 +557,9 @@ int communicate(const std::string& host, uint16_t port,
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
shutdown(fd, SHUT_WR); shutdown(fd, SHUT_WR);
close(fd); close(fd);
if(config.stat) {
print_stats(spdySession);
}
return ok ? 0 : -1; return ok ? 0 : -1;
} }
@ -469,17 +570,14 @@ int run(char **uris, int n)
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;
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback2;
callbacks.on_ctrl_send_callback = on_ctrl_send_callback2;
if(config.verbose) { if(config.verbose) {
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback3;
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_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;
callbacks.on_ctrl_recv_parse_error_callback = callbacks.on_ctrl_recv_parse_error_callback =
on_ctrl_recv_parse_error_callback; on_ctrl_recv_parse_error_callback;
callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback; callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback;
} else {
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback2;
callbacks.on_ctrl_send_callback = on_ctrl_send_callback2;
} }
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
ssl_debug = config.verbose; ssl_debug = config.verbose;
@ -515,7 +613,7 @@ int run(char **uris, int n)
void print_usage(std::ostream& out) void print_usage(std::ostream& out)
{ {
out << "Usage: spdycat [-Oanv23] [-t <SECONDS>] [-w <WINDOW_BITS>] [--cert=<CERT>]\n" out << "Usage: spdycat [-Oansv23] [-t <SECONDS>] [-w <WINDOW_BITS>] [--cert=<CERT>]\n"
<< " [--key=<KEY>] <URI>..." << " [--key=<KEY>] <URI>..."
<< std::endl; << std::endl;
} }
@ -542,6 +640,7 @@ void print_help(std::ostream& out)
<< " resource. Only links whose origins are the\n" << " resource. Only links whose origins are the\n"
<< " same with the linking resource will be\n" << " same with the linking resource will be\n"
<< " downloaded.\n" << " downloaded.\n"
<< " -s, --stat Print statistics.\n"
<< " --cert=<CERT> Use the specified client certificate file.\n" << " --cert=<CERT> Use the specified client certificate file.\n"
<< " The file must be in PEM format.\n" << " The file must be in PEM format.\n"
<< " --key=<KEY> Use the client private key file. The file\n" << " --key=<KEY> Use the client private key file. The file\n"
@ -562,13 +661,15 @@ int main(int argc, char **argv)
{"timeout", required_argument, 0, 't' }, {"timeout", required_argument, 0, 't' },
{"window-bits", required_argument, 0, 'w' }, {"window-bits", required_argument, 0, 'w' },
{"get-assets", no_argument, 0, 'a' }, {"get-assets", no_argument, 0, 'a' },
{"stat", no_argument, 0, 's' },
{"cert", required_argument, &flag, 1 }, {"cert", required_argument, &flag, 1 },
{"key", required_argument, &flag, 2 }, {"key", required_argument, &flag, 2 },
{"help", no_argument, 0, 'h' }, {"help", no_argument, 0, 'h' },
{0, 0, 0, 0 } {0, 0, 0, 0 }
}; };
int option_index = 0; int option_index = 0;
int c = getopt_long(argc, argv, "Oanhv23t:w:", long_options, &option_index); int c = getopt_long(argc, argv, "Oanhv23st:w:", long_options,
&option_index);
if(c == -1) { if(c == -1) {
break; break;
} }
@ -615,6 +716,9 @@ int main(int argc, char **argv)
<< std::endl; << std::endl;
#endif // !HAVE_LIBXML2 #endif // !HAVE_LIBXML2
break; break;
case 's':
config.stat = true;
break;
case '?': case '?':
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
case 0: case 0: