From fad7f51f8dd3effcae911cab5f256ceaa5711ae2 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 4 Jun 2012 23:48:31 +0900 Subject: [PATCH] Initial commit of shrpx: SPDY/HTTPS to HTTP reverse proxy Put libhtparse in examples/htparse --- configure.ac | 8 + examples/.gitignore | 1 + examples/Makefile.am | 20 +- examples/shrpx.cc | 323 ++++++++++++++++++ examples/shrpx.h | 37 ++ examples/shrpx_client_handler.cc | 213 ++++++++++++ examples/shrpx_client_handler.h | 64 ++++ examples/shrpx_config.cc | 63 ++++ examples/shrpx_config.h | 74 ++++ examples/shrpx_downstream.cc | 412 ++++++++++++++++++++++ examples/shrpx_downstream.h | 111 ++++++ examples/shrpx_downstream_queue.cc | 67 ++++ examples/shrpx_downstream_queue.h | 52 +++ examples/shrpx_error.h | 38 +++ examples/shrpx_http.cc | 98 ++++++ examples/shrpx_http.h | 42 +++ examples/shrpx_https_upstream.cc | 464 +++++++++++++++++++++++++ examples/shrpx_https_upstream.h | 76 +++++ examples/shrpx_listen_handler.cc | 78 +++++ examples/shrpx_listen_handler.h | 51 +++ examples/shrpx_log.cc | 49 +++ examples/shrpx_log.h | 60 ++++ examples/shrpx_spdy_upstream.cc | 532 +++++++++++++++++++++++++++++ examples/shrpx_spdy_upstream.h | 71 ++++ examples/shrpx_upstream.h | 55 +++ examples/util.cc | 15 + examples/util.h | 2 + 27 files changed, 3073 insertions(+), 3 deletions(-) create mode 100644 examples/shrpx.cc create mode 100644 examples/shrpx.h create mode 100644 examples/shrpx_client_handler.cc create mode 100644 examples/shrpx_client_handler.h create mode 100644 examples/shrpx_config.cc create mode 100644 examples/shrpx_config.h create mode 100644 examples/shrpx_downstream.cc create mode 100644 examples/shrpx_downstream.h create mode 100644 examples/shrpx_downstream_queue.cc create mode 100644 examples/shrpx_downstream_queue.h create mode 100644 examples/shrpx_error.h create mode 100644 examples/shrpx_http.cc create mode 100644 examples/shrpx_http.h create mode 100644 examples/shrpx_https_upstream.cc create mode 100644 examples/shrpx_https_upstream.h create mode 100644 examples/shrpx_listen_handler.cc create mode 100644 examples/shrpx_listen_handler.h create mode 100644 examples/shrpx_log.cc create mode 100644 examples/shrpx_log.h create mode 100644 examples/shrpx_spdy_upstream.cc create mode 100644 examples/shrpx_spdy_upstream.h create mode 100644 examples/shrpx_upstream.h diff --git a/configure.ac b/configure.ac index 734f6f93..4d6a04ac 100644 --- a/configure.ac +++ b/configure.ac @@ -95,6 +95,14 @@ if test "x${have_openssl}" = "xno"; then AC_MSG_NOTICE([The example programs will not be built.]) fi +# libevent_openssl +PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.6], + [have_libevent_openssl=yes], [have_libevent_openssl=no]) +if test "x${have_libevent_openssl}" = "xno"; then + AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS) + AC_MSG_NOTICE([Shrpx example program will not be built.]) +fi + # libxml2 (for examples/spdycat) AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no]) if test "x${have_libxml2}" = "xyes"; then diff --git a/examples/.gitignore b/examples/.gitignore index 8f778269..e6b6a0fb 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -2,3 +2,4 @@ spdycat spdyd spdynative spdycli +shrpx diff --git a/examples/Makefile.am b/examples/Makefile.am index 392c8193..42d97a41 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -25,11 +25,11 @@ if ENABLE_EXAMPLES AM_CFLAGS = -Wall AM_CPPFLAGS = -Wall -I$(srcdir)/../lib/includes -I$(builddir)/../lib/includes \ - @OPENSSL_CFLAGS@ @XML_CPPFLAGS@ @DEFS@ -AM_LDFLAGS = @OPENSSL_LIBS@ @XML_LIBS@ + @OPENSSL_CFLAGS@ @XML_CPPFLAGS@ @LIBEVENT_OPENSSL_CFLAGS@ @DEFS@ +AM_LDFLAGS = @OPENSSL_LIBS@ @XML_LIBS@ @LIBEVENT_OPENSSL_LIBS@ LDADD = $(top_builddir)/lib/libspdylay.la -bin_PROGRAMS = spdycat spdyd +bin_PROGRAMS = spdycat spdyd shrpx HELPER_OBJECTS = uri.cc util.cc spdylay_ssl.cc HELPER_HFILES = uri.h util.h spdylay_ssl.h @@ -65,6 +65,20 @@ spdyd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} \ ${SPDY_SERVER_OBJECTS} ${SPDY_SERVER_HFILES} \ spdyd.cc +shrpx_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} \ + shrpx_config.cc shrpx_config.h \ + shrpx.cc \ + shrpx_listen_handler.cc shrpx_listen_handler.h \ + shrpx_client_handler.cc shrpx_client_handler.h \ + shrpx_upstream.h \ + shrpx_spdy_upstream.cc shrpx_spdy_upstream.h \ + shrpx_https_upstream.cc shrpx_https_upstream.h \ + shrpx_downstream_queue.cc shrpx_downstream_queue.h \ + shrpx_downstream.cc shrpx_downstream.h \ + shrpx_log.cc shrpx_log.h \ + shrpx_http.cc shrpx_http.h \ + htparse/htparse.c htparse/htparse.h + noinst_PROGRAMS = spdycli spdycli_SOURCES = spdycli.c diff --git a/examples/shrpx.cc b/examples/shrpx.cc new file mode 100644 index 00000000..ba57abbc --- /dev/null +++ b/examples/shrpx.cc @@ -0,0 +1,323 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx.h" + +#include +#include +#include +#include +#include + + +#include +#include + +#include +#include + +#include + +#include + +#include "shrpx_config.h" +#include "shrpx_listen_handler.h" + +namespace shrpx { + +namespace { +std::pair next_proto; +unsigned char proto_list[23]; +} // namespace + +namespace { +int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, + void *arg) +{ + std::pair *next_proto = + reinterpret_cast* >(arg); + *data = next_proto->first; + *len = next_proto->second; + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) +{ + // We don't verify the client certificate. Just request it for the + // testing purpose. + return 1; +} +} // namespace + +namespace { +SSL_CTX* create_ssl_ctx() +{ + // TODO lock function + 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 NULL; + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION); + 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, + get_config()->private_key_file, + SSL_FILETYPE_PEM) != 1) { + std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl; + return NULL; + } + if(SSL_CTX_use_certificate_file(ssl_ctx, get_config()->cert_file, + SSL_FILETYPE_PEM) != 1) { + std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl; + return NULL; + } + if(SSL_CTX_check_private_key(ssl_ctx) != 1) { + std::cerr << "SSL_CTX_check_private_key failed." << std::endl; + return NULL; + } + if(get_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 "http/1.1", "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); + proto_list[14] = 8; + memcpy(&proto_list[15], "http/1.1", 8); + + 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); + return ssl_ctx; +} +} // namespace + +namespace { +void ssl_acceptcb(evconnlistener *listener, int fd, + sockaddr *addr, int addrlen, void *arg) +{ + ListenHandler *handler = reinterpret_cast(arg); + handler->accept_connection(fd, addr, addrlen); +} +} // namespace + +namespace { +int cache_downstream_host_address() +{ + addrinfo hints; + int rv; + char service[10]; + snprintf(service, sizeof(service), "%u", get_config()->downstream_port); + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + addrinfo *res, *rp; + rv = getaddrinfo(get_config()->downstream_host, service, &hints, &res); + if(rv != 0) { + LOG(ERROR) << "getaddrinfo: " << gai_strerror(rv); + return -1; + } + for(rp = res; rp; rp = rp->ai_next) { + int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if(fd == -1) { + continue; + } + rv = connect(fd, rp->ai_addr, rp->ai_addrlen); + close(fd); + if(rv == -1) { + continue; + } + break; + } + if(rp == 0 && res) { + LOG(INFO) << "Using first returned address for downstream " + << get_config()->downstream_host + << ", port " + << get_config()->downstream_port; + rp = res; + } + if(rp != 0) { + memcpy(&mod_config()->downstream_addr, rp->ai_addr, rp->ai_addrlen); + mod_config()->downstream_addrlen = rp->ai_addrlen; + } + freeaddrinfo(res); + if(rp == 0) { + LOG(ERROR) << "No usable address found for downstream " + << get_config()->downstream_host + << ", port " + << get_config()->downstream_port; + return -1; + } else { + return 0; + } +} +} // namespace + +namespace { +evconnlistener* create_evlistener(ListenHandler *handler) +{ + // TODO Listen both IPv4 and IPv6 + addrinfo hints; + int fd = -1; + int r; + char service[10]; + snprintf(service, sizeof(service), "%u", get_config()->port); + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + addrinfo *res, *rp; + r = getaddrinfo(get_config()->host, service, &hints, &res); + if(r != 0) { + LOG(ERROR) << "getaddrinfo: " << gai_strerror(r); + return NULL; + } + for(rp = res; rp; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if(fd == -1) { + continue; + } + int val = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } + evutil_make_socket_nonblocking(fd); + if(bind(fd, rp->ai_addr, rp->ai_addrlen) == 0) { + break; + } + close(fd); + } + freeaddrinfo(res); + if(rp == 0) { + LOG(ERROR) << "No valid address returned for host " << get_config()->host + << ", port " << get_config()->port; + return 0; + } + evconnlistener *evlistener = evconnlistener_new + (handler->get_evbase(), + ssl_acceptcb, + handler, + LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, + 256, + fd); + return evlistener; +} +} // namespace + +namespace { +int event_loop() +{ + SSL_CTX *ssl_ctx = create_ssl_ctx(); + if(ssl_ctx == NULL) { + return -1; + } + event_base *evbase = event_base_new(); + ListenHandler *listener_handler = new ListenHandler(evbase, ssl_ctx); + evconnlistener *evlistener = create_evlistener(listener_handler); + if(evlistener == NULL) { + return -1; + } + if(ENABLE_LOG) { + LOG(INFO) << "Entering event loop"; + } + event_base_loop(evbase, 0); + + evconnlistener_free(evlistener); + SSL_CTX_free(ssl_ctx); + return 0; +} +} // namespace + +int main(int argc, char **argv) +{ + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, 0); + + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + create_config(); + mod_config()->server_name = "shrpx spdylay/"SPDYLAY_VERSION; + mod_config()->port = 3000; + mod_config()->private_key_file = "server.key"; + mod_config()->cert_file = "server.crt"; + + mod_config()->upstream_read_timeout.tv_sec = 30; + mod_config()->upstream_read_timeout.tv_usec = 0; + mod_config()->upstream_write_timeout.tv_sec = 30; + mod_config()->upstream_write_timeout.tv_usec = 0; + + mod_config()->spdy_upstream_read_timeout.tv_sec = 600; + mod_config()->spdy_upstream_read_timeout.tv_usec = 0; + mod_config()->spdy_upstream_write_timeout.tv_sec = 30; + mod_config()->spdy_upstream_write_timeout.tv_usec = 0; + + mod_config()->downstream_read_timeout.tv_sec = 30; + mod_config()->downstream_read_timeout.tv_usec = 0; + mod_config()->downstream_write_timeout.tv_sec = 30; + mod_config()->downstream_write_timeout.tv_usec = 0; + + mod_config()->downstream_host = "localhost"; + mod_config()->downstream_port = 80; + char hostport[256]; + if(get_config()->downstream_port == 80) { + mod_config()->downstream_hostport = get_config()->downstream_host; + } else { + snprintf(hostport, sizeof(hostport), "%s:%u", + get_config()->downstream_host, get_config()->downstream_port); + mod_config()->downstream_hostport = hostport; + } + if(cache_downstream_host_address() == -1) { + exit(EXIT_FAILURE); + } + event_loop(); + return 0; +} + +} // namespace shrpx + +int main(int argc, char **argv) +{ + return shrpx::main(argc, argv); +} diff --git a/examples/shrpx.h b/examples/shrpx.h new file mode 100644 index 00000000..2258b436 --- /dev/null +++ b/examples/shrpx.h @@ -0,0 +1,37 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_H +#define SHRPX_H + +#ifdef HAVE_CONFIG_H +# include +#endif // HAVE_CONFIG_H + +#include "shrpx_log.h" + +#define DIE() \ + assert(0); + +#endif // SHRPX_H diff --git a/examples/shrpx_client_handler.cc b/examples/shrpx_client_handler.cc new file mode 100644 index 00000000..4fceaaed --- /dev/null +++ b/examples/shrpx_client_handler.cc @@ -0,0 +1,213 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_client_handler.h" + +#include "shrpx_upstream.h" +#include "shrpx_spdy_upstream.h" +#include "shrpx_https_upstream.h" +#include "shrpx_config.h" + +namespace shrpx { + +namespace { +void upstream_readcb(bufferevent *bev, void *arg) +{ + ClientHandler *handler = reinterpret_cast(arg); + int rv = handler->on_read(); + if(rv != 0) { + if(ENABLE_LOG) { + LOG(INFO) << " Read operation (application level) failure"; + } + delete handler; + } +} +} // namespace + +namespace { +void upstream_writecb(bufferevent *bev, void *arg) +{ + if(ENABLE_LOG) { + LOG(INFO) << " upstream_writecb"; + } + ClientHandler *handler = reinterpret_cast(arg); + if(handler->get_should_close_after_write()) { + delete handler; + } +} +} // namespace + +namespace { +void upstream_eventcb(bufferevent *bev, short events, void *arg) +{ + ClientHandler *handler = reinterpret_cast(arg); + bool finish = false; + if(events & BEV_EVENT_EOF) { + if(ENABLE_LOG) { + LOG(INFO) << " SSL/TLS handshake EOF"; + } + finish = true; + } + if(events & BEV_EVENT_ERROR) { + if(ENABLE_LOG) { + LOG(INFO) << " SSL/TLS network error"; + } + finish = true; + } + if(events & BEV_EVENT_TIMEOUT) { + if(ENABLE_LOG) { + LOG(INFO) << "SPDY upstream SSL/TLS time out"; + } + finish = true; + } + if(finish) { + delete handler; + } else { + if(events & BEV_EVENT_CONNECTED) { + if(ENABLE_LOG) { + LOG(INFO) << "Connected Handler " + << handler; + } + handler->set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb); + handler->validate_next_proto(); + // At this point, input buffer is already filled with some + // bytes. The read callback is not called until new data + // come. So consume input buffer here. + handler->get_upstream()->on_read(); + } + } +} +} // namespace + +ClientHandler::ClientHandler(bufferevent *bev, SSL *ssl, const char *ipaddr) + : bev_(bev), + ssl_(ssl), + upstream_(0), + ipaddr_(ipaddr), + should_close_after_write_(false) +{ + bufferevent_enable(bev_, EV_READ | EV_WRITE); + set_upstream_timeouts(&get_config()->upstream_read_timeout, + &get_config()->upstream_write_timeout); + set_bev_cb(0, upstream_writecb, upstream_eventcb); +} + +ClientHandler::~ClientHandler() +{ + if(ENABLE_LOG) { + LOG(INFO) << "Deleting ClientHandler " << this; + } + int fd = SSL_get_fd(ssl_); + SSL_shutdown(ssl_); + bufferevent_disable(bev_, EV_READ | EV_WRITE); + bufferevent_free(bev_); + SSL_free(ssl_); + shutdown(fd, SHUT_WR); + close(fd); + delete upstream_; + if(ENABLE_LOG) { + LOG(INFO) << "Deleted"; + } +} + +Upstream* ClientHandler::get_upstream() +{ + return upstream_; +} + +bufferevent* ClientHandler::get_bev() const +{ + return bev_; +} + +event_base* ClientHandler::get_evbase() const +{ + return bufferevent_get_base(bev_); +} + +void ClientHandler::set_bev_cb +(bufferevent_data_cb readcb, bufferevent_data_cb writecb, + bufferevent_event_cb eventcb) +{ + bufferevent_setcb(bev_, readcb, writecb, eventcb, this); +} + +void ClientHandler::set_upstream_timeouts(const timeval *read_timeout, + const timeval *write_timeout) +{ + bufferevent_set_timeouts(bev_, read_timeout, write_timeout); +} + +int ClientHandler::validate_next_proto() +{ + const unsigned char *next_proto = 0; + unsigned int next_proto_len; + SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len); + if(next_proto) { + std::string proto(next_proto, next_proto+next_proto_len); + if(ENABLE_LOG) { + LOG(INFO) << " The negotiated next protocol: " << proto; + } + uint16_t version = spdylay_npn_get_version(next_proto, next_proto_len); + if(version) { + SpdyUpstream *spdy_upstream = new SpdyUpstream(version, this); + upstream_ = spdy_upstream; + return 0; + } + } else { + if(ENABLE_LOG) { + LOG(INFO) << " No proto negotiated"; + } + } + HttpsUpstream *https_upstream = new HttpsUpstream(this); + upstream_ = https_upstream; + return 0; +} + +int ClientHandler::on_read() +{ + return upstream_->on_read(); +} + +int ClientHandler::on_event() +{ + return upstream_->on_event(); +} + +const std::string& ClientHandler::get_ipaddr() const +{ + return ipaddr_; +} + +bool ClientHandler::get_should_close_after_write() const +{ + return should_close_after_write_; +} + +void ClientHandler::set_should_close_after_write(bool f) +{ + should_close_after_write_ = f; +} + +} // namespace shrpx diff --git a/examples/shrpx_client_handler.h b/examples/shrpx_client_handler.h new file mode 100644 index 00000000..0392f764 --- /dev/null +++ b/examples/shrpx_client_handler.h @@ -0,0 +1,64 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CLIENT_HANDLER_H +#define SHRPX_CLIENT_HANDLER_H + +#include "shrpx.h" + +#include +#include + +namespace shrpx { + +class Upstream; + +class ClientHandler { +public: + ClientHandler(bufferevent *bev, SSL *ssl, const char *ipaddr); + ~ClientHandler(); + int on_read(); + int on_event(); + bufferevent* get_bev() const; + event_base* get_evbase() const; + void set_bev_cb(bufferevent_data_cb readcb, bufferevent_data_cb writecb, + bufferevent_event_cb eventcb); + void set_upstream_timeouts(const timeval *read_timeout, + const timeval *write_timeout); + int validate_next_proto(); + const std::string& get_ipaddr() const; + bool get_should_close_after_write() const; + void set_should_close_after_write(bool f); + Upstream* get_upstream(); +private: + bufferevent *bev_; + SSL *ssl_; + Upstream *upstream_; + std::string ipaddr_; + bool should_close_after_write_; +}; + +} // namespace shrpx + +#endif // SHRPX_CLIENT_HANDLER_H diff --git a/examples/shrpx_config.cc b/examples/shrpx_config.cc new file mode 100644 index 00000000..234f7eb5 --- /dev/null +++ b/examples/shrpx_config.cc @@ -0,0 +1,63 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_config.h" + +namespace shrpx { + +Config::Config() + : verbose(false), + daemon(false), + host(0), + port(0), + private_key_file(0), + cert_file(0), + verify_client(false), + server_name(0), + downstream_host(0), + downstream_port(0), + downstream_hostport(0), + downstream_addrlen(0) +{} + +namespace { +Config *config = 0; +} // namespace + +const Config* get_config() +{ + return config; +} + +Config* mod_config() +{ + return config; +} + +void create_config() +{ + config = new Config(); +} + +} // namespace shrpx diff --git a/examples/shrpx_config.h b/examples/shrpx_config.h new file mode 100644 index 00000000..9b955882 --- /dev/null +++ b/examples/shrpx_config.h @@ -0,0 +1,74 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONFIG_H +#define SHRPX_CONFIG_H + +#include +#include +#include +#include +#include + +#include + +namespace shrpx { + +union sockaddr_union { + sockaddr sa; + sockaddr_storage storage; + sockaddr_in6 in6; + sockaddr_in in; +}; + +struct Config { + bool verbose; + bool daemon; + const char *host; + uint16_t port; + const char *private_key_file; + const char *cert_file; + bool verify_client; + const char *server_name; + const char *downstream_host; + uint16_t downstream_port; + const char *downstream_hostport; + sockaddr_union downstream_addr; + size_t downstream_addrlen; + timeval upstream_read_timeout; + timeval upstream_write_timeout; + timeval spdy_upstream_read_timeout; + timeval spdy_upstream_write_timeout; + timeval downstream_read_timeout; + timeval downstream_write_timeout; + Config(); +}; + +const Config* get_config(); +Config* mod_config(); +void create_config(); + +} // namespace shrpx + +#endif // SHRPX_CONFIG_H diff --git a/examples/shrpx_downstream.cc b/examples/shrpx_downstream.cc new file mode 100644 index 00000000..b8755b8a --- /dev/null +++ b/examples/shrpx_downstream.cc @@ -0,0 +1,412 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream.h" + +#include + +#include "shrpx_upstream.h" +#include "shrpx_client_handler.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "util.h" + +using namespace spdylay; + +namespace shrpx { + +Downstream::Downstream(Upstream *upstream, int stream_id, int priority) + : upstream_(upstream), + bev_(0), + stream_id_(stream_id), + priority_(priority), + request_state_(INITIAL), + chunked_request_(false), + request_connection_close_(false), + response_state_(INITIAL), + response_http_status_(0), + chunked_response_(false), + response_htp_(htparser_new()), + response_body_buf_(0) +{ + htparser_init(response_htp_, htp_type_response); + htparser_set_userdata(response_htp_, this); + event_base *evbase = upstream_->get_client_handler()->get_evbase(); + bev_ = bufferevent_socket_new + (evbase, -1, + BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); +} + +Downstream::~Downstream() +{ + if(ENABLE_LOG) { + LOG(INFO) << "Deleting downstream" << this; + } + if(response_body_buf_) { + // Passing NULL to evbuffer_free() causes segmentation fault. + evbuffer_free(response_body_buf_); + } + bufferevent_disable(bev_, EV_READ | EV_WRITE); + bufferevent_free(bev_); + free(response_htp_); + if(ENABLE_LOG) { + LOG(INFO) << "Deleted"; + } +} + +namespace { +void check_transfer_encoding_chunked(bool *chunked, + const Headers::value_type &item) +{ + if(util::strieq(item.first.c_str(), "transfer-encoding")) { + if(util::strifind(item.second.c_str(), "chunked")) { + *chunked = true; + } + } +} +} // namespace + +namespace { +void check_request_connection(bool *connection_close, + const Headers::value_type &item) +{ + if(util::strieq(item.first.c_str(), "connection")) { + if(util::strifind(item.second.c_str(), "close")) { + *connection_close = true; + } + } +} +} // namespace + +void Downstream::add_request_header(const std::string& name, + const std::string& value) +{ + request_headers_.push_back(std::make_pair(name, value)); +} + +void Downstream::set_last_request_header_value(const std::string& value) +{ + Headers::value_type &item = request_headers_.back(); + item.second = value; + check_transfer_encoding_chunked(&chunked_request_, item); + check_request_connection(&request_connection_close_, item); +} + +void Downstream::set_request_method(const std::string& method) +{ + request_method_ = method; +} + +void Downstream::set_request_path(const std::string& path) +{ + request_path_ = path; +} + +Upstream* Downstream::get_upstream() const +{ + return upstream_; +} + +int32_t Downstream::get_stream_id() const +{ + return stream_id_; +} + +int Downstream::start_connection() +{ + bufferevent_setcb(bev_, + upstream_->get_downstream_readcb(), + upstream_->get_downstream_writecb(), + upstream_->get_downstream_eventcb(), this); + bufferevent_enable(bev_, EV_READ | EV_WRITE); + bufferevent_set_timeouts(bev_, + &get_config()->downstream_read_timeout, + &get_config()->downstream_write_timeout); + bufferevent_socket_connect + (bev_, + // TODO maybe not thread-safe? + const_cast(&get_config()->downstream_addr.sa), + get_config()->downstream_addrlen); + return 0; +} + +void Downstream::set_request_state(int state) +{ + request_state_ = state; +} + +int Downstream::get_request_state() const +{ + return request_state_; +} + +bool Downstream::get_chunked_request() const +{ + return chunked_request_; +} + +bool Downstream::get_request_connection_close() const +{ + return request_connection_close_; +} + +void Downstream::set_request_connection_close(bool f) +{ + request_connection_close_ = f; +} + +int Downstream::push_request_headers() +{ + bool xff_found = false; + std::string hdrs = request_method_; + hdrs += " "; + hdrs += request_path_; + hdrs += " "; + hdrs += "HTTP/1.1\r\n"; + hdrs += "Host: "; + hdrs += get_config()->downstream_hostport; + hdrs += "\r\n"; + // TODO Rewrite user-agent? + for(Headers::const_iterator i = request_headers_.begin(); + i != request_headers_.end(); ++i) { + if(util::strieq((*i).first.c_str(), "X-Forwarded-Proto")) { + continue; + } + if(util::strieq((*i).first.c_str(), "user-agent")) { + hdrs += "User-Agent: "; + hdrs += get_config()->server_name; + } else { + hdrs += (*i).first; + hdrs += ": "; + hdrs += (*i).second; + if(!xff_found && util::strieq((*i).first.c_str(), "X-Forwarded-For")) { + xff_found = true; + hdrs += ", "; + hdrs += upstream_->get_client_handler()->get_ipaddr(); + } + } + hdrs += "\r\n"; + } + hdrs += "Connection: close\r\n"; + if(!xff_found) { + hdrs += "X-Forwarded-For: "; + hdrs += upstream_->get_client_handler()->get_ipaddr(); + hdrs += "\r\n"; + } + hdrs += "X-Forwarded-Proto: https\r\n"; + + hdrs += "\r\n"; + if(ENABLE_LOG) { + LOG(INFO) << " request headers\n" << hdrs; + } + evbuffer *output = bufferevent_get_output(bev_); + evbuffer_add(output, hdrs.c_str(), hdrs.size()); + return 0; +} + +int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) +{ + // Assumes that request headers have already been pushed to output + // buffer using push_request_headers(). + ssize_t res = 0; + int rv; + evbuffer *output = bufferevent_get_output(bev_); + if(chunked_request_) { + char chunk_size_hex[16]; + rv = snprintf(chunk_size_hex, sizeof(chunk_size_hex), "%X\r\n", + static_cast(datalen)); + evbuffer_add(output, chunk_size_hex, rv); + res += rv; + } + rv = evbuffer_add(output, data, datalen); + if(rv == -1) { + return -1; + } + res += rv; + return res; +} + +int Downstream::end_upload_data() +{ + if(chunked_request_) { + evbuffer *output = bufferevent_get_output(bev_); + evbuffer_add(output, "0\r\n\r\n", 5); + } + return 0; +} + +const Headers& Downstream::get_response_headers() const +{ + return response_headers_; +} + +void Downstream::add_response_header(const std::string& name, + const std::string& value) +{ + response_headers_.push_back(std::make_pair(name, value)); +} + +void Downstream::set_last_response_header_value(const std::string& value) +{ + Headers::value_type &item = response_headers_.back(); + item.second = value; + check_transfer_encoding_chunked(&chunked_response_, item); +} + +unsigned int Downstream::get_response_http_status() const +{ + return response_http_status_; +} + +void Downstream::set_response_http_status(unsigned int status) +{ + response_http_status_ = status; +} + +bool Downstream::get_chunked_response() const +{ + return chunked_response_; +} + +namespace { +int htp_hdrs_completecb(htparser *htp) +{ + Downstream *downstream; + downstream = reinterpret_cast(htparser_get_userdata(htp)); + downstream->set_response_http_status(htparser_get_status(htp)); + downstream->set_response_state(Downstream::HEADER_COMPLETE); + downstream->get_upstream()->on_downstream_header_complete(downstream); + return 0; +} +} // namespace + +namespace { +int htp_hdr_keycb(htparser *htp, const char *data, size_t len) +{ + Downstream *downstream; + downstream = reinterpret_cast(htparser_get_userdata(htp)); + downstream->add_response_header(std::string(data, len), ""); + return 0; +} +} // namespace + +namespace { +int htp_hdr_valcb(htparser *htp, const char *data, size_t len) +{ + Downstream *downstream; + downstream = reinterpret_cast(htparser_get_userdata(htp)); + downstream->set_last_response_header_value(std::string(data, len)); + return 0; +} +} // namespace + +namespace { +int htp_bodycb(htparser *htp, const char *data, size_t len) +{ + Downstream *downstream; + downstream = reinterpret_cast(htparser_get_userdata(htp)); + downstream->get_upstream()->on_downstream_body + (downstream, reinterpret_cast(data), len); + return 0; +} +} // namespace + +namespace { +int htp_body_completecb(htparser *htp) +{ + Downstream *downstream; + downstream = reinterpret_cast(htparser_get_userdata(htp)); + downstream->set_response_state(Downstream::MSG_COMPLETE); + downstream->get_upstream()->on_downstream_body_complete(downstream); + return 0; +} +} // namespace + +namespace { +htparse_hooks htp_hooks = { + 0, /*htparse_hook on_msg_begin;*/ + 0, /*htparse_data_hook method;*/ + 0, /* htparse_data_hook scheme;*/ + 0, /* htparse_data_hook host; */ + 0, /* htparse_data_hook port; */ + 0, /* htparse_data_hook path; */ + 0, /* htparse_data_hook args; */ + 0, /* htparse_data_hook uri; */ + 0, /* htparse_hook on_hdrs_begin; */ + htp_hdr_keycb, /* htparse_data_hook hdr_key; */ + htp_hdr_valcb, /* htparse_data_hook hdr_val; */ + htp_hdrs_completecb, /* htparse_hook on_hdrs_complete; */ + 0, /*htparse_hook on_new_chunk;*/ + 0, /*htparse_hook on_chunk_complete;*/ + 0, /*htparse_hook on_chunks_complete;*/ + htp_bodycb, /* htparse_data_hook body; */ + htp_body_completecb /* htparse_hook on_msg_complete;*/ +}; +} // namespace + +int Downstream::parse_http_response() +{ + evbuffer *input = bufferevent_get_input(bev_); + unsigned char *mem = evbuffer_pullup(input, -1); + size_t nread = htparser_run(response_htp_, &htp_hooks, + reinterpret_cast(mem), + evbuffer_get_length(input)); + evbuffer_drain(input, nread); + if(htparser_get_error(response_htp_) == htparse_error_none) { + return 0; + } else { + if(ENABLE_LOG) { + LOG(INFO) << " http parser failure: " + << htparser_get_strerror(response_htp_); + } + return SHRPX_ERR_HTTP_PARSE; + } +} + +void Downstream::set_response_state(int state) +{ + response_state_ = state; +} + +int Downstream::get_response_state() const +{ + return response_state_; +} + +int Downstream::init_response_body_buf() +{ + assert(response_body_buf_ == 0); + response_body_buf_ = evbuffer_new(); + if(response_body_buf_ == 0) { + DIE(); + } + return 0; +} + +evbuffer* Downstream::get_response_body_buf() +{ + return response_body_buf_; +} + +} // namespace shrpx diff --git a/examples/shrpx_downstream.h b/examples/shrpx_downstream.h new file mode 100644 index 00000000..d4b824db --- /dev/null +++ b/examples/shrpx_downstream.h @@ -0,0 +1,111 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_H +#define SHRPX_DOWNSTREAM_H + +#include "shrpx.h" + +#include + +#include +#include + +#include +#include + +extern "C" { +#include "htparse/htparse.h" +} + +namespace shrpx { + +class Upstream; + +typedef std::vector > Headers; + +class Downstream { +public: + Downstream(Upstream *upstream, int stream_id, int priority); + ~Downstream(); + int start_connection(); + Upstream* get_upstream() const; + int32_t get_stream_id() const; + // downstream request API + const Headers& get_request_headers() const; + void add_request_header(const std::string& name, const std::string& value); + void set_last_request_header_value(const std::string& value); + void set_request_method(const std::string& method); + void set_request_path(const std::string& path); + int push_request_headers(); + bool get_chunked_request() const; + bool get_request_connection_close() const; + void set_request_connection_close(bool f); + int push_upload_data_chunk(const uint8_t *data, size_t datalen); + int end_upload_data(); + enum { + INITIAL, + HEADER_COMPLETE, + MSG_COMPLETE, + STREAM_CLOSED + }; + void set_request_state(int state); + int get_request_state() const; + // downstream response API + const Headers& get_response_headers() const; + void add_response_header(const std::string& name, const std::string& value); + void set_last_response_header_value(const std::string& value); + unsigned int get_response_http_status() const; + void set_response_http_status(unsigned int status); + bool get_chunked_response() const; + int parse_http_response(); + void set_response_state(int state); + int get_response_state() const; + int init_response_body_buf(); + evbuffer* get_response_body_buf(); +private: + Upstream *upstream_; + bufferevent *bev_; + int32_t stream_id_; + int priority_; + + int request_state_; + std::string request_method_; + std::string request_path_; + bool chunked_request_; + bool request_connection_close_; + Headers request_headers_; + + int response_state_; + unsigned int response_http_status_; + bool chunked_response_; + Headers response_headers_; + htparser *response_htp_; + + evbuffer *response_body_buf_; +}; + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_H diff --git a/examples/shrpx_downstream_queue.cc b/examples/shrpx_downstream_queue.cc new file mode 100644 index 00000000..8c61d835 --- /dev/null +++ b/examples/shrpx_downstream_queue.cc @@ -0,0 +1,67 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream_queue.h" + +#include "shrpx_downstream.h" + +namespace shrpx { + +DownstreamQueue::DownstreamQueue() +{} + +DownstreamQueue::~DownstreamQueue() +{ + for(std::map::iterator i = downstreams_.begin(); + i != downstreams_.end(); ++i) { + delete (*i).second; + } +} + +void DownstreamQueue::add(Downstream *downstream) +{ + downstreams_[downstream->get_stream_id()] = downstream; +} + +void DownstreamQueue::start(Downstream *downstream) +{ + downstream->start_connection(); +} + +void DownstreamQueue::remove(Downstream *downstream) +{ + downstreams_.erase(downstream->get_stream_id()); +} + +Downstream* DownstreamQueue::find(int32_t stream_id) +{ + std::map::iterator i = downstreams_.find(stream_id); + if(i == downstreams_.end()) { + return 0; + } else { + return (*i).second; + } +} + +} // namespace shrpx diff --git a/examples/shrpx_downstream_queue.h b/examples/shrpx_downstream_queue.h new file mode 100644 index 00000000..d8c76c85 --- /dev/null +++ b/examples/shrpx_downstream_queue.h @@ -0,0 +1,52 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_QUEUE_H +#define SHRPX_DOWNSTREAM_QUEUE_H + +#include "shrpx.h" + +#include + +#include + +namespace shrpx { + +class Downstream; + +class DownstreamQueue { +public: + DownstreamQueue(); + ~DownstreamQueue(); + void add(Downstream *downstream); + void start(Downstream *downstream); + void remove(Downstream *downstream); + Downstream* find(int32_t stream_id); +private: + std::map downstreams_; +}; + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_QUEUE_H diff --git a/examples/shrpx_error.h b/examples/shrpx_error.h new file mode 100644 index 00000000..0c8dbd37 --- /dev/null +++ b/examples/shrpx_error.h @@ -0,0 +1,38 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_ERROR_H +#define SHRPX_ERROR_H + +namespace shrpx { + +enum ErrorCode { + SHRPX_ERR_SUCCESS = 0, + SHRPX_ERR_UNKNOWN = -1, + SHRPX_ERR_HTTP_PARSE = -2 +}; + +} // namespace shrpx + +#endif // SHRPX_ERROR_H diff --git a/examples/shrpx_http.cc b/examples/shrpx_http.cc new file mode 100644 index 00000000..8d775cf3 --- /dev/null +++ b/examples/shrpx_http.cc @@ -0,0 +1,98 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http.h" + +#include + +#include "shrpx_config.h" +namespace shrpx { + +namespace http { + +const char* get_status_string(int status_code) +{ + switch(status_code) { + case 100: return "100 Continue"; + case 101: return "101 Switching Protocols"; + case 200: return "200 OK"; + case 201: return "201 Created"; + case 202: return "202 Accepted"; + case 203: return "203 Non-Authoritative Information"; + case 204: return "204 No Content"; + case 205: return "205 Reset Content"; + case 206: return "206 Partial Content"; + case 300: return "300 Multiple Choices"; + case 301: return "301 Moved Permanently"; + case 302: return "302 Found"; + case 303: return "303 See Other"; + case 304: return "304 Not Modified"; + case 305: return "305 Use Proxy"; + // case 306: return "306 (Unused)"; + case 307: return "307 Temporary Redirect"; + case 400: return "400 Bad Request"; + case 401: return "401 Unauthorized"; + case 402: return "402 Payment Required"; + case 403: return "403 Forbidden"; + case 404: return "404 Not Found"; + case 405: return "405 Method Not Allowed"; + case 406: return "406 Not Acceptable"; + case 407: return "407 Proxy Authentication Required"; + case 408: return "408 Request Timeout"; + case 409: return "409 Conflict"; + case 410: return "410 Gone"; + case 411: return "411 Length Required"; + case 412: return "412 Precondition Failed"; + case 413: return "413 Request Entity Too Large"; + case 414: return "414 Request-URI Too Long"; + case 415: return "415 Unsupported Media Type"; + case 416: return "416 Requested Range Not Satisfiable"; + case 417: return "417 Expectation Failed"; + case 500: return "500 Internal Server Error"; + case 501: return "501 Not Implemented"; + case 502: return "502 Bad Gateway"; + case 503: return "503 Service Unavailable"; + case 504: return "504 Gateway Timeout"; + case 505: return "505 HTTP Version Not Supported"; + default: return ""; + } +} + +std::string create_error_html(int status_code) +{ + std::stringstream ss; + const char *status = http::get_status_string(status_code); + ss << "" << status << "" + << "

