Added experimental spdy/3 support to spdyd, spdynative and spdycat

This commit is contained in:
Tatsuhiro Tsujikawa 2012-02-26 01:31:45 +09:00
parent 0a723aa10f
commit 70ebf673fc
6 changed files with 122 additions and 40 deletions

View File

@ -32,6 +32,7 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <cassert>
#include <set> #include <set>
#include <iostream> #include <iostream>
@ -54,7 +55,8 @@ const std::string DEFAULT_HTML = "index.html";
const std::string SPDYD_SERVER = "spdyd spdylay/"SPDYLAY_VERSION; const std::string SPDYD_SERVER = "spdyd spdylay/"SPDYLAY_VERSION;
} // namespace } // 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) 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, SpdyEventHandler::SpdyEventHandler(const Config* config,
int fd, SSL *ssl, int fd, SSL *ssl,
uint16_t version,
const spdylay_session_callbacks *callbacks, const spdylay_session_callbacks *callbacks,
int64_t session_id) int64_t session_id)
: EventHandler(config), : 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() SpdyEventHandler::~SpdyEventHandler()
@ -190,6 +196,11 @@ 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;
@ -253,8 +264,8 @@ int SpdyEventHandler::submit_file_response(const std::string& status,
spdylay_data_provider *data_prd) spdylay_data_provider *data_prd)
{ {
const char *nv[] = { const char *nv[] = {
"status", status.c_str(), get_header_field(version_, HD_STATUS).c_str(), status.c_str(),
"version", "HTTP/1.1", get_header_field(version_, HD_VERSION).c_str(), "HTTP/1.1",
"server", SPDYD_SERVER.c_str(), "server", SPDYD_SERVER.c_str(),
"content-length", util::to_str(file_length).c_str(), "content-length", util::to_str(file_length).c_str(),
"cache-control", "max-age=3600", "cache-control", "max-age=3600",
@ -276,9 +287,9 @@ int SpdyEventHandler::submit_response
spdylay_data_provider *data_prd) spdylay_data_provider *data_prd)
{ {
const char **nv = new const char*[8+headers.size()*2+1]; 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[1] = status.c_str();
nv[2] = "version"; nv[2] = get_header_field(version_, HD_VERSION).c_str();
nv[3] = "HTTP/1.1"; nv[3] = "HTTP/1.1";
nv[4] = "server"; nv[4] = "server";
nv[5] = SPDYD_SERVER.c_str(); nv[5] = SPDYD_SERVER.c_str();
@ -299,8 +310,8 @@ int SpdyEventHandler::submit_response(const std::string& status,
spdylay_data_provider *data_prd) spdylay_data_provider *data_prd)
{ {
const char *nv[] = { const char *nv[] = {
"status", status.c_str(), get_header_field(version_, HD_STATUS).c_str(), status.c_str(),
"version", "HTTP/1.1", get_header_field(version_, HD_VERSION).c_str(), "HTTP/1.1",
"server", SPDYD_SERVER.c_str(), "server", SPDYD_SERVER.c_str(),
0 0
}; };
@ -432,26 +443,30 @@ void prepare_response(Request *req, SpdyEventHandler *hd)
bool method_found = false; bool method_found = false;
bool scheme_found = false; bool scheme_found = false;
bool version_found = false; bool version_found = false;
bool host_found = false;
time_t last_mod = 0; time_t last_mod = 0;
bool last_mod_found = false; bool last_mod_found = false;
for(int i = 0; i < (int)req->headers.size(); ++i) { for(int i = 0; i < (int)req->headers.size(); ++i) {
const std::string &field = req->headers[i].first; const std::string &field = req->headers[i].first;
const std::string &value = req->headers[i].second; 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_found = true;
url = value; url = value;
} else if(field == "method") { } else if(field == get_header_field(hd->version(), HD_METHOD)) {
method_found = true; method_found = true;
} else if(field == "scheme") { } else if(field == get_header_field(hd->version(), HD_SCHEME)) {
scheme_found = true; scheme_found = true;
} else if(field == "version") { } else if(field == get_header_field(hd->version(), HD_VERSION)) {
version_found = true; 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") { } else if(!last_mod_found && field == "if-modified-since") {
last_mod_found = true; last_mod_found = true;
last_mod = util::parse_http_date(value); 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); prepare_status_response(req, hd, STATUS_400);
return; return;
} }
@ -608,7 +623,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), fail_(false), finish_(false), fd_(fd), ssl_(ssl), version_(0), fail_(false), finish_(false),
want_read_(true), want_write_(true), want_read_(true), want_write_(true),
session_id_(session_id) session_id_(session_id)
{} {}
@ -636,7 +651,8 @@ 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;
} }
if(proto == "spdy/2") { version_ = spdylay_npn_get_version(next_proto, next_proto_len);
if(version_) {
add_next_handler(sessions); add_next_handler(sessions);
} else { } else {
fail_ = true; fail_ = true;
@ -697,7 +713,7 @@ private:
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;
SpdyEventHandler *hd = new SpdyEventHandler(config(), SpdyEventHandler *hd = new SpdyEventHandler(config(),
fd_, ssl_, &callbacks, fd_, ssl_, version_, &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()
@ -709,6 +725,7 @@ private:
int fd_; int fd_;
SSL *ssl_; SSL *ssl_;
uint16_t version_;
bool fail_, finish_; bool fail_, finish_;
bool want_read_, want_write_; bool want_read_, want_write_;
int64_t session_id_; int64_t session_id_;
@ -862,13 +879,23 @@ int SpdyServer::run()
return -1; return -1;
} }
// We only speak "spdy/2". // We speaks "spdy/2" and "spdy/3".
std::pair<unsigned char*, size_t> next_proto; std::pair<unsigned char*, size_t> next_proto;
unsigned char proto_list[7]; unsigned char proto_list[14];
proto_list[0] = 6;
memcpy(&proto_list[1], "spdy/2", 6); if(config_->spdy3_only) {
next_proto.first = proto_list; proto_list[0] = 6;
next_proto.second = sizeof(proto_list); 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); SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);

View File

@ -50,6 +50,7 @@ struct Config {
std::string cert_file; std::string cert_file;
spdylay_on_request_recv_callback on_request_recv_callback; spdylay_on_request_recv_callback on_request_recv_callback;
void *data_ptr; void *data_ptr;
bool spdy3_only;
Config(); Config();
}; };
@ -93,7 +94,8 @@ struct Request {
class SpdyEventHandler : public EventHandler { class SpdyEventHandler : public EventHandler {
public: public:
SpdyEventHandler(const Config* config, 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); int64_t session_id);
virtual ~SpdyEventHandler(); virtual ~SpdyEventHandler();
virtual int execute(Sessions *sessions); virtual int execute(Sessions *sessions);
@ -102,6 +104,8 @@ 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);
@ -132,6 +136,7 @@ private:
spdylay_session *session_; spdylay_session *session_;
int fd_; int fd_;
SSL* ssl_; SSL* ssl_;
uint16_t version_;
int64_t session_id_; int64_t session_id_;
bool want_write_; bool want_write_;
std::map<int32_t, Request*> id2req_; std::map<int32_t, Request*> id2req_;

View File

@ -144,7 +144,8 @@ int communicate(const std::string& host, uint16_t port,
std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;
return -1; return -1;
} }
setup_ssl_ctx(ssl_ctx); std::string next_proto;
setup_ssl_ctx(ssl_ctx, &next_proto);
SSL *ssl = SSL_new(ssl_ctx); SSL *ssl = SSL_new(ssl_ctx);
if(!ssl) { if(!ssl) {
std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; 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); make_non_block(fd);
set_tcp_nodelay(fd); set_tcp_nodelay(fd);
Spdylay sc(fd, ssl, callbacks); Spdylay sc(fd, ssl,
spdylay_npn_get_version(reinterpret_cast<const unsigned char*>
(next_proto.c_str()), next_proto.size()),
callbacks);
nfds_t npollfds = 1; nfds_t npollfds = 1;
pollfd pollfds[1]; pollfd pollfds[1];

View File

@ -28,6 +28,7 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <cassert>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <string> #include <string>
@ -68,6 +69,8 @@ void print_help(std::ostream& out)
<< " -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"
<< "\n" << "\n"
<< " -3, --spdy3 Only use SPDY/3.\n"
<< "\n"
<< " -h, --help Print this help.\n" << " -h, --help Print this help.\n"
<< std::endl; << std::endl;
} }
@ -82,10 +85,11 @@ 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' },
{"spdy3", no_argument, 0, '3' },
{0, 0, 0, 0 } {0, 0, 0, 0 }
}; };
int option_index = 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) { if(c == -1) {
break; break;
} }
@ -102,6 +106,9 @@ int main(int argc, char **argv)
case 'v': case 'v':
config.verbose = true; config.verbose = true;
break; break;
case '3':
config.spdy3_only = true;
break;
case '?': case '?':
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
default: default:

