diff --git a/src/SpdyServer.cc b/src/SpdyServer.cc index 1ed61e1..709bc66 100644 --- a/src/SpdyServer.cc +++ b/src/SpdyServer.cc @@ -61,7 +61,7 @@ const std::string SPDYD_SERVER = "spdyd spdylay/" SPDYLAY_VERSION; } // namespace Config::Config(): verbose(false), daemon(false), port(0), data_ptr(0), - spdy3_only(false), verify_client(false) + version(0), verify_client(false), no_tls(false) {} Request::Request(int32_t stream_id) @@ -180,7 +180,7 @@ SpdyEventHandler::SpdyEventHandler(const Config* config, int64_t session_id) : EventHandler(config), fd_(fd), ssl_(ssl), version_(version), session_id_(session_id), - want_write_(false) + io_flags_(0) { int r; r = spdylay_session_server_new(&session_, version, callbacks, this); @@ -202,8 +202,10 @@ SpdyEventHandler::~SpdyEventHandler() eoi = id2req_.end(); i != eoi; ++i) { delete (*i).second; } - SSL_shutdown(ssl_); - SSL_free(ssl_); + if(ssl_) { + SSL_shutdown(ssl_); + SSL_free(ssl_); + } shutdown(fd_, SHUT_WR); close(fd_); } @@ -225,12 +227,12 @@ int SpdyEventHandler::execute(Sessions *sessions) bool SpdyEventHandler::want_read() { - return spdylay_session_want_read(session_); + return spdylay_session_want_read(session_) || (io_flags_ & WANT_READ); } bool SpdyEventHandler::want_write() { - return spdylay_session_want_write(session_) || want_write_; + return spdylay_session_want_write(session_) || (io_flags_ & WANT_WRITE); } int SpdyEventHandler::fd() const @@ -247,29 +249,44 @@ bool SpdyEventHandler::finish() ssize_t SpdyEventHandler::send_data(const uint8_t *data, size_t len, int flags) { ssize_t r; - ERR_clear_error(); - r = SSL_write(ssl_, data, len); + io_flags_ = 0; + if(ssl_) { + ERR_clear_error(); + r = SSL_write(ssl_, data, len); + if(r < 0) { + io_flags_ = get_ssl_io_demand(ssl_, r); + } + } else { + while((r = ::send(fd_, data, len, 0)) == -1 && errno == EINTR); + if(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + io_flags_ |= WANT_WRITE; + } + } return r; } ssize_t SpdyEventHandler::recv_data(uint8_t *data, size_t len, int flags) { ssize_t r; - want_write_ = false; - ERR_clear_error(); - r = SSL_read(ssl_, data, len); - if(r < 0) { - if(SSL_get_error(ssl_, r) == SSL_ERROR_WANT_WRITE) { - want_write_ = true; + io_flags_ = 0; + if(ssl_) { + ERR_clear_error(); + r = SSL_read(ssl_, data, len); + if(r < 0) { + io_flags_ = get_ssl_io_demand(ssl_, r); + } + } else { + while((r = ::recv(fd_, data, len, 0)) == -1 && errno == EINTR); + if(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + io_flags_ |= WANT_READ; } } return r; } -bool SpdyEventHandler::would_block(int r) +bool SpdyEventHandler::would_block() const { - int e = SSL_get_error(ssl_, r); - return e == SSL_ERROR_WANT_WRITE || e == SSL_ERROR_WANT_READ; + return io_flags_; } int SpdyEventHandler::submit_file_response(const std::string& status, @@ -368,11 +385,14 @@ ssize_t hd_send_callback(spdylay_session *session, SpdyEventHandler *hd = (SpdyEventHandler*)user_data; ssize_t r = hd->send_data(data, len, flags); if(r < 0) { - if(hd->would_block(r)) { + if(hd->would_block()) { r = SPDYLAY_ERR_WOULDBLOCK; } else { r = SPDYLAY_ERR_CALLBACK_FAILURE; } + } else if(r == 0) { + // In OpenSSL, r == 0 means EOF because SSL_write may do read. + r = SPDYLAY_ERR_CALLBACK_FAILURE; } return r; } @@ -385,7 +405,7 @@ ssize_t hd_recv_callback(spdylay_session *session, SpdyEventHandler *hd = (SpdyEventHandler*)user_data; ssize_t r = hd->recv_data(data, len, flags); if(r < 0) { - if(hd->would_block(r)) { + if(hd->would_block()) { r = SPDYLAY_ERR_WOULDBLOCK; } else { r = SPDYLAY_ERR_CALLBACK_FAILURE; @@ -649,13 +669,35 @@ void on_stream_close_callback } } // namespace +namespace { +void fill_callback(spdylay_session_callbacks& callbacks, const Config *config) +{ + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = hd_send_callback; + callbacks.recv_callback = hd_recv_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + callbacks.on_ctrl_recv_callback = hd_on_ctrl_recv_callback; + callbacks.on_ctrl_send_callback = hd_on_ctrl_send_callback; + callbacks.on_data_recv_callback = hd_on_data_recv_callback; + callbacks.on_data_send_callback = hd_on_data_send_callback; + if(config->verbose) { + callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; + callbacks.on_ctrl_recv_parse_error_callback = + on_ctrl_recv_parse_error_callback; + callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback; + } + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + callbacks.on_request_recv_callback = config->on_request_recv_callback; +} +} // namespace + class SSLAcceptEventHandler : public EventHandler { public: SSLAcceptEventHandler(const Config *config, int fd, SSL *ssl, int64_t session_id) : EventHandler(config), fd_(fd), ssl_(ssl), version_(0), fail_(false), finish_(false), - want_read_(true), want_write_(true), + io_flags_(WANT_READ), session_id_(session_id) {} virtual ~SSLAcceptEventHandler() @@ -670,7 +712,7 @@ public: } virtual int execute(Sessions *sessions) { - want_read_ = want_write_ = false; + io_flags_ = 0; ERR_clear_error(); int r = SSL_accept(ssl_); if(r == 1) { @@ -684,6 +726,13 @@ public: std::cout << "The negotiated next protocol: " << proto << std::endl; } version_ = spdylay_npn_get_version(next_proto, next_proto_len); + 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); } else { @@ -693,34 +742,28 @@ public: fail_ = true; } } else if(r == 0) { - int e = SSL_get_error(ssl_, r); - if(e == SSL_ERROR_SSL) { - if(config()->verbose) { - std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; - } - } finish_ = true; fail_ = true; } else { - int d = SSL_get_error(ssl_, r); - if(d == SSL_ERROR_WANT_READ) { - want_read_ = true; - } else if(d == SSL_ERROR_WANT_WRITE) { - want_write_ = true; - } else { + io_flags_ = get_ssl_io_demand(ssl_, r); + if(io_flags_ == 0) { finish_ = true; fail_ = true; } } + if(config()->verbose && fail_) { + std::cerr << "SSL/TLS handshake failed: " + << ERR_error_string(ERR_get_error(), 0) << std::endl; + } return 0; } virtual bool want_read() { - return want_read_; + return io_flags_ & WANT_READ; } virtual bool want_write() { - return want_write_; + return io_flags_ & WANT_WRITE; } virtual int fd() const { @@ -734,22 +777,7 @@ private: void add_next_handler(Sessions *sessions) { spdylay_session_callbacks callbacks; - memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); - callbacks.send_callback = hd_send_callback; - callbacks.recv_callback = hd_recv_callback; - callbacks.on_stream_close_callback = on_stream_close_callback; - callbacks.on_ctrl_recv_callback = hd_on_ctrl_recv_callback; - callbacks.on_ctrl_send_callback = hd_on_ctrl_send_callback; - callbacks.on_data_recv_callback = hd_on_data_recv_callback; - callbacks.on_data_send_callback = hd_on_data_send_callback; - if(config()->verbose) { - callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; - callbacks.on_ctrl_recv_parse_error_callback = - on_ctrl_recv_parse_error_callback; - callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback; - } - callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; - callbacks.on_request_recv_callback = config()->on_request_recv_callback; + fill_callback(callbacks, config()); SpdyEventHandler *hd = new SpdyEventHandler(config(), fd_, ssl_, version_, &callbacks, session_id_); @@ -765,7 +793,7 @@ private: SSL *ssl_; uint16_t version_; bool fail_, finish_; - bool want_read_, want_write_; + uint8_t io_flags_; int64_t session_id_; }; @@ -810,19 +838,34 @@ public: private: void add_next_handler(Sessions *sessions, int cfd) { - SSL *ssl = sessions->ssl_session_new(cfd); - if(ssl == 0) { - close(cfd); - return; - } - SSLAcceptEventHandler *hd = new SSLAcceptEventHandler - (config(), cfd, ssl, ++(*session_id_seed_ptr_)); - if(sessions->add_poll(hd) == -1) { - delete hd; - SSL_free(ssl); - close(cfd); + int64_t session_id = ++(*session_id_seed_ptr_); + if(config()->no_tls) { + spdylay_session_callbacks callbacks; + fill_callback(callbacks, config()); + SpdyEventHandler *hd = new SpdyEventHandler(config(), + cfd, 0, + config()->version, &callbacks, + session_id); + if(sessions->add_poll(hd) == -1) { + delete hd; + } else { + sessions->add_handler(hd); + } } else { - sessions->add_handler(hd); + SSL *ssl = sessions->ssl_session_new(cfd); + if(ssl == 0) { + close(cfd); + return; + } + SSLAcceptEventHandler *hd = new SSLAcceptEventHandler(config(), cfd, ssl, + session_id); + if(sessions->add_poll(hd) == -1) { + delete hd; + SSL_free(ssl); + close(cfd); + } else { + sessions->add_handler(hd); + } } } @@ -901,55 +944,61 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) int SpdyServer::run() { - SSL_CTX *ssl_ctx; - ssl_ctx = SSL_CTX_new(SSLv23_server_method()); - if(!ssl_ctx) { - std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; - return -1; - } - 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); - if(SSL_CTX_use_PrivateKey_file(ssl_ctx, - config_->private_key_file.c_str(), - SSL_FILETYPE_PEM) != 1) { - std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl; - return -1; - } - if(SSL_CTX_use_certificate_chain_file(ssl_ctx, - config_->cert_file.c_str()) != 1) { - std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl; - return -1; - } - if(SSL_CTX_check_private_key(ssl_ctx) != 1) { - std::cerr << "SSL_CTX_check_private_key failed." << std::endl; - return -1; - } - if(config_->verify_client) { - SSL_CTX_set_verify(ssl_ctx, - SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - verify_callback); - } - // We speaks "spdy/2" and "spdy/3". + SSL_CTX *ssl_ctx = 0; std::pair next_proto; unsigned char proto_list[14]; + if(!config_->no_tls) { + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if(!ssl_ctx) { + std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; + return -1; + } + 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); + if(SSL_CTX_use_PrivateKey_file(ssl_ctx, + config_->private_key_file.c_str(), + SSL_FILETYPE_PEM) != 1) { + std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl; + return -1; + } + if(SSL_CTX_use_certificate_chain_file(ssl_ctx, + config_->cert_file.c_str()) != 1) { + std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl; + return -1; + } + if(SSL_CTX_check_private_key(ssl_ctx) != 1) { + std::cerr << "SSL_CTX_check_private_key failed." << std::endl; + return -1; + } + if(config_->verify_client) { + SSL_CTX_set_verify(ssl_ctx, + SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_callback); + } - 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 { + // We speaks "spdy/2" and "spdy/3". proto_list[0] = 6; memcpy(&proto_list[1], "spdy/3", 6); proto_list[7] = 6; memcpy(&proto_list[8], "spdy/2", 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); + switch(config_->version) { + case SPDYLAY_PROTO_SPDY3: + next_proto.first = proto_list; + next_proto.second = 7; + break; + case SPDYLAY_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); + } const size_t MAX_EVENTS = 256; Sessions sessions(MAX_EVENTS, ssl_ctx); diff --git a/src/SpdyServer.h b/src/SpdyServer.h index 2c18861..240fe75 100644 --- a/src/SpdyServer.h +++ b/src/SpdyServer.h @@ -52,8 +52,9 @@ struct Config { std::string cert_file; spdylay_on_request_recv_callback on_request_recv_callback; void *data_ptr; - bool spdy3_only; + uint16_t version; bool verify_client; + bool no_tls; Config(); }; @@ -113,7 +114,7 @@ public: ssize_t recv_data(uint8_t *data, size_t len, int flags); - bool would_block(int r); + bool would_block() const; int submit_file_response(const std::string& status, int32_t stream_id, @@ -141,7 +142,7 @@ private: SSL* ssl_; uint16_t version_; int64_t session_id_; - bool want_write_; + uint8_t io_flags_; std::map id2req_; }; diff --git a/src/spdycat.cc b/src/spdycat.cc index 4020b4c..719631a 100644 --- a/src/spdycat.cc +++ b/src/spdycat.cc @@ -67,6 +67,7 @@ struct Config { bool verbose; bool get_assets; bool stat; + bool no_tls; int spdy_version; // milliseconds int timeout; @@ -75,7 +76,7 @@ struct Config { int window_bits; std::map headers; Config():null_out(false), remote_name(false), verbose(false), - get_assets(false), stat(false), + get_assets(false), stat(false), no_tls(false), spdy_version(-1), timeout(-1), window_bits(-1) {} }; @@ -231,7 +232,7 @@ void submit_request(Spdylay& sc, const std::string& hostport, { uri::UriStruct& us = req->us; std::string path = us.dir+us.file+us.query; - int r = sc.submit_request(hostport, path, headers, 3, req); + int r = sc.submit_request(us.protocol, hostport, path, headers, 3, req); assert(r == 0); } @@ -447,12 +448,7 @@ int communicate(const std::string& host, uint16_t port, return -1; } set_tcp_nodelay(fd); - SSL_CTX *ssl_ctx; - ssl_ctx = SSL_CTX_new(TLSv1_client_method()); - if(!ssl_ctx) { - std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; - return -1; - } + std::string next_proto; switch(config.spdy_version) { case SPDYLAY_PROTO_SPDY2: @@ -462,38 +458,48 @@ int communicate(const std::string& host, uint16_t port, next_proto = "spdy/3"; break; } - setup_ssl_ctx(ssl_ctx, &next_proto); - if(!config.keyfile.empty()) { - if(SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(), - SSL_FILETYPE_PEM) != 1) { + + SSL_CTX *ssl_ctx = 0; + SSL *ssl = 0; + if(!config.no_tls) { + ssl_ctx = SSL_CTX_new(TLSv1_client_method()); + if(!ssl_ctx) { std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; return -1; } - } - if(!config.certfile.empty()) { - if(SSL_CTX_use_certificate_chain_file(ssl_ctx, - config.certfile.c_str()) != 1) { + setup_ssl_ctx(ssl_ctx, &next_proto); + if(!config.keyfile.empty()) { + if(SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(), + SSL_FILETYPE_PEM) != 1) { + std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; + return -1; + } + } + if(!config.certfile.empty()) { + if(SSL_CTX_use_certificate_chain_file(ssl_ctx, + config.certfile.c_str()) != 1) { + std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; + return -1; + } + } + ssl = SSL_new(ssl_ctx); + if(!ssl) { std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; return -1; } - } - SSL *ssl = SSL_new(ssl_ctx); - if(!ssl) { - std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; - return -1; - } - if (!SSL_set_tlsext_host_name(ssl, host.c_str())) { - std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; - return -1; - } - rv = ssl_nonblock_handshake(ssl, fd, timeout); - if(rv == -1) { - return -1; - } else if(rv == -2) { - std::cerr << "Request to " << spdySession.hostport - << " timed out in SSL/TLS handshake." - << std::endl; - return -1; + if (!SSL_set_tlsext_host_name(ssl, host.c_str())) { + std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; + return -1; + } + rv = ssl_nonblock_handshake(ssl, fd, timeout); + if(rv == -1) { + return -1; + } else if(rv == -2) { + std::cerr << "Request to " << spdySession.hostport + << " timed out in SSL/TLS handshake." + << std::endl; + return -1; + } } spdySession.record_handshake_time(); @@ -503,6 +509,7 @@ int communicate(const std::string& host, uint16_t port, if (spdy_version <= 0) { return -1; } + Spdylay sc(fd, ssl, spdy_version, callbacks, &spdySession); spdySession.sc = ≻ @@ -570,9 +577,11 @@ int communicate(const std::string& host, uint16_t port, << spdySession.reqvec.size() << ", processed=" << spdySession.complete << std::endl; } - SSL_shutdown(ssl); - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); + if(ssl) { + SSL_shutdown(ssl); + SSL_free(ssl); + SSL_CTX_free(ssl_ctx); + } shutdown(fd, SHUT_WR); close(fd); if(config.stat) { @@ -632,7 +641,7 @@ int run(char **uris, int n) void print_usage(std::ostream& out) { out << "Usage: spdycat [-Oansv23] [-t ] [-w ] [--cert=]\n" - << " [--key=] ..." + << " [--key=] [--no-tls] ..." << std::endl; } @@ -664,6 +673,8 @@ void print_help(std::ostream& out) << " The file must be in PEM format.\n" << " --key= Use the client private key file. The file\n" << " must be in PEM format.\n" + << " --no-tls Disable SSL/TLS. Use -2 or -3 to specify\n" + << " SPDY protocol version to use.\n" << std::endl; } @@ -685,6 +696,7 @@ int main(int argc, char **argv) {"key", required_argument, &flag, 2 }, {"help", no_argument, 0, 'h' }, {"header", required_argument, 0, 'H' }, + {"no-tls", no_argument, &flag, 3 }, {0, 0, 0, 0 } }; int option_index = 0; @@ -774,12 +786,25 @@ int main(int argc, char **argv) // key option config.keyfile = optarg; break; + case 3: + // no-tls option + config.no_tls = true; + break; } break; default: break; } } + + 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); + } + } + struct sigaction act; memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = SIG_IGN; diff --git a/src/spdyd.cc b/src/spdyd.cc index 441e1ef..215bc81 100644 --- a/src/spdyd.cc +++ b/src/spdyd.cc @@ -47,7 +47,7 @@ extern bool ssl_debug; namespace { void print_usage(std::ostream& out) { - out << "Usage: spdyd [-3DVhv] [-d ] " + out << "Usage: spdyd [-23DVhv] [-d ] [--no-tls] [ ]" << std::endl; } } // namespace @@ -74,7 +74,10 @@ void print_help(std::ostream& out) << " current working directory.\n" << " -v, --verbose Print debug information such as reception/\n" << " transmission of frames and name/value pairs.\n" + << " -2, --spdy2 Only use SPDY/2.\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" << std::endl; } @@ -84,17 +87,20 @@ int main(int argc, char **argv) { Config config; while(1) { + int flag; static option long_options[] = { {"daemon", no_argument, 0, 'D' }, {"htdocs", required_argument, 0, 'd' }, {"help", no_argument, 0, 'h' }, {"verbose", no_argument, 0, 'v' }, + {"spdy2", no_argument, 0, '2' }, {"spdy3", no_argument, 0, '3' }, {"verify-client", no_argument, 0, 'V' }, + {"no-tls", no_argument, &flag, 1 }, {0, 0, 0, 0 } }; int option_index = 0; - int c = getopt_long(argc, argv, "DVd:hv3", long_options, &option_index); + int c = getopt_long(argc, argv, "DVd:hv23", long_options, &option_index); if(c == -1) { break; } @@ -114,20 +120,45 @@ int main(int argc, char **argv) case 'v': config.verbose = true; break; + case '2': + config.version = SPDYLAY_PROTO_SPDY2; + break; case '3': - config.spdy3_only = true; + config.version = SPDYLAY_PROTO_SPDY3; break; case '?': exit(EXIT_FAILURE); + case 0: + switch(flag) { + case 1: + // no-tls option + config.no_tls = true; + break; + } + break; default: break; } } - if(argc-optind < 3) { + if(argc-optind < (config.no_tls ? 1 : 3)) { print_usage(std::cerr); std::cerr << "Too few arguments" << std::endl; exit(EXIT_FAILURE); } + + config.port = strtol(argv[optind++], 0, 10); + + 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.cert_file = argv[optind++]; + } + if(config.daemon) { if(config.htdocs.empty()) { print_usage(std::cerr); @@ -150,9 +181,6 @@ int main(int argc, char **argv) SSL_load_error_strings(); SSL_library_init(); reset_timer(); - config.port = strtol(argv[optind++], 0, 10); - config.private_key_file = argv[optind++]; - config.cert_file = argv[optind++]; config.on_request_recv_callback = htdocs_on_request_recv_callback; ssl_debug = config.verbose; diff --git a/src/spdylay_ssl.cc b/src/spdylay_ssl.cc index d3131bb..44ce044 100644 --- a/src/spdylay_ssl.cc +++ b/src/spdylay_ssl.cc @@ -54,7 +54,7 @@ Spdylay::Spdylay(int fd, SSL *ssl, uint16_t version, const spdylay_session_callbacks *callbacks, void *user_data) : fd_(fd), ssl_(ssl), version_(version), user_data_(user_data), - want_write_(false) + io_flags_(0) { int r = spdylay_session_client_new(&session_, version_, callbacks, this); assert(r == 0); @@ -78,20 +78,36 @@ int Spdylay::send() ssize_t Spdylay::send_data(const uint8_t *data, size_t len, int flags) { ssize_t r; - ERR_clear_error(); - r = SSL_write(ssl_, data, len); + io_flags_ = 0; + if(ssl_) { + ERR_clear_error(); + r = SSL_write(ssl_, data, len); + if(r < 0) { + io_flags_ = get_ssl_io_demand(ssl_, r); + } + } else { + while((r = ::send(fd_, data, len, 0)) == -1 && errno == EINTR); + if(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + io_flags_ |= WANT_WRITE; + } + } return r; } ssize_t Spdylay::recv_data(uint8_t *data, size_t len, int flags) { ssize_t r; - want_write_ = false; - ERR_clear_error(); - r = SSL_read(ssl_, data, len); - if(r < 0) { - if(SSL_get_error(ssl_, r) == SSL_ERROR_WANT_WRITE) { - want_write_ = true; + io_flags_ = 0; + if(ssl_) { + ERR_clear_error(); + r = SSL_read(ssl_, data, len); + if(r < 0) { + io_flags_ = get_ssl_io_demand(ssl_, r); + } + } else { + while((r = ::recv(fd_, data, len, 0)) == -1 && errno == EINTR); + if(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + io_flags_ |= WANT_READ; } } return r; @@ -99,12 +115,12 @@ ssize_t Spdylay::recv_data(uint8_t *data, size_t len, int flags) bool Spdylay::want_read() { - return spdylay_session_want_read(session_); + return spdylay_session_want_read(session_) || (io_flags_ & WANT_READ); } bool Spdylay::want_write() { - return spdylay_session_want_write(session_) || want_write_; + return spdylay_session_want_write(session_) || (io_flags_ & WANT_WRITE); } bool Spdylay::finish() @@ -123,7 +139,8 @@ void* Spdylay::user_data() return user_data_; } -int Spdylay::submit_request(const std::string& hostport, +int Spdylay::submit_request(const std::string& scheme, + const std::string& hostport, const std::string& path, const std::map &headers, uint8_t pri, @@ -144,7 +161,7 @@ int Spdylay::submit_request(const std::string& hostport, ":method", "GET", ":path", path.c_str(), ":version", "HTTP/1.1", - ":scheme", "https", + ":scheme", scheme.c_str(), ":host", hostport.c_str(), "accept", "*/*", "user-agent", "spdylay/" SPDYLAY_VERSION @@ -196,10 +213,9 @@ int Spdylay::submit_settings(int flags, spdylay_settings_entry *iv, size_t niv) return spdylay_submit_settings(session_, flags, iv, niv); } -bool Spdylay::would_block(int r) +bool Spdylay::would_block() { - int e = SSL_get_error(ssl_, r); - return e == SSL_ERROR_WANT_WRITE || e == SSL_ERROR_WANT_READ; + return io_flags_; } int connect_to(const std::string& host, uint16_t port) @@ -395,11 +411,14 @@ ssize_t send_callback(spdylay_session *session, Spdylay *sc = (Spdylay*)user_data; ssize_t r = sc->send_data(data, len, flags); if(r < 0) { - if(sc->would_block(r)) { + if(sc->would_block()) { r = SPDYLAY_ERR_WOULDBLOCK; } else { r = SPDYLAY_ERR_CALLBACK_FAILURE; } + } else if(r == 0) { + // In OpenSSL, r == 0 means EOF because SSL_write may do read. + r = SPDYLAY_ERR_CALLBACK_FAILURE; } return r; } @@ -410,7 +429,7 @@ ssize_t recv_callback(spdylay_session *session, Spdylay *sc = (Spdylay*)user_data; ssize_t r = sc->recv_data(data, len, flags); if(r < 0) { - if(sc->would_block(r)) { + if(sc->would_block()) { r = SPDYLAY_ERR_WOULDBLOCK; } else { r = SPDYLAY_ERR_CALLBACK_FAILURE; @@ -792,6 +811,18 @@ int64_t time_delta(const timeval& a, const timeval& b) return res; } +uint8_t get_ssl_io_demand(SSL *ssl, ssize_t r) +{ + switch(SSL_get_error(ssl, r)) { + case SSL_ERROR_WANT_WRITE: + return WANT_WRITE; + case SSL_ERROR_WANT_READ: + return WANT_READ; + default: + return 0; + } +} + namespace { timeval base_tv; } // namespace diff --git a/src/spdylay_ssl.h b/src/spdylay_ssl.h index f090951..3c53bcc 100644 --- a/src/spdylay_ssl.h +++ b/src/spdylay_ssl.h @@ -55,11 +55,12 @@ public: bool want_write(); bool finish(); int fd() const; - int submit_request(const std::string& hostport, const std::string& path, + int submit_request(const std::string& scheme, + const std::string& hostport, const std::string& path, const std::map& headers, uint8_t pri, void *stream_user_data); int submit_settings(int flags, spdylay_settings_entry *iv, size_t niv); - bool would_block(int r); + bool would_block(); void* user_data(); private: int fd_; @@ -67,7 +68,7 @@ private: uint16_t version_; spdylay_session *session_; void *user_data_; - bool want_write_; + uint8_t io_flags_; bool debug_; }; @@ -148,6 +149,13 @@ void get_timer(timeval *tv); void print_timer(); +enum { + WANT_READ = 1, + WANT_WRITE = 1 << 1 +}; + +uint8_t get_ssl_io_demand(SSL *ssl, ssize_t r); + } // namespace spdylay #endif // SPDYLAY_SSL_H