Merge branch 'rproxy'

This commit is contained in:
Tatsuhiro Tsujikawa 2012-06-07 01:59:20 +09:00
commit 673f9c7df0
42 changed files with 6218 additions and 3 deletions

View File

@ -41,11 +41,16 @@ needed:
* OpenSSL >= 1.0.1
To enable ``-a`` option (getting linked assets from the downloaded
resouce) in spdycat (one of the example program), the following
resouce) in ``spdycat`` (one of the example program), the following
packages are needed:
* libxml2 >= 2.7.7
To build SPDY/HTTPS to HTTP reverse proxy ``shrpx`` (one of the
example program), the following packages are needed:
* libevent-openssl >= 2.0.8
Build from git
--------------
@ -88,6 +93,9 @@ purposes. Please note that OpenSSL with `NPN
required in order to build and run these programs. At the time of
this writing, the OpenSSL 1.0.1 supports NPN.
Spdycat - SPDY client
+++++++++++++++++++++
The SPDY client is called ``spdycat``. It is a dead simple downloader
like wget/curl. It connects to SPDY server and gets resources given in
the command-line::
@ -154,6 +162,9 @@ the command-line::
[ 0.077] send GOAWAY frame <version=3, flags=0, length=8>
(last_good_stream_id=0)
Spdyd - SPDY server
+++++++++++++++++++
SPDY server is called ``spdyd`` and serves static files. It is single
threaded and multiplexes connections using non-blocking socket. The
static files are read using blocking I/O system call, read(2). It
@ -193,6 +204,45 @@ speaks SPDY/2 and SPDY/3::
Currently, ``spdyd`` needs ``epoll`` or ``kqueue``.
Shrpx - A reverse proxy for SPDY/HTTPS
++++++++++++++++++++++++++++++++++++++
The ``shrpx`` is a multi-threaded reverse proxy for SPDY/HTTPS. It
converts SPDY/HTTPS traffic to plain HTTP.
Here is the command-line options::
Usage: shrpx [-Dh] [-b <HOST,PORT>] [-f <HOST,PORT>] [-n <CORES>]
[-c <NUM>] [-L <LEVEL>] <PRIVATE_KEY> <CERT>
A reverse proxy for SPDY/HTTPS.
OPTIONS:
-b, --backend=<HOST,PORT>
Set backend host and port.
Default: 'localhost,80'
-f, --frontend=<HOST,PORT>
Set frontend host and port.
Default: 'localhost,3000'
-n, --workers=<CORES>
Set the number of worker threads.
-c, --spdy-max-concurrent-streams=<NUM>
Set the maximum number of the concurrent
streams in one SPDY session.
-L, --log-level=<LEVEL>
Set the severity level of log output.
INFO, WARNING, ERROR and FATAL
-D, --daemon Run in a background. If -D is used, the
current working directory is changed to '/'.
-h, --help Print this help.
For those of you who are curious, ``shrpx`` is an abbreviation of
"Spdy/https to Http Reverse ProXy".
Other examples
++++++++++++++
There is another SPDY server called ``spdynative``, which is
`node.native <https://github.com/d5/node.native>`_ style simple SPDY
server::

View File

@ -95,6 +95,17 @@ if test "x${have_openssl}" = "xno"; then
AC_MSG_NOTICE([The example programs will not be built.])
fi
# libevent_openssl
# 2.0.8 is required because we use evconnlistener_set_error_cb()
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
[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
AM_CONDITIONAL([HAVE_LIBEVENT_OPENSSL],
[ test "x${have_libevent_openssl}" = "xyes" ])
# libxml2 (for examples/spdycat)
AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no])
if test "x${have_libxml2}" = "xyes"; then
@ -178,5 +189,6 @@ AC_MSG_NOTICE([summary of build options:
CUnit: ${have_cunit}
OpenSSL: ${have_openssl}
Libxml2: ${have_libxml2}
Libevent(SSL): ${have_libevent_openssl}
Examples: ${enable_examples}
])

1
examples/.gitignore vendored
View File

@ -2,3 +2,4 @@ spdycat
spdyd
spdynative
spdycli
shrpx

View File

@ -25,12 +25,16 @@ 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
if HAVE_LIBEVENT_OPENSSL
bin_PROGRAMS += shrpx
endif # HAVE_LIBEVENT_OPENSSL
HELPER_OBJECTS = uri.cc util.cc spdylay_ssl.cc
HELPER_HFILES = uri.h util.h spdylay_ssl.h
@ -65,6 +69,26 @@ spdyd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} \
${SPDY_SERVER_OBJECTS} ${SPDY_SERVER_HFILES} \
spdyd.cc
if HAVE_LIBEVENT_OPENSSL
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 \
shrpx_io_control.cc shrpx_io_control.h \
shrpx_ssl.cc shrpx_ssl.h \
shrpx_thread_event_receiver.cc shrpx_thread_event_receiver.h \
shrpx_worker.cc shrpx_worker.h \
htparse/htparse.c htparse/htparse.h
endif # HAVE_LIBEVENT_OPENSSL
noinst_PROGRAMS = spdycli
spdycli_SOURCES = spdycli.c

34
examples/htparse/LICENSE Normal file
View File

@ -0,0 +1,34 @@
Libevhtp is available for use under the following license, commonly known
as the 3-clause (or "modified") BSD license:
==============================
Copyright (c) 2010-2011 Mark Ellzey
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
==============================
Portions of Libevhtp are based on works by others, also made available by them
under the three-clause BSD license above. The functions include:
evhtp.c: _evhtp_glob_match():
Copyright (c) 2006-2009, Salvatore Sanfilippo

23
examples/htparse/Makefile Normal file
View File

@ -0,0 +1,23 @@
SRC = htparse.c
OUT = libhtparse.a
OBJ = $(SRC:.c=.o)
INCLUDES = -I.
CFLAGS += -ggdb -Wall -Wextra
LDFLAGS +=
CC = gcc
.SUFFIXES: .c
default: $(OUT)
.c.o:
$(CC) $(INCLUDES) $(CFLAGS) -c $< -o $@
$(OUT): $(OBJ)
ar rcs $(OUT) $(OBJ)
test: $(OUT) test.c
$(CC) $(INCLUDES) $(CFLAGS) test.c -o test $(OUT)
clean:
rm -f $(OBJ) $(OUT) test

1656
examples/htparse/htparse.c Normal file

File diff suppressed because it is too large Load Diff

108
examples/htparse/htparse.h Normal file
View File

@ -0,0 +1,108 @@
#ifndef __HTPARSE_H__
#define __HTPARSE_H__
struct htparser;
enum htp_type {
htp_type_request = 0,
htp_type_response
};
enum htp_scheme {
htp_scheme_none = 0,
htp_scheme_ftp,
htp_scheme_http,
htp_scheme_https,
htp_scheme_nfs,
htp_scheme_unknown
};
enum htp_method {
htp_method_GET = 0,
htp_method_HEAD,
htp_method_POST,
htp_method_PUT,
htp_method_DELETE,
htp_method_MKCOL,
htp_method_COPY,
htp_method_MOVE,
htp_method_OPTIONS,
htp_method_PROPFIND,
htp_method_PROPPATCH,
htp_method_LOCK,
htp_method_UNLOCK,
htp_method_TRACE,
htp_method_UNKNOWN
};
enum htpparse_error {
htparse_error_none = 0,
htparse_error_too_big,
htparse_error_inval_method,
htparse_error_inval_reqline,
htparse_error_inval_schema,
htparse_error_inval_proto,
htparse_error_inval_ver,
htparse_error_inval_hdr,
htparse_error_inval_chunk_sz,
htparse_error_inval_chunk,
htparse_error_inval_state,
htparse_error_user,
htparse_error_status,
htparse_error_generic
};
typedef struct htparser htparser;
typedef struct htparse_hooks htparse_hooks;
typedef enum htp_scheme htp_scheme;
typedef enum htp_method htp_method;
typedef enum htp_type htp_type;
typedef enum htpparse_error htpparse_error;
typedef int (*htparse_hook)(htparser *);
typedef int (*htparse_data_hook)(htparser *, const char *, size_t);
struct htparse_hooks {
htparse_hook on_msg_begin;
htparse_data_hook method;
htparse_data_hook scheme; /* called if scheme is found */
htparse_data_hook host; /* called if a host was in the request scheme */
htparse_data_hook port; /* called if a port was in the request scheme */
htparse_data_hook path; /* only the path of the uri */
htparse_data_hook args; /* only the arguments of the uri */
htparse_data_hook uri; /* the entire uri including path/args */
htparse_hook on_hdrs_begin;
htparse_data_hook hdr_key;
htparse_data_hook hdr_val;
htparse_hook on_hdrs_complete;
htparse_hook on_new_chunk; /* called after parsed chunk octet */
htparse_hook on_chunk_complete; /* called after single parsed chunk */
htparse_hook on_chunks_complete; /* called after all parsed chunks processed */
htparse_data_hook body;
htparse_hook on_msg_complete;
};
size_t htparser_run(htparser *, htparse_hooks *, const char *, size_t);
int htparser_should_keep_alive(htparser * p);
htp_scheme htparser_get_scheme(htparser *);
htp_method htparser_get_method(htparser *);
const char * htparser_get_methodstr(htparser *);
void htparser_set_major(htparser *, unsigned char);
void htparser_set_minor(htparser *, unsigned char);
unsigned char htparser_get_major(htparser *);
unsigned char htparser_get_minor(htparser *);
unsigned int htparser_get_status(htparser *);
uint64_t htparser_get_content_length(htparser *);
uint64_t htparser_get_total_bytes_read(htparser *);
htpparse_error htparser_get_error(htparser *);
const char * htparser_get_strerror(htparser *);
void * htparser_get_userdata(htparser *);
void htparser_set_userdata(htparser *, void *);
void htparser_init(htparser *, htp_type);
htparser * htparser_new(void);
#endif

