Make spdycat and spdyd barely work

This commit is contained in:
Tatsuhiro Tsujikawa 2013-07-16 00:15:04 +09:00
parent 48cb017245
commit 24cab312cf
12 changed files with 344 additions and 390 deletions

View File

@ -492,7 +492,7 @@ typedef struct {
/** /**
* The error code. See :type:`nghttp2_error_code`. * The error code. See :type:`nghttp2_error_code`.
*/ */
uint32_t error_code; nghttp2_error_code error_code;
} nghttp2_rst_stream; } nghttp2_rst_stream;
/** /**
@ -575,7 +575,7 @@ typedef struct {
/** /**
* The error code. See :type:`nghttp2_error_code`. * The error code. See :type:`nghttp2_error_code`.
*/ */
uint32_t error_code; nghttp2_error_code error_code;
/** /**
* The additional debug data * The additional debug data
*/ */
@ -743,7 +743,7 @@ typedef void (*nghttp2_on_data_chunk_recv_callback)
* :type:`nghttp2_on_data_chunk_recv_callback`. * :type:`nghttp2_on_data_chunk_recv_callback`.
*/ */
typedef void (*nghttp2_on_data_recv_callback) typedef void (*nghttp2_on_data_recv_callback)
(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t length, (nghttp2_session *session, uint16_t length, uint8_t flags, int32_t stream_id,
void *user_data); void *user_data);
/** /**
@ -784,7 +784,7 @@ typedef void (*nghttp2_on_frame_not_send_callback)
* Callback function invoked after DATA frame is sent. * Callback function invoked after DATA frame is sent.
*/ */
typedef void (*nghttp2_on_data_send_callback) typedef void (*nghttp2_on_data_send_callback)
(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t length, (nghttp2_session *session, uint16_t length, uint8_t flags, int32_t stream_id,
void *user_data); void *user_data);
/** /**

View File

@ -1126,10 +1126,10 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
if(session->callbacks.on_data_send_callback) { if(session->callbacks.on_data_send_callback) {
session->callbacks.on_data_send_callback session->callbacks.on_data_send_callback
(session, (session,
session->aob.framebuflen - NGHTTP2_FRAME_HEAD_LENGTH,
data_frame->eof ? data_frame->hd.flags : data_frame->eof ? data_frame->hd.flags :
(data_frame->hd.flags & (~NGHTTP2_FLAG_END_STREAM)), (data_frame->hd.flags & (~NGHTTP2_FLAG_END_STREAM)),
data_frame->hd.stream_id, data_frame->hd.stream_id,
session->aob.framebuflen - NGHTTP2_FRAME_HEAD_LENGTH,
session->user_data); session->user_data);
} }
if(data_frame->eof && (data_frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { if(data_frame->eof && (data_frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
@ -1650,6 +1650,8 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
if(rv != 0) { if(rv != 0) {
return rv; return rv;
} }
} else {
return nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR);
} }
} }
session->remote_settings[entry->settings_id] = entry->value; session->remote_settings[entry->settings_id] = entry->value;
@ -1915,7 +1917,7 @@ int nghttp2_session_on_data_received(nghttp2_session *session,
valid = 1; valid = 1;
if(session->callbacks.on_data_recv_callback) { if(session->callbacks.on_data_recv_callback) {
session->callbacks.on_data_recv_callback session->callbacks.on_data_recv_callback
(session, flags, stream_id, length, session->user_data); (session, length, flags, stream_id, session->user_data);
} }
} else if(stream->state != NGHTTP2_STREAM_CLOSING) { } else if(stream->state != NGHTTP2_STREAM_CLOSING) {
error_code = NGHTTP2_PROTOCOL_ERROR; error_code = NGHTTP2_PROTOCOL_ERROR;
@ -1927,7 +1929,7 @@ int nghttp2_session_on_data_received(nghttp2_session *session,
valid = 1; valid = 1;
if(session->callbacks.on_data_recv_callback) { if(session->callbacks.on_data_recv_callback) {
session->callbacks.on_data_recv_callback session->callbacks.on_data_recv_callback
(session, flags, stream_id, length, session->user_data); (session, length, flags, stream_id, session->user_data);
} }
if(flags & NGHTTP2_FLAG_END_STREAM) { if(flags & NGHTTP2_FLAG_END_STREAM) {
nghttp2_session_call_on_request_recv(session, stream_id); nghttp2_session_call_on_request_recv(session, stream_id);

View File

@ -31,6 +31,7 @@ AM_CFLAGS = -Wall
AM_CPPFLAGS = -Wall -I$(srcdir)/../lib/includes -I$(builddir)/../lib/includes \ AM_CPPFLAGS = -Wall -I$(srcdir)/../lib/includes -I$(builddir)/../lib/includes \
@OPENSSL_CFLAGS@ @XML_CPPFLAGS@ @LIBEVENT_OPENSSL_CFLAGS@ @DEFS@ @OPENSSL_CFLAGS@ @XML_CPPFLAGS@ @LIBEVENT_OPENSSL_CFLAGS@ @DEFS@
AM_LDFLAGS = @OPENSSL_LIBS@ @XML_LIBS@ @LIBEVENT_OPENSSL_LIBS@ @SRC_LIBS@ AM_LDFLAGS = @OPENSSL_LIBS@ @XML_LIBS@ @LIBEVENT_OPENSSL_LIBS@ @SRC_LIBS@
AM_CXXFLAGS = -std=c++11
LDADD = $(top_builddir)/lib/libnghttp2.la LDADD = $(top_builddir)/lib/libnghttp2.la

View File

@ -61,7 +61,7 @@ const std::string SPDYD_SERVER = "spdyd nghttp2/" NGHTTP2_VERSION;
Config::Config(): verbose(false), daemon(false), port(0), Config::Config(): verbose(false), daemon(false), port(0),
on_request_recv_callback(0), data_ptr(0), on_request_recv_callback(0), data_ptr(0),
version(0), verify_client(false), no_tls(false) verify_client(false), no_tls(false)
{} {}
Request::Request(int32_t stream_id) Request::Request(int32_t stream_id)
@ -93,10 +93,9 @@ public:
{} {}
~Sessions() ~Sessions()
{ {
for(std::set<EventHandler*>::iterator i = handlers_.begin(), for(auto handler : handlers_) {
eoi = handlers_.end(); i != eoi; ++i) { on_close(*this, handler);
on_close(*this, *i); delete handler;
delete *i;
} }
SSL_CTX_free(ssl_ctx_); SSL_CTX_free(ssl_ctx_);
} }
@ -175,22 +174,19 @@ void on_session_closed(EventHandler *hd, int64_t session_id)
SpdyEventHandler::SpdyEventHandler(const Config* config, SpdyEventHandler::SpdyEventHandler(const Config* config,
int fd, SSL *ssl, int fd, SSL *ssl,
uint16_t version,
const nghttp2_session_callbacks *callbacks, const nghttp2_session_callbacks *callbacks,
int64_t session_id) int64_t session_id)
: EventHandler(config), : EventHandler(config),
fd_(fd), ssl_(ssl), version_(version), session_id_(session_id), fd_(fd), ssl_(ssl), session_id_(session_id),
io_flags_(0) io_flags_(0)
{ {
int r; int r;
r = nghttp2_session_server_new(&session_, version, callbacks, this); r = nghttp2_session_server_new(&session_, callbacks, this);
assert(r == 0); assert(r == 0);
nghttp2_settings_entry entry; nghttp2_settings_entry entry;
entry.settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; entry.settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
entry.value = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; entry.value = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
entry.flags = NGHTTP2_ID_FLAG_SETTINGS_NONE; r = nghttp2_submit_settings(session_, &entry, 1);
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_SETTINGS_NONE,
&entry, 1);
assert(r == 0); assert(r == 0);
} }
@ -198,9 +194,8 @@ SpdyEventHandler::~SpdyEventHandler()
{ {
on_session_closed(this, session_id_); on_session_closed(this, session_id_);
nghttp2_session_del(session_); nghttp2_session_del(session_);
for(std::map<int32_t, Request*>::iterator i = id2req_.begin(), for(auto& item : id2req_) {
eoi = id2req_.end(); i != eoi; ++i) { delete item.second;
delete (*i).second;
} }
if(ssl_) { if(ssl_) {
SSL_shutdown(ssl_); SSL_shutdown(ssl_);
@ -210,11 +205,6 @@ SpdyEventHandler::~SpdyEventHandler()
close(fd_); close(fd_);
} }
uint16_t SpdyEventHandler::version() const
{
return version_;
}
int SpdyEventHandler::execute(Sessions *sessions) int SpdyEventHandler::execute(Sessions *sessions)
{ {
int r; int r;
@ -300,18 +290,17 @@ int SpdyEventHandler::submit_file_response(const std::string& status,
std::string last_modified_str; std::string last_modified_str;
const char *nv[] = { const char *nv[] = {
":status", status.c_str(), ":status", status.c_str(),
":version", "HTTP/1.1",
"server", SPDYD_SERVER.c_str(), "server", SPDYD_SERVER.c_str(),
"content-length", content_length.c_str(), "content-length", content_length.c_str(),
"cache-control", "max-age=3600", "cache-control", "max-age=3600",
"date", date_str.c_str(), "date", date_str.c_str(),
0, 0, nullptr, nullptr,
0 nullptr
}; };
if(last_modified != 0) { if(last_modified != 0) {
last_modified_str = util::http_date(last_modified); last_modified_str = util::http_date(last_modified);
nv[12] = "last-modified"; nv[10] = "last-modified";
nv[13] = last_modified_str.c_str(); nv[11] = last_modified_str.c_str();
} }
return nghttp2_submit_response(session_, stream_id, nv, data_prd); return nghttp2_submit_response(session_, stream_id, nv, data_prd);
} }
@ -323,20 +312,18 @@ int SpdyEventHandler::submit_response
nghttp2_data_provider *data_prd) nghttp2_data_provider *data_prd)
{ {
std::string date_str = util::http_date(time(0)); std::string date_str = util::http_date(time(0));
const char **nv = new const char*[8+headers.size()*2+1]; const char **nv = new const char*[6+headers.size()*2+1];
nv[0] = ":status"; nv[0] = ":status";
nv[1] = status.c_str(); nv[1] = status.c_str();
nv[2] = ":version"; nv[2] = "server";
nv[3] = "HTTP/1.1"; nv[3] = SPDYD_SERVER.c_str();
nv[4] = "server"; nv[4] = "date";
nv[5] = SPDYD_SERVER.c_str(); nv[5] = date_str.c_str();
nv[6] = "date";
nv[7] = date_str.c_str();
for(int i = 0; i < (int)headers.size(); ++i) { for(int i = 0; i < (int)headers.size(); ++i) {
nv[8+i*2] = headers[i].first.c_str(); nv[6+i*2] = headers[i].first.c_str();
nv[8+i*2+1] = headers[i].second.c_str(); nv[6+i*2+1] = headers[i].second.c_str();
} }
nv[8+headers.size()*2] = 0; nv[6+headers.size()*2] = nullptr;
int r = nghttp2_submit_response(session_, stream_id, nv, data_prd); int r = nghttp2_submit_response(session_, stream_id, nv, data_prd);
delete [] nv; delete [] nv;
return r; return r;
@ -348,9 +335,8 @@ int SpdyEventHandler::submit_response(const std::string& status,
{ {
const char *nv[] = { const char *nv[] = {
":status", status.c_str(), ":status", status.c_str(),
":version", "HTTP/1.1",
"server", SPDYD_SERVER.c_str(), "server", SPDYD_SERVER.c_str(),
0 nullptr
}; };
return nghttp2_submit_response(session_, stream_id, nv, data_prd); return nghttp2_submit_response(session_, stream_id, nv, data_prd);
} }
@ -568,29 +554,33 @@ void append_nv(Request *req, char **nv)
} // namespace } // namespace
namespace { namespace {
void hd_on_ctrl_recv_callback void hd_on_frame_recv_callback
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, (nghttp2_session *session, nghttp2_frame *frame, void *user_data)
void *user_data)
{ {
SpdyEventHandler *hd = (SpdyEventHandler*)user_data; SpdyEventHandler *hd = (SpdyEventHandler*)user_data;
if(hd->config()->verbose) { if(hd->config()->verbose) {
print_session_id(hd->session_id()); print_session_id(hd->session_id());
on_ctrl_recv_callback(session, type, frame, user_data); on_frame_recv_callback(session, frame, user_data);
} }
switch(type) { switch(frame->hd.type) {
case NGHTTP2_SYN_STREAM: { case NGHTTP2_HEADERS:
int32_t stream_id = frame->syn_stream.stream_id; switch(frame->headers.cat) {
Request *req = new Request(stream_id); case NGHTTP2_HCAT_START_STREAM: {
append_nv(req, frame->syn_stream.nv); int32_t stream_id = frame->hd.stream_id;
hd->add_stream(stream_id, req); auto req = new Request(stream_id);
append_nv(req, frame->headers.nv);
hd->add_stream(stream_id, req);
break;
}
case NGHTTP2_HCAT_HEADERS: {
Request *req = hd->get_stream(frame->hd.stream_id);
append_nv(req, frame->headers.nv);
break;
}
default:
break;
}
break; break;
}
case NGHTTP2_HEADERS: {
int32_t stream_id = frame->headers.stream_id;
Request *req = hd->get_stream(stream_id);
append_nv(req, frame->headers.nv);
break;
}
default: default:
break; break;
} }
@ -605,14 +595,14 @@ void htdocs_on_request_recv_callback
} }
namespace { namespace {
void hd_on_ctrl_send_callback void hd_on_frame_send_callback
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, (nghttp2_session *session, nghttp2_frame *frame,
void *user_data) void *user_data)
{ {
SpdyEventHandler *hd = (SpdyEventHandler*)user_data; SpdyEventHandler *hd = (SpdyEventHandler*)user_data;
if(hd->config()->verbose) { if(hd->config()->verbose) {
print_session_id(hd->session_id()); print_session_id(hd->session_id());
on_ctrl_send_callback(session, type, frame, user_data); on_frame_send_callback(session, frame, user_data);
} }
} }
} // namespace } // namespace
@ -628,34 +618,34 @@ void on_data_chunk_recv_callback
namespace { namespace {
void hd_on_data_recv_callback void hd_on_data_recv_callback
(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t length, (nghttp2_session *session, uint16_t length, uint8_t flags, int32_t stream_id,
void *user_data) void *user_data)
{ {
// TODO Handle POST // TODO Handle POST
SpdyEventHandler *hd = (SpdyEventHandler*)user_data; SpdyEventHandler *hd = (SpdyEventHandler*)user_data;
if(hd->config()->verbose) { if(hd->config()->verbose) {
print_session_id(hd->session_id()); print_session_id(hd->session_id());
on_data_recv_callback(session, flags, stream_id, length, user_data); on_data_recv_callback(session, length, flags, stream_id, user_data);
} }
} }
} // namespace } // namespace
namespace { namespace {
void hd_on_data_send_callback void hd_on_data_send_callback
(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t length, (nghttp2_session *session, uint16_t length, uint8_t flags, int32_t stream_id,
void *user_data) void *user_data)
{ {
SpdyEventHandler *hd = (SpdyEventHandler*)user_data; SpdyEventHandler *hd = (SpdyEventHandler*)user_data;
if(hd->config()->verbose) { if(hd->config()->verbose) {
print_session_id(hd->session_id()); print_session_id(hd->session_id());
on_data_send_callback(session, flags, stream_id, length, user_data); on_data_send_callback(session, length, flags, stream_id, user_data);
} }
} }
} // namespace } // namespace
namespace { namespace {
void on_stream_close_callback void on_stream_close_callback
(nghttp2_session *session, int32_t stream_id, nghttp2_status_code status_code, (nghttp2_session *session, int32_t stream_id, nghttp2_error_code error_code,
void *user_data) void *user_data)
{ {
SpdyEventHandler *hd = (SpdyEventHandler*)user_data; SpdyEventHandler *hd = (SpdyEventHandler*)user_data;
@ -676,15 +666,15 @@ void fill_callback(nghttp2_session_callbacks& callbacks, const Config *config)
callbacks.send_callback = hd_send_callback; callbacks.send_callback = hd_send_callback;
callbacks.recv_callback = hd_recv_callback; callbacks.recv_callback = hd_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 = hd_on_ctrl_recv_callback; callbacks.on_frame_recv_callback = hd_on_frame_recv_callback;
callbacks.on_ctrl_send_callback = hd_on_ctrl_send_callback; callbacks.on_frame_send_callback = hd_on_frame_send_callback;
callbacks.on_data_recv_callback = hd_on_data_recv_callback; callbacks.on_data_recv_callback = hd_on_data_recv_callback;
callbacks.on_data_send_callback = hd_on_data_send_callback; callbacks.on_data_send_callback = hd_on_data_send_callback;
if(config->verbose) { if(config->verbose) {
callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
callbacks.on_ctrl_recv_parse_error_callback = callbacks.on_frame_recv_parse_error_callback =
on_ctrl_recv_parse_error_callback; on_frame_recv_parse_error_callback;
callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback; callbacks.on_unknown_frame_recv_callback = on_unknown_frame_recv_callback;
} }
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
callbacks.on_request_recv_callback = config->on_request_recv_callback; callbacks.on_request_recv_callback = config->on_request_recv_callback;
@ -696,7 +686,7 @@ public:
SSLAcceptEventHandler(const Config *config, SSLAcceptEventHandler(const Config *config,
int fd, SSL *ssl, int64_t session_id) int fd, SSL *ssl, int64_t session_id)
: EventHandler(config), : EventHandler(config),
fd_(fd), ssl_(ssl), version_(0), fail_(false), finish_(false), fd_(fd), ssl_(ssl), fail_(false), finish_(false),
io_flags_(WANT_READ), io_flags_(WANT_READ),
session_id_(session_id) session_id_(session_id)
{} {}
@ -725,17 +715,11 @@ public:
if(config()->verbose) { if(config()->verbose) {
std::cout << "The negotiated next protocol: " << proto << std::endl; std::cout << "The negotiated next protocol: " << proto << std::endl;
} }
version_ = nghttp2_npn_get_version(next_proto, next_proto_len); if(proto == "HTTP-draft-04/2.0") {
if(config()->version != 0) {
if(config()->version != version_) {
version_ = 0;
std::cerr << "The negotiated next protocol is not supported."
<< std::endl;
}
}
if(version_) {
add_next_handler(sessions); add_next_handler(sessions);
} else { } else {
std::cerr << "The negotiated next protocol is not supported."
<< std::endl;
fail_ = true; fail_ = true;
} }
} else { } else {
@ -779,7 +763,7 @@ private:
nghttp2_session_callbacks callbacks; nghttp2_session_callbacks callbacks;
fill_callback(callbacks, config()); fill_callback(callbacks, config());
SpdyEventHandler *hd = new SpdyEventHandler(config(), SpdyEventHandler *hd = new SpdyEventHandler(config(),
fd_, ssl_, version_, &callbacks, fd_, ssl_, &callbacks,
session_id_); session_id_);
if(sessions->mod_poll(hd) == -1) { if(sessions->mod_poll(hd) == -1) {
// fd_, ssl_ are freed by ~SpdyEventHandler() // fd_, ssl_ are freed by ~SpdyEventHandler()
@ -791,7 +775,6 @@ private:
int fd_; int fd_;
SSL *ssl_; SSL *ssl_;
uint16_t version_;
bool fail_, finish_; bool fail_, finish_;
uint8_t io_flags_; uint8_t io_flags_;
int64_t session_id_; int64_t session_id_;
@ -842,10 +825,8 @@ private:
if(config()->no_tls) { if(config()->no_tls) {
nghttp2_session_callbacks callbacks; nghttp2_session_callbacks callbacks;
fill_callback(callbacks, config()); fill_callback(callbacks, config());
SpdyEventHandler *hd = new SpdyEventHandler(config(), auto hd = new SpdyEventHandler(config(), cfd, nullptr, &callbacks,
cfd, 0, session_id);
config()->version, &callbacks,
session_id);
if(sessions->add_poll(hd) == -1) { if(sessions->add_poll(hd) == -1) {
delete hd; delete hd;
} else { } else {
@ -857,8 +838,7 @@ private:
close(cfd); close(cfd);
return; return;
} }
SSLAcceptEventHandler *hd = new SSLAcceptEventHandler(config(), cfd, ssl, auto hd = new SSLAcceptEventHandler(config(), cfd, ssl, session_id);
session_id);
if(sessions->add_poll(hd) == -1) { if(sessions->add_poll(hd) == -1) {
delete hd; delete hd;
SSL_free(ssl); SSL_free(ssl);
@ -946,7 +926,7 @@ int SpdyServer::run()
{ {
SSL_CTX *ssl_ctx = 0; SSL_CTX *ssl_ctx = 0;
std::pair<unsigned char*, size_t> next_proto; std::pair<unsigned char*, size_t> next_proto;
unsigned char proto_list[14]; unsigned char proto_list[255];
if(!config_->no_tls) { if(!config_->no_tls) {
ssl_ctx = SSL_CTX_new(SSLv23_server_method()); ssl_ctx = SSL_CTX_new(SSLv23_server_method());
if(!ssl_ctx) { if(!ssl_ctx) {
@ -978,25 +958,11 @@ int SpdyServer::run()
verify_callback); verify_callback);
} }
// We speaks "spdy/2" and "spdy/3". proto_list[0] = 17;
proto_list[0] = 6; memcpy(&proto_list[1], "HTTP-draft-04/2.0", 17);
memcpy(&proto_list[1], "spdy/3", 6); next_proto.first = proto_list;
proto_list[7] = 6; next_proto.second = 18;
memcpy(&proto_list[8], "spdy/2", 6);
switch(config_->version) {
case NGHTTP2_PROTO_SPDY3:
next_proto.first = proto_list;
next_proto.second = 7;
break;
case NGHTTP2_PROTO_SPDY2:
next_proto.first = proto_list+7;
next_proto.second = 7;
break;
default:
next_proto.first = proto_list;
next_proto.second = sizeof(proto_list);
}
SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto); SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);
} }
@ -1011,9 +977,8 @@ int SpdyServer::run()
if(sfd_[i] == -1) { if(sfd_[i] == -1) {
continue; continue;
} }
ListenEventHandler *listen_hd = new ListenEventHandler(config_, auto listen_hd = new ListenEventHandler(config_, sfd_[i],
sfd_[i], &session_id_seed);
&session_id_seed);
if(sessions.add_poll(listen_hd) == -1) { if(sessions.add_poll(listen_hd) == -1) {
std::cerr << ipv << ": Adding listening socket to poll failed." std::cerr << ipv << ": Adding listening socket to poll failed."
<< std::endl; << std::endl;
@ -1034,8 +999,7 @@ int SpdyServer::run()
perror("EventPoll"); perror("EventPoll");
} else { } else {
for(int i = 0; i < n; ++i) { for(int i = 0; i < n; ++i) {
EventHandler *hd = reinterpret_cast<EventHandler*> auto hd = reinterpret_cast<EventHandler*>(sessions.get_user_data(i));
(sessions.get_user_data(i));
int events = sessions.get_events(i); int events = sessions.get_events(i);
int r = 0; int r = 0;
if(hd->mark_del()) { if(hd->mark_del()) {
@ -1059,10 +1023,9 @@ int SpdyServer::run()
del_list.push_back(hd); del_list.push_back(hd);
} }
} }
for(std::vector<EventHandler*>::iterator i = del_list.begin(), for(auto handler : del_list) {
eoi = del_list.end(); i != eoi; ++i) { on_close(sessions, handler);
on_close(sessions, *i); sessions.remove_handler(handler);
sessions.remove_handler(*i);
} }
del_list.clear(); del_list.clear();
} }

View File

@ -52,7 +52,6 @@ struct Config {
std::string cert_file; std::string cert_file;
nghttp2_on_request_recv_callback on_request_recv_callback; nghttp2_on_request_recv_callback on_request_recv_callback;
void *data_ptr; void *data_ptr;
uint16_t version;
bool verify_client; bool verify_client;
bool no_tls; bool no_tls;
Config(); Config();
@ -88,7 +87,7 @@ private:
struct Request { struct Request {
int32_t stream_id; int32_t stream_id;
std::vector<std::pair<std::string, std::string> > headers; std::vector<std::pair<std::string, std::string>> headers;
int file; int file;
std::pair<std::string, size_t> response_body; std::pair<std::string, size_t> response_body;
Request(int32_t stream_id); Request(int32_t stream_id);
@ -98,7 +97,7 @@ struct Request {
class SpdyEventHandler : public EventHandler { class SpdyEventHandler : public EventHandler {
public: public:
SpdyEventHandler(const Config* config, SpdyEventHandler(const Config* config,
int fd, SSL *ssl, uint16_t version, int fd, SSL *ssl,
const nghttp2_session_callbacks *callbacks, const nghttp2_session_callbacks *callbacks,
int64_t session_id); int64_t session_id);
virtual ~SpdyEventHandler(); virtual ~SpdyEventHandler();
@ -108,8 +107,6 @@ public:
virtual int fd() const; virtual int fd() const;
virtual bool finish(); virtual bool finish();
uint16_t version() const;
ssize_t send_data(const uint8_t *data, size_t len, int flags); ssize_t send_data(const uint8_t *data, size_t len, int flags);
ssize_t recv_data(uint8_t *data, size_t len, int flags); ssize_t recv_data(uint8_t *data, size_t len, int flags);
@ -129,7 +126,7 @@ public:
int submit_response int submit_response
(const std::string& status, (const std::string& status,
int32_t stream_id, int32_t stream_id,
const std::vector<std::pair<std::string, std::string> >& headers, const std::vector<std::pair<std::string, std::string>>& headers,
nghttp2_data_provider *data_prd); nghttp2_data_provider *data_prd);
void add_stream(int32_t stream_id, Request *req); void add_stream(int32_t stream_id, Request *req);
@ -140,7 +137,6 @@ private:
nghttp2_session *session_; nghttp2_session *session_;
int fd_; int fd_;
SSL* ssl_; SSL* ssl_;
uint16_t version_;
int64_t session_id_; int64_t session_id_;
uint8_t io_flags_; uint8_t io_flags_;
std::map<int32_t, Request*> id2req_; std::map<int32_t, Request*> id2req_;

View File

@ -50,13 +50,12 @@ namespace nghttp2 {
bool ssl_debug = false; bool ssl_debug = false;
Spdylay::Spdylay(int fd, SSL *ssl, uint16_t version, Spdylay::Spdylay(int fd, SSL *ssl,
const nghttp2_session_callbacks *callbacks, const nghttp2_session_callbacks *callbacks,
void *user_data) void *user_data)
: fd_(fd), ssl_(ssl), version_(version), user_data_(user_data), : fd_(fd), ssl_(ssl), user_data_(user_data), io_flags_(0)
io_flags_(0)
{ {
int r = nghttp2_session_client_new(&session_, version_, callbacks, this); int r = nghttp2_session_client_new(&session_, callbacks, this);
assert(r == 0); assert(r == 0);
} }
@ -152,17 +151,16 @@ int Spdylay::submit_request(const std::string& scheme,
{ {
POS_METHOD = 0, POS_METHOD = 0,
POS_PATH, POS_PATH,
POS_VERSION,
POS_SCHEME, POS_SCHEME,
POS_HOST, POS_HOST,
POS_ACCEPT, POS_ACCEPT,
POS_ACCEPT_ENCODING,
POS_USERAGENT POS_USERAGENT
}; };
const char *static_nv[] = { const char *static_nv[] = {
":method", data_prd ? "POST" : "GET", ":method", data_prd ? "POST" : "GET",
":path", path.c_str(), ":path", path.c_str(),
":version", "HTTP/1.1",
":scheme", scheme.c_str(), ":scheme", scheme.c_str(),
":host", hostport.c_str(), ":host", hostport.c_str(),
"accept", "*/*", "accept", "*/*",
@ -181,8 +179,8 @@ int Spdylay::submit_request(const std::string& scheme,
memcpy(nv, static_nv, hardcoded_entry_count * sizeof(*static_nv)); memcpy(nv, static_nv, hardcoded_entry_count * sizeof(*static_nv));
std::map<std::string,std::string>::const_iterator i = headers.begin(); auto i = std::begin(headers);
std::map<std::string,std::string>::const_iterator end = headers.end(); auto end = std::end(headers);
int pos = hardcoded_entry_count; int pos = hardcoded_entry_count;
@ -213,7 +211,7 @@ int Spdylay::submit_request(const std::string& scheme,
} }
++i; ++i;
} }
nv[pos] = NULL; nv[pos] = nullptr;
int r = nghttp2_submit_request(session_, pri, nv, data_prd, int r = nghttp2_submit_request(session_, pri, nv, data_prd,
stream_user_data); stream_user_data);
@ -223,9 +221,9 @@ int Spdylay::submit_request(const std::string& scheme,
return r; return r;
} }
int Spdylay::submit_settings(int flags, nghttp2_settings_entry *iv, size_t niv) int Spdylay::submit_settings(nghttp2_settings_entry *iv, size_t niv)
{ {
return nghttp2_submit_settings(session_, flags, iv, niv); return nghttp2_submit_settings(session_, iv, niv);
} }
bool Spdylay::would_block() bool Spdylay::would_block()
@ -456,15 +454,41 @@ ssize_t recv_callback(nghttp2_session *session,
} }
namespace { namespace {
const char *ctrl_names[] = { const char* strstatus(nghttp2_error_code error_code)
"SYN_STREAM", {
"SYN_REPLY", switch(error_code) {
case NGHTTP2_NO_ERROR:
return "NO_ERROR";
case NGHTTP2_PROTOCOL_ERROR:
return "PROTOCOL_ERROR";
case NGHTTP2_INTERNAL_ERROR:
return "INTERNAL_ERROR";
case NGHTTP2_FLOW_CONTROL_ERROR:
return "FLOW_CONTROL_ERROR";
case NGHTTP2_STREAM_CLOSED:
return "STREAM_CLOSED";
case NGHTTP2_FRAME_TOO_LARGE:
return "FRAME_TOO_LARGE";
case NGHTTP2_REFUSED_STREAM:
return "REFUSED_STREAM";
case NGHTTP2_CANCEL:
return "CANCEL";
default:
return "UNKNOWN";
}
}
} // namespace
namespace {
const char *frame_names[] = {
"DATA",
"HEADERS",
"PRIORITY",
"RST_STREAM", "RST_STREAM",
"SETTINGS", "SETTINGS",
"NOOP", "PUSH_PROMISE",
"PING", "PING",
"GOAWAY", "GOAWAY",
"HEADERS",
"WINDOW_UPDATE" "WINDOW_UPDATE"
}; };
} // namespace } // namespace
@ -521,10 +545,57 @@ void print_timer()
} }
namespace { namespace {
void print_ctrl_hd(const nghttp2_ctrl_hd& hd) void print_frame_hd(const nghttp2_frame_hd& hd)
{ {
printf("<version=%u, flags=%u, length=%d>\n", printf("<length=%d, flags=%u, stream_id=%d>\n",
hd.version, hd.flags, hd.length); hd.length, hd.flags, hd.stream_id);
}
} // namespace
namespace {
void print_flags(const nghttp2_frame_hd& hd)
{
std::string s;
switch(hd.type) {
case NGHTTP2_DATA:
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
s += "END_STREAM";
}
break;
case NGHTTP2_HEADERS:
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
s += "END_STREAM";
}
if(hd.flags & NGHTTP2_FLAG_END_HEADERS) {
if(!s.empty()) {
s += " | ";
}
s += "END_HEADERS";
}
if(hd.flags & NGHTTP2_FLAG_PRIORITY) {
if(!s.empty()) {
s += " | ";
}
s += "PRIORITY";
}
break;
case NGHTTP2_PUSH_PROMISE:
if(hd.flags & NGHTTP2_FLAG_END_PUSH_PROMISE) {
s += "END_PUSH_PROMISE";
}
break;
case NGHTTP2_PING:
if(hd.flags & NGHTTP2_FLAG_PONG) {
s += "PONG";
}
break;
case NGHTTP2_WINDOW_UPDATE:
if(hd.flags & NGHTTP2_FLAG_END_FLOW_CONTROL) {
s += "END_FLOW_CONTROL";
}
break;
}
printf("; %s\n", s.c_str());
} }
} // namespace } // namespace
@ -541,60 +612,69 @@ const char* frame_name_ansi_esc(print_type ptype)
} // namespace } // namespace
namespace { namespace {
void print_frame(print_type ptype, nghttp2_frame_type type, void print_frame(print_type ptype, nghttp2_frame *frame)
nghttp2_frame *frame)
{ {
printf("%s%s%s frame ", printf("%s%s%s frame ",
frame_name_ansi_esc(ptype), frame_name_ansi_esc(ptype),
ctrl_names[type-1], frame_names[frame->hd.type],
ansi_escend()); ansi_escend());
print_ctrl_hd(frame->syn_stream.hd); print_frame_hd(frame->hd);
switch(type) { if(frame->hd.flags) {
case NGHTTP2_SYN_STREAM:
print_frame_attr_indent(); print_frame_attr_indent();
printf("(stream_id=%d, assoc_stream_id=%d, pri=%u)\n", print_flags(frame->hd);
frame->syn_stream.stream_id, frame->syn_stream.assoc_stream_id, }
frame->syn_stream.pri); switch(frame->hd.type) {
print_nv(frame->syn_stream.nv); case NGHTTP2_HEADERS:
break;
case NGHTTP2_SYN_REPLY:
print_frame_attr_indent(); print_frame_attr_indent();
printf("(stream_id=%d)\n", frame->syn_reply.stream_id); printf("(pri=%d)\n", frame->headers.pri);
print_nv(frame->syn_reply.nv); switch(frame->headers.cat) {
case NGHTTP2_HCAT_START_STREAM:
print_frame_attr_indent();
printf("; Open new stream\n");
break;
case NGHTTP2_HCAT_REPLY:
print_frame_attr_indent();
printf("; First response header\n");
break;
default:
break;
}
print_nv(frame->headers.nv);
break; break;
case NGHTTP2_RST_STREAM: case NGHTTP2_RST_STREAM:
print_frame_attr_indent(); print_frame_attr_indent();
printf("(stream_id=%d, status_code=%u)\n", printf("(error_code=%s(%u))\n",
frame->rst_stream.stream_id, frame->rst_stream.status_code); strstatus(frame->rst_stream.error_code),
frame->rst_stream.error_code);
break; break;
case NGHTTP2_SETTINGS: case NGHTTP2_SETTINGS:
print_frame_attr_indent(); print_frame_attr_indent();
printf("(niv=%lu)\n", static_cast<unsigned long>(frame->settings.niv)); printf("(niv=%lu)\n", static_cast<unsigned long>(frame->settings.niv));
for(size_t i = 0; i < frame->settings.niv; ++i) { for(size_t i = 0; i < frame->settings.niv; ++i) {
print_frame_attr_indent(); print_frame_attr_indent();
printf("[%d(%u):%u]\n", printf("[%d:%u]\n",
frame->settings.iv[i].settings_id, frame->settings.iv[i].settings_id,
frame->settings.iv[i].flags, frame->settings.iv[i].value); frame->settings.iv[i].value);
} }
break; break;
case NGHTTP2_PING: case NGHTTP2_PING:
print_frame_attr_indent(); print_frame_attr_indent();
printf("(unique_id=%d)\n", frame->ping.unique_id); printf("(opaque_data=%s)\n",
util::format_hex(frame->ping.opaque_data, 8).c_str());
break; break;
case NGHTTP2_GOAWAY: case NGHTTP2_GOAWAY:
print_frame_attr_indent(); print_frame_attr_indent();
printf("(last_good_stream_id=%d)\n", frame->goaway.last_good_stream_id); printf("(last_stream_id=%d, error_code=%s(%u), opaque_data=%s)\n",
break; frame->goaway.last_stream_id,
case NGHTTP2_HEADERS: strstatus(frame->goaway.error_code),
print_frame_attr_indent(); frame->goaway.error_code,
printf("(stream_id=%d)\n", frame->headers.stream_id); util::format_hex(frame->goaway.opaque_data,
print_nv(frame->headers.nv); frame->goaway.opaque_data_len).c_str());
break; break;
case NGHTTP2_WINDOW_UPDATE: case NGHTTP2_WINDOW_UPDATE:
print_frame_attr_indent(); print_frame_attr_indent();
printf("(stream_id=%d, delta_window_size=%d)\n", printf("(window_size_increment=%d)\n",
frame->window_update.stream_id, frame->window_update.window_size_increment);
frame->window_update.delta_window_size);
break; break;
default: default:
printf("\n"); printf("\n");
@ -603,57 +683,22 @@ void print_frame(print_type ptype, nghttp2_frame_type type,
} }
} // namespace } // namespace
void on_ctrl_recv_callback void on_frame_recv_callback
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, (nghttp2_session *session, nghttp2_frame *frame, void *user_data)
void *user_data)
{ {
print_timer(); print_timer();
printf(" recv "); printf(" recv ");
print_frame(PRINT_RECV, type, frame); print_frame(PRINT_RECV, frame);
fflush(stdout); fflush(stdout);
} }
namespace { void on_invalid_frame_recv_callback
const char* strstatus(uint32_t status_code) (nghttp2_session *session, nghttp2_frame *frame,
{ nghttp2_error_code error_code, void *user_data)
switch(status_code) {
case NGHTTP2_OK:
return "OK";
case NGHTTP2_PROTOCOL_ERROR:
return "PROTOCOL_ERROR";
case NGHTTP2_INVALID_STREAM:
return "INVALID_STREAM";
case NGHTTP2_REFUSED_STREAM:
return "REFUSED_STREAM";
case NGHTTP2_UNSUPPORTED_VERSION:
return "UNSUPPORTED_VERSION";
case NGHTTP2_CANCEL:
return "CANCEL";
case NGHTTP2_INTERNAL_ERROR:
return "INTERNAL_ERROR";
case NGHTTP2_FLOW_CONTROL_ERROR:
return "FLOW_CONTROL_ERROR";
case NGHTTP2_STREAM_IN_USE:
return "STREAM_IN_USE";
case NGHTTP2_STREAM_ALREADY_CLOSED:
return "STREAM_ALREADY_CLOSED";
case NGHTTP2_INVALID_CREDENTIALS:
return "INVALID_CREDENTIALS";
case NGHTTP2_FRAME_TOO_LARGE:
return "FRAME_TOO_LARGE";
default:
return "Unknown status code";
}
}
} // namespace
void on_invalid_ctrl_recv_callback
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame,
uint32_t status_code, void *user_data)
{ {
print_timer(); print_timer();
printf(" [INVALID; status=%s] recv ", strstatus(status_code)); printf(" [INVALID; status=%s] recv ", strstatus(error_code));
print_frame(PRINT_RECV, type, frame); print_frame(PRINT_RECV, frame);
fflush(stdout); fflush(stdout);
} }
@ -670,7 +715,7 @@ void dump_header(const uint8_t *head, size_t headlen)
} }
} // namespace } // namespace
void on_ctrl_recv_parse_error_callback(nghttp2_session *session, void on_frame_recv_parse_error_callback(nghttp2_session *session,
nghttp2_frame_type type, nghttp2_frame_type type,
const uint8_t *head, const uint8_t *head,
size_t headlen, size_t headlen,
@ -681,7 +726,7 @@ void on_ctrl_recv_parse_error_callback(nghttp2_session *session,
print_timer(); print_timer();
printf(" [PARSE_ERROR] recv %s%s%s frame\n", printf(" [PARSE_ERROR] recv %s%s%s frame\n",
frame_name_ansi_esc(PRINT_RECV), frame_name_ansi_esc(PRINT_RECV),
ctrl_names[type-1], frame_names[type],
ansi_escend()); ansi_escend());
print_frame_attr_indent(); print_frame_attr_indent();
printf("Error: %s\n", nghttp2_strerror(error_code)); printf("Error: %s\n", nghttp2_strerror(error_code));
@ -689,7 +734,7 @@ void on_ctrl_recv_parse_error_callback(nghttp2_session *session,
fflush(stdout); fflush(stdout);
} }
void on_unknown_ctrl_recv_callback(nghttp2_session *session, void on_unknown_frame_recv_callback(nghttp2_session *session,
const uint8_t *head, const uint8_t *head,
size_t headlen, size_t headlen,
const uint8_t *payload, const uint8_t *payload,
@ -702,43 +747,43 @@ void on_unknown_ctrl_recv_callback(nghttp2_session *session,
fflush(stdout); fflush(stdout);
} }
void on_ctrl_send_callback void on_frame_send_callback
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, (nghttp2_session *session, nghttp2_frame *frame, void *user_data)
void *user_data)
{ {
print_timer(); print_timer();
printf(" send "); printf(" send ");
print_frame(PRINT_SEND, type, frame); print_frame(PRINT_SEND, frame);
fflush(stdout); fflush(stdout);
} }
namespace { namespace {
void print_data_frame(print_type ptype, uint8_t flags, int32_t stream_id, void print_data_frame(print_type ptype, uint16_t length, uint8_t flags,
int32_t length) int32_t stream_id)
{ {
printf("%sDATA%s frame (stream_id=%d, flags=%d, length=%d)\n", printf("%sDATA%s frame (length=%d, flags=%d, stream_id=%d)\n",
frame_name_ansi_esc(ptype), ansi_escend(), frame_name_ansi_esc(ptype), ansi_escend(),
stream_id, flags, length); length, flags, stream_id);
print_frame_attr_indent();
} }
} // namespace } // namespace
void on_data_recv_callback void on_data_recv_callback
(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t length, (nghttp2_session *session, uint16_t length, uint8_t flags, int32_t stream_id,
void *user_data) void *user_data)
{ {
print_timer(); print_timer();
printf(" recv "); printf(" recv ");
print_data_frame(PRINT_RECV, flags, stream_id, length); print_data_frame(PRINT_RECV, length, flags, stream_id);
fflush(stdout); fflush(stdout);
} }
void on_data_send_callback void on_data_send_callback
(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t length, (nghttp2_session *session, uint16_t length, uint8_t flags, int32_t stream_id,
void *user_data) void *user_data)
{ {
print_timer(); print_timer();
printf(" send "); printf(" send ");
print_data_frame(PRINT_SEND, flags, stream_id, length); print_data_frame(PRINT_SEND, length, flags, stream_id);
fflush(stdout); fflush(stdout);
} }
@ -770,18 +815,10 @@ int select_next_proto_cb(SSL* ssl,
std::cout << std::endl; std::cout << std::endl;
} }
} }
std::string& next_proto = *(std::string*)arg; if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
if(next_proto.empty()) { std::cerr << "Server did not advertise HTTP/2.0 protocol."
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { << std::endl;
std::cerr << "Server did not advertise spdy/2 or spdy/3 protocol." abort();
<< std::endl;
abort();
} else {
next_proto.assign(&(*out)[0], &(*out)[*outlen]);
}
} else {
*out = (unsigned char*)(next_proto.c_str());
*outlen = next_proto.size();
} }
if(ssl_debug) { if(ssl_debug) {
std::cout << " NPN selected the protocol: " std::cout << " NPN selected the protocol: "

View File

@ -43,7 +43,7 @@ extern bool ssl_debug;
class Spdylay { class Spdylay {
public: public:
Spdylay(int fd, SSL *ssl, uint16_t version, Spdylay(int fd, SSL *ssl,
const nghttp2_session_callbacks *callbacks, const nghttp2_session_callbacks *callbacks,
void *user_data); void *user_data);
~Spdylay(); ~Spdylay();
@ -62,13 +62,12 @@ public:
const nghttp2_data_provider *data_prd, const nghttp2_data_provider *data_prd,
int64_t data_length, int64_t data_length,
void *stream_user_data); void *stream_user_data);
int submit_settings(int flags, nghttp2_settings_entry *iv, size_t niv); int submit_settings(nghttp2_settings_entry *iv, size_t niv);
bool would_block(); bool would_block();
void* user_data(); void* user_data();
private: private:
int fd_; int fd_;
SSL *ssl_; SSL *ssl_;
uint16_t version_;
nghttp2_session *session_; nghttp2_session *session_;
void *user_data_; void *user_data_;
uint8_t io_flags_; uint8_t io_flags_;
@ -94,15 +93,14 @@ ssize_t recv_callback(nghttp2_session *session,
void print_nv(char **nv); void print_nv(char **nv);
void on_ctrl_recv_callback void on_frame_recv_callback
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, (nghttp2_session *session, nghttp2_frame *frame, void *user_data);
void *user_data);
void on_invalid_ctrl_recv_callback void on_invalid_frame_recv_callback
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, (nghttp2_session *session, nghttp2_frame *frame,
uint32_t status_code, void *user_data); nghttp2_error_code error_code, void *user_data);
void on_ctrl_recv_parse_error_callback(nghttp2_session *session, void on_frame_recv_parse_error_callback(nghttp2_session *session,
nghttp2_frame_type type, nghttp2_frame_type type,
const uint8_t *head, const uint8_t *head,
size_t headlen, size_t headlen,
@ -110,23 +108,22 @@ void on_ctrl_recv_parse_error_callback(nghttp2_session *session,
size_t payloadlen, size_t payloadlen,
int error_code, void *user_data); int error_code, void *user_data);
void on_unknown_ctrl_recv_callback(nghttp2_session *session, void on_unknown_frame_recv_callback(nghttp2_session *session,
const uint8_t *head, const uint8_t *head,
size_t headlen, size_t headlen,
const uint8_t *payload, const uint8_t *payload,
size_t payloadlen, size_t payloadlen,
void *user_data); void *user_data);
void on_ctrl_send_callback void on_frame_send_callback
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, (nghttp2_session *session, nghttp2_frame *frame, void *user_data);
void *user_data);
void on_data_recv_callback void on_data_recv_callback
(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t length, (nghttp2_session *session, uint16_t length, uint8_t flags, int32_t stream_id,
void *user_data); void *user_data);
void on_data_send_callback void on_data_send_callback
(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t length, (nghttp2_session *session, uint16_t length, uint8_t flags, int32_t stream_id,
void *user_data); void *user_data);
void ctl_poll(pollfd *pollfd, Spdylay *sc); void ctl_poll(pollfd *pollfd, Spdylay *sc);

View File

@ -75,7 +75,6 @@ struct Config {
bool stat; bool stat;
bool no_tls; bool no_tls;
int multiply; int multiply;
int spdy_version;
// milliseconds // milliseconds
int timeout; int timeout;
std::string certfile; std::string certfile;
@ -85,7 +84,7 @@ struct Config {
std::string datafile; std::string datafile;
Config():null_out(false), remote_name(false), verbose(false), Config():null_out(false), remote_name(false), verbose(false),
get_assets(false), stat(false), no_tls(false), multiply(1), get_assets(false), stat(false), no_tls(false), multiply(1),
spdy_version(-1), timeout(-1), window_bits(-1) timeout(-1), window_bits(-1)
{} {}
}; };
@ -447,47 +446,42 @@ void on_data_chunk_recv_callback
} }
} }
void check_stream_id(nghttp2_session *session, void check_stream_id(nghttp2_session *session, nghttp2_frame *frame,
nghttp2_frame_type type, nghttp2_frame *frame,
void *user_data) void *user_data)
{ {
SpdySession *spdySession = get_session(user_data); SpdySession *spdySession = get_session(user_data);
int32_t stream_id = frame->syn_stream.stream_id; int32_t stream_id = frame->hd.stream_id;
Request *req = (Request*)nghttp2_session_get_stream_user_data(session, Request *req = (Request*)nghttp2_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(); req->record_syn_stream_time();
} }
void on_ctrl_send_callback2 void on_frame_send_callback2
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, (nghttp2_session *session, nghttp2_frame *frame, void *user_data)
void *user_data)
{ {
if(type == NGHTTP2_SYN_STREAM) { if(frame->hd.type == NGHTTP2_HEADERS &&
check_stream_id(session, type, frame, user_data); frame->headers.cat == NGHTTP2_HCAT_START_STREAM) {
check_stream_id(session, frame, user_data);
} }
if(config.verbose) { if(config.verbose) {
on_ctrl_send_callback(session, type, frame, user_data); on_frame_send_callback(session, frame, user_data);
} }
} }
void check_response_header void check_response_header
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, (nghttp2_session *session, nghttp2_frame *frame, void *user_data)
void *user_data)
{ {
char **nv; char **nv;
int32_t stream_id; int32_t stream_id;
if(type == NGHTTP2_SYN_REPLY) { if(frame->hd.type == NGHTTP2_HEADERS) {
nv = frame->syn_reply.nv;
stream_id = frame->syn_reply.stream_id;
} else if(type == NGHTTP2_HEADERS) {
nv = frame->headers.nv; nv = frame->headers.nv;
stream_id = frame->headers.stream_id; stream_id = frame->hd.stream_id;
} else { } else {
return; return;
} }
Request *req = (Request*)nghttp2_session_get_stream_user_data(session, auto req = (Request*)nghttp2_session_get_stream_user_data(session,
stream_id); stream_id);
if(!req) { if(!req) {
// Server-pushed stream does not have stream user data // Server-pushed stream does not have stream user data
return; return;
@ -512,35 +506,34 @@ void check_response_header
} }
} }
void on_ctrl_recv_callback2 void on_frame_recv_callback2
(nghttp2_session *session, nghttp2_frame_type type, nghttp2_frame *frame, (nghttp2_session *session, nghttp2_frame *frame, void *user_data)
void *user_data)
{ {
if(type == NGHTTP2_SYN_REPLY) { if(frame->hd.type == NGHTTP2_HEADERS &&
Request *req = (Request*)nghttp2_session_get_stream_user_data frame->headers.cat == NGHTTP2_HCAT_REPLY) {
(session, frame->syn_reply.stream_id); auto req = (Request*)nghttp2_session_get_stream_user_data
(session, frame->hd.stream_id);
assert(req); assert(req);
req->record_syn_reply_time(); req->record_syn_reply_time();
} }
check_response_header(session, type, frame, user_data); check_response_header(session, frame, user_data);
if(config.verbose) { if(config.verbose) {
on_ctrl_recv_callback(session, type, frame, user_data); on_frame_recv_callback(session, frame, user_data);
} }
} }
void on_stream_close_callback void on_stream_close_callback
(nghttp2_session *session, int32_t stream_id, nghttp2_status_code status_code, (nghttp2_session *session, int32_t stream_id, nghttp2_error_code error_code,
void *user_data) void *user_data)
{ {
SpdySession *spdySession = get_session(user_data); SpdySession *spdySession = get_session(user_data);
std::map<int32_t, Request*>::iterator itr = auto itr = 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(); (*itr).second->record_complete_time();
++spdySession->complete; ++spdySession->complete;
if(spdySession->all_requests_processed()) { if(spdySession->all_requests_processed()) {
nghttp2_submit_goaway(session, NGHTTP2_GOAWAY_OK); nghttp2_submit_goaway(session, NGHTTP2_NO_ERROR, NULL, 0);
} }
} }
} }
@ -549,7 +542,7 @@ void print_stats(const SpdySession& spdySession)
{ {
std::cout << "***** Statistics *****" << std::endl; std::cout << "***** Statistics *****" << std::endl;
for(size_t i = 0; i < spdySession.reqvec.size(); ++i) { for(size_t i = 0; i < spdySession.reqvec.size(); ++i) {
const Request *req = spdySession.reqvec[i]; auto req = spdySession.reqvec[i];
std::cout << "#" << i+1 << ": " << req->uri << std::endl; std::cout << "#" << i+1 << ": " << req->uri << std::endl;
std::cout << " Status: " << req->status << std::endl; std::cout << " Status: " << req->status << std::endl;
std::cout << " Delta (ms) from SSL/TLS handshake(SYN_STREAM):" std::cout << " Delta (ms) from SSL/TLS handshake(SYN_STREAM):"
@ -578,22 +571,21 @@ void print_stats(const SpdySession& spdySession)
} }
} }
int spdy_evloop(int fd, SSL *ssl, int spdy_version, SpdySession& spdySession, int spdy_evloop(int fd, SSL *ssl, SpdySession& spdySession,
const nghttp2_session_callbacks *callbacks, int timeout) const nghttp2_session_callbacks *callbacks, int timeout)
{ {
int result = 0; int result = 0;
Spdylay sc(fd, ssl, spdy_version, callbacks, &spdySession); int rv;
Spdylay sc(fd, ssl, callbacks, &spdySession);
spdySession.sc = &sc; spdySession.sc = &sc;
nfds_t npollfds = 1; nfds_t npollfds = 1;
pollfd pollfds[1]; pollfd pollfds[1];
if(config.window_bits != -1) {
if(spdy_version >= NGHTTP2_PROTO_SPDY3 && config.window_bits != -1) {
nghttp2_settings_entry iv[1]; nghttp2_settings_entry iv[1];
iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[0].flags = NGHTTP2_ID_FLAG_SETTINGS_NONE;
iv[0].value = 1 << config.window_bits; iv[0].value = 1 << config.window_bits;
int rv = sc.submit_settings(NGHTTP2_FLAG_SETTINGS_NONE, iv, 1); rv = sc.submit_settings(iv, 1);
assert(rv == 0); assert(rv == 0);
} }
for(int i = 0, n = spdySession.reqvec.size(); i < n; ++i) { for(int i = 0, n = spdySession.reqvec.size(); i < n; ++i) {
@ -660,8 +652,6 @@ int communicate(const std::string& host, uint16_t port,
{ {
int result = 0; int result = 0;
int rv; int rv;
int spdy_version;
std::string next_proto;
int timeout = config.timeout; int timeout = config.timeout;
SSL_CTX *ssl_ctx = 0; SSL_CTX *ssl_ctx = 0;
SSL *ssl = 0; SSL *ssl = 0;
@ -683,15 +673,6 @@ int communicate(const std::string& host, uint16_t port,
<< std::endl; << std::endl;
} }
switch(config.spdy_version) {
case NGHTTP2_PROTO_SPDY2:
next_proto = "spdy/2";
break;
case NGHTTP2_PROTO_SPDY3:
next_proto = "spdy/3";
break;
}
if(!config.no_tls) { if(!config.no_tls) {
ssl_ctx = SSL_CTX_new(TLSv1_client_method()); ssl_ctx = SSL_CTX_new(TLSv1_client_method());
if(!ssl_ctx) { if(!ssl_ctx) {
@ -699,7 +680,7 @@ int communicate(const std::string& host, uint16_t port,
result = -1; result = -1;
goto fin; goto fin;
} }
setup_ssl_ctx(ssl_ctx, &next_proto); setup_ssl_ctx(ssl_ctx, nullptr);
if(!config.keyfile.empty()) { if(!config.keyfile.empty()) {
if(SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(), if(SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(),
SSL_FILETYPE_PEM) != 1) { SSL_FILETYPE_PEM) != 1) {
@ -722,23 +703,23 @@ int communicate(const std::string& host, uint16_t port,
result = -1; result = -1;
goto fin; goto fin;
} }
{
// If the user overrode the host header, use that value for the
// SNI extension
const char *host_string = 0;
auto i = config.headers.find( "Host" );
if ( i != config.headers.end() ) {
host_string = (*i).second.c_str();
}
else {
host_string = host.c_str();
}
// If the user overrode the host header, use that value for the if (!SSL_set_tlsext_host_name(ssl, host_string)) {
// SNI extension std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;
const char *host_string = 0; result = -1;
std::map<std::string,std::string>::const_iterator i = goto fin;
config.headers.find( "Host" ); }
if ( i != config.headers.end() ) {
host_string = (*i).second.c_str();
}
else {
host_string = host.c_str();
}
if (!SSL_set_tlsext_host_name(ssl, host_string)) {
std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;
result = -1;
goto fin;
} }
rv = ssl_nonblock_handshake(ssl, fd, timeout); rv = ssl_nonblock_handshake(ssl, fd, timeout);
if(rv == -1) { if(rv == -1) {
@ -759,16 +740,7 @@ int communicate(const std::string& host, uint16_t port,
} }
spdySession.record_handshake_time(); spdySession.record_handshake_time();
spdy_version = nghttp2_npn_get_version( result = spdy_evloop(fd, ssl, spdySession, callbacks, timeout);
reinterpret_cast<const unsigned char*>(next_proto.c_str()),
next_proto.size());
if (spdy_version <= 0) {
std::cerr << "No supported SPDY version was negotiated." << std::endl;
result = -1;
goto fin;
}
result = spdy_evloop(fd, ssl, spdy_version, spdySession, callbacks, timeout);
fin: fin:
if(ssl) { if(ssl) {
SSL_shutdown(ssl); SSL_shutdown(ssl);
@ -787,7 +759,7 @@ ssize_t file_read_callback
uint8_t *buf, size_t length, int *eof, uint8_t *buf, size_t length, int *eof,
nghttp2_data_source *source, void *user_data) nghttp2_data_source *source, void *user_data)
{ {
Request *req = (Request*)nghttp2_session_get_stream_user_data auto req = (Request*)nghttp2_session_get_stream_user_data
(session, stream_id); (session, stream_id);
int fd = source->fd; int fd = source->fd;
ssize_t r; ssize_t r;
@ -812,14 +784,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_frame_recv_callback = on_frame_recv_callback2;
callbacks.on_ctrl_send_callback = on_ctrl_send_callback2; callbacks.on_frame_send_callback = on_frame_send_callback2;
if(config.verbose) { if(config.verbose) {
callbacks.on_data_recv_callback = on_data_recv_callback; callbacks.on_data_recv_callback = on_data_recv_callback;
callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
callbacks.on_ctrl_recv_parse_error_callback = callbacks.on_frame_recv_parse_error_callback =
on_ctrl_recv_parse_error_callback; on_frame_recv_parse_error_callback;
callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback; callbacks.on_unknown_frame_recv_callback = on_unknown_frame_recv_callback;
} }
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;
@ -882,7 +854,7 @@ int run(char **uris, int n)
void print_usage(std::ostream& out) void print_usage(std::ostream& out)
{ {
out << "Usage: spdycat [-Oansv23] [-t <SECONDS>] [-w <WINDOW_BITS>] [--cert=<CERT>]\n" out << "Usage: spdycat [-Oansv] [-t <SECONDS>] [-w <WINDOW_BITS>] [--cert=<CERT>]\n"
<< " [--key=<KEY>] [--no-tls] [-d <FILE>] [-m <N>] <URI>..." << " [--key=<KEY>] [--no-tls] [-d <FILE>] [-m <N>] <URI>..."
<< std::endl; << std::endl;
} }
@ -899,8 +871,6 @@ void print_help(std::ostream& out)
<< " The filename is dereived from URI. If URI\n" << " The filename is dereived from URI. If URI\n"
<< " ends with '/', 'index.html' is used as a\n" << " ends with '/', 'index.html' is used as a\n"
<< " filename. Not implemented yet.\n" << " filename. Not implemented yet.\n"
<< " -2, --spdy2 Only use SPDY/2.\n"
<< " -3, --spdy3 Only use SPDY/3.\n"
<< " -t, --timeout=<N> Timeout each request after <N> seconds.\n" << " -t, --timeout=<N> Timeout each request after <N> seconds.\n"
<< " -w, --window-bits=<N>\n" << " -w, --window-bits=<N>\n"
<< " Sets the initial window size to 2**<N>.\n" << " Sets the initial window size to 2**<N>.\n"
@ -915,8 +885,7 @@ void print_help(std::ostream& out)
<< " 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"
<< " must be in PEM format.\n" << " must be in PEM format.\n"
<< " --no-tls Disable SSL/TLS. Use -2 or -3 to specify\n" << " --no-tls Disable SSL/TLS.\n"
<< " SPDY protocol version to use.\n"
<< " -d, --data=<FILE> Post FILE to server. If - is given, data\n" << " -d, --data=<FILE> Post FILE to server. If - is given, data\n"
<< " will be read from stdin.\n" << " will be read from stdin.\n"
<< " -m, --multiply=<N> Request each URI <N> times. By default, same\n" << " -m, --multiply=<N> Request each URI <N> times. By default, same\n"
@ -933,8 +902,6 @@ int main(int argc, char **argv)
{"verbose", no_argument, 0, 'v' }, {"verbose", no_argument, 0, 'v' },
{"null-out", no_argument, 0, 'n' }, {"null-out", no_argument, 0, 'n' },
{"remote-name", no_argument, 0, 'O' }, {"remote-name", no_argument, 0, 'O' },
{"spdy2", no_argument, 0, '2' },
{"spdy3", no_argument, 0, '3' },
{"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' },
@ -949,7 +916,7 @@ int main(int argc, char **argv)
{0, 0, 0, 0 } {0, 0, 0, 0 }
}; };
int option_index = 0; int option_index = 0;
int c = getopt_long(argc, argv, "Oad:m:nhH:v23st:w:", long_options, int c = getopt_long(argc, argv, "Oad:m:nhH:vst:w:", long_options,
&option_index); &option_index);
if(c == -1) { if(c == -1) {
break; break;
@ -967,12 +934,6 @@ int main(int argc, char **argv)
case 'v': case 'v':
config.verbose = true; config.verbose = true;
break; break;
case '2':
config.spdy_version = NGHTTP2_PROTO_SPDY2;
break;
case '3':
config.spdy_version = NGHTTP2_PROTO_SPDY3;
break;
case 't': case 't':
config.timeout = atoi(optarg) * 1000; config.timeout = atoi(optarg) * 1000;
break; break;
@ -1052,14 +1013,6 @@ int main(int argc, char **argv)
} }
} }
if(config.no_tls) {
if(config.spdy_version == -1) {
std::cerr << "Specify SPDY protocol version using either -2 or -3."
<< std::endl;
exit(EXIT_FAILURE);
}
}
set_color_output(isatty(fileno(stdout))); set_color_output(isatty(fileno(stdout)));
struct sigaction act; struct sigaction act;

View File

@ -47,7 +47,7 @@ extern bool ssl_debug;
namespace { namespace {
void print_usage(std::ostream& out) void print_usage(std::ostream& out)
{ {
out << "Usage: spdyd [-23DVhv] [-d <PATH>] [--no-tls] <PORT> [<PRIVATE_KEY> <CERT>]" out << "Usage: spdyd [-DVhv] [-d <PATH>] [--no-tls] <PORT> [<PRIVATE_KEY> <CERT>]"
<< std::endl; << std::endl;
} }
} // namespace } // namespace
@ -74,10 +74,7 @@ void print_help(std::ostream& out)
<< " current working directory.\n" << " current working directory.\n"
<< " -v, --verbose Print debug information such as reception/\n" << " -v, --verbose Print debug information such as reception/\n"
<< " transmission of frames and name/value pairs.\n" << " transmission of frames and name/value pairs.\n"
<< " -2, --spdy2 Only use SPDY/2.\n" << " --no-tls Disable SSL/TLS.\n"
<< " -3, --spdy3 Only use SPDY/3.\n"
<< " --no-tls Disable SSL/TLS. Use -2 or -3 to specify\n"
<< " SPDY protocol version to use.\n"
<< " -h, --help Print this help.\n" << " -h, --help Print this help.\n"
<< std::endl; << std::endl;
} }
@ -93,14 +90,12 @@ int main(int argc, char **argv)
{"htdocs", required_argument, 0, 'd' }, {"htdocs", required_argument, 0, 'd' },
{"help", no_argument, 0, 'h' }, {"help", no_argument, 0, 'h' },
{"verbose", no_argument, 0, 'v' }, {"verbose", no_argument, 0, 'v' },
{"spdy2", no_argument, 0, '2' },
{"spdy3", no_argument, 0, '3' },
{"verify-client", no_argument, 0, 'V' }, {"verify-client", no_argument, 0, 'V' },
{"no-tls", no_argument, &flag, 1 }, {"no-tls", no_argument, &flag, 1 },
{0, 0, 0, 0 } {0, 0, 0, 0 }
}; };
int option_index = 0; int option_index = 0;
int c = getopt_long(argc, argv, "DVd:hv23", long_options, &option_index); int c = getopt_long(argc, argv, "DVd:hv", long_options, &option_index);
if(c == -1) { if(c == -1) {
break; break;
} }
@ -120,12 +115,6 @@ int main(int argc, char **argv)
case 'v': case 'v':
config.verbose = true; config.verbose = true;
break; break;
case '2':
config.version = NGHTTP2_PROTO_SPDY2;
break;
case '3':
config.version = NGHTTP2_PROTO_SPDY3;
break;
case '?': case '?':
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
case 0: case 0:
@ -140,7 +129,7 @@ int main(int argc, char **argv)
break; break;
} }
} }
if(argc-optind < (config.no_tls ? 1 : 3)) { if(argc - optind < (config.no_tls ? 1 : 3)) {
print_usage(std::cerr); print_usage(std::cerr);
std::cerr << "Too few arguments" << std::endl; std::cerr << "Too few arguments" << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -148,13 +137,7 @@ int main(int argc, char **argv)
config.port = strtol(argv[optind++], 0, 10); config.port = strtol(argv[optind++], 0, 10);
if(config.no_tls) { if(!config.no_tls) {
if(config.version == 0) {
std::cerr << "Specify SPDY protocol version using either -2 or -3."
<< std::endl;
exit(EXIT_FAILURE);
}
} else {
config.private_key_file = argv[optind++]; config.private_key_file = argv[optind++];
config.cert_file = argv[optind++]; config.cert_file = argv[optind++];
} }

View File

@ -184,6 +184,26 @@ char upcase(char c)
} }
} }
std::string format_hex(const unsigned char *s, size_t len)
{
std::string res;
for(size_t i = 0; i < len; ++i) {
unsigned char c = s[i] >> 4;
if(c > 9) {
res += c - 10 + 'a';
} else {
res += c + '0';
}
c = s[i] & 0xf;
if(c > 9) {
res += c - 10 + 'a';
} else {
res += c + '0';
}
}
return res;
}
} // namespace util } // namespace util
} // namespace nghttp2 } // namespace nghttp2

View File

@ -204,6 +204,8 @@ std::string percentEncode(const std::string& target);
std::string percentDecode std::string percentDecode
(std::string::const_iterator first, std::string::const_iterator last); (std::string::const_iterator first, std::string::const_iterator last);
std::string format_hex(const unsigned char *s, size_t len);
std::string http_date(time_t t); std::string http_date(time_t t);
time_t parse_http_date(const std::string& s); time_t parse_http_date(const std::string& s);

View File

@ -174,8 +174,8 @@ static void on_data_chunk_recv_callback(nghttp2_session *session,
} }
static void on_data_recv_callback(nghttp2_session *session, static void on_data_recv_callback(nghttp2_session *session,
uint8_t flags, int32_t stream_id, uint16_t length, uint8_t flags,
int32_t length, void *user_data) int32_t stream_id, void *user_data)
{ {
my_user_data *ud = (my_user_data*)user_data; my_user_data *ud = (my_user_data*)user_data;
++ud->data_recv_cb_called; ++ud->data_recv_cb_called;