From 70ebf673fcea07b753829d3afba91c09258fd32a Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 26 Feb 2012 01:31:45 +0900 Subject: [PATCH] Added experimental spdy/3 support to spdyd, spdynative and spdycat --- examples/SpdyServer.cc | 73 ++++++++++++++++++++++++++++------------- examples/SpdyServer.h | 7 +++- examples/spdycat.cc | 8 +++-- examples/spdyd.cc | 9 ++++- examples/spdylay_ssl.cc | 40 +++++++++++++++------- examples/spdylay_ssl.h | 25 ++++++++++++-- 6 files changed, 122 insertions(+), 40 deletions(-) diff --git a/examples/SpdyServer.cc b/examples/SpdyServer.cc index 73454822..4e5557fe 100644 --- a/examples/SpdyServer.cc +++ b/examples/SpdyServer.cc @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -54,7 +55,8 @@ const std::string DEFAULT_HTML = "index.html"; const std::string SPDYD_SERVER = "spdyd spdylay/"SPDYLAY_VERSION; } // namespace -Config::Config(): verbose(false), daemon(false), port(0), data_ptr(0) +Config::Config(): verbose(false), daemon(false), port(0), data_ptr(0), + spdy3_only(false) {} Request::Request(int32_t stream_id) @@ -168,12 +170,16 @@ void on_session_closed(EventHandler *hd, int64_t session_id) SpdyEventHandler::SpdyEventHandler(const Config* config, int fd, SSL *ssl, + uint16_t version, const spdylay_session_callbacks *callbacks, int64_t session_id) : EventHandler(config), - fd_(fd), ssl_(ssl), session_id_(session_id), want_write_(false) + fd_(fd), ssl_(ssl), version_(version), session_id_(session_id), + want_write_(false) { - spdylay_session_server_new(&session_, SPDYLAY_PROTO_SPDY2, callbacks, this); + int r; + r = spdylay_session_server_new(&session_, version, callbacks, this); + assert(r == 0); } SpdyEventHandler::~SpdyEventHandler() @@ -190,6 +196,11 @@ SpdyEventHandler::~SpdyEventHandler() close(fd_); } +uint16_t SpdyEventHandler::version() const +{ + return version_; +} + int SpdyEventHandler::execute(Sessions *sessions) { int r; @@ -253,8 +264,8 @@ int SpdyEventHandler::submit_file_response(const std::string& status, spdylay_data_provider *data_prd) { const char *nv[] = { - "status", status.c_str(), - "version", "HTTP/1.1", + get_header_field(version_, HD_STATUS).c_str(), status.c_str(), + get_header_field(version_, HD_VERSION).c_str(), "HTTP/1.1", "server", SPDYD_SERVER.c_str(), "content-length", util::to_str(file_length).c_str(), "cache-control", "max-age=3600", @@ -276,9 +287,9 @@ int SpdyEventHandler::submit_response spdylay_data_provider *data_prd) { const char **nv = new const char*[8+headers.size()*2+1]; - nv[0] = "status"; + nv[0] = get_header_field(version_, HD_STATUS).c_str(); nv[1] = status.c_str(); - nv[2] = "version"; + nv[2] = get_header_field(version_, HD_VERSION).c_str(); nv[3] = "HTTP/1.1"; nv[4] = "server"; nv[5] = SPDYD_SERVER.c_str(); @@ -299,8 +310,8 @@ int SpdyEventHandler::submit_response(const std::string& status, spdylay_data_provider *data_prd) { const char *nv[] = { - "status", status.c_str(), - "version", "HTTP/1.1", + get_header_field(version_, HD_STATUS).c_str(), status.c_str(), + get_header_field(version_, HD_VERSION).c_str(), "HTTP/1.1", "server", SPDYD_SERVER.c_str(), 0 }; @@ -432,26 +443,30 @@ void prepare_response(Request *req, SpdyEventHandler *hd) bool method_found = false; bool scheme_found = false; bool version_found = false; + bool host_found = false; time_t last_mod = 0; bool last_mod_found = false; for(int i = 0; i < (int)req->headers.size(); ++i) { const std::string &field = req->headers[i].first; const std::string &value = req->headers[i].second; - if(!url_found && field == "url") { + if(!url_found && field == get_header_field(hd->version(), HD_PATH)) { url_found = true; url = value; - } else if(field == "method") { + } else if(field == get_header_field(hd->version(), HD_METHOD)) { method_found = true; - } else if(field == "scheme") { + } else if(field == get_header_field(hd->version(), HD_SCHEME)) { scheme_found = true; - } else if(field == "version") { + } else if(field == get_header_field(hd->version(), HD_VERSION)) { version_found = true; + } else if(field == get_header_field(hd->version(), HD_HOST)) { + host_found = true; } else if(!last_mod_found && field == "if-modified-since") { last_mod_found = true; last_mod = util::parse_http_date(value); } } - if(!url_found || !method_found || !scheme_found || !version_found) { + if(!url_found || !method_found || !scheme_found || !version_found || + !host_found) { prepare_status_response(req, hd, STATUS_400); return; } @@ -608,7 +623,7 @@ public: SSLAcceptEventHandler(const Config *config, int fd, SSL *ssl, int64_t session_id) : EventHandler(config), - fd_(fd), ssl_(ssl), fail_(false), finish_(false), + fd_(fd), ssl_(ssl), version_(0), fail_(false), finish_(false), want_read_(true), want_write_(true), session_id_(session_id) {} @@ -636,7 +651,8 @@ public: if(config()->verbose) { std::cout << "The negotiated next protocol: " << proto << std::endl; } - if(proto == "spdy/2") { + version_ = spdylay_npn_get_version(next_proto, next_proto_len); + if(version_) { add_next_handler(sessions); } else { fail_ = true; @@ -697,7 +713,7 @@ private: callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; callbacks.on_request_recv_callback = config()->on_request_recv_callback; SpdyEventHandler *hd = new SpdyEventHandler(config(), - fd_, ssl_, &callbacks, + fd_, ssl_, version_, &callbacks, session_id_); if(sessions->mod_poll(hd) == -1) { // fd_, ssl_ are freed by ~SpdyEventHandler() @@ -709,6 +725,7 @@ private: int fd_; SSL *ssl_; + uint16_t version_; bool fail_, finish_; bool want_read_, want_write_; int64_t session_id_; @@ -862,13 +879,23 @@ int SpdyServer::run() return -1; } - // We only speak "spdy/2". + // We speaks "spdy/2" and "spdy/3". std::pair next_proto; - unsigned char proto_list[7]; - proto_list[0] = 6; - memcpy(&proto_list[1], "spdy/2", 6); - next_proto.first = proto_list; - next_proto.second = sizeof(proto_list); + unsigned char proto_list[14]; + + if(config_->spdy3_only) { + proto_list[0] = 6; + memcpy(&proto_list[1], "spdy/3", 6); + next_proto.first = proto_list; + next_proto.second = 7; + } else { + proto_list[0] = 6; + memcpy(&proto_list[1], "spdy/2", 6); + proto_list[7] = 6; + memcpy(&proto_list[8], "spdy/3", 6); + 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); diff --git a/examples/SpdyServer.h b/examples/SpdyServer.h index b76dd33e..4aefde40 100644 --- a/examples/SpdyServer.h +++ b/examples/SpdyServer.h @@ -50,6 +50,7 @@ struct Config { std::string cert_file; spdylay_on_request_recv_callback on_request_recv_callback; void *data_ptr; + bool spdy3_only; Config(); }; @@ -93,7 +94,8 @@ struct Request { class SpdyEventHandler : public EventHandler { public: SpdyEventHandler(const Config* config, - int fd, SSL *ssl, const spdylay_session_callbacks *callbacks, + int fd, SSL *ssl, uint16_t version, + const spdylay_session_callbacks *callbacks, int64_t session_id); virtual ~SpdyEventHandler(); virtual int execute(Sessions *sessions); @@ -102,6 +104,8 @@ public: virtual int fd() const; virtual bool finish(); + uint16_t version() const; + 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); @@ -132,6 +136,7 @@ private: spdylay_session *session_; int fd_; SSL* ssl_; + uint16_t version_; int64_t session_id_; bool want_write_; std::map id2req_; diff --git a/examples/spdycat.cc b/examples/spdycat.cc index b433958e..9bf8b72e 100644 --- a/examples/spdycat.cc +++ b/examples/spdycat.cc @@ -144,7 +144,8 @@ int communicate(const std::string& host, uint16_t port, std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; return -1; } - setup_ssl_ctx(ssl_ctx); + std::string next_proto; + setup_ssl_ctx(ssl_ctx, &next_proto); SSL *ssl = SSL_new(ssl_ctx); if(!ssl) { std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; @@ -155,7 +156,10 @@ int communicate(const std::string& host, uint16_t port, } make_non_block(fd); set_tcp_nodelay(fd); - Spdylay sc(fd, ssl, callbacks); + Spdylay sc(fd, ssl, + spdylay_npn_get_version(reinterpret_cast + (next_proto.c_str()), next_proto.size()), + callbacks); nfds_t npollfds = 1; pollfd pollfds[1]; diff --git a/examples/spdyd.cc b/examples/spdyd.cc index 314387d6..f66a4a39 100644 --- a/examples/spdyd.cc +++ b/examples/spdyd.cc @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,8 @@ void print_help(std::ostream& out) << " -v, --verbose Print debug information such as reception/\n" << " transmission of frames and name/value pairs.\n" << "\n" + << " -3, --spdy3 Only use SPDY/3.\n" + << "\n" << " -h, --help Print this help.\n" << std::endl; } @@ -82,10 +85,11 @@ int main(int argc, char **argv) {"htdocs", required_argument, 0, 'd' }, {"help", no_argument, 0, 'h' }, {"verbose", no_argument, 0, 'v' }, + {"spdy3", no_argument, 0, '3' }, {0, 0, 0, 0 } }; int option_index = 0; - int c = getopt_long(argc, argv, "Dd:hv", long_options, &option_index); + int c = getopt_long(argc, argv, "Dd:hv3", long_options, &option_index); if(c == -1) { break; } @@ -102,6 +106,9 @@ int main(int argc, char **argv) case 'v': config.verbose = true; break; + case '3': + config.spdy3_only = true; + break; case '?': exit(EXIT_FAILURE); default: diff --git a/examples/spdylay_ssl.cc b/examples/spdylay_ssl.cc index b62826dd..7b764de2 100644 --- a/examples/spdylay_ssl.cc +++ b/examples/spdylay_ssl.cc @@ -48,10 +48,23 @@ namespace spdylay { bool ssl_debug = false; -Spdylay::Spdylay(int fd, SSL *ssl, const spdylay_session_callbacks *callbacks) - : fd_(fd), ssl_(ssl), want_write_(false) +const std::string& get_header_field(uint16_t version, size_t field) { - spdylay_session_client_new(&session_, SPDYLAY_PROTO_SPDY2, callbacks, this); + if(version == SPDYLAY_PROTO_SPDY2) { + return header_fields_spdy2[field]; + } else if(version == SPDYLAY_PROTO_SPDY3) { + return header_fields_spdy3[field]; + } else { + abort(); + } +} + +Spdylay::Spdylay(int fd, SSL *ssl, uint16_t version, + const spdylay_session_callbacks *callbacks) + : fd_(fd), ssl_(ssl), version_(version), want_write_(false) +{ + int r = spdylay_session_client_new(&session_, version_, callbacks, this); + assert(r == 0); } Spdylay::~Spdylay() @@ -109,12 +122,12 @@ int Spdylay::submit_request(const std::string& hostport, void *stream_user_data) { const char *nv[] = { - "host", hostport.c_str(), - "method", "GET", - "scheme", "https", - "url", path.c_str(), + get_header_field(version_, HD_METHOD).c_str(), "GET", + get_header_field(version_, HD_PATH).c_str(), path.c_str(), + get_header_field(version_, HD_VERSION).c_str(), "HTTP/1.1", + get_header_field(version_, HD_SCHEME).c_str(), "https", + get_header_field(version_, HD_HOST).c_str(), hostport.c_str(), "user-agent", "spdylay/0.0.0", - "version", "HTTP/1.1", NULL }; return spdylay_submit_request(session_, pri, nv, NULL, stream_user_data); @@ -458,8 +471,12 @@ int select_next_proto_cb(SSL* ssl, } } if(spdylay_select_next_protocol(out, outlen, in, inlen) != 1) { - std::cerr << "Server did not advertise spdy/2 protocol." << std::endl; + std::cerr << "Server did not advertise spdy/2 or spdy/3 protocol." + << std::endl; abort(); + } else { + std::string& next_proto = *(std::string*)arg; + next_proto.assign(&(*out)[0], &(*out)[*outlen]); } if(ssl_debug) { std::cout << " NPN selected the protocol: " @@ -468,13 +485,14 @@ int select_next_proto_cb(SSL* ssl, return SSL_TLSEXT_ERR_OK; } -void setup_ssl_ctx(SSL_CTX *ssl_ctx) +void setup_ssl_ctx(SSL_CTX *ssl_ctx, void *next_proto_select_cb_arg) { /* Disable SSLv2 and enable all workarounds for buggy servers */ SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, 0); + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, + next_proto_select_cb_arg); } int ssl_handshake(SSL *ssl, int fd) diff --git a/examples/spdylay_ssl.h b/examples/spdylay_ssl.h index 51e5b9a0..db97383c 100644 --- a/examples/spdylay_ssl.h +++ b/examples/spdylay_ssl.h @@ -38,9 +38,29 @@ namespace spdylay { extern bool ssl_debug; +enum HeaderField { + HD_METHOD = 0, + HD_PATH = 1, + HD_VERSION = 2, + HD_HOST = 3, + HD_SCHEME = 4, + HD_STATUS = 5 +}; + +const std::string header_fields_spdy2[] = { + "method", "url", "version", "host", "scheme", "status", "version" +}; + +const std::string header_fields_spdy3[] = { + ":method", ":path", ":version", ":host", ":scheme", ":status", ":version" +}; + +const std::string& get_header_field(uint16_t version, size_t field); + class Spdylay { public: - Spdylay(int fd, SSL *ssl, const spdylay_session_callbacks *callbacks); + Spdylay(int fd, SSL *ssl, uint16_t version, + const spdylay_session_callbacks *callbacks); ~Spdylay(); int recv(); int send(); @@ -55,6 +75,7 @@ public: private: int fd_; SSL *ssl_; + uint16_t version_; spdylay_session *session_; bool want_write_; bool debug_; @@ -100,7 +121,7 @@ int select_next_proto_cb(SSL* ssl, const unsigned char *in, unsigned int inlen, void *arg); -void setup_ssl_ctx(SSL_CTX *ssl_ctx); +void setup_ssl_ctx(SSL_CTX *ssl_ctx, void *next_proto_select_cb_arg); int ssl_handshake(SSL *ssl, int fd);