250
examples/htparse/test.c Normal file
View File

@ -0,0 +1,250 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include "htparse.h"
static int
_on_msg_start(htparser * p) {
printf("START {\n");
return 0;
}
static int
_on_msg_end(htparser * p) {
printf("}\n");
return 0;
}
static int
_path(htparser * p, const char * data, size_t len) {
printf("\tpath = '%.*s'\n", (int)len, data);
return 0;
}
static int
_method(htparser * p, const char * data, size_t len) {
printf("\tmethod = '%.*s'\n", (int)len, data);
return 0;
}
static int
_uri(htparser * p, const char * data, size_t len) {
printf("\turi = '%.*s'\n", (int)len, data);
return 0;
}
static int
_args(htparser * p, const char * data, size_t len) {
printf("\targs = '%.*s'\n", (int)len, data);
return 0;
}
static int
_hdrs_end(htparser * p) {
printf("\t}\n");
return 0;
}
static int
_hdrs_start(htparser * p) {
printf("\thdrs {\n");
return 0;
}
static int
_hdr_key(htparser * p, const char * data, size_t len) {
printf("\t\thdr_key = '%.*s'\n", (int)len, data);
return 0;
}
static int
_hdr_val(htparser * p, const char * data, size_t len) {
printf("\t\thdr_val = '%.*s'\n", (int)len, data);
return 0;
}
static int
_read_body(htparser * p, const char * data, size_t len) {
printf("\t'%.*s'\n", (int)len, data);
return 0;
}
static int
_on_new_chunk(htparser * p) {
printf("\t--chunk payload (%zu)--\n", htparser_get_content_length(p));
/* printf("..chunk..\n"); */
return 0;
}
static void
_test(htparser * p, htparse_hooks * hooks, const char * l, htp_type type) {
printf("---- test ----\n");
printf("%zu, %s\n", strlen(l), l);
htparser_init(p, type);
printf("%zu == %zu\n", htparser_run(p, hooks, l, strlen(l)), strlen(l));
if (htparser_get_error(p)) {
printf("ERROR: %s\n", htparser_get_strerror(p));
}
printf("\n");
}
static void
_test_fragments(htparser * p, htparse_hooks * hooks, const char ** fragments,
htp_type type) {
int i = 0;
printf("---- test fragment ----\n");
htparser_init(p, type);
while (1) {
const char * l = fragments[i++];
if (l == NULL) {
break;
}
htparser_run(p, hooks, l, strlen(l));
if (htparser_get_error(p)) {
printf("ERROR: %s\n", htparser_get_strerror(p));
}
}
printf("\n");
}
static const char * test_fragment_1[] = {
"GET \0",
" /fjdksf\0",
"jfkdslfds H\0",
"TTP/1.\0",
"1\r\0",
"\n\0",
"\r\0",
"\n\0",
NULL
};
static const char * test_fragment_2[] = {
"POST /\0",
"h?a=b HTTP/1.0\r\n\0",
"Content-Len\0",
"gth\0",
": 1\0",
"0\r\n\0",
"\r\n\0",
"12345\0",
"67890\0",
NULL
};
static const char * test_chunk_fragment_1[] = {
"POST /stupid HTTP/1.1\r\n",
"Transfer-Encoding: chunked\r\n",
"\r\n",
"25\r\n",
"This is the data in the first chunk\r\n",
"\r\n",
"1C\r\n",
"and this is the second one\r\n",
"\r\n",
"3\r\n",
"con\r\n",
"8\r\n",
"sequence\r\n",
"0\r\n",
"\r\n",
NULL
};
static const char * test_chunk_fragment_2[] = {
"POST /stupid HTTP/1.1\r\n",
"Transfer-Encoding: chunked\r\n",
"\r\n",
"25\r\n",
"This is the data in the first chunk\r\n",
"\r\n",
"1C\r\n",
"and this is the second one\r\n",
"\r\n",
"3\r\n",
"c",
"on\r\n",
"8\r\n",
"sequence\r\n",
"0\r\n",
"\r\n",
"GET /foo?bar/baz? HTTP/1.0\r\n",
"Host: stupid.com\r\n",
"\r\n",
NULL
};
int
main(int argc, char ** argv) {
htparser * p = htparser_new();
htparse_hooks hooks = {
.on_msg_begin = _on_msg_start,
.method = _method,
.scheme = NULL,
.host = NULL,
.port = NULL,
.path = _path,
.args = _args,
.uri = _uri,
.on_hdrs_begin = _hdrs_start,
.hdr_key = _hdr_key,
.hdr_val = _hdr_val,
.on_hdrs_complete = _hdrs_end,
.on_new_chunk = _on_new_chunk,
.on_chunk_complete = NULL,
.on_chunks_complete = NULL,
.body = _read_body,
.on_msg_complete = _on_msg_end
};
const char * test_1 = "GET / HTTP/1.0\r\n\r\n";
const char * test_2 = "GET /hi?a=b&c=d HTTP/1.1\r\n\r\n";
const char * test_3 = "GET /hi/die/?a=b&c=d HTTP/1.1\r\n\r\n";
const char * test_4 = "POST /fjdls HTTP/1.0\r\n"
"Content-Length: 4\r\n"
"\r\n"
"abcd";
const char * test_7 = "POST /derp HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n\r\n"
"1e\r\nall your base are belong to us\r\n"
"0\r\n"
"\r\n\0";
const char * test_8 = "GET /DIE HTTP/1.1\r\n"
"HERP: DE\r\n"
"\tRP\r\nthings:stuff\r\n\r\n";
const char * test_9 = "GET /big_content_len HTTP/1.1\r\n"
"Content-Length: 18446744073709551615\r\n\r\n";
const char * test_fail = "GET /JF HfD]\r\n\r\n";
const char * test_resp_1 = "HTTP/1.0 200 OK\r\n"
"Stuff: junk\r\n\r\n";
_test(p, &hooks, test_resp_1, htp_type_response);
_test(p, &hooks, test_1, htp_type_request);
_test(p, &hooks, test_2, htp_type_request);
_test(p, &hooks, test_3, htp_type_request);
_test(p, &hooks, test_4, htp_type_request);
_test(p, &hooks, test_7, htp_type_request);
_test(p, &hooks, test_8, htp_type_request);
_test(p, &hooks, test_9, htp_type_request);
_test(p, &hooks, test_fail, htp_type_request);
_test_fragments(p, &hooks, test_fragment_1, htp_type_request);
_test_fragments(p, &hooks, test_fragment_2, htp_type_request);
_test_fragments(p, &hooks, test_chunk_fragment_1, htp_type_request);
_test_fragments(p, &hooks, test_chunk_fragment_2, htp_type_request);
return 0;
} /* main */