View File

@ -48,10 +48,23 @@ namespace spdylay {
bool ssl_debug = false; bool ssl_debug = false;
Spdylay::Spdylay(int fd, SSL *ssl, const spdylay_session_callbacks *callbacks) const std::string& get_header_field(uint16_t version, size_t field)
: fd_(fd), ssl_(ssl), want_write_(false)
{ {
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() Spdylay::~Spdylay()
@ -109,12 +122,12 @@ int Spdylay::submit_request(const std::string& hostport,
void *stream_user_data) void *stream_user_data)
{ {
const char *nv[] = { const char *nv[] = {
"host", hostport.c_str(), get_header_field(version_, HD_METHOD).c_str(), "GET",
"method", "GET", get_header_field(version_, HD_PATH).c_str(), path.c_str(),
"scheme", "https", get_header_field(version_, HD_VERSION).c_str(), "HTTP/1.1",
"url", path.c_str(), 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", "user-agent", "spdylay/0.0.0",
"version", "HTTP/1.1",
NULL NULL
}; };
return spdylay_submit_request(session_, pri, nv, NULL, stream_user_data); 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) { 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(); abort();
} else {
std::string& next_proto = *(std::string*)arg;
next_proto.assign(&(*out)[0], &(*out)[*outlen]);
} }
if(ssl_debug) { if(ssl_debug) {
std::cout << " NPN selected the protocol: " std::cout << " NPN selected the protocol: "
@ -468,13 +485,14 @@ int select_next_proto_cb(SSL* ssl,
return SSL_TLSEXT_ERR_OK; 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 */ /* 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_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_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); 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) int ssl_handshake(SSL *ssl, int fd)

View File

@ -38,9 +38,29 @@ namespace spdylay {
extern bool ssl_debug; 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 { class Spdylay {
public: 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(); ~Spdylay();
int recv(); int recv();
int send(); int send();
@ -55,6 +75,7 @@ public:
private: private:
int fd_; int fd_;
SSL *ssl_; SSL *ssl_;
uint16_t version_;
spdylay_session *session_; spdylay_session *session_;
bool want_write_; bool want_write_;
bool debug_; bool debug_;
@ -100,7 +121,7 @@ int select_next_proto_cb(SSL* ssl,
const unsigned char *in, unsigned int inlen, const unsigned char *in, unsigned int inlen,
void *arg); 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); int ssl_handshake(SSL *ssl, int fd);