Merge branch 'rproxy'
This commit is contained in:
commit
673f9c7df0
52
README.rst
52
README.rst
|
@ -41,11 +41,16 @@ needed:
|
||||||
* OpenSSL >= 1.0.1
|
* OpenSSL >= 1.0.1
|
||||||
|
|
||||||
To enable ``-a`` option (getting linked assets from the downloaded
|
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:
|
packages are needed:
|
||||||
|
|
||||||
* libxml2 >= 2.7.7
|
* 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
|
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
|
required in order to build and run these programs. At the time of
|
||||||
this writing, the OpenSSL 1.0.1 supports NPN.
|
this writing, the OpenSSL 1.0.1 supports NPN.
|
||||||
|
|
||||||
|
Spdycat - SPDY client
|
||||||
|
+++++++++++++++++++++
|
||||||
|
|
||||||
The SPDY client is called ``spdycat``. It is a dead simple downloader
|
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
|
like wget/curl. It connects to SPDY server and gets resources given in
|
||||||
the command-line::
|
the command-line::
|
||||||
|
@ -154,6 +162,9 @@ the command-line::
|
||||||
[ 0.077] send GOAWAY frame <version=3, flags=0, length=8>
|
[ 0.077] send GOAWAY frame <version=3, flags=0, length=8>
|
||||||
(last_good_stream_id=0)
|
(last_good_stream_id=0)
|
||||||
|
|
||||||
|
Spdyd - SPDY server
|
||||||
|
+++++++++++++++++++
|
||||||
|
|
||||||
SPDY server is called ``spdyd`` and serves static files. It is single
|
SPDY server is called ``spdyd`` and serves static files. It is single
|
||||||
threaded and multiplexes connections using non-blocking socket. The
|
threaded and multiplexes connections using non-blocking socket. The
|
||||||
static files are read using blocking I/O system call, read(2). It
|
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``.
|
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
|
There is another SPDY server called ``spdynative``, which is
|
||||||
`node.native <https://github.com/d5/node.native>`_ style simple SPDY
|
`node.native <https://github.com/d5/node.native>`_ style simple SPDY
|
||||||
server::
|
server::
|
||||||
|
|
12
configure.ac
12
configure.ac
|
@ -95,6 +95,17 @@ if test "x${have_openssl}" = "xno"; then
|
||||||
AC_MSG_NOTICE([The example programs will not be built.])
|
AC_MSG_NOTICE([The example programs will not be built.])
|
||||||
fi
|
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)
|
# libxml2 (for examples/spdycat)
|
||||||
AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no])
|
AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no])
|
||||||
if test "x${have_libxml2}" = "xyes"; then
|
if test "x${have_libxml2}" = "xyes"; then
|
||||||
|
@ -178,5 +189,6 @@ AC_MSG_NOTICE([summary of build options:
|
||||||
CUnit: ${have_cunit}
|
CUnit: ${have_cunit}
|
||||||
OpenSSL: ${have_openssl}
|
OpenSSL: ${have_openssl}
|
||||||
Libxml2: ${have_libxml2}
|
Libxml2: ${have_libxml2}
|
||||||
|
Libevent(SSL): ${have_libevent_openssl}
|
||||||
Examples: ${enable_examples}
|
Examples: ${enable_examples}
|
||||||
])
|
])
|
||||||
|
|
|
@ -2,3 +2,4 @@ spdycat
|
||||||
spdyd
|
spdyd
|
||||||
spdynative
|
spdynative
|
||||||
spdycli
|
spdycli
|
||||||
|
shrpx
|
||||||
|
|
|
@ -25,12 +25,16 @@ if ENABLE_EXAMPLES
|
||||||
|
|
||||||
AM_CFLAGS = -Wall
|
AM_CFLAGS = -Wall
|
||||||
AM_CPPFLAGS = -Wall -I$(srcdir)/../lib/includes -I$(builddir)/../lib/includes \
|
AM_CPPFLAGS = -Wall -I$(srcdir)/../lib/includes -I$(builddir)/../lib/includes \
|
||||||
@OPENSSL_CFLAGS@ @XML_CPPFLAGS@ @DEFS@
|
@OPENSSL_CFLAGS@ @XML_CPPFLAGS@ @LIBEVENT_OPENSSL_CFLAGS@ @DEFS@
|
||||||
AM_LDFLAGS = @OPENSSL_LIBS@ @XML_LIBS@
|
AM_LDFLAGS = @OPENSSL_LIBS@ @XML_LIBS@ @LIBEVENT_OPENSSL_LIBS@
|
||||||
LDADD = $(top_builddir)/lib/libspdylay.la
|
LDADD = $(top_builddir)/lib/libspdylay.la
|
||||||
|
|
||||||
bin_PROGRAMS = spdycat spdyd
|
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_OBJECTS = uri.cc util.cc spdylay_ssl.cc
|
||||||
HELPER_HFILES = uri.h util.h spdylay_ssl.h
|
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} \
|
${SPDY_SERVER_OBJECTS} ${SPDY_SERVER_HFILES} \
|
||||||
spdyd.cc
|
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
|
noinst_PROGRAMS = spdycli
|
||||||
spdycli_SOURCES = spdycli.c
|
spdycli_SOURCES = spdycli.c
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -169,6 +169,21 @@ bool strieq(const char *a, const char *b)
|
||||||
return !*a && !*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 util
|
||||||
|
|
||||||
} // namespace spdylay
|
} // namespace spdylay
|
||||||
|
|
|
@ -235,6 +235,8 @@ bool endsWith(const std::string& a, const std::string& b);
|
||||||
|
|
||||||
bool strieq(const char *a, const char *b);
|
bool strieq(const char *a, const char *b);
|
||||||
|
|
||||||
|
bool strifind(const char *a, const char *b);
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
||||||
} // namespace spdylay
|
} // namespace spdylay
|
||||||
|
|
|
@ -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)) {
|
if(!spdylay_session_check_version(session, frame->rst_stream.hd.version)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_RST_STREAM,
|
||||||
|
frame);
|
||||||
if(session->server &&
|
if(session->server &&
|
||||||
!spdylay_session_is_my_stream_id(session, frame->rst_stream.stream_id) &&
|
!spdylay_session_is_my_stream_id(session, frame->rst_stream.stream_id) &&
|
||||||
frame->rst_stream.status_code == SPDYLAY_CANCEL) {
|
frame->rst_stream.status_code == SPDYLAY_CANCEL) {
|
||||||
|
|
Loading…
Reference in New Issue