454
examples/shrpx.cc Normal file
View File

@ -0,0 +1,454 @@
/*
* 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 <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <getopt.h>
#include <limits>
#include <cstdlib>
#include <iostream>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <event2/listener.h>
#include <spdylay/spdylay.h>
#include "shrpx_config.h"
#include "shrpx_listen_handler.h"
namespace shrpx {
namespace {
void ssl_acceptcb(evconnlistener *listener, int fd,
sockaddr *addr, int addrlen, void *arg)
{
ListenHandler *handler = reinterpret_cast<ListenHandler*>(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;
rv = getaddrinfo(get_config()->downstream_host, service, &hints, &res);
if(rv != 0) {
LOG(FATAL) << "Unable to get downstream address: " << gai_strerror(rv);
DIE();
}
char host[NI_MAXHOST];
rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host),
0, 0, NI_NUMERICHOST);
if(rv == 0) {
LOG(INFO) << "Using first returned address for downstream "
<< host
<< ", port "
<< get_config()->downstream_port;
} else {
LOG(FATAL) << gai_strerror(rv);
DIE();
}
memcpy(&mod_config()->downstream_addr, res->ai_addr, res->ai_addrlen);
mod_config()->downstream_addrlen = res->ai_addrlen;
freeaddrinfo(res);
return 0;
}
} // namespace
namespace {
void evlistener_errorcb(evconnlistener *listener, void *ptr)
{
LOG(ERROR) << "Accepting incoming connection failed";
}
} // namespace
namespace {
evconnlistener* create_evlistener(ListenHandler *handler, int family)
{
// 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 = family;
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) << "Unable to get address for " << get_config()->host << ": "
<< 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<socklen_t>(sizeof(val))) == -1) {
close(fd);
continue;
}
evutil_make_socket_nonblocking(fd);
#ifdef IPV6_V6ONLY
if(family == AF_INET6) {
if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
static_cast<socklen_t>(sizeof(val))) == -1) {
close(fd);
continue;
}
}
#endif // IPV6_V6ONLY
if(bind(fd, rp->ai_addr, rp->ai_addrlen) == 0) {
break;
}
close(fd);
}
if(rp) {
char host[NI_MAXHOST];
r = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host),
0, 0, NI_NUMERICHOST);
if(r == 0) {
LOG(INFO) << "Listening on " << host << ", port " << get_config()->port;
} else {
LOG(FATAL) << gai_strerror(r);
DIE();
}
}
freeaddrinfo(res);
if(rp == 0) {
if(ENABLE_LOG) {
LOG(INFO) << "Listening " << (family == AF_INET ? "IPv4" : "IPv6")
<< " socket failed";
}
return 0;
}
evconnlistener *evlistener = evconnlistener_new
(handler->get_evbase(),
ssl_acceptcb,
handler,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
512,
fd);
evconnlistener_set_error_cb(evlistener, evlistener_errorcb);
return evlistener;
}
} // namespace
namespace {
int event_loop()
{
event_base *evbase = event_base_new();
ListenHandler *listener_handler = new ListenHandler(evbase);
evconnlistener *evlistener6, *evlistener4;
evlistener6 = create_evlistener(listener_handler, AF_INET6);
evlistener4 = create_evlistener(listener_handler, AF_INET);
if(!evlistener6 && !evlistener4) {
LOG(FATAL) << "Failed to listen on address "
<< get_config()->host << ", port " << get_config()->port;
exit(EXIT_FAILURE);
}
if(get_config()->num_worker > 1) {
listener_handler->create_worker_thread(get_config()->num_worker);
}
if(ENABLE_LOG) {
LOG(INFO) << "Entering event loop";
}
event_base_loop(evbase, 0);
if(evlistener4) {
evconnlistener_free(evlistener4);
}
if(evlistener6) {
evconnlistener_free(evlistener6);
}
return 0;
}
} // namespace
namespace {
void fill_default_config()
{
mod_config()->daemon = false;
mod_config()->server_name = "shrpx spdylay/"SPDYLAY_VERSION;
mod_config()->host = "localhost";
mod_config()->port = 3000;
mod_config()->private_key_file = 0;
mod_config()->cert_file = 0;
mod_config()->upstream_read_timeout.tv_sec = 30;
mod_config()->upstream_read_timeout.tv_usec = 0;
mod_config()->upstream_write_timeout.tv_sec = 60;
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;
mod_config()->num_worker = 4;
mod_config()->spdy_max_concurrent_streams =
SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS;
}
} // namespace
namespace {
int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,
const char *hostport)
{
// host and port in |hostport| is separated by single ','.
const char *p = strchr(hostport, ',');
if(!p) {
std::cerr << "Invalid host, port: " << hostport << std::endl;
return -1;
}
size_t len = p-hostport;
if(hostlen < len+1) {
std::cerr << "Hostname too long: " << hostport << std::endl;
return -1;
}
memcpy(host, hostport, len);
host[len] = '\0';
errno = 0;
unsigned long d = strtoul(p+1, 0, 10);
if(errno == 0 && 1 <= d && d <= std::numeric_limits<uint16_t>::max()) {
*port_ptr = d;
return 0;
} else {
std::cerr << "Port is invalid: " << p+1 << std::endl;
return -1;
}
}
} // namespace
namespace {
void print_usage(std::ostream& out)
{
out << "Usage: shrpx [-Dh] [-b <HOST,PORT>] [-f <HOST,PORT>] [-n <CORES>]\n"
<< " [-c <NUM>] [-L <LEVEL>] <PRIVATE_KEY> <CERT>\n"
<< "\n"
<< "A reverse proxy for SPDY/HTTPS.\n"
<< std::endl;
}
} // namespace
namespace {
void print_help(std::ostream& out)
{
fill_default_config();
print_usage(out);
out << "\n"
<< "OPTIONS:\n"
<< " -b, --backend=<HOST,PORT>\n"
<< " Set backend host and port.\n"
<< " Default: '"
<< get_config()->downstream_host << ","
<< get_config()->downstream_port << "'\n"
<< " -f, --frontend=<HOST,PORT>\n"
<< " Set frontend host and port.\n"
<< " Default: '"
<< get_config()->host << "," << get_config()->port << "'\n"
<< " -n, --workers=<CORES>\n"
<< " Set the number of worker threads.\n"
<< " -c, --spdy-max-concurrent-streams=<NUM>\n"
<< " Set the maximum number of the concurrent\n"
<< " streams in one SPDY session.\n"
<< " -L, --log-level=<LEVEL>\n"
<< " Set the severity level of log output.\n"
<< " INFO, WARNING, ERROR and FATAL\n"
<< " -D, --daemon Run in a background. If -D is used, the\n"
<< " current working directory is changed to '/'.\n"
<< " -h, --help Print this help.\n"
<< std::endl;
}
} // namespace
int main(int argc, char **argv)
{
Log::set_severity_level(WARNING);
create_config();
fill_default_config();
char frontend_host[NI_MAXHOST];
uint16_t frontend_port;
char backend_host[NI_MAXHOST];
uint16_t backend_port;
while(1) {
static option long_options[] = {
{"backend", required_argument, 0, 'b' },
{"frontend", required_argument, 0, 'f' },
{"workers", required_argument, 0, 'n' },
{"spdy-max-concurrent-streams", required_argument, 0, 'c' },
{"log-level", required_argument, 0, 'L' },
{"daemon", no_argument, 0, 'D' },
{"help", no_argument, 0, 'h' },
{0, 0, 0, 0 }
};
int option_index = 0;
int c = getopt_long(argc, argv, "DL:b:c:f:n:h", long_options,
&option_index);
if(c == -1) {
break;
}
switch(c) {
case 'D':
mod_config()->daemon = true;
break;
case 'h':
print_help(std::cout);
exit(EXIT_SUCCESS);
case 'L':
if(Log::set_severity_level_by_name(optarg) == -1) {
std::cerr << "Invalid severity level: " << optarg << std::endl;
exit(EXIT_SUCCESS);
}
break;
case 'b':
if(split_host_port(backend_host, sizeof(backend_host),
&backend_port, optarg) == -1) {
exit(EXIT_FAILURE);
} else {
mod_config()->downstream_host = backend_host;
mod_config()->downstream_port = backend_port;
}
break;
case 'f':
if(split_host_port(frontend_host, sizeof(frontend_host),
&frontend_port, optarg) == -1) {
exit(EXIT_FAILURE);
} else {
mod_config()->host = frontend_host;
mod_config()->port = frontend_port;
}
break;
case 'n':
mod_config()->num_worker = strtol(optarg, 0, 10);
break;
case 'c':
mod_config()->spdy_max_concurrent_streams = strtol(optarg, 0, 10);
break;
case '?':
exit(EXIT_FAILURE);
default:
break;
}
}
if(argc-optind < 2) {
print_usage(std::cerr);
std::cerr << "Too few arguments" << std::endl;
exit(EXIT_FAILURE);
}
mod_config()->private_key_file = argv[optind++];
mod_config()->cert_file = argv[optind++];
char hostport[NI_MAXHOST];
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);
}
if(get_config()->daemon) {
if(daemon(0, 0) == -1) {
perror("daemon");
exit(EXIT_FAILURE);
}
}
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();
event_loop();
return 0;
}
} // namespace shrpx
int main(int argc, char **argv)
{
return shrpx::main(argc, argv);
}

39
examples/shrpx.h Normal file
View File

@ -0,0 +1,39 @@
/*
* 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 <config.h>
#endif // HAVE_CONFIG_H
#include <cassert>
#include "shrpx_log.h"
#define DIE() \
assert(0);
#endif // SHRPX_H

View File

@ -0,0 +1,216 @@
/*
* 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<ClientHandler*>(arg);
int rv = handler->on_read();
if(rv != 0) {
if(ENABLE_LOG) {
LOG(INFO) << "<upstream> Read operation (application level) failure";
}
delete handler;
}
}
} // namespace
namespace {
void upstream_writecb(bufferevent *bev, void *arg)
{
ClientHandler *handler = reinterpret_cast<ClientHandler*>(arg);
// We actually depend on write low-warter mark == 0.
if(handler->get_should_close_after_write()) {
delete handler;
} else {
Upstream *upstream = handler->get_upstream();
upstream->on_write();
}
}
} // namespace
namespace {
void upstream_eventcb(bufferevent *bev, short events, void *arg)
{
ClientHandler *handler = reinterpret_cast<ClientHandler*>(arg);
bool finish = false;
if(events & BEV_EVENT_EOF) {
if(ENABLE_LOG) {
LOG(INFO) << "Upstream handshake EOF";
}
finish = true;
}
if(events & BEV_EVENT_ERROR) {
if(ENABLE_LOG) {
LOG(INFO) << "Upstream network error";
}
finish = true;
}
if(events & BEV_EVENT_TIMEOUT) {
if(ENABLE_LOG) {
LOG(INFO) << "Upstream time out";
}
finish = true;
}
if(finish) {
delete handler;
} else {
if(events & BEV_EVENT_CONNECTED) {
if(ENABLE_LOG) {
LOG(INFO) << "Upstream 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) << "Upstream 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.";
}
}
if(ENABLE_LOG) {
LOG(INFO) << "Use HTTP/1.1";
}
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

View File

@ -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 <event.h>
#include <openssl/ssl.h>
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

65
examples/shrpx_config.cc Normal file
View File

@ -0,0 +1,65 @@
/*
* 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),
num_worker(0),
spdy_max_concurrent_streams(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

78
examples/shrpx_config.h Normal file
View File

@ -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.
*/
#ifndef SHRPX_CONFIG_H
#define SHRPX_CONFIG_H
#include "shrpx.h"
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
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;
size_t num_worker;
size_t spdy_max_concurrent_streams;
Config();
};
const Config* get_config();
Config* mod_config();
void create_config();
} // namespace shrpx
#endif // SHRPX_CONFIG_H

View File

@ -0,0 +1,489 @@
/*
* 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 <cassert>
#include "shrpx_upstream.h"
#include "shrpx_client_handler.h"
#include "shrpx_config.h"
#include "shrpx_error.h"
#include "shrpx_http.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),
ioctrl_(0),
request_state_(INITIAL),
request_major_(1),
request_minor_(1),
chunked_request_(false),
request_connection_close_(false),
response_state_(INITIAL),
response_http_status_(0),
response_major_(1),
response_minor_(1),
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);
ioctrl_.set_bev(bev_);
}
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";
}
}
void Downstream::pause_read(IOCtrlReason reason)
{
ioctrl_.pause_read(reason);
}
bool Downstream::resume_read(IOCtrlReason reason)
{
return ioctrl_.resume_read(reason);
}
void Downstream::force_resume_read()
{
ioctrl_.force_resume_read();
}
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;
}
void Downstream::set_request_major(int major)
{
request_major_ = major;
}
void Downstream::set_request_minor(int minor)
{
request_minor_ = minor;
}
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);
int rv = bufferevent_socket_connect
(bev_,
// TODO maybe not thread-safe?
const_cast<sockaddr*>(&get_config()->downstream_addr.sa),
get_config()->downstream_addrlen);
if(rv == 0) {
return 0;
} else {
return SHRPX_ERR_NETWORK;
}
}
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";
std::string via_value;
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(), "via")) {
via_value = (*i).second;
continue;
}
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 += "Via: ";
hdrs += via_value;
if(!via_value.empty()) {
hdrs += ", ";
}
hdrs += http::create_via_header_value(request_major_, request_minor_);
hdrs += "\r\n";
hdrs += "\r\n";
if(ENABLE_LOG) {
LOG(INFO) << "Downstream 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<unsigned int>(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;
}
void Downstream::set_response_major(int major)
{
response_major_ = major;
}
void Downstream::set_response_minor(int minor)
{
response_minor_ = minor;
}
int Downstream::get_response_major() const
{
return response_major_;
}
int Downstream::get_response_minor() const
{
return response_minor_;
}
bool Downstream::get_chunked_response() const
{
return chunked_response_;
}
namespace {
int htp_hdrs_completecb(htparser *htp)
{
Downstream *downstream;
downstream = reinterpret_cast<Downstream*>(htparser_get_userdata(htp));
downstream->set_response_http_status(htparser_get_status(htp));
downstream->set_response_major(htparser_get_major(htp));
downstream->set_response_minor(htparser_get_minor(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<Downstream*>(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<Downstream*>(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<Downstream*>(htparser_get_userdata(htp));
downstream->get_upstream()->on_downstream_body
(downstream, reinterpret_cast<const uint8_t*>(data), len);
return 0;
}
} // namespace
namespace {
int htp_body_completecb(htparser *htp)
{
Downstream *downstream;
downstream = reinterpret_cast<Downstream*>(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<const char*>(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) << "Downstream 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_;
}
namespace {
void body_buf_cb(evbuffer *body, size_t oldlen, size_t newlen, void *arg)
{
Downstream *downstream = reinterpret_cast<Downstream*>(arg);
if(newlen == 0) {
downstream->resume_read(SHRPX_NO_BUFFER);
}
}
} // namespace
int Downstream::init_response_body_buf()
{
if(!response_body_buf_) {
response_body_buf_ = evbuffer_new();
if(response_body_buf_ == 0) {
DIE();
}
evbuffer_setcb(response_body_buf_, body_buf_cb, this);
}
return 0;
}
evbuffer* Downstream::get_response_body_buf()
{
return response_body_buf_;
}
} // namespace shrpx

128
examples/shrpx_downstream.h Normal file
View File

@ -0,0 +1,128 @@
/*
* 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 <stdint.h>
#include <vector>
#include <string>
#include <event.h>
#include <event2/bufferevent.h>
extern "C" {
#include "htparse/htparse.h"
}
#include "shrpx_io_control.h"
namespace shrpx {
class Upstream;
typedef std::vector<std::pair<std::string, std::string> > 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;
void pause_read(IOCtrlReason reason);
bool resume_read(IOCtrlReason reason);
void force_resume_read();
// 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);
void set_request_major(int major);
void set_request_minor(int minor);
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,
CONNECT_FAIL
};
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);
void set_response_major(int major);
void set_response_minor(int minor);
int get_response_major() const;
int get_response_minor() const;
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_;
IOControl ioctrl_;
int request_state_;
std::string request_method_;
std::string request_path_;
int request_major_;
int request_minor_;
bool chunked_request_;
bool request_connection_close_;
Headers request_headers_;
int response_state_;
unsigned int response_http_status_;
int response_major_;
int response_minor_;
bool chunked_response_;
Headers response_headers_;
htparser *response_htp_;
// This buffer is used to temporarily store downstream response
// body. Spdylay reads data from this in the callback.
evbuffer *response_body_buf_;
};
} // namespace shrpx
#endif // SHRPX_DOWNSTREAM_H

View File

@ -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<int32_t, Downstream*>::iterator i = downstreams_.begin();
i != downstreams_.end(); ++i) {
delete (*i).second;
}
}
void DownstreamQueue::add(Downstream *downstream)
{
downstreams_[downstream->get_stream_id()] = downstream;
}
int DownstreamQueue::start(Downstream *downstream)
{
return downstream->start_connection();
}
void DownstreamQueue::remove(Downstream *downstream)
{
downstreams_.erase(downstream->get_stream_id());
}
Downstream* DownstreamQueue::find(int32_t stream_id)
{
std::map<int32_t, Downstream*>::iterator i = downstreams_.find(stream_id);
if(i == downstreams_.end()) {
return 0;
} else {
return (*i).second;
}
}
} // namespace shrpx

View File

@ -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 <stdint.h>
#include <map>
namespace shrpx {
class Downstream;
class DownstreamQueue {
public:
DownstreamQueue();
~DownstreamQueue();
void add(Downstream *downstream);
int start(Downstream *downstream);
void remove(Downstream *downstream);
Downstream* find(int32_t stream_id);
private:
std::map<int32_t, Downstream*> downstreams_;
};
} // namespace shrpx
#endif // SHRPX_DOWNSTREAM_QUEUE_H

41
examples/shrpx_error.h Normal file
View File

@ -0,0 +1,41 @@
/*
* 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
#include "shrpx.h"
namespace shrpx {
enum ErrorCode {
SHRPX_ERR_SUCCESS = 0,
SHRPX_ERR_UNKNOWN = -1,
SHRPX_ERR_HTTP_PARSE = -2,
SHRPX_ERR_NETWORK = -3
};
} // namespace shrpx
#endif // SHRPX_ERROR_H

108
examples/shrpx_http.cc Normal file
View File

@ -0,0 +1,108 @@
/*
* 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 <sstream>
#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 << "<html><head><title>" << status << "</title></head><body>"
<< "<h1>" << status << "</h1>"
<< "<hr>"
<< "<address>" << get_config()->server_name << " at port "
<< get_config()->port
<< "</address>"
<< "</body></html>\n";
return ss.str();
}
std::string create_via_header_value(int major, int minor)
{
std::string hdrs;
hdrs += static_cast<char>(major+'0');
hdrs += ".";
hdrs += static_cast<char>(minor+'0');
hdrs += " shrpx";
return hdrs;
}
} // namespace http
} // namespace shrpx

44
examples/shrpx_http.h Normal file
View File

@ -0,0 +1,44 @@
/*
* 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 <string>
namespace shrpx {
namespace http {
const char* get_status_string(int status_code);
std::string create_error_html(int status_code);
std::string create_via_header_value(int major, int minor);
} // namespace http
} // namespace shrpx
#endif // SHRPX_HTTP_H

View File

@ -0,0 +1,528 @@
/*
* 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 <cassert>
#include <set>
#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 {
namespace {
const size_t SHRPX_HTTPS_UPSTREAM_OUTPUT_UPPER_THRES = 512*1024;
const size_t SHRPX_HTTPS_MAX_HEADER_LENGTH = 64*1024;
} // namespace
HttpsUpstream::HttpsUpstream(ClientHandler *handler)
: handler_(handler),
htp_(htparser_new()),
current_header_length_(0),
ioctrl_(handler->get_bev())
{
htparser_init(htp_, htp_type_request);
htparser_set_userdata(htp_, this);
}
HttpsUpstream::~HttpsUpstream()
{
free(htp_);
for(std::deque<Downstream*>::iterator i = downstream_queue_.begin();
i != downstream_queue_.end(); ++i) {
delete *i;
}
}
void HttpsUpstream::reset_current_header_length()
{
current_header_length_ = 0;
}
namespace {
int htp_msg_begin(htparser *htp)
{
if(ENABLE_LOG) {
LOG(INFO) << "Upstream https request start";
}
HttpsUpstream *upstream;
upstream = reinterpret_cast<HttpsUpstream*>(htparser_get_userdata(htp));
upstream->reset_current_header_length();
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<HttpsUpstream*>(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<HttpsUpstream*>(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) << "Upstream https request headers start";
}
HttpsUpstream *upstream;
upstream = reinterpret_cast<HttpsUpstream*>(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<HttpsUpstream*>(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<HttpsUpstream*>(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) << "Upstream https request headers complete";
}
HttpsUpstream *upstream;
upstream = reinterpret_cast<HttpsUpstream*>(htparser_get_userdata(htp));
Downstream *downstream = upstream->get_last_downstream();
downstream->set_request_major(htparser_get_major(htp));
downstream->set_request_minor(htparser_get_minor(htp));
downstream->push_request_headers();
downstream->set_request_state(Downstream::HEADER_COMPLETE);
int rv = downstream->start_connection();
if(rv != 0) {
LOG(ERROR) << "Upstream connection failed";
downstream->set_request_state(Downstream::CONNECT_FAIL);
return 1;
}
return 0;
}
} // namespace
namespace {
int htp_bodycb(htparser *htp, const char *data, size_t len)
{
HttpsUpstream *upstream;
upstream = reinterpret_cast<HttpsUpstream*>(htparser_get_userdata(htp));
Downstream *downstream = upstream->get_last_downstream();
downstream->push_upload_data_chunk(reinterpret_cast<const uint8_t*>(data),
len);
return 0;
}
} // namespace
namespace {
int htp_msg_completecb(htparser *htp)
{
if(ENABLE_LOG) {
LOG(INFO) << "Upstream https request complete";
}
HttpsUpstream *upstream;
upstream = reinterpret_cast<HttpsUpstream*>(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
// on_read() does not consume all available data in input buffer if
// one http request is fully received.
int HttpsUpstream::on_read()
{
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<const char*>(mem),
evbuffer_get_length(input));
evbuffer_drain(input, nread);
// Well, actually header length + some body bytes
current_header_length_ += nread;
htpparse_error htperr = htparser_get_error(htp_);
if(htperr == htparse_error_user) {
Downstream *downstream = get_top_downstream();
if(downstream &&
downstream->get_request_state() == Downstream::CONNECT_FAIL) {
get_client_handler()->set_should_close_after_write(true);
error_reply(503);
} else if(current_header_length_ > SHRPX_HTTPS_MAX_HEADER_LENGTH) {
LOG(WARNING) << "Request Header too long:" << current_header_length_
<< " bytes";
get_client_handler()->set_should_close_after_write(true);
error_reply(400);
} else {
pause_read(SHRPX_MSG_BLOCK);
}
} else if(htperr != htparse_error_none) {
if(ENABLE_LOG) {
LOG(INFO) << "Upstream http parse failure: "
<< htparser_get_strerror(htp_);
}
get_client_handler()->set_should_close_after_write(true);
error_reply(400);
}
return 0;
}
int HttpsUpstream::on_write()
{
Downstream *downstream = get_top_downstream();
if(downstream) {
downstream->resume_read(SHRPX_NO_BUFFER);
}
return 0;
}
int HttpsUpstream::on_event()
{
return 0;
}
ClientHandler* HttpsUpstream::get_client_handler() const
{
return handler_;
}
void HttpsUpstream::pause_read(IOCtrlReason reason)
{
ioctrl_.pause_read(reason);
}
void HttpsUpstream::resume_read(IOCtrlReason reason)
{
if(ioctrl_.resume_read(reason)) {
// Process remaining data in input buffer here because these bytes
// are not notified by readcb until new data arrive.
on_read();
}
}
namespace {
void https_downstream_readcb(bufferevent *bev, void *ptr)
{
Downstream *downstream = reinterpret_cast<Downstream*>(ptr);
HttpsUpstream *upstream;
upstream = static_cast<HttpsUpstream*>(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(SHRPX_MSG_BLOCK);
} else {
ClientHandler *handler = upstream->get_client_handler();
bufferevent *bev = handler->get_bev();
size_t outputlen = evbuffer_get_length(bufferevent_get_output(bev));
if(outputlen > SHRPX_HTTPS_UPSTREAM_OUTPUT_UPPER_THRES) {
downstream->pause_read(SHRPX_NO_BUFFER);
}
}
} else {
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
delete upstream->get_client_handler();
} else {
upstream->error_reply(502);
assert(downstream == upstream->get_top_downstream());
upstream->pop_downstream();
delete downstream;
upstream->resume_read(SHRPX_MSG_BLOCK);
}
}
}
} // 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<Downstream*>(ptr);
HttpsUpstream *upstream;
upstream = static_cast<HttpsUpstream*>(downstream->get_upstream());
if(events & BEV_EVENT_CONNECTED) {
if(ENABLE_LOG) {
LOG(INFO) << "Downstream connection established. downstream "
<< downstream;
}
}
if(events & BEV_EVENT_EOF) {
if(ENABLE_LOG) {
LOG(INFO) << "Downstream 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 downstream 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 downstream error";
}
upstream->error_reply(502);
}
upstream->pop_downstream();
delete downstream;
upstream->resume_read(SHRPX_MSG_BLOCK);
} else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
if(ENABLE_LOG) {
LOG(INFO) << "Downstream 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(status);
}
upstream->pop_downstream();
delete downstream;
upstream->resume_read(SHRPX_MSG_BLOCK);
}
}
} // namespace
void HttpsUpstream::error_reply(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";
if(get_client_handler()->get_should_close_after_write()) {
ss << "Connection: close\r\n";
}
ss << "\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());
}
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()
{
if(downstream_queue_.empty()) {
return 0;
} else {
return downstream_queue_.front();
}
}
Downstream* HttpsUpstream::get_last_downstream()
{
if(downstream_queue_.empty()) {
return 0;
} else {
return downstream_queue_.back();
}
}
int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
{
if(ENABLE_LOG) {
LOG(INFO) << "Downstream on_downstream_header_complete";
}
std::string via_value;
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(), "via")) {
via_value = (*i).second;
} else {
hdrs += (*i).first;
hdrs += ": ";
hdrs += (*i).second;
hdrs += "\r\n";
}
}
if(get_client_handler()->get_should_close_after_write()) {
hdrs += "Connection: close\r\n";
}
hdrs += "Via: ";
hdrs += via_value;
if(!via_value.empty()) {
hdrs += ", ";
}
hdrs += http::create_via_header_value
(downstream->get_response_major(), downstream->get_response_minor());
hdrs += "\r\n";
hdrs += "\r\n";
if(ENABLE_LOG) {
LOG(INFO) << "Upstream https 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<unsigned int>(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) << "Downstream 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

View File

@ -0,0 +1,82 @@
/*
* 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 <stdint.h>
#include <deque>
extern "C" {
#include "htparse/htparse.h"
}
#include "shrpx_upstream.h"
#include "shrpx_io_control.h"
namespace shrpx {
class ClientHandler;
class HttpsUpstream : public Upstream {
public:
HttpsUpstream(ClientHandler *handler);
virtual ~HttpsUpstream();
virtual int on_read();
virtual int on_write();
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(int status_code);
void pause_read(IOCtrlReason reason);
void resume_read(IOCtrlReason reason);
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);
void reset_current_header_length();
private:
ClientHandler *handler_;
htparser *htp_;
size_t current_header_length_;
std::deque<Downstream*> downstream_queue_;
IOControl ioctrl_;
};
} // namespace shrpx
#endif // SHRPX_HTTPS_UPSTREAM_H

View File

@ -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_io_control.h"
#include <algorithm>
namespace shrpx {
IOControl::IOControl(bufferevent *bev)
: bev_(bev),
ctrlv_(SHRPX_REASON_MAX)
{}
IOControl::~IOControl()
{}
void IOControl::set_bev(bufferevent *bev)
{
bev_ = bev;
}
void IOControl::pause_read(IOCtrlReason reason)
{
ctrlv_[reason] = 1;
bufferevent_disable(bev_, EV_READ);
}
bool IOControl::resume_read(IOCtrlReason reason)
{
ctrlv_[reason] = 0;
if(std::find(ctrlv_.begin(), ctrlv_.end(), 1) == ctrlv_.end()) {
bufferevent_enable(bev_, EV_READ);
return true;
} else {
return false;
}
}
void IOControl::force_resume_read()
{
std::fill(ctrlv_.begin(), ctrlv_.end(), 0);
bufferevent_enable(bev_, EV_READ);
}
} // namespace shrpx

View File

@ -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_IO_CONTROL_H
#define SHRPX_IO_CONTROL_H
#include "shrpx.h"
#include <vector>
#include <event.h>
#include <event2/bufferevent.h>
namespace shrpx {
enum IOCtrlReason {
SHRPX_NO_BUFFER = 0,
SHRPX_MSG_BLOCK,
SHRPX_REASON_MAX
};
class IOControl {
public:
IOControl(bufferevent *bev);
~IOControl();
void set_bev(bufferevent *bev);
void pause_read(IOCtrlReason reason);
// Returns true if read operation is enabled after this call
bool resume_read(IOCtrlReason reason);
// Clear all pause flags and enable read
void force_resume_read();
private:
bufferevent *bev_;
std::vector<int> ctrlv_;
};
} // namespace shrpx
#endif // SHRPX_IO_CONTROL_H

View File

@ -0,0 +1,113 @@
/*
* 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 <pthread.h>
#include <cerrno>
#include <event2/bufferevent_ssl.h>
#include "shrpx_client_handler.h"
#include "shrpx_thread_event_receiver.h"
#include "shrpx_ssl.h"
#include "shrpx_worker.h"
namespace shrpx {
ListenHandler::ListenHandler(event_base *evbase)
: evbase_(evbase),
ssl_ctx_(ssl::create_ssl_context()),
worker_round_robin_cnt_(0),
workers_(0),
num_worker_(0)
{}
ListenHandler::~ListenHandler()
{}
void ListenHandler::create_worker_thread(size_t num)
{
workers_ = new WorkerInfo[num];
num_worker_ = 0;
for(size_t i = 0; i < num; ++i) {
int rv;
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
WorkerInfo *info = &workers_[num_worker_];
rv = socketpair(AF_UNIX, SOCK_STREAM, 0, info->sv);
if(rv == -1) {
LOG(ERROR) << "socketpair() failed: " << strerror(errno);
continue;
}
rv = pthread_create(&thread, &attr, start_threaded_worker, &info->sv[1]);
if(rv != 0) {
LOG(ERROR) << "pthread_create() failed: " << strerror(rv);
for(size_t j = 0; j < 2; ++j) {
close(info->sv[j]);
}
continue;
}
bufferevent *bev = bufferevent_socket_new(evbase_, info->sv[0],
BEV_OPT_DEFER_CALLBACKS);
info->bev = bev;
if(ENABLE_LOG) {
LOG(INFO) << "Created thread#" << num_worker_;
}
++num_worker_;
}
}
int ListenHandler::accept_connection(evutil_socket_t fd,
sockaddr *addr, int addrlen)
{
if(ENABLE_LOG) {
LOG(INFO) << "<listener> Accepted connection. fd=" << fd;
}
if(num_worker_ == 0) {
/*ClientHandler* client = */
ssl::accept_ssl_connection(evbase_, ssl_ctx_, fd, addr, addrlen);
} else {
size_t idx = worker_round_robin_cnt_ % num_worker_;
++worker_round_robin_cnt_;
WorkerEvent wev;
memset(&wev, 0, sizeof(wev));
wev.client_fd = fd;
memcpy(&wev.client_addr, addr, addrlen);
wev.client_addrlen = addrlen;
evbuffer *output = bufferevent_get_output(workers_[idx].bev);
evbuffer_add(output, &wev, sizeof(wev));
}
return 0;
}
event_base* ListenHandler::get_evbase() const
{
return evbase_;
}
} // namespace shrpx

View File

@ -0,0 +1,61 @@
/*
* 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 <sys/types.h>
#include <sys/socket.h>
#include <openssl/ssl.h>
#include <event.h>
namespace shrpx {
struct WorkerInfo {
int sv[2];
bufferevent *bev;
};
class ListenHandler {
public:
ListenHandler(event_base *evbase);
~ListenHandler();
int accept_connection(evutil_socket_t fd, sockaddr *addr, int addrlen);
void create_worker_thread(size_t num);
event_base* get_evbase() const;
private:
event_base *evbase_;
SSL_CTX *ssl_ctx_;
unsigned int worker_round_robin_cnt_;
WorkerInfo *workers_;
size_t num_worker_;
};
} // namespace shrpx
#endif // SHRPX_LISTEN_HANDLER_H

70
examples/shrpx_log.cc Normal file
View File

@ -0,0 +1,70 @@
/*
* 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 <cstdio>
#include <cstring>
namespace shrpx {
const char *SEVERITY_STR[] = {
"INFO", "WARN", "ERROR", "FATAL"
};
int Log::severity_thres_ = WARNING;
void Log::set_severity_level(int severity)
{
severity_thres_ = severity;
}
int Log::set_severity_level_by_name(const char *name)
{
for(size_t i = 0, max = sizeof(SEVERITY_STR)/sizeof(char*); i < max; ++i) {
if(strcmp(SEVERITY_STR[i], name) == 0) {
severity_thres_ = i;
return 0;
}
}
return -1;
}
Log::Log(int severity, const char *filename, int linenum)
: severity_(severity),
filename_(filename),
linenum_(linenum)
{}
Log::~Log()
{
if(severity_ >= severity_thres_) {
fprintf(stderr, "[%s] %s\n (%s, line %d)\n",
SEVERITY_STR[severity_], stream_.str().c_str(),
filename_, linenum_);
fflush(stderr);
}
}
} // namespace shrpx

63
examples/shrpx_log.h Normal file
View File

@ -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.
*/
#ifndef SHRPX_LOG_H
#define SHRPX_LOG_H
#include "shrpx.h"
#include <sstream>
namespace shrpx {
#define ENABLE_LOG 1
#define LOG(SEVERITY) Log(SEVERITY, __FILE__, __LINE__)
enum SeverityLevel {
INFO, WARNING, ERROR, FATAL
};
class Log {
public:
Log(int severity, const char *filename, int linenum);
~Log();
template<typename Type> Log& operator<<(Type s)
{
stream_ << s;
return *this;
}
static void set_severity_level(int severity);
static int set_severity_level_by_name(const char *name);
private:
int severity_;
const char *filename_;
int linenum_;
std::stringstream stream_;
static int severity_thres_;
};
} // namespace shrpx
#endif // SHRPX_LOG_H

View File

@ -0,0 +1,561 @@
/*
* 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 <assert.h>
#include <sstream>
#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 {
const size_t SHRPX_SPDY_UPSTREAM_OUTPUT_UPPER_THRES = 512*1024;
} // namespace
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<SpdyUpstream*>(user_data);
ClientHandler *handler = upstream->get_client_handler();
bufferevent *bev = handler->get_bev();
evbuffer *output = bufferevent_get_output(bev);
// Check buffer length and return WOULDBLOCK if it is large enough.
if(evbuffer_get_length(output) > SHRPX_SPDY_UPSTREAM_OUTPUT_UPPER_THRES) {
return SPDYLAY_ERR_WOULDBLOCK;
}
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<SpdyUpstream*>(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) << "Upstream spdy Stream " << stream_id << " is being closed";
}
SpdyUpstream *upstream = reinterpret_cast<SpdyUpstream*>(user_data);
Downstream *downstream = upstream->get_downstream_queue()->find(stream_id);
if(downstream) {
if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {
upstream->get_downstream_queue()->remove(downstream);
delete downstream;
} else {
downstream->set_request_state(Downstream::STREAM_CLOSED);
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
upstream->get_downstream_queue()->remove(downstream);
delete downstream;
} else {
// At this point, downstream read may be paused. To reclaim
// file descriptor, enable read here and catch read
// notification. And delete downstream there.
downstream->force_resume_read();
}
}
}
}
} // namespace
namespace {
void on_ctrl_recv_callback
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data)
{
SpdyUpstream *upstream = reinterpret_cast<SpdyUpstream*>(user_data);
switch(type) {
case SPDYLAY_SYN_STREAM: {
if(ENABLE_LOG) {
LOG(INFO) << "Upstream spdy 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) << "Upstream spdy 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) << "Upstream spdy "
<< "Setting Downstream::MSG_COMPLETE for Downstream "
<< downstream;
}
downstream->set_request_state(Downstream::MSG_COMPLETE);
}
upstream->add_downstream(downstream);
if(upstream->start_downstream(downstream) != 0) {
// If downstream connection fails, issue RST_STREAM.
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
downstream->set_request_state(Downstream::CONNECT_FAIL);
}
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) << "Upstream spdy received upstream DATA data stream_id="
<< stream_id;
}
SpdyUpstream *upstream = reinterpret_cast<SpdyUpstream*>(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) << "Upstream spdy "
<< "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::on_write()
{
send();
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<Downstream*>(ptr);
SpdyUpstream *upstream;
upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());
// If upstream SPDY stream was closed, we just close downstream,
// because there is no consumer now.
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
upstream->remove_downstream(downstream);
delete downstream;
return;
}
int rv = downstream->parse_http_response();
if(rv != 0) {
if(ENABLE_LOG) {
LOG(INFO) << "Downstream 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();
}
} // 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<Downstream*>(ptr);
SpdyUpstream *upstream;
upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());
if(events & BEV_EVENT_CONNECTED) {
if(ENABLE_LOG) {
LOG(INFO) << "Downstream connection established. Downstream "
<< downstream;
}
}
if(events & BEV_EVENT_EOF) {
if(ENABLE_LOG) {
LOG(INFO) << "Downstream EOF stream_id="
<< downstream->get_stream_id();
}
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
//If stream was closed already, we don't need to send reply at
// the first place. We can delete downstream.
upstream->remove_downstream(downstream);
delete downstream;
} else {
// downstream wil be deleted in on_stream_close_callback.
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
// Server may indicate the end of the request by EOF
if(ENABLE_LOG) {
LOG(INFO) << "Assuming downstream content-length is 0 byte";
}
downstream->set_response_state(Downstream::MSG_COMPLETE);
upstream->on_downstream_body_complete(downstream);
} else if(downstream->get_response_state() != Downstream::MSG_COMPLETE) {
// If stream was not closed, then we set MSG_COMPLETE and let
// on_stream_close_callback delete downstream.
upstream->error_reply(downstream, 502);
downstream->set_response_state(Downstream::MSG_COMPLETE);
upstream->send();
}
}
} else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
if(ENABLE_LOG) {
LOG(INFO) << "Downstream error/timeout. Downstream " << downstream;
}
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
upstream->remove_downstream(downstream);
delete downstream;
} else {
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);
}
downstream->set_response_state(Downstream::MSG_COMPLETE);
upstream->send();
}
}
}
}
} // 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<Downstream*>(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);
}
int SpdyUpstream::start_downstream(Downstream *downstream)
{
return 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) << "Downstream 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 {
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) << "Upstream spdy 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;
return 0;
}
int SpdyUpstream::on_downstream_body(Downstream *downstream,
const uint8_t *data, size_t len)
{
if(ENABLE_LOG) {
LOG(INFO) << "Downstream on_downstream_body";
}
evbuffer *body = downstream->get_response_body_buf();
evbuffer_add(body, data, len);
spdylay_session_resume_data(session_, downstream->get_stream_id());
size_t bodylen = evbuffer_get_length(body);
if(bodylen > SHRPX_SPDY_UPSTREAM_OUTPUT_UPPER_THRES) {
downstream->pause_read(SHRPX_NO_BUFFER);
}
return 0;
}
int SpdyUpstream::on_downstream_body_complete(Downstream *downstream)
{
if(ENABLE_LOG) {
LOG(INFO) << "Downstream on_downstream_body_complete";
}
spdylay_session_resume_data(session_, downstream->get_stream_id());
return 0;
}
DownstreamQueue* SpdyUpstream::get_downstream_queue()
{
return &downstream_queue_;
}
} // namespace shrpx

View File

@ -0,0 +1,72 @@
/*
* 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 <spdylay/spdylay.h>
#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_write();
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);
int 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

142
examples/shrpx_ssl.cc Normal file
View File

@ -0,0 +1,142 @@
/*
* 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_ssl.h"
#include <sys/socket.h>
#include <netdb.h>
#include <event2/bufferevent.h>
#include <event2/bufferevent_ssl.h>
#include "shrpx_log.h"
#include "shrpx_client_handler.h"
#include "shrpx_config.h"
namespace shrpx {
namespace ssl {
namespace {
std::pair<unsigned char*, size_t> 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<unsigned char*, size_t> *next_proto =
reinterpret_cast<std::pair<unsigned char*, size_t>* >(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
SSL_CTX* create_ssl_context()
{
SSL_CTX *ssl_ctx;
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
if(!ssl_ctx) {
LOG(FATAL) << ERR_error_string(ERR_get_error(), 0);
DIE();
}
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_ENABLE_PARTIAL_WRITE);
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) {
LOG(FATAL) << "SSL_CTX_use_PrivateKey_file failed.";
DIE();
}
if(SSL_CTX_use_certificate_file(ssl_ctx, get_config()->cert_file,
SSL_FILETYPE_PEM) != 1) {
LOG(FATAL) << "SSL_CTX_use_certificate_file failed.";
DIE();
}
if(SSL_CTX_check_private_key(ssl_ctx) != 1) {
LOG(FATAL) << "SSL_CTX_check_private_key failed.";
DIE();
}
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;
}
ClientHandler* accept_ssl_connection(event_base *evbase, SSL_CTX *ssl_ctx,
evutil_socket_t fd,
sockaddr *addr, int addrlen)
{
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);
if(!ssl) {
LOG(ERROR) << "SSL_new() failed";
return 0;
}
bufferevent *bev = bufferevent_openssl_socket_new
(evbase, fd, ssl,
BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_DEFER_CALLBACKS);
ClientHandler *client_handler = new ClientHandler(bev, ssl, host);
return client_handler;
} else {
LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv);
return 0;
}
}
} // namespace ssl
} // namespace shrpx

51
examples/shrpx_ssl.h Normal file
View File

@ -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_SSL_H
#define SHRPX_SSL_H
#include "shrpx.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <event.h>
namespace shrpx {
class ClientHandler;
namespace ssl {
SSL_CTX* create_ssl_context();
ClientHandler* accept_ssl_connection(event_base *evbase, SSL_CTX *ssl_ctx,
evutil_socket_t fd,
sockaddr *addr, int addrlen);
} // namespace ssl
} // namespace shrpx
#endif // SHRPX_SSL_H

View File

@ -0,0 +1,69 @@
/*
* 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_thread_event_receiver.h"
#include "shrpx_ssl.h"
#include "shrpx_log.h"
#include "shrpx_client_handler.h"
namespace shrpx {
ThreadEventReceiver::ThreadEventReceiver(SSL_CTX *ssl_ctx)
: ssl_ctx_(ssl_ctx)
{}
ThreadEventReceiver::~ThreadEventReceiver()
{}
void ThreadEventReceiver::on_read(bufferevent *bev)
{
evbuffer *input = bufferevent_get_input(bev);
while(evbuffer_get_length(input) >= sizeof(WorkerEvent)) {
WorkerEvent wev;
evbuffer_remove(input, &wev, sizeof(WorkerEvent));
if(ENABLE_LOG) {
LOG(INFO) << "WorkerEvent: client_fd=" << wev.client_fd
<< ", addrlen=" << wev.client_addrlen;
}
event_base *evbase = bufferevent_get_base(bev);
ClientHandler *client_handler;
client_handler = ssl::accept_ssl_connection(evbase, ssl_ctx_,
wev.client_fd,
&wev.client_addr.sa,
wev.client_addrlen);
if(client_handler) {
if(ENABLE_LOG) {
LOG(INFO) << "ClientHandler " << client_handler << " created";
}
} else {
if(ENABLE_LOG) {
LOG(ERROR) << "ClientHandler creation failed";
}
close(wev.client_fd);
}
}
}
} // namespace shrpx

View File

@ -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_THREAD_EVENT_RECEIVER_H
#define SHRPX_THREAD_EVENT_RECEIVER_H
#include "shrpx.h"
#include <openssl/ssl.h>
#include <event2/bufferevent.h>
#include "shrpx_config.h"
namespace shrpx {
struct WorkerEvent {
evutil_socket_t client_fd;
sockaddr_union client_addr;
size_t client_addrlen;
};
class ThreadEventReceiver {
public:
ThreadEventReceiver(SSL_CTX *ssl_ctx);
~ThreadEventReceiver();
void on_read(bufferevent *bev);
private:
SSL_CTX *ssl_ctx_;
};
} // namespace shrpx
#endif // SHRPX_THREAD_EVENT_RECEIVER_H

56
examples/shrpx_upstream.h Normal file
View File

@ -0,0 +1,56 @@
/*
* 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 <event2/bufferevent.h>
namespace shrpx {
class ClientHandler;
class Downstream;
class Upstream {
public:
virtual ~Upstream() {}
virtual int on_read() = 0;
virtual int on_write() = 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

93
examples/shrpx_worker.cc Normal file
View File

@ -0,0 +1,93 @@
/*
* 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_worker.h"
#include <unistd.h>
#include <sys/socket.h>
#include <event.h>
#include <event2/bufferevent.h>
#include "shrpx_ssl.h"
#include "shrpx_thread_event_receiver.h"
#include "shrpx_log.h"
namespace shrpx {
Worker::Worker(int fd)
: fd_(fd),
ssl_ctx_(ssl::create_ssl_context())
{}
Worker::~Worker()
{
SSL_CTX_free(ssl_ctx_);
shutdown(fd_, SHUT_WR);
close(fd_);
}
namespace {
void readcb(bufferevent *bev, void *arg)
{
ThreadEventReceiver *receiver = reinterpret_cast<ThreadEventReceiver*>(arg);
receiver->on_read(bev);
}
} // namespace
namespace {
void eventcb(bufferevent *bev, short events, void *arg)
{
if(events & BEV_EVENT_EOF) {
LOG(ERROR) << "Connection to main thread lost: eof";
}
if(events & BEV_EVENT_ERROR) {
LOG(ERROR) << "Connection to main thread lost: network error";
}
}
} // namespace
void Worker::run()
{
event_base *evbase = event_base_new();
bufferevent *bev = bufferevent_socket_new(evbase, fd_,
BEV_OPT_DEFER_CALLBACKS);
ThreadEventReceiver *receiver = new ThreadEventReceiver(ssl_ctx_);
bufferevent_enable(bev, EV_READ);
bufferevent_setcb(bev, readcb, 0, eventcb, receiver);
event_base_loop(evbase, 0);
delete receiver;
}
void* start_threaded_worker(void *arg)
{
int fd = *reinterpret_cast<int*>(arg);
Worker worker(fd);
worker.run();
return 0;
}
} // namespace shrpx

50
examples/shrpx_worker.h Normal file
View File

@ -0,0 +1,50 @@
/*
* 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_WORKER_H
#define SHRPX_WORKER_H
#include "shrpx.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
namespace shrpx {
class Worker {
public:
Worker(int fd);
~Worker();
void run();
private:
// Channel to the main thread
int fd_;
SSL_CTX *ssl_ctx_;
};
void* start_threaded_worker(void *arg);
} // namespace shrpx
#endif // SHRPX_WORKER_H

View File

@ -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

View File

@ -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

View File

@ -1815,6 +1815,8 @@ int spdylay_session_on_rst_stream_received(spdylay_session *session,
if(!spdylay_session_check_version(session, frame->rst_stream.hd.version)) {
return 0;
}
spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_RST_STREAM,
frame);
if(session->server &&
!spdylay_session_is_my_stream_id(session, frame->rst_stream.stream_id) &&
frame->rst_stream.status_code == SPDYLAY_CANCEL) {