" << status << "

" + << "
" + << "
" << get_config()->server_name << " at port " + << get_config()->port + << "
" + << ""; + return ss.str(); +} + +} // namespace http + +} // namespace shrpx diff --git a/examples/shrpx_http.h b/examples/shrpx_http.h new file mode 100644 index 00000000..75b1b18c --- /dev/null +++ b/examples/shrpx_http.h @@ -0,0 +1,42 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP_H +#define SHRPX_HTTP_H + +#include + +namespace shrpx { + +namespace http { + +const char* get_status_string(int status_code); + +std::string create_error_html(int status_code); + +} // namespace http + +} // namespace shrpx + +#endif // SHRPX_HTTP_H diff --git a/examples/shrpx_https_upstream.cc b/examples/shrpx_https_upstream.cc new file mode 100644 index 00000000..92e6e542 --- /dev/null +++ b/examples/shrpx_https_upstream.cc @@ -0,0 +1,464 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_https_upstream.h" + +#include +#include + +#include "shrpx_client_handler.h" +#include "shrpx_downstream.h" +#include "shrpx_http.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "util.h" + +using namespace spdylay; + +namespace shrpx { + +HttpsUpstream::HttpsUpstream(ClientHandler *handler) + : handler_(handler), + htp_(htparser_new()) +{ + if(ENABLE_LOG) { + LOG(INFO) << "HttpsUpstream ctor"; + } + htparser_init(htp_, htp_type_request); + htparser_set_userdata(htp_, this); +} + +HttpsUpstream::~HttpsUpstream() +{ + free(htp_); + for(std::deque::iterator i = downstream_queue_.begin(); + i != downstream_queue_.end(); ++i) { + delete *i; + } +} + +namespace { +int htp_msg_begin(htparser *htp) +{ + if(ENABLE_LOG) { + LOG(INFO) << ":: request start"; + } + HttpsUpstream *upstream; + upstream = reinterpret_cast(htparser_get_userdata(htp)); + Downstream *downstream = new Downstream(upstream, 0, 0); + upstream->add_downstream(downstream); + return 0; +} +} // namespace + +namespace { +int htp_methodcb(htparser *htp, const char *data, size_t len) +{ + HttpsUpstream *upstream; + upstream = reinterpret_cast(htparser_get_userdata(htp)); + Downstream *downstream = upstream->get_last_downstream(); + downstream->set_request_method(std::string(data, len)); + return 0; +} +} // namespace + +namespace { +int htp_uricb(htparser *htp, const char *data, size_t len) +{ + HttpsUpstream *upstream; + upstream = reinterpret_cast(htparser_get_userdata(htp)); + Downstream *downstream = upstream->get_last_downstream(); + downstream->set_request_path(std::string(data, len)); + return 0; +} +} // namespace + +namespace { +int htp_hdrs_begincb(htparser *htp) +{ + if(ENABLE_LOG) { + LOG(INFO) << ":: request headers start"; + } + HttpsUpstream *upstream; + upstream = reinterpret_cast(htparser_get_userdata(htp)); + Downstream *downstream = upstream->get_last_downstream(); + + int version = htparser_get_major(htp)*100 + htparser_get_minor(htp); + if(version < 101) { + downstream->set_request_connection_close(true); + } + return 0; +} +} // namespace + +namespace { +int htp_hdr_keycb(htparser *htp, const char *data, size_t len) +{ + HttpsUpstream *upstream; + upstream = reinterpret_cast(htparser_get_userdata(htp)); + Downstream *downstream = upstream->get_last_downstream(); + downstream->add_request_header(std::string(data, len), ""); + return 0; +} +} // namespace + +namespace { +int htp_hdr_valcb(htparser *htp, const char *data, size_t len) +{ + HttpsUpstream *upstream; + upstream = reinterpret_cast(htparser_get_userdata(htp)); + Downstream *downstream = upstream->get_last_downstream(); + downstream->set_last_request_header_value(std::string(data, len)); + return 0; +} +} // namespace + +namespace { +int htp_hdrs_completecb(htparser *htp) +{ + if(ENABLE_LOG) { + LOG(INFO) << ":: request headers complete"; + } + HttpsUpstream *upstream; + upstream = reinterpret_cast(htparser_get_userdata(htp)); + Downstream *downstream = upstream->get_last_downstream(); + + downstream->push_request_headers(); + downstream->set_request_state(Downstream::HEADER_COMPLETE); + + downstream->start_connection(); + return 0; +} +} // namespace + +namespace { +int htp_bodycb(htparser *htp, const char *data, size_t len) +{ + HttpsUpstream *upstream; + upstream = reinterpret_cast(htparser_get_userdata(htp)); + Downstream *downstream = upstream->get_last_downstream(); + downstream->push_upload_data_chunk(reinterpret_cast(data), + len); + return 0; +} +} // namespace + +namespace { +int htp_msg_completecb(htparser *htp) +{ + if(ENABLE_LOG) { + LOG(INFO) << ":: request complete"; + } + HttpsUpstream *upstream; + upstream = reinterpret_cast(htparser_get_userdata(htp)); + Downstream *downstream = upstream->get_last_downstream(); + downstream->end_upload_data(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + // Stop further processing to complete this request + return 1; +} +} // namespace + +namespace { +htparse_hooks htp_hooks = { + htp_msg_begin, /*htparse_hook on_msg_begin;*/ + htp_methodcb, /*htparse_data_hook method;*/ + 0, /* htparse_data_hook scheme;*/ + 0, /* htparse_data_hook host; */ + 0, /* htparse_data_hook port; */ + 0, /* htparse_data_hook path; */ + 0, /* htparse_data_hook args; */ + htp_uricb, /* htparse_data_hook uri; */ + htp_hdrs_begincb, /* htparse_hook on_hdrs_begin; */ + htp_hdr_keycb, /* htparse_data_hook hdr_key; */ + htp_hdr_valcb, /* htparse_data_hook hdr_val; */ + htp_hdrs_completecb, /* htparse_hook on_hdrs_complete; */ + 0, /*htparse_hook on_new_chunk;*/ + 0, /*htparse_hook on_chunk_complete;*/ + 0, /*htparse_hook on_chunks_complete;*/ + htp_bodycb, /* htparse_data_hook body; */ + htp_msg_completecb /* htparse_hook on_msg_complete;*/ +}; +} // namespace + +std::set cache; + +// on_read() does not consume all available data in input buffer if +// one http request is fully received. +int HttpsUpstream::on_read() +{ + if(cache.count(this) == 0) { + LOG(INFO) << "HttpsUpstream::on_read"; + cache.insert(this); + } + bufferevent *bev = handler_->get_bev(); + evbuffer *input = bufferevent_get_input(bev); + unsigned char *mem = evbuffer_pullup(input, -1); + int nread = htparser_run(htp_, &htp_hooks, + reinterpret_cast(mem), + evbuffer_get_length(input)); + evbuffer_drain(input, nread); + htpparse_error htperr = htparser_get_error(htp_); + if(htperr == htparse_error_user) { + bufferevent_disable(bev, EV_READ); + if(ENABLE_LOG) { + LOG(INFO) << " remaining bytes " << evbuffer_get_length(input); + } + } else if(htperr != htparse_error_none) { + if(ENABLE_LOG) { + LOG(INFO) << " http parse failure: " + << htparser_get_strerror(htp_); + } + return SHRPX_ERR_HTTP_PARSE; + } + return 0; +} + +int HttpsUpstream::on_event() +{ + return 0; +} + +ClientHandler* HttpsUpstream::get_client_handler() const +{ + return handler_; +} + +void HttpsUpstream::resume_read() +{ + bufferevent_enable(handler_->get_bev(), EV_READ); + // Process remaining data in input buffer here. + on_read(); +} + +namespace { +void https_downstream_readcb(bufferevent *bev, void *ptr) +{ + Downstream *downstream = reinterpret_cast(ptr); + HttpsUpstream *upstream; + upstream = static_cast(downstream->get_upstream()); + int rv = downstream->parse_http_response(); + if(rv == 0) { + if(downstream->get_response_state() == Downstream::MSG_COMPLETE) { + assert(downstream == upstream->get_top_downstream()); + upstream->pop_downstream(); + delete downstream; + upstream->resume_read(); + } + } else { + if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + delete upstream->get_client_handler(); + } else { + upstream->error_reply(downstream, 502); + assert(downstream == upstream->get_top_downstream()); + upstream->pop_downstream(); + delete downstream; + upstream->resume_read(); + } + } +} +} // namespace + +namespace { +void https_downstream_writecb(bufferevent *bev, void *ptr) +{ +} +} // namespace + +namespace { +void https_downstream_eventcb(bufferevent *bev, short events, void *ptr) +{ + Downstream *downstream = reinterpret_cast(ptr); + HttpsUpstream *upstream; + upstream = static_cast(downstream->get_upstream()); + if(events & BEV_EVENT_CONNECTED) { + if(ENABLE_LOG) { + LOG(INFO) << " Connection established. " << downstream; + } + } + if(events & BEV_EVENT_EOF) { + if(ENABLE_LOG) { + LOG(INFO) << " EOF stream_id=" + << downstream->get_stream_id(); + } + if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // Server may indicate the end of the request by EOF + if(ENABLE_LOG) { + LOG(INFO) << " Assuming content-length is 0 byte"; + } + upstream->on_downstream_body_complete(downstream); + //downstream->set_response_state(Downstream::MSG_COMPLETE); + } else if(downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // Nothing to do + } else { + // error + if(ENABLE_LOG) { + LOG(INFO) << " Treated as error"; + } + upstream->error_reply(downstream, 502); + } + upstream->pop_downstream(); + delete downstream; + upstream->resume_read(); + } else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) { + if(ENABLE_LOG) { + LOG(INFO) << " error/timeout. " << downstream; + } + if(downstream->get_response_state() == Downstream::INITIAL) { + int status; + if(events & BEV_EVENT_TIMEOUT) { + status = 504; + } else { + status = 502; + } + upstream->error_reply(downstream, status); + } + upstream->pop_downstream(); + delete downstream; + upstream->resume_read(); + } +} +} // namespace + +void HttpsUpstream::error_reply(Downstream *downstream, int status_code) +{ + std::string html = http::create_error_html(status_code); + std::stringstream ss; + ss << "HTTP/1.1 " << http::get_status_string(status_code) << "\r\n" + << "Server: " << get_config()->server_name << "\r\n" + << "Content-Length: " << html.size() << "\r\n" + << "Content-Type: " << "text/html; charset=UTF-8\r\n" + << "\r\n"; + std::string header = ss.str(); + evbuffer *output = bufferevent_get_output(handler_->get_bev()); + evbuffer_add(output, header.c_str(), header.size()); + evbuffer_add(output, html.c_str(), html.size()); + downstream->set_response_state(Downstream::MSG_COMPLETE); +} + +bufferevent_data_cb HttpsUpstream::get_downstream_readcb() +{ + return https_downstream_readcb; +} + +bufferevent_data_cb HttpsUpstream::get_downstream_writecb() +{ + return https_downstream_writecb; +} + +bufferevent_event_cb HttpsUpstream::get_downstream_eventcb() +{ + return https_downstream_eventcb; +} + +void HttpsUpstream::add_downstream(Downstream *downstream) +{ + downstream_queue_.push_back(downstream); +} + +void HttpsUpstream::pop_downstream() +{ + downstream_queue_.pop_front(); +} + +Downstream* HttpsUpstream::get_top_downstream() +{ + return downstream_queue_.front(); +} + +Downstream* HttpsUpstream::get_last_downstream() +{ + return downstream_queue_.back(); +} + +int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) +{ + if(ENABLE_LOG) { + LOG(INFO) << " on_downstream_header_complete"; + } + std::string hdrs = "HTTP/1.1 "; + hdrs += http::get_status_string(downstream->get_response_http_status()); + hdrs += "\r\n"; + for(Headers::const_iterator i = downstream->get_response_headers().begin(); + i != downstream->get_response_headers().end(); ++i) { + if(util::strieq((*i).first.c_str(), "keep-alive") || // HTTP/1.0? + util::strieq((*i).first.c_str(), "connection") || + util:: strieq((*i).first.c_str(), "proxy-connection")) { + // These are ignored + } else { + if(util::strieq((*i).first.c_str(), "server")) { + hdrs += "Server: "; + hdrs += get_config()->server_name; + } else { + hdrs += (*i).first; + hdrs += ": "; + hdrs += (*i).second; + } + hdrs += "\r\n"; + } + } + if(downstream->get_request_connection_close()) { + hdrs += "Connection: close\r\n"; + } + hdrs += "\r\n"; + if(ENABLE_LOG) { + LOG(INFO) << ":: Response headers\n" << hdrs; + } + evbuffer *output = bufferevent_get_output(handler_->get_bev()); + evbuffer_add(output, hdrs.c_str(), hdrs.size()); + return 0; +} + +int HttpsUpstream::on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len) +{ + int rv; + evbuffer *output = bufferevent_get_output(handler_->get_bev()); + if(downstream->get_chunked_response()) { + char chunk_size_hex[16]; + rv = snprintf(chunk_size_hex, sizeof(chunk_size_hex), "%X\r\n", + static_cast(len)); + evbuffer_add(output, chunk_size_hex, rv); + } + evbuffer_add(output, data, len); + return 0; +} + +int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) +{ + if(downstream->get_chunked_response()) { + evbuffer *output = bufferevent_get_output(handler_->get_bev()); + evbuffer_add(output, "0\r\n\r\n", 5); + } + if(ENABLE_LOG) { + LOG(INFO) << " on_downstream_body_complete"; + } + if(downstream->get_request_connection_close()) { + ClientHandler *handler = downstream->get_upstream()->get_client_handler(); + handler->set_should_close_after_write(true); + } + return 0; +} + +} // namespace shrpx diff --git a/examples/shrpx_https_upstream.h b/examples/shrpx_https_upstream.h new file mode 100644 index 00000000..f05a48c9 --- /dev/null +++ b/examples/shrpx_https_upstream.h @@ -0,0 +1,76 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTPS_UPSTREAM_H +#define SHRPX_HTTPS_UPSTREAM_H + +#include "shrpx.h" + +#include + +#include + +extern "C" { +#include "htparse/htparse.h" +} + +#include "shrpx_upstream.h" + +namespace shrpx { + +class ClientHandler; + +class HttpsUpstream : public Upstream { +public: + HttpsUpstream(ClientHandler *handler); + virtual ~HttpsUpstream(); + virtual int on_read(); + virtual int on_event(); + //int send(); + virtual ClientHandler* get_client_handler() const; + virtual bufferevent_data_cb get_downstream_readcb(); + virtual bufferevent_data_cb get_downstream_writecb(); + virtual bufferevent_event_cb get_downstream_eventcb(); + void add_downstream(Downstream *downstream); + void pop_downstream(); + Downstream* get_top_downstream(); + Downstream* get_last_downstream(); + void error_reply(Downstream *downstream, int status_code); + + void resume_read(); + + virtual int on_downstream_header_complete(Downstream *downstream); + virtual int on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len); + virtual int on_downstream_body_complete(Downstream *downstream); + +private: + ClientHandler *handler_; + htparser *htp_; + std::deque downstream_queue_; +}; + +} // namespace shrpx + +#endif // SHRPX_HTTPS_UPSTREAM_H diff --git a/examples/shrpx_listen_handler.cc b/examples/shrpx_listen_handler.cc new file mode 100644 index 00000000..8be62a11 --- /dev/null +++ b/examples/shrpx_listen_handler.cc @@ -0,0 +1,78 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_listen_handler.h" + +#include + +#include "shrpx_client_handler.h" + +namespace shrpx { + +ListenHandler::ListenHandler(event_base *evbase, SSL_CTX *ssl_ctx) + : evbase_(evbase), + ssl_ctx_(ssl_ctx) +{} + +ListenHandler::~ListenHandler() +{} + +int ListenHandler::accept_connection(evutil_socket_t fd, + sockaddr *addr, int addrlen) +{ + if(ENABLE_LOG) { + LOG(INFO) << " Accepted connection. fd=" << fd; + } + char host[NI_MAXHOST]; + int rv; + rv = getnameinfo(addr, addrlen, host, sizeof(host), 0, 0, NI_NUMERICHOST); + if(rv == 0) { + SSL *ssl = SSL_new(ssl_ctx_); + bufferevent *bev = bufferevent_openssl_socket_new + (evbase_, fd, ssl, + BUFFEREVENT_SSL_ACCEPTING, + BEV_OPT_DEFER_CALLBACKS); + if(bev == NULL) { + if(ENABLE_LOG) { + LOG(ERROR) << " bufferevent_openssl_socket_new failed"; + } + close(fd); + } else { + /*ClientHandler *client_handler =*/ new ClientHandler(bev, ssl, host); + } + } else { + if(ENABLE_LOG) { + LOG(INFO) << " getnameinfo failed"; + } + close(fd); + } + return 0; +} + +event_base* ListenHandler::get_evbase() const +{ + return evbase_; +} + +} // namespace shrpx diff --git a/examples/shrpx_listen_handler.h b/examples/shrpx_listen_handler.h new file mode 100644 index 00000000..4b3e31f0 --- /dev/null +++ b/examples/shrpx_listen_handler.h @@ -0,0 +1,51 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_LISTEN_HANDLER_H +#define SHRPX_LISTEN_HANDLER_H + +#include "shrpx.h" + +#include +#include + +#include +#include + +namespace shrpx { + +class ListenHandler { +public: + ListenHandler(event_base *evbase, SSL_CTX *ssl_ctx); + ~ListenHandler(); + int accept_connection(evutil_socket_t fd, sockaddr *addr, int addrlen); + event_base* get_evbase() const; +private: + event_base *evbase_; + SSL_CTX *ssl_ctx_; +}; + +} // namespace shrpx + +#endif // SHRPX_LISTEN_HANDLER_H diff --git a/examples/shrpx_log.cc b/examples/shrpx_log.cc new file mode 100644 index 00000000..5f7469eb --- /dev/null +++ b/examples/shrpx_log.cc @@ -0,0 +1,49 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_log.h" + +#include + +namespace shrpx { + +const char *SEVERITY_STR[] = { + "INFO", "WARN", "ERROR", "FATAL" +}; + +Log::Log(int severity, const char *filename, int linenum) + : severity_(severity), + filename_(filename), + linenum_(linenum) +{} + +Log::~Log() +{ + fprintf(stderr, "[%s] %s\n (%s, line %d)\n", + SEVERITY_STR[severity_], stream_.str().c_str(), + filename_, linenum_); + fflush(stderr); +} + +} // namespace shrpx diff --git a/examples/shrpx_log.h b/examples/shrpx_log.h new file mode 100644 index 00000000..9a1515ab --- /dev/null +++ b/examples/shrpx_log.h @@ -0,0 +1,60 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_LOG_H +#define SHRPX_LOG_H + +#include "shrpx.h" + +#include + +namespace shrpx { + +#define ENABLE_LOG 1 + +#define LOG(SEVERITY) Log(SEVERITY, __FILE__, __LINE__) + +enum SeverityLevel { + INFO, WARNING, ERROR, FATAL +}; + +class Log { +public: + Log(int severiy, const char *filename, int linenum); + ~Log(); + template Log& operator<<(Type s) + { + stream_ << s; + return *this; + } +private: + int severity_; + const char *filename_; + int linenum_; + std::stringstream stream_; +}; + +} // namespace shrpx + +#endif // SHRPX_LOG_H diff --git a/examples/shrpx_spdy_upstream.cc b/examples/shrpx_spdy_upstream.cc new file mode 100644 index 00000000..f217dad2 --- /dev/null +++ b/examples/shrpx_spdy_upstream.cc @@ -0,0 +1,532 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_spdy_upstream.h" + +#include +#include + +#include "shrpx_client_handler.h" +#include "shrpx_downstream.h" +#include "shrpx_config.h" +#include "shrpx_http.h" +#include "util.h" + +using namespace spdylay; + +namespace shrpx { + +namespace { +ssize_t send_callback(spdylay_session *session, + const uint8_t *data, size_t len, int flags, + void *user_data) +{ + int rv; + SpdyUpstream *upstream = reinterpret_cast(user_data); + ClientHandler *handler = upstream->get_client_handler(); + bufferevent *bev = handler->get_bev(); + evbuffer *output = bufferevent_get_output(bev); + rv = evbuffer_add(output, data, len); + if(rv == -1) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } else { + return len; + } +} +} // namespace + +namespace { +ssize_t recv_callback(spdylay_session *session, + uint8_t *data, size_t len, int flags, void *user_data) +{ + SpdyUpstream *upstream = reinterpret_cast(user_data); + ClientHandler *handler = upstream->get_client_handler(); + bufferevent *bev = handler->get_bev(); + evbuffer *input = bufferevent_get_input(bev); + int nread = evbuffer_remove(input, data, len); + if(nread == -1) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } else if(nread == 0) { + return SPDYLAY_ERR_WOULDBLOCK; + } else { + return nread; + } +} +} // namespace + +namespace { +void on_stream_close_callback +(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code, + void *user_data) +{ + if(ENABLE_LOG) { + LOG(INFO) << ":: Stream " << stream_id + << " is being closed"; + } + SpdyUpstream *upstream = reinterpret_cast(user_data); + Downstream *downstream = upstream->get_downstream_queue()->find(stream_id); + if(downstream) { + downstream->set_request_state(Downstream::STREAM_CLOSED); + if(downstream->get_response_state() == Downstream::MSG_COMPLETE) { + upstream->get_downstream_queue()->remove(downstream); + delete downstream; + } + } +} +} // namespace + +namespace { +void on_ctrl_recv_callback +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data) +{ + SpdyUpstream *upstream = reinterpret_cast(user_data); + switch(type) { + case SPDYLAY_SYN_STREAM: { + if(ENABLE_LOG) { + LOG(INFO) << ":: Received upstream SYN_STREAM stream_id=" + << frame->syn_stream.stream_id; + } + Downstream *downstream = new Downstream(upstream, + frame->syn_stream.stream_id, + frame->syn_stream.pri); + downstream->init_response_body_buf(); + + char **nv = frame->syn_stream.nv; + for(size_t i = 0; nv[i]; i += 2) { + if(strcmp(nv[i], ":path") == 0) { + downstream->set_request_path(nv[i+1]); + } else if(strcmp(nv[i], ":method") == 0) { + downstream->set_request_method(nv[i+1]); + } else if(nv[i][0] != ':') { + downstream->add_request_header(nv[i], nv[i+1]); + } + } + downstream->add_request_header("X-Forwarded-Spdy", "true"); + + if(ENABLE_LOG) { + std::stringstream ss; + for(size_t i = 0; nv[i]; i += 2) { + ss << nv[i] << ": " << nv[i+1] << "\n"; + } + LOG(INFO) << ":: Request headers:\n" << ss.str(); + } + + downstream->push_request_headers(); + downstream->set_request_state(Downstream::HEADER_COMPLETE); + if(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { + if(ENABLE_LOG) { + LOG(INFO) << ":: " + << "Setting Downstream::MSG_COMPLETE for Downstream " + << downstream; + } + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + upstream->add_downstream(downstream); + upstream->start_downstream(downstream); + break; + } + default: + break; + } +} +} // namespace + +namespace { +void on_data_chunk_recv_callback(spdylay_session *session, + uint8_t flags, int32_t stream_id, + const uint8_t *data, size_t len, + void *user_data) +{ + if(ENABLE_LOG) { + LOG(INFO) << ":: Received upstream DATA data stream_id=" + << stream_id; + } + SpdyUpstream *upstream = reinterpret_cast(user_data); + Downstream *downstream = upstream->get_downstream_queue()->find(stream_id); + if(downstream) { + downstream->push_upload_data_chunk(data, len); + if(flags & SPDYLAY_DATA_FLAG_FIN) { + if(ENABLE_LOG) { + LOG(INFO) << ":: " + << "Setting Downstream::MSG_COMPLETE for Downstream " + << downstream; + } + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + } +} +} // namespace + +SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler) + : handler_(handler), + session_(0) +{ + //handler->set_bev_cb(spdy_readcb, 0, spdy_eventcb); + handler->set_upstream_timeouts(&get_config()->spdy_upstream_read_timeout, + &get_config()->spdy_upstream_write_timeout); + + spdylay_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = send_callback; + callbacks.recv_callback = recv_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + + int rv; + rv = spdylay_session_server_new(&session_, version, &callbacks, this); + assert(rv == 0); + // TODO Maybe call from outside? + spdylay_settings_entry entry; + entry.settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; + entry.value = SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS; + entry.flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + rv = spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE, + &entry, 1); + assert(rv == 0); + // TODO Maybe call from outside? + send(); +} + +SpdyUpstream::~SpdyUpstream() +{ + spdylay_session_del(session_); +} + +int SpdyUpstream::on_read() +{ + int rv; + if((rv = spdylay_session_recv(session_)) || + (rv = spdylay_session_send(session_))) { + if(rv != SPDYLAY_ERR_EOF) { + LOG(ERROR) << "spdylay error: " << spdylay_strerror(rv); + DIE(); + } + } + return 0; +} + +int SpdyUpstream::send() +{ + int rv; + if((rv = spdylay_session_send(session_))) { + LOG(ERROR) << "spdylay error: " << spdylay_strerror(rv); + DIE(); + } + return 0; +} + +int SpdyUpstream::on_event() +{ + return 0; +} + +ClientHandler* SpdyUpstream::get_client_handler() const +{ + return handler_; +} + +namespace { +void spdy_downstream_readcb(bufferevent *bev, void *ptr) +{ + if(ENABLE_LOG) { + LOG(INFO) << "spdy_downstream_readcb"; + } + Downstream *downstream = reinterpret_cast(ptr); + SpdyUpstream *upstream; + upstream = static_cast(downstream->get_upstream()); + int rv = downstream->parse_http_response(); + if(rv == 0) { + if(downstream->get_request_state() == Downstream::STREAM_CLOSED && + downstream->get_response_state() == Downstream::MSG_COMPLETE) { + upstream->get_downstream_queue()->remove(downstream); + delete downstream; + } else { + upstream->send(); + } + } else { + if(ENABLE_LOG) { + LOG(INFO) << " http parser failure"; + } + if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + } else { + upstream->error_reply(downstream, 502); + } + downstream->set_response_state(Downstream::MSG_COMPLETE); + upstream->send(); + // upstream->remove_downstream(downstream); + // delete downstream; + } +} +} // namespace + +namespace { +void spdy_downstream_writecb(bufferevent *bev, void *ptr) +{ +} +} // namespace + +namespace { +void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr) +{ + Downstream *downstream = reinterpret_cast(ptr); + SpdyUpstream *upstream; + upstream = static_cast(downstream->get_upstream()); + if(events & BEV_EVENT_CONNECTED) { + if(ENABLE_LOG) { + LOG(INFO) << " Connection established. Downstream " + << downstream; + } + } + if(events & BEV_EVENT_EOF) { + if(ENABLE_LOG) { + LOG(INFO) << " EOF stream_id=" + << downstream->get_stream_id(); + } + if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // Server may indicate the end of the request by EOF + if(ENABLE_LOG) { + LOG(INFO) << " Assuming content-length is 0 byte"; + } + upstream->on_downstream_body_complete(downstream); + downstream->set_response_state(Downstream::MSG_COMPLETE); + // downstream wil be deleted in on_stream_close_callback. + } else if(downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // nothing todo + } else { + // error + if(ENABLE_LOG) { + LOG(INFO) << " Treated as error"; + } + upstream->error_reply(downstream, 502); + upstream->send(); + upstream->remove_downstream(downstream); + delete downstream; + } + } else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) { + if(ENABLE_LOG) { + LOG(INFO) << " error/timeout. Downstream " << downstream; + } + // For Downstream::MSG_COMPLETE case, downstream will be deleted + // in on_stream_close_callback. + if(downstream->get_response_state() != Downstream::MSG_COMPLETE) { + if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + } else { + int status; + if(events & BEV_EVENT_TIMEOUT) { + status = 504; + } else { + status = 502; + } + upstream->error_reply(downstream, status); + } + upstream->send(); + upstream->remove_downstream(downstream); + delete downstream; + } + } +} +} // namespace + +int SpdyUpstream::rst_stream(Downstream *downstream, int status_code) +{ + int rv; + rv = spdylay_submit_rst_stream(session_, downstream->get_stream_id(), + status_code); + if(rv < SPDYLAY_ERR_FATAL) { + DIE(); + } else { + return 0; + } +} + +namespace { +ssize_t spdy_data_read_callback(spdylay_session *session, + int32_t stream_id, + uint8_t *buf, size_t length, + int *eof, + spdylay_data_source *source, + void *user_data) +{ + Downstream *downstream = reinterpret_cast(source->ptr); + evbuffer *body = downstream->get_response_body_buf(); + assert(body); + int nread = evbuffer_remove(body, buf, length); + if(nread == 0 && + downstream->get_response_state() == Downstream::MSG_COMPLETE) { + *eof = 1; + } + if(nread == 0 && *eof != 1) { + return SPDYLAY_ERR_DEFERRED; + } + return nread; +} +} // namespace + +int SpdyUpstream::error_reply(Downstream *downstream, int status_code) +{ + int rv; + std::string html = http::create_error_html(status_code); + downstream->init_response_body_buf(); + evbuffer *body = downstream->get_response_body_buf(); + rv = evbuffer_add(body, html.c_str(), html.size()); + if(rv == -1) { + DIE(); + } + downstream->set_response_state(Downstream::MSG_COMPLETE); + + spdylay_data_provider data_prd; + data_prd.source.ptr = downstream; + data_prd.read_callback = spdy_data_read_callback; + + const char *nv[] = { + ":status", http::get_status_string(status_code), + ":version", "http/1.1", + "content-type", "text/html; charset=UTF-8", + "server", get_config()->server_name, + 0 + }; + + rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv, + &data_prd); + if(rv < SPDYLAY_ERR_FATAL) { + DIE(); + } else { + return 0; + } +} + +bufferevent_data_cb SpdyUpstream::get_downstream_readcb() +{ + return spdy_downstream_readcb; +} + +bufferevent_data_cb SpdyUpstream::get_downstream_writecb() +{ + return spdy_downstream_writecb; +} + +bufferevent_event_cb SpdyUpstream::get_downstream_eventcb() +{ + return spdy_downstream_eventcb; +} + +void SpdyUpstream::add_downstream(Downstream *downstream) +{ + downstream_queue_.add(downstream); +} + +void SpdyUpstream::start_downstream(Downstream *downstream) +{ + downstream_queue_.start(downstream); +} + +void SpdyUpstream::remove_downstream(Downstream *downstream) +{ + downstream_queue_.remove(downstream); +} + +spdylay_session* SpdyUpstream::get_spdy_session() +{ + return session_; +} + +int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) +{ + if(ENABLE_LOG) { + LOG(INFO) << " on_downstream_header_complete"; + } + size_t nheader = downstream->get_response_headers().size(); + const char **nv = new const char*[nheader * 2 + 4 + 1]; + size_t hdidx = 0; + for(Headers::const_iterator i = downstream->get_response_headers().begin(); + i != downstream->get_response_headers().end(); ++i) { + if(util::strieq((*i).first.c_str(), "transfer-encoding") || + util::strieq((*i).first.c_str(), "keep-alive") || // HTTP/1.0? + util::strieq((*i).first.c_str(), "connection") || + util:: strieq((*i).first.c_str(), "proxy-connection")) { + // These are ignored + } else if(util::strieq((*i).first.c_str(), "server")) { + nv[hdidx++] = "server"; + nv[hdidx++] = get_config()->server_name; + } else { + nv[hdidx++] = (*i).first.c_str(); + nv[hdidx++] = (*i).second.c_str(); + } + } + nv[hdidx++] = ":status"; + nv[hdidx++] = http::get_status_string(downstream->get_response_http_status()); + nv[hdidx++] = ":version"; + nv[hdidx++] = "HTTP/1.1"; + nv[hdidx++] = 0; + if(ENABLE_LOG) { + std::stringstream ss; + for(size_t i = 0; nv[i]; i += 2) { + ss << nv[i] << ": " << nv[i+1] << "\n"; + } + LOG(INFO) << ":: Response headers\n" << ss.str(); + } + spdylay_data_provider data_prd; + data_prd.source.ptr = downstream; + data_prd.read_callback = spdy_data_read_callback; + + spdylay_submit_response(session_, downstream->get_stream_id(), nv, + &data_prd); + delete [] nv; + //send(); + return 0; +} + +int SpdyUpstream::on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len) +{ + if(ENABLE_LOG) { + LOG(INFO) << " on_downstream_body"; + } + evbuffer *body = downstream->get_response_body_buf(); + evbuffer_add(body, data, len); + spdylay_session_resume_data(session_, downstream->get_stream_id()); + //send(); + return 0; +} + +int SpdyUpstream::on_downstream_body_complete(Downstream *downstream) +{ + if(ENABLE_LOG) { + LOG(INFO) << " on_downstream_body_complete"; + } + spdylay_session_resume_data(session_, downstream->get_stream_id()); + //send(); + return 0; +} + +DownstreamQueue* SpdyUpstream::get_downstream_queue() +{ + return &downstream_queue_; +} + +} // namespace shrpx diff --git a/examples/shrpx_spdy_upstream.h b/examples/shrpx_spdy_upstream.h new file mode 100644 index 00000000..c9db8bb7 --- /dev/null +++ b/examples/shrpx_spdy_upstream.h @@ -0,0 +1,71 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_SPDY_UPSTREAM_H +#define SHRPX_SPDY_UPSTREAM_H + +#include "shrpx.h" + +#include + +#include "shrpx_upstream.h" +#include "shrpx_downstream_queue.h" + +namespace shrpx { + +class ClientHandler; + +class SpdyUpstream : public Upstream { +public: + SpdyUpstream(uint16_t version, ClientHandler *handler); + virtual ~SpdyUpstream(); + virtual int on_read(); + virtual int on_event(); + int send(); + virtual ClientHandler* get_client_handler() const; + virtual bufferevent_data_cb get_downstream_readcb(); + virtual bufferevent_data_cb get_downstream_writecb(); + virtual bufferevent_event_cb get_downstream_eventcb(); + void add_downstream(Downstream *downstream); + void remove_downstream(Downstream *downstream); + void start_downstream(Downstream *downstream); + spdylay_session* get_spdy_session(); + DownstreamQueue* get_downstream_queue(); + + int rst_stream(Downstream *downstream, int status_code); + int error_reply(Downstream *downstream, int status_code); + + virtual int on_downstream_header_complete(Downstream *downstream); + virtual int on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len); + virtual int on_downstream_body_complete(Downstream *downstream); +private: + ClientHandler *handler_; + spdylay_session *session_; + DownstreamQueue downstream_queue_; +}; + +} // namespace shrpx + +#endif // SHRPX_SPDY_UPSTREAM_H diff --git a/examples/shrpx_upstream.h b/examples/shrpx_upstream.h new file mode 100644 index 00000000..bb886ada --- /dev/null +++ b/examples/shrpx_upstream.h @@ -0,0 +1,55 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_UPSTREAM_H +#define SHRPX_UPSTREAM_H + +#include "shrpx.h" + +#include + +namespace shrpx { + +class ClientHandler; +class Downstream; + +class Upstream { +public: + virtual ~Upstream() {} + virtual int on_read() = 0; + virtual int on_event() = 0; + virtual bufferevent_data_cb get_downstream_readcb() = 0; + virtual bufferevent_data_cb get_downstream_writecb() = 0; + virtual bufferevent_event_cb get_downstream_eventcb() = 0; + virtual ClientHandler* get_client_handler() const = 0; + + virtual int on_downstream_header_complete(Downstream *downstream) = 0; + virtual int on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len) = 0; + virtual int on_downstream_body_complete(Downstream *downstream) = 0; +}; + +} // namespace shrpx + +#endif // SHRPX_UPSTREAM_H diff --git a/examples/util.cc b/examples/util.cc index ec22dc8f..c7b8e969 100644 --- a/examples/util.cc +++ b/examples/util.cc @@ -169,6 +169,21 @@ bool strieq(const char *a, const char *b) return !*a && !*b; } +bool strifind(const char *a, const char *b) +{ + if(!a || !b) { + return false; + } + for(size_t i = 0; a[i]; ++i) { + const char *ap = &a[i], *bp = b; + for(; *ap && *bp && lowcase(*ap) == lowcase(*bp); ++ap, ++bp); + if(!*bp) { + return true; + } + } + return false; +} + } // namespace util } // namespace spdylay diff --git a/examples/util.h b/examples/util.h index d9979955..8c208737 100644 --- a/examples/util.h +++ b/examples/util.h @@ -235,6 +235,8 @@ bool endsWith(const std::string& a, const std::string& b); bool strieq(const char *a, const char *b); +bool strifind(const char *a, const char *b); + } // namespace util } // namespace spdylay