Added experimental spdy/3 support to spdyd, spdynative and spdycat
This commit is contained in:
parent
0a723aa10f
commit
70ebf673fc
|
@ -32,6 +32,7 @@
|
|||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
|
||||
|
@ -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<unsigned char*, size_t> next_proto;
|
||||
unsigned char proto_list[7];
|
||||
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);
|
||||
|
||||
|
|
|
@ -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<int32_t, Request*> id2req_;
|
||||
|
|
|
@ -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<const unsigned char*>
|
||||
(next_proto.c_str()), next_proto.size()),
|
||||
callbacks);
|
||||
|
||||
nfds_t npollfds = 1;
|
||||
pollfd pollfds[1];
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue