1
0
mirror of https://github.com/facebook/proxygen.git synced 2025-04-19 01:04:16 +03:00

Initial code import

This commit is contained in:
dcsommer 2014-11-04 15:28:47 -08:00 committed by Daniel Sommermann
parent 227f8bb2ee
commit 97546cd0f2
310 changed files with 51597 additions and 0 deletions

30
.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
*~
\#*\#
# downloaded dependencies
/proxygen/fbthrift/
/proxygen/lib/test/gmock*
# autoreconf artifacts
Makefile.in
/proxygen/aclocal.m4
/proxygen/autom4te.cache/
/proxygen/build-aux/
/proxygen/config.guess
/proxygen/config.hin
/proxygen/configure
# Configure artifacts
.deps
.dirstamp
_configs.sed
Makefile
# Build artifacts
.libs
*.o
*.lo
*.a
*.la
gen-cpp
gen-cpp2

9
.travis.yml Normal file
View File

@ -0,0 +1,9 @@
language: cpp
compiler: gcc
install:
- sudo apt-get update -qq
before_script:
- cd proxygen
- ./deps.sh
script:
- make check

44
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,44 @@
# Contributing to Proxygen
Here's a quick rundown of how to contribute to this project.
## Our Development Process
We develop on a private branch internally at Facebook. We regularly update
this github project with the changes from the internal repo. External pull
requests are cherry-picked into our repo and then pushed back out.
## Pull Requests
We actively welcome your pull requests.
1. Fork the repo and create your branch from `master`.
1. If you've added code that should be tested, add tests
1. If you've changed APIs, update the documentation.
1. Ensure the test suite passes.
1. Make sure your code lints.
1. If you haven't already, complete the Contributor License Agreement ("CLA").
## Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You
only need
to do this once to work on any of Facebook's open source projects.
Complete your CLA here: <https://code.facebook.com/cla>
## Issues
We use GitHub issues to track public bugs. Please ensure your description
is clear and has sufficient instructions to be able to reproduce the issue.
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for
the safe disclosure of security bugs. In those cases, please go through
the process outlined on that page and do not file a public issue.
## Coding Style
* 2 spaces for indentation rather than tabs
* 80 character line length
* Use `Type* foo` not `Type *foo`.
* Align parameters passed to functions.
* Prefer `folly::make_unique<Foo>` to `new Foo`. In general, we discourage
use of raw `new` or `delete`.
## License
By contributing to Proxygen, you agree that your contributions will be
licensed under its BSD license.

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

1252
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

30
LICENSE Normal file
View File

@ -0,0 +1,30 @@
BSD License
For Proxygen software
Copyright (c) 2014, Facebook, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* 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.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.

23
PATENTS Normal file
View File

@ -0,0 +1,23 @@
Additional Grant of Patent Rights
"Software" means the Proxygen software distributed by Facebook, Inc.
Facebook hereby grants you a perpetual, worldwide, royalty-free, non-exclusive,
irrevocable (subject to the termination provision below) license under any
rights in any patent claims owned by Facebook, to make, have made, use, sell,
offer to sell, import, and otherwise transfer the Software. For avoidance of
doubt, no license is granted under Facebooks rights in any patent claims that
are infringed by (i) modifications to the Software made by you or a third party,
or (ii) the Software in combination with any software or other technology
provided by you or a third party.
The license granted hereunder will terminate, automatically and without notice,
for anyone that makes any claim (including by filing any lawsuit, assertion or
other action) alleging (a) direct, indirect, or contributory infringement or
inducement to infringe any patent: (i) by Facebook or any of its subsidiaries or
affiliates, whether or not such claim is related to the Software, (ii) by any
party if such claim arises in whole or in part from any software, product or
service of Facebook or any of its subsidiaries or affiliates, whether or not
such claim is related to the Software, or (iii) by any party relating to the
Software; or (b) that any right in any patent claim of Facebook is invalid or
unenforceable.

141
README.md Normal file
View File

@ -0,0 +1,141 @@
## Proxygen: Facebook's C++ HTTP Libraries
This project comprises the core C++ HTTP abstractions used at
Facebook. Internally, it is used as the basis for building many HTTP
servers, proxies, and clients. This release focuses on the common HTTP
abstractions and our simple HTTPServer framework. Future releases will
provide simple client APIs as well. The framework supports HTTP/1.1,
SPDY/3, and SPDY/3.1. HTTP/2 support is in progress. The goal is to
provide a simple, performant, and modern C++ HTTP library.
We have a Google group for general discussions at https://groups.google.com/d/forum/facebook-proxygen.
Build Status: [![Build Status](https://travis-ci.org/facebook/proxygen.png?branch=master)](https://travis-ci.org/facebook/proxygen)
### Installing
Note that currently this project has only been tested on Ubuntu 14.04,
although it likely works on many other platforms. Support for Mac OSX is
incomplete.
You will need at least 2 GiB of memory to compile proxygen and its
dependencies.
##### Easy Install
Just run `./deps.sh` from the `proxygen/` directory to get all the
dependencies and build proxygen. This will also install folly, fbthrift,
and proxygen on your machine.
A note on compatibility: this project relies on system installed fbthrift
and folly. If you rebase proxygen and `make` starts to fail, you likely
need to update to the latest version of fbthrift and folly. First try to
uninstall, rebase, and reinstall fbthrift and folly. We are still working
on a solution to make upgrading less painful.
For now, you can rerun `./deps.sh` after fetching and rebasing
proxygen. This will update the installed fbthrift and folly libraries to
the correct (latest) version.
##### Other Platforms
If you are running on another platform, you may need to install several
packages first. Proxygen, fbthrift, and folly are all autotools based projects.
### Introduction
Directory structure and contents:
* `proxygen/external/` Contains non-installed 3rd-party code proxygen depends on.
* `proxygen/lib/` Core networking abstractions.
* `proxygen/lib/http/` HTTP specific code.
* `proxygen/lib/services/` Connection management and server code.
* `proxygen/lib/ssl/` TLS abstractions and OpenSSL wrappers.
* `proxygen/lib/utils/` Miscellaneous helper code.
* `proxygen/httpserver/` Contains code wrapping `proxygen/lib/` for building simple C++ http servers. We recommend building on top of these APIs.
### Architecture
The central abstractions to understand in `proxygen/lib` are the session, codec,
transaction, and handler. These are the lowest level abstractions, and we
don't generally recommend building off of these directly.
When bytes are read off the wire, the `HTTPCodec` stored inside
`HTTPSession` parses these into higher level objects and associates with
it a transaction identifier. The codec then calls into `HTTPSession` which
is responsible for maintaining the mapping between transaction identifier
and `HTTPTransaction` objects. Each HTTP request/response pair has a
separate `HTTPTransaction` object. Finally, `HTTPTransaction` forwards the
call to a handler object which implements `HTTPTransation::Handler`. The
handler is responsible for implementing business logic for the request or
response.
The handler then calls back into the transaction to generate egress
(whether the egress is a request or response). The call flows from the
transaction back to the session, which uses the codec to convert the
higher level semantics of the particular call into the appropriate bytes
to send on the wire.
The same handler and transaction interfaces are used to both create requests
and handle responses. The API is generic enough to allow
both. `HTTPSession` is specialized slightly differently depending on
whether you are using the connection to issue or respond to HTTP
requests.
![Core Proxygen Architecture](CoreProxygenArchitecture.png)
Moving into higher levels of abstraction, `proxygen/httpserver` has a
simpler set of APIs and is the recommended way to interface with proxygen
when acting as a server if you don't need the full control of the lower
level abstractions.
The basic components here are `HTTPServer`, `RequestHandlerFactory`, and
`RequestHandler`. An `HTTPServer` takes some configuration and is given a
`RequestHandlerFactory`. Once the server is started, the installed
`RequestHandlerFactory` spawns a `RequestHandler` for each HTTP
request. `RequestHandler` is a simple interface users of the library
implement. Subclasses of `RequestHandler` should use the inherited
protected member `ResponseHandler* downstream_` to send the response.
### Using it
Proxygen is a library. After installing it, you can build your own C++
server. Try `cd`ing to the directory containing the echo server at
`proxygen/httpserver/samples/echo/`. You can then build it with this one
liner:
<code>
g++ -std=c++11 -o my_echo EchoServer.cpp EchoHandler.cpp -lproxygenhttpserver -lfolly -lglog -lgflags -pthread
</code>
After running `./my_echo`, we can verify it works using curl in a different terminal:
```shell
$ curl -v http://localhost:11000/
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:11000
> Accept: */*
>
< HTTP/1.1 200 OK
< Request-Number: 1
< Date: Thu, 30 Oct 2014 17:07:36 GMT
< Connection: keep-alive
< Content-Length: 0
<
* Connection #0 to host localhost left intact
```
### Documentation
We use Doxygen for Proxygen's internal documentation. You can generate a
copy of these docs by running `doxygen Doxyfile` from the project
root. You'll want to look at `html/namespaceproxygen.html` to start. This
will also generate folly and thrift documentation.
### Whitehat
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for
the safe disclosure of security bugs. If you find a vulnerability, please
go through the process outlined on that page and do not file a public issue.

1
proxygen/Makefile.am Normal file
View File

@ -0,0 +1 @@
SUBDIRS = lib httpserver

1
proxygen/VERSION Normal file
View File

@ -0,0 +1 @@
1

264
proxygen/configure.ac Normal file
View File

@ -0,0 +1,264 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65)
m4_define([proxygen_version_str], m4_esyscmd_s([cat VERSION]))
AC_INIT([proxygen], m4_translit(proxygen_version_str, [:], [.]))
LT_VERSION=proxygen_version_str:0
AC_SUBST([LT_VERSION])
AC_CONFIG_SRCDIR([lib/http/Window.h])
AC_CONFIG_HEADERS([config.h])
AX_PREFIX_CONFIG_H([proxygen-config.h], [proxygen], [config.h])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.9 foreign tar-ustar nostdinc subdir-objects])
AC_CONFIG_MACRO_DIR([m4])
PKG_PROG_PKG_CONFIG
AC_PROG_INSTALL
AC_PROG_LIBTOOL
AC_LANG([C++])
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
AC_PROG_CPP
AC_CXX_COMPILE_STDCXX_0X
AC_PROG_RANLIB
# Be sure to add any -std option to CXXFLAGS before we invoke any
# AC_COMPILE_IFELSE() or similar macros. Any such macros that are invoked
# before we update CXXFLAGS will not be run with the same options that we use
# during the real build.
STD=""
if test "x$ac_cv_cxx_compile_cxx0x_cxx" = xyes; then
STD="-std=c++0x"
fi
if test "x$ac_cv_cxx_compile_cxx0x_gxx" = xyes; then
STD="-std=gnu++0x"
fi
CXXFLAGS="$STD $CXXFLAGS"
CXXFLAGS="-fno-strict-aliasing $CXXFLAGS"
CXXFLAGS="-O3 -g -W -Wall -Wextra -Wno-unused-parameter $CXXFLAGS"
CXXFLAGS=" -Wno-missing-field-initializers -Wno-deprecated $CXXFLAGS"
CXXFLAGS="-DLIBMC_FBTRACE_DISABLE $CXXFLAGS"
CFLAGS="-DLIBMC_FBTRACE_DISABLE $CFLAGS"
# Checks for libraries.
AC_CHECK_LIB([glog],[openlog],[],[AC_MSG_ERROR(
[Please install google-glog library])])
AC_CHECK_LIB([gflags],[getenv],[],AC_MSG_ERROR(
[Please install google-gflags library]))
# check for boost libs
AX_BOOST_BASE([1.51.0], [], [AC_MSG_ERROR(
[Please install boost >= 1.51.0])])
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netdb.h stdint.h string.h sys/file.h sys/time.h syslog.h unistd.h malloc.h])
AC_CHECK_LIB([event], [event_set], [], [AC_MSG_ERROR([Unable to find libevent])])
AC_CHECK_LIB([cap], [cap_get_proc], [], [AC_MSG_ERROR([Unable to find libcap])])
AC_CHECK_LIB([crypto], [MD5_Init], [], [AC_MSG_ERROR([Unable to find libcrypto])])
AC_CHECK_LIB([ssl], [SSL_library_init], [], [AC_MSG_ERROR([Unable to find libssl])])
AC_CHECK_LIB([z], [gzread], [], [AC_MSG_ERROR([Unable to find zlib])])
AC_CHECK_LIB([folly],[getenv],[],[AC_MSG_ERROR(
[Please install the folly library from https://github.com/facebook/folly])])
AC_CHECK_HEADER([folly/Likely.h], [], [AC_MSG_ERROR(
[Couldn't find folly, please download from https://github.com/facebook/folly]
)], [])
AC_CHECK_LIB([thriftcpp2], [main], [], [AC_MSG_ERROR(
[Unable to find thriftcpp2, you need to install FBThrift: https://github.com/facebook/fbthrift])])
AC_CHECK_LIB([thrift], [main], [], [AC_MSG_ERROR(
[Unable to find thrift, you need to install FBThrift: https://github.com/facebook/fbthrift])])
# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
AC_C_CONST
AC_C_INLINE
AC_TYPE_SIZE_T
AC_HEADER_TIME
AC_C_VOLATILE
AC_FUNC_ERROR_AT_LINE
AC_FUNC_FORK
AC_TYPE_INT32_T
AC_TYPE_INT64_T
AC_TYPE_MODE_T
AC_TYPE_PID_T
AC_TYPE_SSIZE_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT64_T
AC_CHECK_TYPE([__int128],
[AC_DEFINE([HAVE_INT128_T], [1], [Define if __int128 exists])],
[AC_DEFINE([HAVE_INT128_T], [0], [Define if __int128 does not exist])])
AC_CHECK_TYPES([ptrdiff_t])
AC_COMPILE_IFELSE(
[AC_LANG_SOURCE[
#pragma GCC diagnostic error "-Wattributes"
extern "C" void (*test_ifunc(void))() { return 0; }
void func() __attribute__((ifunc("test_ifunc")));]
],
[AC_DEFINE([HAVE_IFUNC], [1], [Define to 1 if the compiler supports ifunc])],
[AC_DEFINE([HAVE_IFUNC], [0], [Define to 0 if the compiler doesn't support ifunc])]
)
AC_COMPILE_IFELSE(
[AC_LANG_SOURCE[class C { virtual void f() final {} virtual void g() {} };
class D : public C { virtual void g() override {} };]],
[AC_DEFINE([FINAL], [final],
[Define to "final" if the compiler supports C++11 "final"])
AC_DEFINE([OVERRIDE], [override],
[Define to "override" if the compiler supports C++11 "override"])],
[AC_DEFINE([FINAL], [],
[Define to "final" if the compiler supports C++11 "final"])
AC_DEFINE([OVERRIDE], [],
[Define to "override" if the compiler supports C++11 "override"])]
)
AC_MSG_CHECKING([for static_assert])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([static_assert (1, "");],
[(void) 0])],
[AC_DEFINE([HAVE_STATIC_ASSERT], [1],
[Whether static_assert can be used or not])
AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])])
AC_COMPILE_IFELSE(
[AC_LANG_SOURCE[
#include <thread>
#include <chrono>
void func() { std::this_thread::sleep_for(std::chrono::seconds(1)); }]],
[AC_DEFINE([HAVE_STD__THIS_THREAD__SLEEP_FOR], [1],
[Define to 1 if std::this_thread::sleep_for() is defined.])])
AC_COMPILE_IFELSE(
[AC_LANG_SOURCE[
#include <cstring>
static constexpr int val = strlen("foo");]],
[AC_DEFINE([HAVE_CONSTEXPR_STRLEN], [1],
[Define to 1 if strlen(3) is constexpr.])])
AC_COMPILE_IFELSE(
[AC_LANG_SOURCE[
#include <type_traits>
#if !_LIBCPP_VERSION
#error No libc++
#endif
void func() {}]
],
[AC_DEFINE([USE_LIBCPP], [1], [Define to 1 if we're using libc++.])])
AC_COMPILE_IFELSE(
[AC_LANG_SOURCE[
#include <type_traits>
const bool val = std::is_trivially_copyable<bool>::value;]
],
[AC_DEFINE([HAVE_STD__IS_TRIVIALLY_COPYABLE], [1],
[Define to 1 if we have a usable std::is_trivially_copyable<T>
implementation.])])
# Figure out if we support weak symbols. If not, we will link in some null
# stubs for functions that would otherwise be weak.
AC_LINK_IFELSE(
[AC_LANG_SOURCE[
extern "C" void configure_link_extern_weak_test() __attribute__((weak));
int main(int argc, char** argv) {
return configure_link_extern_weak_test == nullptr;
}]
],
[
ac_have_weak_symbols="yes"
AC_DEFINE([HAVE_WEAK_SYMBOLS], [1],
[Define to 1 if the linker supports weak symbols.])])
AC_SEARCH_LIBS([cplus_demangle_v3_callback], [iberty])
if test "$ac_cv_search_cplus_demangle_v3_callback" != "no" ; then
AC_DEFINE([HAVE_CPLUS_DEMANGLE_V3_CALLBACK], [1],
[Define to 1 if we have cplus_demangle_v3_callback.])
fi
# Check for clock_gettime(2). This is not in an AC_CHECK_FUNCS() because we
# want to link with librt if necessary.
AC_SEARCH_LIBS([clock_gettime], [rt],
AC_DEFINE(
[HAVE_CLOCK_GETTIME],
[1],
[Define to 1 if we support clock_gettime(2).]),
[])
# Checks for library functions.
AC_CHECK_FUNCS([gettimeofday \
localtime_r \
memchr \
memset \
mkdir \
socket \
strcasecmp \
strdup \
strerror \
strtol \
dup2 \
ftruncate])
# Check for python, ruby, and gperf (needed for auto generated code)
AC_CHECK_PROG(HAVE_PYTHON, python, yes)
if test x"$HAVE_PYTHON" != x"yes"; then
AC_MSG_ERROR([Please install python first.])
fi
AC_CHECK_PROG(HAVE_RUBY, ruby, yes)
if test x"$HAVE_RUBY" != x"yes"; then
AC_MSG_ERROR([Please install ruby first.])
fi
AC_CHECK_PROG(HAVE_GPERF, gperf, yes)
if test x"$HAVE_GPERF" != x"yes"; then
AC_MSG_ERROR([Please install gperf first.])
fi
LIBS="$LIBS $BOOST_LDFLAGS -lpthread -pthread -lfolly -lglog -lthrift"
LIBS="$LIBS -ldouble-conversion -lboost_system -lboost_thread"
AM_CONDITIONAL([HAVE_STD_THREAD], [test "$ac_cv_header_features" = "yes"])
AM_CONDITIONAL([HAVE_X86_64], [test "$build_cpu" = "x86_64"])
AM_CONDITIONAL([HAVE_LINUX], [test "$build_os" == "linux-gnu"])
AM_CONDITIONAL([HAVE_WEAK_SYMBOLS], [test "$ac_have_weak_symbols" = "yes"])
AM_CONDITIONAL([HAVE_BITS_FUNCTEXCEPT], [test "$ac_cv_header_bits_functexcept" = "yes"])
# Include directory that contains "proxygen" so #include "proxygen/Foo.h" works
# Also add includes for gmock and gtest
AM_CPPFLAGS='-I$(top_srcdir)/.. -I$(top_srcdir)/lib/test/gmock-1.6.0/include -I$(top_srcdir)/lib/test/gmock-1.6.0/gtest/include'
AM_CPPFLAGS="$AM_CPPFLAGS $CXX_FLAGS $BOOST_CPPFLAGS"
AC_SUBST([AM_CPPFLAGS])
# Output
AC_CONFIG_FILES([Makefile
lib/Makefile
lib/test/Makefile
lib/utils/Makefile
lib/utils/test/Makefile
lib/ssl/Makefile
lib/ssl/test/Makefile
lib/services/Makefile
lib/http/Makefile
lib/http/codec/Makefile
lib/http/codec/test/Makefile
lib/http/session/Makefile
lib/http/session/test/Makefile
lib/http/test/Makefile
httpserver/Makefile
httpserver/samples/Makefile
httpserver/samples/echo/Makefile
httpserver/tests/Makefile])
AC_OUTPUT

64
proxygen/deps.sh Executable file
View File

@ -0,0 +1,64 @@
#!/bin/bash
set -e
start_dir=`pwd`
trap "cd $start_dir" EXIT
# Must execute from the directory containing this script
cd "$(dirname "$0")"
# Some extra dependencies for Ubuntu 13.10 and 14.04
sudo apt-get install \
flex \
bison \
libkrb5-dev \
libsasl2-dev \
libnuma-dev \
pkg-config \
libssl-dev \
libcap-dev \
ruby \
gperf \
autoconf-archive \
libevent-dev \
libgoogle-glog-dev
git clone https://github.com/facebook/fbthrift || true
cd fbthrift/thrift
# Rebase and uninstall in case we've already downloaded thrift and folly
git fetch && git rebase origin/master
sudo make uninstall || true
if [ -e folly/folly ]; then
# We have folly already downloaded
cd folly/folly
git fetch && git rebase origin/master
sudo make uninstall || true
cd ../..
fi
# Build folly and fbthrift
./deps.sh
# Install folly
cd folly/folly
sudo make install
# Install fbthrift
cd ../..
sudo make install
# Build proxygen
sudo /sbin/ldconfig
cd ../..
autoreconf -ivf
./configure
make -j8
# Run tests
make check
# Install
sudo make install
sudo /sbin/ldconfig

View File

@ -0,0 +1,4 @@
Contributors must agree to the Contributor License Agreement before patches
can be accepted.
http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ

View File

@ -0,0 +1,23 @@
http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
Igor Sysoev.
Additional changes are licensed under the same terms as NGINX and
copyright Joyent, Inc. and other Node contributors. All rights reserved.
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.

171
proxygen/external/http_parser/README.md vendored Normal file
View File

@ -0,0 +1,171 @@
HTTP Parser
===========
This is a parser for HTTP messages written in C. It parses both requests and
responses. The parser is designed to be used in performance HTTP
applications. It does not make any syscalls nor allocations, it does not
buffer data, it can be interrupted at anytime. Depending on your
architecture, it only requires about 40 bytes of data per message
stream (in a web server that is per connection).
Features:
* No dependencies
* Handles persistent streams (keep-alive).
* Decodes chunked encoding.
* Upgrade support
* Defends against buffer overflow attacks.
The parser extracts the following information from HTTP messages:
* Header fields and values
* Content-Length
* Request method
* Response status code
* Transfer-Encoding
* HTTP version
* Request URL
* Message body
Usage
-----
One `http_parser` object is used per TCP connection. Initialize the struct
using `http_parser_init()` and set the callbacks. That might look something
like this for a request parser:
http_parser_settings settings;
settings.on_path = my_path_callback;
settings.on_header_field = my_header_field_callback;
/* ... */
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;
When data is received on the socket execute the parser and check for errors.
size_t len = 80*1024, nparsed;
char buf[len];
ssize_t recved;
recved = recv(fd, buf, len, 0);
if (recved < 0) {
/* Handle error. */
}
/* Start up / continue the parser.
* Note we pass recved==0 to signal that EOF has been recieved.
*/
nparsed = http_parser_execute(parser, &settings, buf, recved);
if (parser->upgrade) {
/* handle new protocol */
} else if (nparsed != recved) {
/* Handle error. Usually just close the connection. */
}
HTTP needs to know where the end of the stream is. For example, sometimes
servers send responses without Content-Length and expect the client to
consume input (for the body) until EOF. To tell http_parser about EOF, give
`0` as the forth parameter to `http_parser_execute()`. Callbacks and errors
can still be encountered during an EOF, so one must still be prepared
to receive them.
Scalar valued message information such as `status_code`, `method`, and the
HTTP version are stored in the parser structure. This data is only
temporally stored in `http_parser` and gets reset on each new message. If
this information is needed later, copy it out of the structure during the
`headers_complete` callback.
The parser decodes the transfer-encoding for both requests and responses
transparently. That is, a chunked encoding is decoded before being sent to
the on_body callback.
The Special Problem of Upgrade
------------------------------
HTTP supports upgrading the connection to a different protocol. An
increasingly common example of this is the Web Socket protocol which sends
a request like
GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample
followed by non-HTTP data.
(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more
information the Web Socket protocol.)
To support this, the parser will treat this as a normal HTTP message without a
body. Issuing both on_headers_complete and on_message_complete callbacks. However
http_parser_execute() will stop parsing at the end of the headers and return.
The user is expected to check if `parser->upgrade` has been set to 1 after
`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
offset by the return value of `http_parser_execute()`.
Callbacks
---------
During the `http_parser_execute()` call, the callbacks set in
`http_parser_settings` will be executed. The parser maintains state and
never looks behind, so buffering the data is not necessary. If you need to
save certain data for later usage, you can do that from the callbacks.
There are two types of callbacks:
* notification `typedef int (*http_cb) (http_parser*);`
Callbacks: on_message_begin, on_headers_complete, on_message_complete.
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
Callbacks: (requests only) on_uri,
(common) on_header_field, on_header_value, on_body;
Callbacks must return 0 on success. Returning a non-zero value indicates
error to the parser, making it exit immediately.
In case you parse HTTP message in chunks (i.e. `read()` request line
from socket, parse, read half headers, parse, etc) your data callbacks
may be called more than once. Http-parser guarantees that data pointer is only
valid for the lifetime of callback. You can also `read()` into a heap allocated
buffer to avoid copying memory around if this fits your application.
Reading headers may be a tricky task if you read/parse headers partially.
Basically, you need to remember whether last header callback was field or value
and apply following logic:
(on_header_field and on_header_value shortened to on_h_*)
------------------------ ------------ --------------------------------------------
| State (prev. callback) | Callback | Description/action |
------------------------ ------------ --------------------------------------------
| nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
| | | into it |
------------------------ ------------ --------------------------------------------
| value | on_h_field | New header started. |
| | | Copy current name,value buffers to headers |
| | | list and allocate new buffer for new name |
------------------------ ------------ --------------------------------------------
| field | on_h_field | Previous name continues. Reallocate name |
| | | buffer and append callback data to it |
------------------------ ------------ --------------------------------------------
| field | on_h_value | Value for current header started. Allocate |
| | | new buffer and copy callback data to it |
------------------------ ------------ --------------------------------------------
| value | on_h_value | Value continues. Reallocate value buffer |
| | | and append callback data to it |
------------------------ ------------ --------------------------------------------
See examples of reading in headers:
* [partial example](http://gist.github.com/155877) in C
* [from http-parser tests](http://github.com/ry/http-parser/blob/37a0ff8928fb0d83cec0d0d8909c5a4abcd221af/test.c#L403) in C
* [from Node library](http://github.com/ry/node/blob/842eaf446d2fdcb33b296c67c911c32a0dabc747/src/http.js#L284) in Javascript

View File

@ -0,0 +1 @@
http_parser_cpp.cpp

View File

@ -0,0 +1,319 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* 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 http_parser_h
#define http_parser_h
#define HTTP_PARSER_VERSION_MAJOR 1
#define HTTP_PARSER_VERSION_MINOR 0
#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__)
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef unsigned int size_t;
typedef int ssize_t;
#else
#include <stdint.h>
#endif
#if __cplusplus
namespace proxygen {
#endif /* __cplusplus */
/* Compile with -DHTTP_PARSER_STRICT=1 to parse URLs and hostnames
* strictly according to the RFCs
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 0
#endif
/* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to
* the error reporting facility.
*/
#ifndef HTTP_PARSER_DEBUG
# define HTTP_PARSER_DEBUG 0
#endif
/* Maximium header size allowed */
#define HTTP_MAX_HEADER_SIZE (80*1024)
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
typedef struct http_parser_result http_parser_result;
/* Callbacks should return non-zero to indicate an error. The parser will
* then halt execution.
*
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
* returning '1' from on_headers_complete will tell the parser that it
* should not expect a body. This is used when receiving a response to a
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* http_data_cb does not return data chunks. It will be call arbitrarally
* many times for each string. E.G. you might get 10 callbacks for "on_path"
* each providing just a few characters more data.
*/
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
/* Request Methods */
enum http_method
{ HTTP_DELETE = 0
, HTTP_GET
, HTTP_HEAD
, HTTP_POST
, HTTP_PUT
/* pathological */
, HTTP_CONNECT
, HTTP_OPTIONS
, HTTP_TRACE
/* webdav */
, HTTP_COPY
, HTTP_LOCK
, HTTP_MKCOL
, HTTP_MOVE
, HTTP_PROPFIND
, HTTP_PROPPATCH
, HTTP_UNLOCK
/* subversion */
, HTTP_REPORT
, HTTP_MKACTIVITY
, HTTP_CHECKOUT
, HTTP_MERGE
/* upnp */
, HTTP_MSEARCH
, HTTP_NOTIFY
, HTTP_SUBSCRIBE
, HTTP_UNSUBSCRIBE
/* RFC-5789 */
, HTTP_PATCH
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags
{ F_CHUNKED = 1 << 0
, F_TRAILING = 1 << 3
, F_UPGRADE = 1 << 4
, F_SKIPBODY = 1 << 5
};
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_path, "the on_path callback failed") \
XX(CB_query_string, "the on_query_string callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_fragment, "the on_fragment callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_reason, "the on_reason callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(HUGE_CONTENT_LENGTH, \
"content-length header too large") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(HUGE_CHUNK_SIZE, \
"chunk header size too large") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
/* Get the line number that generated the current error */
#if HTTP_PARSER_DEBUG
#define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno)
#else
#define HTTP_PARSER_ERRNO_LINE(p) 0
#endif
struct http_parser {
/** PRIVATE **/
unsigned char type : 2; /* enum http_parser_type */
unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
unsigned char state; /* enum state from http_parser.c */
unsigned char header_state; /* enum header_state from http_parser.c */
unsigned char index; /* index into current matcher */
uint32_t nread; /* # bytes read in various scenarios */
int64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned short status_code; /* responses only */
unsigned char method; /* requests only */
unsigned char http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
char upgrade : 1;
#if HTTP_PARSER_DEBUG
uint32_t error_lineno;
#endif
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_data_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
http_data_cb on_reason;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
void http_parser_init(http_parser *parser, enum http_parser_type type);
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);
#if __cplusplus
}
#endif /* __cplusplus */
#endif

File diff suppressed because it is too large Load Diff

3142
proxygen/external/http_parser/test.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/RequestHandler.h"
#include "proxygen/httpserver/ResponseHandler.h"
namespace proxygen {
/**
* Filters are a way to add functionality to HTTPServer without complicating app
* specific RequestHandler. The basic idea is
*
* App-handler <=> Filter-1 <=> Filter-2 <=> Client
*
* The data flows through these filters between client and handler. They can do
* things like modify the data flowing through them, can update stats, create
* traces and even send direct response if they feel like.
*
* The default implementation just lets everything pass through.
*/
class Filter : public RequestHandler, public ResponseHandler {
public:
explicit Filter(RequestHandler* upstream)
: ResponseHandler(upstream) {
}
// Request handler
void setResponseHandler(ResponseHandler* handler) noexcept override {
// Save downstream handler and pass ourselves as downstream handler
downstream_ = handler;
upstream_->setResponseHandler(this);
}
void onRequest(std::unique_ptr<HTTPMessage> headers) noexcept override {
upstream_->onRequest(std::move(headers));
}
void onBody(std::unique_ptr<folly::IOBuf> body) noexcept override {
upstream_->onBody(std::move(body));
}
void onUpgrade(UpgradeProtocol protocol) noexcept override {
upstream_->onUpgrade(protocol);
}
void onEOM() noexcept override {
upstream_->onEOM();
}
void requestComplete() noexcept override {
downstream_ = nullptr;
upstream_->requestComplete();
delete this;
}
void onError(ProxygenError err) noexcept override {
downstream_ = nullptr;
upstream_->onError(err);
delete this;
}
void onEgressPaused() noexcept {
upstream_->onEgressPaused();
}
void onEgressResumed() noexcept {
upstream_->onEgressResumed();
}
// Response handler
void sendHeaders(HTTPMessage& msg) noexcept override {
downstream_->sendHeaders(msg);
}
void sendChunkHeader(size_t len) noexcept override {
downstream_->sendChunkHeader(len);
}
void sendBody(std::unique_ptr<folly::IOBuf> body) noexcept override {
downstream_->sendBody(std::move(body));
}
void sendChunkTerminator() noexcept override {
downstream_->sendChunkTerminator();
}
void sendEOM() noexcept override {
downstream_->sendEOM();
}
void sendAbort() noexcept override {
downstream_->sendAbort();
}
void refreshTimeout() noexcept override {
downstream_->refreshTimeout();
}
void pauseIngress() noexcept override {
downstream_->pauseIngress();
}
void resumeIngress() noexcept override {
downstream_->resumeIngress();
}
const TransportInfo& getSetupTransportInfo() const noexcept override {
return downstream_->getSetupTransportInfo();
}
void getCurrentTransportInfo(TransportInfo* tinfo) const override {
downstream_->getCurrentTransportInfo(tinfo);
}
};
}

View File

@ -0,0 +1,205 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/httpserver/HTTPServer.h"
#include "proxygen/httpserver/HTTPServerAcceptor.h"
#include "proxygen/httpserver/SignalHandler.h"
#include "proxygen/httpserver/filters/RejectConnectFilter.h"
#include <boost/thread.hpp>
#include <folly/ThreadName.h>
#include <folly/io/async/EventBaseManager.h>
using apache::thrift::async::TAsyncServerSocket;
using folly::EventBase;
using folly::EventBaseManager;
using folly::SocketAddress;
namespace proxygen {
HTTPServer::HTTPServer(HTTPServerOptions options):
options_(std::move(options)) {
// Insert a filter to fail all the CONNECT request, if required
if (!options_.supportsConnect) {
options_.handlerFactories.insert(
options_.handlerFactories.begin(),
folly::make_unique<RejectConnectFilterFactory>());
}
}
HTTPServer::~HTTPServer() {
CHECK(!mainEventBase_) << "Forgot to stop() server?";
}
void HTTPServer::bind(std::vector<IPConfig>& addrs) {
CHECK(serverSockets_.empty()) << "Server sockets are already bound";
// Set a scope guard that brings us to unintialized state
folly::ScopeGuard g = folly::makeGuard([&] {
serverSockets_.clear();
});
auto evb = EventBaseManager::get()->getEventBase();
for (auto& addr: addrs) {
serverSockets_.emplace_back(new TAsyncServerSocket(evb));
serverSockets_.back()->bind(addr.address);
// Use might have asked to register with some ephemeral port
serverSockets_.back()->getAddress(&addr.address);
}
// If everything succeeded, dismiss the cleanup guard
g.dismiss();
addresses_ = addrs;
}
void HTTPServer::start(std::function<void()> onSuccess,
std::function<void(std::exception_ptr)> onError) {
// Step 1: Check that server sockets are bound
CHECK(!serverSockets_.empty()) << "Need to call `bind()` before `start()`";
// Global Event base manager that will be used to create all the worker
// threads eventbases
auto manager = EventBaseManager::get();
mainEventBase_ = manager->getEventBase();
// Will be used to make sure that all the event loops are running
// before we proceed
boost::barrier barrier(2);
// Step 2: Setup handler threads
handlerThreads_ = std::vector<HandlerThread>(options_.threads);
std::vector<AcceptorConfiguration> accConfigs;
FOR_EACH_RANGE (i, 0, addresses_.size()) {
accConfigs.emplace_back(HTTPServerAcceptor::makeConfig(addresses_[i],
options_));
}
for (auto& handlerThread: handlerThreads_) {
handlerThread.thread = std::thread([&] () {
folly::setThreadName("http-worker");
handlerThread.eventBase = manager->getEventBase();
barrier.wait();
handlerThread.eventBase->loopForever();
// Call loop() again to drain all the events
handlerThread.eventBase->loop();
});
// Wait for eventbase pointer to be set
barrier.wait();
// Make sure event loop is running before we proceed
handlerThread.eventBase->runInEventBaseThread([&] () {
barrier.wait();
for (auto& factory: options_.handlerFactories) {
factory->onServerStart();
}
});
barrier.wait();
// Create acceptors
FOR_EACH_RANGE (i, 0, accConfigs.size()) {
auto acc = HTTPServerAcceptor::make(accConfigs[i], options_);
acc->init(serverSockets_[i].get(), handlerThread.eventBase);
++handlerThread.acceptorsRunning;
// Set completion callback such that it invokes onServerStop when
// all acceptors are drained
HandlerThread* ptr = &handlerThread;
acc->setCompletionCallback([ptr, this] () {
--ptr->acceptorsRunning;
if (ptr->acceptorsRunning == 0) {
for (auto& factory: options_.handlerFactories) {
factory->onServerStop();
}
}
});
handlerThread.acceptors.push_back(std::move(acc));
}
}
// Step 3: Install signal handler if required
if (!options_.shutdownOn.empty()) {
signalHandler_ = folly::make_unique<SignalHandler>(this);
signalHandler_->install(options_.shutdownOn);
}
// Step 4: Switch the server socket eventbase (bind may have been invoked
// in a separate thread).
for (auto& serverSocket: serverSockets_) {
serverSocket->detachEventBase();
serverSocket->attachEventBase(mainEventBase_);
}
// Step 5: Start listening for connections. This can throw if somebody else
// binds to same address and calls listen before this.
try {
for (auto& serverSocket: serverSockets_) {
serverSocket->listen(options_.listenBacklog);
serverSocket->startAccepting();
}
} catch (...) {
stop();
if (onError) {
onError(std::current_exception());
return;
}
throw;
}
// Step 6: Start the main event loop
if (onSuccess) {
mainEventBase_->runInEventBaseThread([onSuccess] () {
onSuccess();
});
}
mainEventBase_->loopForever();
}
void HTTPServer::stop() {
CHECK(mainEventBase_);
if (!mainEventBase_->isInEventBaseThread()) {
auto barrier = std::make_shared<boost::barrier>(2);
mainEventBase_->runInEventBaseThread([this, barrier] () {
stop();
barrier->wait();
});
barrier->wait();
return;
}
signalHandler_.reset();
serverSockets_.clear();
mainEventBase_->terminateLoopSoon();
mainEventBase_ = nullptr;
for (auto& handlerThread: handlerThreads_) {
handlerThread.eventBase->terminateLoopSoon();
}
for (auto& handlerThread: handlerThreads_) {
handlerThread.thread.join();
}
handlerThreads_.clear();
}
}

View File

@ -0,0 +1,142 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/HTTPServerOptions.h"
#include "proxygen/lib/ssl/SSLContextConfig.h"
#include <folly/io/async/EventBase.h>
#include <thread>
#include <thrift/lib/cpp/async/TAsyncServerSocket.h>
namespace proxygen {
class SignalHandler;
class HTTPServerAcceptor;
/**
* HTTPServer based on proxygen http libraries
*/
class HTTPServer final {
public:
/**
* For each IP you can specify HTTP protocol to use. You can use plain old
* HTTP/1.1 protocol or SPDY/3.1 for now.
*/
enum class Protocol: uint8_t {
HTTP,
SPDY,
};
struct IPConfig {
IPConfig(folly::SocketAddress a,
Protocol p)
: address(a),
protocol(p) {}
folly::SocketAddress address;
Protocol protocol;
std::vector<SSLContextConfig> sslConfigs;
};
/**
* Create a new HTTPServer
*/
explicit HTTPServer(HTTPServerOptions options);
~HTTPServer();
/**
* Bind server to the following addresses. Can be called from any thread.
*
* Throws exception on error (say port is already busy). You can try binding
* to different set of ports. Though once it succeeds, it is a FATAL to call
* it again.
*
* The list is updated in-place to contain final port server bound to if
* ephemeral port was given. If the call fails, the list might be partially
* updated.
*/
void bind(std::vector<IPConfig>& addrs);
/**
* Start HTTPServer.
*
* Note this is a blocking call and the current thread will be used to listen
* for incoming connections. Throws exception if something goes wrong (say
* somebody else is already listening on that socket).
*
* `onSuccess` callback will be invoked from the event loop which shows that
* all the setup was successfully done.
*
* `onError` callback will be invoked if some errors occurs while starting the
* server instead of throwing exception.
*/
void start(std::function<void()> onSuccess = nullptr,
std::function<void(std::exception_ptr)> onError = nullptr);
/**
* Stop HTTPServer.
*
* Can be called from any thread. Server will stop listening for new
* connections and will wait for running requests to finish.
*
* TODO: Separate method to do hard shutdown?
*/
void stop();
/**
* Get the list of addresses server is listening on. Empty if sockets are not
* bound yet.
*/
std::vector<IPConfig> addresses() const {
return addresses_;
}
private:
HTTPServerOptions options_;
/**
* Event base in which we binded server sockets.
*/
folly::EventBase* mainEventBase_{nullptr};
/**
* Server socket
*/
std::vector<apache::thrift::async::TAsyncServerSocket::UniquePtr>
serverSockets_;
/**
* Struct to hold info about handle threads
*/
struct HandlerThread {
std::thread thread;
folly::EventBase* eventBase{nullptr};
std::vector<std::unique_ptr<HTTPServerAcceptor>> acceptors;
uint32_t acceptorsRunning{0};
};
std::vector<HandlerThread> handlerThreads_;
/**
* Optional signal handlers on which we should shutdown server
*/
std::unique_ptr<SignalHandler> signalHandler_;
/**
* Addresses we are listening on
*/
std::vector<IPConfig> addresses_;
};
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/httpserver/HTTPServerAcceptor.h"
#include "proxygen/httpserver/RequestHandlerAdaptor.h"
#include "proxygen/httpserver/RequestHandlerFactory.h"
#include "proxygen/lib/http/codec/HTTP1xCodec.h"
#include "proxygen/lib/http/session/HTTPDownstreamSession.h"
using folly::SocketAddress;
namespace proxygen {
AcceptorConfiguration HTTPServerAcceptor::makeConfig(
const HTTPServer::IPConfig& ipConfig,
const HTTPServerOptions& opts) {
AcceptorConfiguration conf;
conf.bindAddress = ipConfig.address;
conf.connectionIdleTimeout = opts.idleTimeout;
conf.transactionIdleTimeout = opts.idleTimeout;
if (ipConfig.protocol == HTTPServer::Protocol::SPDY) {
conf.plaintextProtocol = "spdy/3.1";
}
conf.sslContextConfigs = ipConfig.sslConfigs;
return conf;
}
std::unique_ptr<HTTPServerAcceptor> HTTPServerAcceptor::make(
const AcceptorConfiguration& conf,
const HTTPServerOptions& opts) {
// Create a copy of the filter chain in reverse order since we need to create
// Handlers in that order.
std::vector<RequestHandlerFactory*> handlerFactories;
for (auto& f: opts.handlerFactories) {
handlerFactories.push_back(f.get());
}
std::reverse(handlerFactories.begin(), handlerFactories.end());
return std::unique_ptr<HTTPServerAcceptor>(
new HTTPServerAcceptor(conf, handlerFactories));
}
HTTPServerAcceptor::HTTPServerAcceptor(
const AcceptorConfiguration& conf,
std::vector<RequestHandlerFactory*> handlerFactories)
: HTTPSessionAcceptor(conf),
handlerFactories_(handlerFactories) {
}
void HTTPServerAcceptor::setCompletionCallback(std::function<void()> f) {
completionCallback_ = f;
}
HTTPServerAcceptor::~HTTPServerAcceptor() {
}
HTTPTransactionHandler* HTTPServerAcceptor::newHandler(
HTTPTransaction& txn,
HTTPMessage* msg) noexcept {
SocketAddress clientAddr, vipAddr;
txn.getPeerAddress(clientAddr);
txn.getLocalAddress(vipAddr);
msg->setClientAddress(clientAddr);
msg->setDstAddress(vipAddr);
// Create filters chain
RequestHandler* h = nullptr;
for (auto& factory: handlerFactories_) {
h = factory->onRequest(h, msg);
}
return new RequestHandlerAdaptor(h);
}
void HTTPServerAcceptor::onConnectionsDrained() {
if (completionCallback_) {
completionCallback_();
}
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/HTTPServer.h"
#include "proxygen/httpserver/HTTPServerOptions.h"
#include "proxygen/lib/http/session/HTTPSessionAcceptor.h"
namespace proxygen {
class HTTPServerAcceptor final : public HTTPSessionAcceptor {
public:
static AcceptorConfiguration makeConfig(
const HTTPServer::IPConfig& ipConfig,
const HTTPServerOptions& opts);
static std::unique_ptr<HTTPServerAcceptor> make(
const AcceptorConfiguration& conf,
const HTTPServerOptions& opts);
/**
* Invokes the given method when all the connections are drained
*/
void setCompletionCallback(std::function<void()> f);
~HTTPServerAcceptor();
private:
HTTPServerAcceptor(const AcceptorConfiguration& conf,
std::vector<RequestHandlerFactory*> handlerFactories);
// HTTPSessionAcceptor
HTTPTransaction::Handler* newHandler(HTTPTransaction& txn,
HTTPMessage* msg) noexcept override;
void onConnectionsDrained() override;
std::function<void()> completionCallback_;
const std::vector<RequestHandlerFactory*> handlerFactories_{nullptr};
};
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/Filters.h"
#include "proxygen/httpserver/RequestHandlerFactory.h"
#include <folly/SocketAddress.h>
#include <signal.h>
namespace proxygen {
/**
* Configuration options for HTTPServer
*
* XXX: Provide a helper that can convert thrift/json to this config
* directly. We keep this object type-safe.
*/
class HTTPServerOptions {
public:
/**
* Number of threads to start to handle requests. Note that this excludes
* the thread you call `HTTPServer.start()` in.
*
* XXX: Put some perf numbers to help user decide how many threads to
* create.
* XXX: Maybe support not creating any more worker threads and doing all
* the work in same thread when `threads == 0`.
*/
size_t threads = 1;
/**
* Chain of RequestHandlerFactory that are used to create RequestHandler
* which handles requests.
*
* You can do something like -
*
* handlerFactories = RequestHandlerChain()
* .addThen<StatsFilter>()
* .addThen<TraceFilter>()
* .addThen<AccessLogFilter>()
* .addThen<AppSpecificHandler>()
* .build();
*/
std::vector<std::unique_ptr<RequestHandlerFactory>> handlerFactories;
/**
* This idle timeout serves two purposes -
*
* 1. How long to keep idle connections around before throwing them away.
*
* 2. If it takes client more than this time to send request, we fail the
* request.
*
* XXX: Maybe have separate time limit for both?
*/
std::chrono::milliseconds idleTimeout{60000};
/**
* TCP server socket backlog to start with.
*/
uint32_t listenBacklog{1024};
/**
* Signals on which to shutdown the server. Mostly you will want
* {SIGINT, SIGTERM}. Note, if you have multiple deamons running or you want
* to have a separate signal handler, leave this empty and handle signals
* yourself.
*/
std::vector<int> shutdownOn{};
/**
* Set to true if you want to support CONNECT request. Most likely you
* don't want that.
*/
bool supportsConnect{false};
};
}

View File

@ -0,0 +1,27 @@
SUBDIRS = . samples tests
lib_LTLIBRARIES = libproxygenhttpserver.la
libproxygenhttpserverdir = $(includedir)/proxygen/httpserver
nobase_libproxygenhttpserver_HEADERS = \
Filters.h \
HTTPServer.h \
HTTPServerAcceptor.h \
HTTPServerOptions.h \
Mocks.h \
RequestHandler.h \
RequestHandlerAdaptor.h \
RequestHandlerFactory.h \
ResponseBuilder.h \
ResponseHandler.h \
ScopedHTTPServer.h \
SignalHandler.h
libproxygenhttpserver_la_SOURCES = \
HTTPServer.cpp \
HTTPServerAcceptor.cpp \
RequestHandlerAdaptor.cpp \
SignalHandler.cpp
libproxygenhttpserver_la_LIBADD = \
../lib/libproxygenlib.la

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/RequestHandler.h"
#include "proxygen/httpserver/ResponseHandler.h"
#include <gmock/gmock.h>
namespace proxygen {
/**
* Mocks to help with testing
*/
class MockResponseHandler : public ResponseHandler {
public:
explicit MockResponseHandler(RequestHandler* h): ResponseHandler(h) {
}
GMOCK_METHOD1_(, noexcept,, sendHeaders, void(HTTPMessage&));
GMOCK_METHOD1_(, noexcept,, sendChunkHeader, void(size_t));
GMOCK_METHOD1_(, noexcept,, sendBody, void(std::shared_ptr<folly::IOBuf>));
GMOCK_METHOD0_(, noexcept,, sendChunkTerminator, void());
GMOCK_METHOD0_(, noexcept,, sendEOM, void());
GMOCK_METHOD0_(, noexcept,, sendAbort, void());
GMOCK_METHOD0_(, noexcept,, refreshTimeout, void());
GMOCK_METHOD0_(, noexcept,, pauseIngress, void());
GMOCK_METHOD0_(, noexcept,, resumeIngress, void());
const TransportInfo& getSetupTransportInfo() const noexcept {
return transportInfo;
}
MOCK_CONST_METHOD1(getCurrentTransportInfo, void(TransportInfo*));
void sendBody(std::unique_ptr<folly::IOBuf> body) noexcept override {
if (body) {
sendBody(std::shared_ptr<folly::IOBuf>(body.release()));
} else {
sendBody(std::shared_ptr<folly::IOBuf>(nullptr));
}
}
TransportInfo transportInfo;
};
class MockRequestHandler : public RequestHandler {
public:
GMOCK_METHOD1_(, noexcept,, setResponseHandler, void(ResponseHandler*));
GMOCK_METHOD1_(, noexcept,, onRequest, void(std::shared_ptr<HTTPMessage>));
GMOCK_METHOD1_(, noexcept,, onBody, void(std::shared_ptr<folly::IOBuf>));
GMOCK_METHOD1_(, noexcept,, onUpgrade, void(UpgradeProtocol));
GMOCK_METHOD0_(, noexcept,, onEOM, void());
GMOCK_METHOD0_(, noexcept,, requestComplete, void());
GMOCK_METHOD1_(, noexcept,, onError, void(ProxygenError));
GMOCK_METHOD0_(, noexcept,, onEgressPaused, void());
GMOCK_METHOD0_(, noexcept,, onEgressResumed, void());
void onRequest(std::unique_ptr<HTTPMessage> headers) noexcept override {
onRequest(std::shared_ptr<HTTPMessage>(headers.release()));
}
void onBody(std::unique_ptr<folly::IOBuf> body) noexcept override {
if (body) {
onBody(std::shared_ptr<folly::IOBuf>(body.release()));
} else {
onBody(std::shared_ptr<folly::IOBuf>(nullptr));
}
}
};
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/lib/http/session/HTTPTransaction.h"
namespace proxygen {
class ResponseHandler;
/**
* Interface to be implemented by objects that handle requests from
* client. ResponseHandler acts as the client for these objects and
* provides methods to send back the response
*/
class RequestHandler {
public:
/**
* Saves the downstream handle with itself. Implementations of this
* interface should use downstream_ to send back response.
*
* XXX: Only override this method if you are ABSOLUTELY sure what you
* are doing. If possible, just use downstream_ variable and dont
* mess with these things.
*/
virtual void setResponseHandler(ResponseHandler* handler) noexcept {
downstream_ = CHECK_NOTNULL(handler);
}
/**
* Invoked when we have successfully fetched headers from client. This will
* always be the first callback invoked on your handler.
*/
virtual void onRequest(std::unique_ptr<HTTPMessage> headers) noexcept = 0;
/**
* Invoked when we get part of body for the request.
*/
virtual void onBody(std::unique_ptr<folly::IOBuf> body) noexcept = 0;
/**
* Invoked when the session has been upgraded to a different protocol
*/
virtual void onUpgrade(proxygen::UpgradeProtocol prot) noexcept = 0;
/**
* Invoked when we finish receiving the body.
*/
virtual void onEOM() noexcept = 0;
/**
* Invoked when request processing has been completed and nothing more
* needs to be done. This may be a good place to log some stats and
* clean up resources. This is distinct from onEOM() because it is
* invoked after the response is fully sent. Once this callback has been
* received, `downstream_` should be considered invalid.
*/
virtual void requestComplete() noexcept = 0;
/**
* Request failed. Maybe because of read/write error on socket or client
* not being able to send request in time.
*
* NOTE: Can be invoked at any time (except for before onRequest).
*
* No more callbacks will be invoked after this. You should clean up after
* yourself.
*/
virtual void onError(ProxygenError err) noexcept = 0;
/**
* Signals from HTTP layer when client queue is full or empty. If you are
* sending a streaming response, consider implementing these and acting
* accordingly. Saves your server from running out of memory.
*/
virtual void onEgressPaused() noexcept {
}
virtual void onEgressResumed() noexcept {
}
virtual ~RequestHandler() {}
protected:
/**
* A place designated for the response handler. You can use this to send back
* the response in your RequestHandler.
*/
ResponseHandler* downstream_{nullptr};
};
}

View File

@ -0,0 +1,186 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/httpserver/RequestHandlerAdaptor.h"
#include "proxygen/httpserver/RequestHandler.h"
#include "proxygen/httpserver/ResponseBuilder.h"
#include <boost/algorithm/string.hpp>
namespace proxygen {
RequestHandlerAdaptor::RequestHandlerAdaptor(RequestHandler* requestHandler)
: ResponseHandler(requestHandler) {
}
void RequestHandlerAdaptor::setTransaction(HTTPTransaction* txn) noexcept {
txn_ = txn;
// We become that transparent layer
upstream_->setResponseHandler(this);
}
void RequestHandlerAdaptor::detachTransaction() noexcept {
if (err_ == kErrorNone) {
upstream_->requestComplete();
}
// Otherwise we would have got some error call back and invoked onError
// on RequestHandler
delete this;
}
void RequestHandlerAdaptor::onHeadersComplete(std::unique_ptr<HTTPMessage> msg)
noexcept {
if (msg->getHeaders().exists(HTTP_HEADER_EXPECT)) {
auto expectation = msg->getHeaders().getSingleOrEmpty(HTTP_HEADER_EXPECT);
if (!boost::iequals(expectation, "100-continue")) {
setError(kErrorUnsupportedExpectation);
ResponseBuilder(this)
.status(417, "Expectation Failed")
.closeConnection()
.sendWithEOM();
} else {
ResponseBuilder(this)
.status(100, "Continue")
.send();
}
}
// Only in case of no error
if (err_ == kErrorNone) {
upstream_->onRequest(std::move(msg));
}
}
void RequestHandlerAdaptor::onBody(std::unique_ptr<folly::IOBuf> c) noexcept {
upstream_->onBody(std::move(c));
}
void RequestHandlerAdaptor::onChunkHeader(size_t length) noexcept {
}
void RequestHandlerAdaptor::onChunkComplete() noexcept {
}
void RequestHandlerAdaptor::onTrailers(std::unique_ptr<HTTPHeaders> trailers)
noexcept {
// XXX: Support trailers
}
void RequestHandlerAdaptor::onEOM() noexcept {
if (err_ == kErrorNone) {
upstream_->onEOM();
}
}
void RequestHandlerAdaptor::onUpgrade(UpgradeProtocol protocol) noexcept {
upstream_->onUpgrade(protocol);
}
void RequestHandlerAdaptor::onError(const HTTPException& error) noexcept {
if (err_ != kErrorNone) {
// we have already handled an error and upstream would have been deleted
return;
}
if (error.getProxygenError() == kErrorTimeout) {
setError(kErrorTimeout);
if (responseStarted_) {
sendAbort();
} else {
ResponseBuilder(this)
.status(408, "Request Timeout")
.closeConnection()
.sendWithEOM();
}
} else if (error.getDirection() == HTTPException::Direction::INGRESS) {
setError(kErrorRead);
if (responseStarted_) {
sendAbort();
} else {
ResponseBuilder(this)
.status(400, "Bad Request")
.closeConnection()
.sendWithEOM();
}
} else {
setError(kErrorWrite);
}
// Wait for detachTransaction to clean up
}
void RequestHandlerAdaptor::onEgressPaused() noexcept {
upstream_->onEgressPaused();
}
void RequestHandlerAdaptor::onEgressResumed() noexcept {
upstream_->onEgressResumed();
}
void RequestHandlerAdaptor::sendHeaders(HTTPMessage& msg) noexcept {
responseStarted_ = true;
txn_->sendHeaders(msg);
}
void RequestHandlerAdaptor::sendChunkHeader(size_t len) noexcept {
txn_->sendChunkHeader(len);
}
void RequestHandlerAdaptor::sendBody(std::unique_ptr<folly::IOBuf> b) noexcept {
txn_->sendBody(std::move(b));
}
void RequestHandlerAdaptor::sendChunkTerminator() noexcept {
txn_->sendChunkTerminator();
}
void RequestHandlerAdaptor::sendEOM() noexcept {
txn_->sendEOM();
}
void RequestHandlerAdaptor::sendAbort() noexcept {
txn_->sendAbort();
}
void RequestHandlerAdaptor::refreshTimeout() noexcept {
txn_->refreshTimeout();
}
void RequestHandlerAdaptor::pauseIngress() noexcept {
txn_->pauseIngress();
}
void RequestHandlerAdaptor::resumeIngress() noexcept {
txn_->resumeIngress();
}
const TransportInfo&
RequestHandlerAdaptor::getSetupTransportInfo() const noexcept {
return txn_->getSetupTransportInfo();
}
void RequestHandlerAdaptor::getCurrentTransportInfo(
TransportInfo* tinfo) const {
txn_->getCurrentTransportInfo(tinfo);
}
void RequestHandlerAdaptor::setError(ProxygenError err) noexcept {
err_ = err;
upstream_->onError(err);
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/ResponseHandler.h"
#include "proxygen/lib/http/session/HTTPTransaction.h"
namespace proxygen {
class RequestHandler;
/**
* An adaptor that converts HTTPTransactionHandler to RequestHandler.
* Apart from that it also -
*
* - Handles all the error cases itself as described below. It makes a terminal
* call onError(...) where you are expected to log something and stop
* processing the request if you have started so.
*
* onError - Send a direct response back if no response has started and
* writing is still possible. Otherwise sends an abort.
*
* - Handles 100-continue case for you (by sending Continue response)
*/
class RequestHandlerAdaptor
: public HTTPTransactionHandler,
public ResponseHandler {
public:
explicit RequestHandlerAdaptor(RequestHandler* requestHandler);
private:
// HTTPTransactionHandler
void setTransaction(HTTPTransaction* txn) noexcept override;
void detachTransaction() noexcept override;
void onHeadersComplete(std::unique_ptr<HTTPMessage> msg) noexcept override;
void onBody(std::unique_ptr<folly::IOBuf> chain) noexcept override;
void onChunkHeader(size_t length) noexcept override;
void onChunkComplete() noexcept override;
void onTrailers(std::unique_ptr<HTTPHeaders> trailers) noexcept override;
void onEOM() noexcept override;
void onUpgrade(UpgradeProtocol protocol) noexcept override;
void onError(const HTTPException& error) noexcept override;
void onEgressPaused() noexcept override;
void onEgressResumed() noexcept override;
// ResponseHandler
void sendHeaders(HTTPMessage& msg) noexcept override;
void sendChunkHeader(size_t len) noexcept override;
void sendBody(std::unique_ptr<folly::IOBuf> body) noexcept override;
void sendChunkTerminator() noexcept override;
void sendEOM() noexcept override;
void sendAbort() noexcept override;
void refreshTimeout() noexcept override;
void pauseIngress() noexcept override;
void resumeIngress() noexcept override;
const TransportInfo& getSetupTransportInfo() const noexcept override;
void getCurrentTransportInfo(TransportInfo* tinfo) const override;
// Helper method
void setError(ProxygenError err) noexcept;
HTTPTransaction* txn_{nullptr};
ProxygenError err_{kErrorNone};
bool responseStarted_{false};
};
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/RequestHandler.h"
namespace proxygen {
/**
* Factory for RequestHandlers.
*/
class RequestHandlerFactory {
public:
virtual ~RequestHandlerFactory() {}
/**
* Invoked in each thread server is going to handle requests
* before we start handling requests. Can be used to setup
* thread-local setup for each thread (stats and such).
*/
virtual void onServerStart() noexcept = 0;
/**
* Invoked in each handler thread after all the connections are
* drained from that thread. Can be used to tear down thread-local setup.
*/
virtual void onServerStop() noexcept = 0;
/**
* Invoked for each new request server handles. HTTPMessage is provided
* so that user can potentially choose among several implementation of
* handler based on URL or something. No, need to save/copy this
* HTTPMessage. RequestHandler will be given the HTTPMessage
* in a separate callback.
*
* Some request handlers don't handle the request themselves (think filters).
* They do take some actions based on request/response but otherwise they
* just hand-off request to some other RequestHandler. This upstream
* RequestHandler is given as first parameter. For the terminal RequestHandler
* this will by nullptr.
*/
virtual RequestHandler* onRequest(RequestHandler*, HTTPMessage*) noexcept = 0;
};
/**
* Helper class to help beautify the way we make chains of these filters
*/
class RequestHandlerChain {
public:
std::vector<std::unique_ptr<RequestHandlerFactory>> build() {
return std::move(chain_);
}
template <typename T, typename... Args>
RequestHandlerChain& addThen(Args&&... args) {
chain_.push_back(folly::make_unique<T>(std::forward<Args>(args)...));
return *this;
}
RequestHandlerChain& addThen(std::unique_ptr<RequestHandlerFactory> h) {
chain_.push_back(std::move(h));
return *this;
}
private:
std::vector<std::unique_ptr<RequestHandlerFactory>> chain_;
};
}

View File

@ -0,0 +1,198 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/ResponseHandler.h"
#include <folly/ScopeGuard.h>
namespace proxygen {
/**
* Helps you make responses and send them on demand.
*
* NOTE: We don't do any correctness checks here, we depend on
* state machine in HTTPTransaction to tell us when an
* error occurs
*
* Three expected use cases are
*
* 1. Send all response at once. If this is an error
* response, most probably you also want 'closeConnection'.
*
* ResponseBuilder(handler)
* .status(200, "OK")
* .body(...)
* .sendWithEOM();
*
* 2. Sending back response in chunks.
*
* ResponseBuilder(handler)
* .status(200, "OK")
* .body(...)
* .send(); // Without `WithEOM` we make it chunked
*
* 1 or more time
*
* ResponseBuilder(handler)
* .body(...)
* .send();
*
* At last
*
* ResponseBuilder(handler)
* .body(...)
* .sendWithEOM();
*
* 3. Accept or reject Upgrade Requests
*
* ResponseBuilder(handler)
* .acceptUpgradeRequest() // send '200 OK' without EOM
*
* or
*
* ResponseBuilder(handler)
* .rejectUpgradeRequest() // send '400 Bad Request'
*
*/
class ResponseBuilder {
public:
explicit ResponseBuilder(ResponseHandler* txn): txn_(txn) {
}
ResponseBuilder& status(uint16_t code, std::string message) {
headers_ = folly::make_unique<HTTPMessage>();
headers_->setHTTPVersion(1, 1);
headers_->setStatusCode(code);
headers_->setStatusMessage(message);
return *this;
}
template <typename T>
ResponseBuilder& header(const std::string& headerIn, const T& value) {
CHECK(headers_) << "You need to call `status` before adding headers";
headers_->getHeaders().add(headerIn, value);
return *this;
}
template <typename T>
ResponseBuilder& header(HTTPHeaderCode code, const T& value) {
CHECK(headers_) << "You need to call `status` before adding headers";
headers_->getHeaders().add(code, value);
return *this;
}
ResponseBuilder& body(std::unique_ptr<folly::IOBuf> bodyIn) {
if (bodyIn) {
if (body_) {
body_->prependChain(std::move(bodyIn));
} else {
body_ = std::move(bodyIn);
}
}
return *this;
}
template <typename T>
ResponseBuilder& body(T&& t) {
return body(folly::IOBuf::maybeCopyBuffer(
folly::to<std::string>(std::forward<T>(t))));
}
ResponseBuilder& closeConnection() {
return header(HTTP_HEADER_CONNECTION, "close");
}
void sendWithEOM() {
sendEOM_ = true;
send();
}
void send() {
// Once we send them, we don't want to send them again
SCOPE_EXIT { headers_.reset(); };
// By default, chunked
bool chunked = true;
// If we have complete response, we can use Content-Length and get done
if (headers_ && sendEOM_) {
chunked = false;
}
if (headers_) {
// We don't need to add Content-Length or Encoding for 1xx responses
if (headers_->getStatusCode() >= 200) {
if (chunked) {
headers_->setIsChunked(true);
} else {
const auto len = body_ ? body_->computeChainDataLength() : 0;
headers_->getHeaders().add(
HTTP_HEADER_CONTENT_LENGTH,
folly::to<std::string>(len));
}
}
txn_->sendHeaders(*headers_);
}
if (body_) {
if (chunked) {
txn_->sendChunkHeader(body_->computeChainDataLength());
txn_->sendBody(std::move(body_));
txn_->sendChunkTerminator();
} else {
txn_->sendBody(std::move(body_));
}
}
if (sendEOM_) {
txn_->sendEOM();
}
}
enum class UpgradeType {
CONNECT_REQUEST = 0,
HTTP_UPGRADE,
};
void acceptUpgradeRequest(UpgradeType upgradeType,
const std::string upgradeProtocol = "") {
headers_ = folly::make_unique<HTTPMessage>();
if (upgradeType == UpgradeType::CONNECT_REQUEST) {
headers_->constructDirectResponse({1, 1}, 200, "OK");
} else {
CHECK(!upgradeProtocol.empty());
headers_->constructDirectResponse({1, 1}, 101, "Switching Protocols");
headers_->getHeaders().add(HTTP_HEADER_UPGRADE, upgradeProtocol);
headers_->getHeaders().add(HTTP_HEADER_CONNECTION, "Upgrade");
}
txn_->sendHeaders(*headers_);
}
void rejectUpgradeRequest() {
headers_ = folly::make_unique<HTTPMessage>();
headers_->constructDirectResponse({1, 1}, 400, "Bad Request");
txn_->sendHeaders(*headers_);
txn_->sendEOM();
}
private:
ResponseHandler* const txn_{nullptr};
std::unique_ptr<HTTPMessage> headers_;
std::unique_ptr<folly::IOBuf> body_;
// If true, sends EOM.
bool sendEOM_{false};
};
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/lib/http/session/HTTPTransaction.h"
namespace proxygen {
class RequestHandler;
/**
* Interface that acts as client for RequestHandler. It also has a hook
* for the RequestHandler so that it is easy to chain these Request/Response
* handlers and be able to modify these chains.
*
* The names are pretty much self explanatory. You only need
* to get into details about this interface if you are implementing filters.
*
* NOTE: All the writes are done at the end of the event loop. So this is safe
* to do in your RequestHandler.
*
* {
* ...
* downstream_->sendHeader(...);
* downstream_->sendEOM();
* }
*
* You dont need to worry about any callbacks being invoked after
* sendHeader.
*
* Consider using proxygen/httpserver/ResponseBuilder to send back the
* response. It will take care of chunking response if required and
* everything.
*/
class ResponseHandler {
public:
explicit ResponseHandler(RequestHandler* upstream)
: upstream_(CHECK_NOTNULL(upstream)) {
}
virtual ~ResponseHandler() {}
/**
* NOTE: We take response message as non-const reference, to allow filters
* between your handler and client to be able to modify response
* if they want to.
*
* eg. a compression filter might want to change the content-encoding
*/
virtual void sendHeaders(HTTPMessage& msg) noexcept = 0;
virtual void sendChunkHeader(size_t len) noexcept = 0;
virtual void sendBody(std::unique_ptr<folly::IOBuf> body) noexcept = 0;
virtual void sendChunkTerminator() noexcept = 0;
virtual void sendEOM() noexcept = 0;
virtual void sendAbort() noexcept = 0;
virtual void refreshTimeout() noexcept = 0;
virtual void pauseIngress() noexcept = 0;
virtual void resumeIngress() noexcept = 0;
// Accessors for Transport/Connection information
virtual const TransportInfo& getSetupTransportInfo() const noexcept = 0;
virtual void getCurrentTransportInfo(TransportInfo* tinfo) const = 0;
protected:
RequestHandler* upstream_{nullptr};
};
}

View File

@ -0,0 +1,192 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/HTTPServer.h"
#include "proxygen/httpserver/ResponseBuilder.h"
#include <boost/thread.hpp>
#include <folly/ThreadName.h>
namespace proxygen {
template <typename HandlerType>
class ScopedHandler : public RequestHandler {
public:
explicit ScopedHandler(HandlerType* ptr): handlerPtr_(ptr) {
}
void onRequest(std::unique_ptr<HTTPMessage> headers) noexcept {
request_ = std::move(headers);
}
void onBody(std::unique_ptr<folly::IOBuf> body) noexcept {
requestBody_.append(std::move(body));
}
void onUpgrade(proxygen::UpgradeProtocol proto) noexcept {
}
void onEOM() noexcept {
try {
ResponseBuilder r(downstream_);
(*handlerPtr_)(*request_, requestBody_.move(), r);
r.sendWithEOM();
} catch (const std::exception& ex) {
ResponseBuilder(downstream_)
.status(500, "Internal Server Error")
.body(ex.what())
.sendWithEOM();
} catch (...) {
ResponseBuilder(downstream_)
.status(500, "Internal Server Error")
.body("Unknown exception thrown")
.sendWithEOM();
}
}
void requestComplete() noexcept {
delete this;
}
void onError(ProxygenError err) noexcept {
delete this;
}
private:
HandlerType* const handlerPtr_{nullptr};
std::unique_ptr<HTTPMessage> request_;
folly::IOBufQueue requestBody_;
};
template <typename HandlerType>
class ScopedHandlerFactory : public RequestHandlerFactory {
public:
explicit ScopedHandlerFactory(HandlerType handler): handler_(handler) {
}
void onServerStart() noexcept override {
}
void onServerStop() noexcept override {
}
RequestHandler* onRequest(RequestHandler*, HTTPMessage*) noexcept {
return new ScopedHandler<HandlerType>(&handler_);
}
private:
HandlerType handler_;
};
/**
* A basic server that can be used for testing http clients. Since most such
* servers are short lived, this server takes care of starting and stopping
* automatically.
*/
class ScopedHTTPServer final {
public:
/**
* Start a server listening on the requested `port`.
* If `port` is 0, it will choose a random port.
*/
template <typename HandlerType>
static std::unique_ptr<ScopedHTTPServer> start(HandlerType handler,
int port = 0,
int numThreads = 4);
/**
* Get the port the server is listening on. This is helpful if the port was
* randomly chosen.
*/
int getPort() const {
auto addresses = server_->addresses();
CHECK(!addresses.empty());
return addresses[0].address.getPort();
}
~ScopedHTTPServer() {
server_->stop();
thread_.join();
}
private:
ScopedHTTPServer(std::thread thread,
std::unique_ptr<HTTPServer> server)
: thread_(std::move(thread)),
server_(std::move(server)) {
}
std::thread thread_;
std::unique_ptr<HTTPServer> server_;
};
template <typename HandlerType>
inline std::unique_ptr<ScopedHTTPServer> ScopedHTTPServer::start(
HandlerType handler,
int port,
int numThreads) {
std::unique_ptr<RequestHandlerFactory> f =
folly::make_unique<ScopedHandlerFactory<HandlerType>>(handler);
return start(std::move(f), port, numThreads);
}
template <>
inline std::unique_ptr<ScopedHTTPServer>
ScopedHTTPServer::start<std::unique_ptr<RequestHandlerFactory>>(
std::unique_ptr<RequestHandlerFactory> f,
int port,
int numThreads) {
// This will handle both IPv4 and IPv6 cases
folly::SocketAddress addr;
addr.setFromLocalPort(port);
std::vector<HTTPServer::IPConfig> IPs = {
{addr, HTTPServer::Protocol::HTTP}
};
HTTPServerOptions options;
options.threads = numThreads;
options.handlerFactories.push_back(std::move(f));
auto server = folly::make_unique<HTTPServer>(std::move(options));
server->bind(IPs);
// Start the server
std::exception_ptr eptr;
auto barrier = std::make_shared<boost::barrier>(2);
std::thread t = std::thread([&, barrier] () {
server->start(
[&, barrier] () {
folly::setThreadName("http-acceptor");
barrier->wait();
},
[&, barrier] (std::exception_ptr ex) {
eptr = ex;
barrier->wait();
});
});
// Wait for server to start
barrier->wait();
if (eptr) {
t.join();
std::rethrow_exception(eptr);
}
return std::unique_ptr<ScopedHTTPServer>(
new ScopedHTTPServer(std::move(t), std::move(server)));
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/httpserver/SignalHandler.h"
#include "proxygen/httpserver/HTTPServer.h"
#include <folly/io/async/EventBaseManager.h>
#include <signal.h>
using folly::EventBaseManager;
namespace proxygen {
SignalHandler::SignalHandler(HTTPServer* server)
: TAsyncSignalHandler(EventBaseManager::get()->getEventBase()),
server_(server) {
}
void SignalHandler::install(const std::vector<int>& signals) {
for (const int& signal: signals) {
registerSignalHandler(signal);
}
}
void SignalHandler::signalReceived(int signum) noexcept {
server_->stop();
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <thrift/lib/cpp/async/TAsyncSignalHandler.h>
#include <vector>
namespace proxygen {
class HTTPServer;
/**
* Installs signal handler which will stop HTTPServer when the user presses
* Ctrl-C. To be used if HTTPServer is the main process.
*
* Note: Should only be created from the thread invoking `HTTPServer::start()`.
*/
class SignalHandler: private apache::thrift::async::TAsyncSignalHandler {
public:
explicit SignalHandler(HTTPServer* server);
void install(const std::vector<int>& signals);
private:
// TAsyncSignalHandler
void signalReceived(int signum) noexcept override;
HTTPServer* const server_{nullptr};
};
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/RequestHandler.h"
namespace proxygen {
/**
* Handler that sends back a fixed response back.
*/
class DirectResponseHandler : public RequestHandler {
public:
DirectResponseHandler(int code,
std::string message,
std::string body)
: code_(code),
message_(message),
body_(folly::IOBuf::copyBuffer(body)) {
}
void onRequest(std::unique_ptr<HTTPMessage> headers) noexcept override {
}
void onBody(std::unique_ptr<folly::IOBuf> body) noexcept override {
}
void onUpgrade(proxygen::UpgradeProtocol prot) noexcept override {
}
void onEOM() noexcept override {
ResponseBuilder(downstream_)
.status(code_, message_)
.body(std::move(body_))
.sendWithEOM();
}
void requestComplete() noexcept override {
delete this;
}
void onError(ProxygenError err) noexcept override {
delete this;
}
private:
const int code_;
const std::string message_;
std::unique_ptr<folly::IOBuf> body_;
};
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/Filters.h"
#include "proxygen/httpserver/RequestHandlerFactory.h"
#include "proxygen/httpserver/ResponseBuilder.h"
namespace proxygen {
/**
* A filter that rejects CONNECT/UPGRADE requests.
*/
class RejectConnectFilter : public Filter {
public:
explicit RejectConnectFilter(RequestHandler* upstream): Filter(upstream) {
}
void onRequest(std::unique_ptr<HTTPMessage> msg) noexcept override {
upstream_->onError(kErrorMethodNotSupported);
upstream_ = nullptr;
ResponseBuilder(downstream_).rejectUpgradeRequest();
}
void onBody(std::unique_ptr<folly::IOBuf> body) noexcept override {
}
void onUpgrade(UpgradeProtocol protocol) noexcept override {
}
void onEOM() noexcept override {
}
void requestComplete() noexcept override {
CHECK(!upstream_);
delete this;
}
void onError(ProxygenError err) noexcept override {
// If onError is invoked before we forward the error
if (upstream_) {
upstream_->onError(err);
upstream_ = nullptr;
}
delete this;
}
void onEgressPaused() noexcept override {
}
void onEgressResumed() noexcept override {
}
// Response handler
void sendHeaders(HTTPMessage& msg) noexcept override {
}
void sendChunkHeader(size_t len) noexcept override {
}
void sendBody(std::unique_ptr<folly::IOBuf> body) noexcept override {
}
void sendChunkTerminator() noexcept override {
}
void sendEOM() noexcept override {
}
void sendAbort() noexcept override {
}
void refreshTimeout() noexcept override {
}
};
class RejectConnectFilterFactory : public RequestHandlerFactory {
public:
void onServerStart() noexcept override {
}
void onServerStop() noexcept override {
}
RequestHandler* onRequest(RequestHandler* h, HTTPMessage* msg)
noexcept override {
if (msg->getMethod() == HTTPMethod::CONNECT) {
return new RejectConnectFilter(h);
}
// No need to insert this filter
return h;
}
};
}

View File

@ -0,0 +1 @@
SUBDIRS = echo

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "EchoHandler.h"
#include "EchoStats.h"
#include "proxygen/httpserver/RequestHandler.h"
#include "proxygen/httpserver/ResponseBuilder.h"
using namespace proxygen;
namespace EchoService {
EchoHandler::EchoHandler(EchoStats* stats): stats_(stats) {
}
void EchoHandler::onRequest(std::unique_ptr<HTTPMessage> headers) noexcept {
stats_->recordRequest();
}
void EchoHandler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept {
if (body_) {
body_->prependChain(std::move(body));
} else {
body_ = std::move(body);
}
}
void EchoHandler::onEOM() noexcept {
ResponseBuilder(downstream_)
.status(200, "OK")
.header("Request-Number",
folly::to<std::string>(stats_->getRequestCount()))
.body(std::move(body_))
.sendWithEOM();
}
void EchoHandler::onUpgrade(UpgradeProtocol protocol) noexcept {
// handler doesn't support upgrades
}
void EchoHandler::requestComplete() noexcept {
delete this;
}
void EchoHandler::onError(ProxygenError err) noexcept {
delete this;
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/httpserver/RequestHandler.h"
#include <folly/Memory.h>
namespace proxygen {
class ResponseHandler;
}
namespace EchoService {
class EchoStats;
class EchoHandler : public proxygen::RequestHandler {
public:
explicit EchoHandler(EchoStats* stats);
void onRequest(std::unique_ptr<proxygen::HTTPMessage> headers)
noexcept override;
void onBody(std::unique_ptr<folly::IOBuf> body) noexcept override;
void onEOM() noexcept override;
void onUpgrade(proxygen::UpgradeProtocol proto) noexcept override;
void requestComplete() noexcept override;
void onError(proxygen::ProxygenError err) noexcept override;
private:
EchoStats* const stats_{nullptr};
std::unique_ptr<folly::IOBuf> body_;
};
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "EchoHandler.h"
#include "EchoStats.h"
#include "proxygen/httpserver/HTTPServer.h"
#include "proxygen/httpserver/RequestHandlerFactory.h"
#include <folly/Memory.h>
#include <folly/io/async/EventBaseManager.h>
#include <unistd.h>
using namespace EchoService;
using namespace proxygen;
using folly::EventBase;
using folly::EventBaseManager;
using folly::SocketAddress;
using Protocol = HTTPServer::Protocol;
DEFINE_int32(http_port, 11000, "Port to listen on with HTTP protocol");
DEFINE_int32(spdy_port, 11001, "Port to listen on with SPDY protocol");
DEFINE_int32(thrift_port, 10000, "Port to listen on for thrift");
DEFINE_string(ip, "localhost", "IP/Hostname to bind to");
DEFINE_int32(threads, 0, "Number of threads to listen on. Numbers <= 0 "
"will use the number of cores on this machine.");
class EchoHandlerFactory : public RequestHandlerFactory {
public:
void onServerStart() noexcept override {
stats_.reset(new EchoStats);
}
void onServerStop() noexcept override {
stats_.reset();
}
RequestHandler* onRequest(RequestHandler*, HTTPMessage*) noexcept override {
return new EchoHandler(stats_.get());
}
private:
folly::ThreadLocalPtr<EchoStats> stats_;
};
int main(int argc, char* argv[]) {
google::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
google::InstallFailureSignalHandler();
std::vector<HTTPServer::IPConfig> IPs = {
{SocketAddress(FLAGS_ip, FLAGS_http_port, true), Protocol::HTTP},
{SocketAddress(FLAGS_ip, FLAGS_spdy_port, true), Protocol::SPDY},
};
if (FLAGS_threads <= 0) {
FLAGS_threads = sysconf(_SC_NPROCESSORS_ONLN);
CHECK(FLAGS_threads > 0);
}
HTTPServerOptions options;
options.threads = static_cast<size_t>(FLAGS_threads);
options.idleTimeout = std::chrono::milliseconds(60000);
options.shutdownOn = {SIGINT, SIGTERM};
options.handlerFactories = RequestHandlerChain()
.addThen<EchoHandlerFactory>()
.build();
HTTPServer server(std::move(options));
server.bind(IPs);
// Start HTTPServer mainloop in a separate thread
std::thread t([&] () {
server.start();
});
t.join();
return 0;
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
namespace EchoService {
/**
* Just some dummy class containing request count. Since we keep
* one instance of this in each class, there is no need of
* synchronization
*/
class EchoStats {
public:
virtual ~EchoStats() {
}
// NOTE: We make the following methods `virtual` so that we can
// mock them using Gmock for our C++ unit-tests. EchoStats
// is an external dependency to handler and we should be
// able to mock it.
virtual void recordRequest() {
++reqCount_;
}
virtual uint64_t getRequestCount() {
return reqCount_;
}
private:
uint64_t reqCount_{0};
};
}

View File

@ -0,0 +1,10 @@
SUBDIRS = .
noinst_PROGRAMS = echo_server
echo_server_SOURCES = \
EchoHandler.cpp \
EchoServer.cpp
echo_server_LDADD = \
../../libproxygenhttpserver.la

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/httpserver/Mocks.h"
#include "proxygen/httpserver/samples/echo/EchoHandler.h"
#include "proxygen/httpserver/samples/echo/EchoStats.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace EchoService;
using namespace proxygen;
using namespace testing;
class MockEchoStats : public EchoStats {
public:
MOCK_METHOD0(recordRequest, void());
MOCK_METHOD0(getRequestCount, uint64_t());
};
class EchoHandlerFixture : public testing::Test {
public:
void SetUp() {
handler = new EchoHandler(&stats);
responseHandler = folly::make_unique<MockResponseHandler>(handler);
handler->setResponseHandler(responseHandler.get());
}
void TearDown() {
Mock::VerifyAndClear(&stats);
Mock::VerifyAndClear(responseHandler.get());
// Since there is no easy way to verify that handler has deleted
// itself, its advised to run test binary under AddressSanitzer
// or valgrind to verify that.
}
protected:
EchoHandler* handler{nullptr};
StrictMock<MockEchoStats> stats;
std::unique_ptr<MockResponseHandler> responseHandler;
};
TEST_F(EchoHandlerFixture, OnProperRequestSendsResponse) {
EXPECT_CALL(stats, recordRequest()).WillOnce(Return());
EXPECT_CALL(stats, getRequestCount()).WillOnce(Return(5));
HTTPMessage response;
EXPECT_CALL(*responseHandler, sendHeaders(_)).WillOnce(
DoAll(SaveArg<0>(&response), Return()));
EXPECT_CALL(*responseHandler, sendEOM()).WillOnce(Return());
// Since we know we dont touch request, its ok to pass `nullptr` here.
handler->onRequest(nullptr);
handler->onEOM();
handler->requestComplete();
EXPECT_EQ("5", response.getHeaders().getSingleOrEmpty("Request-Number"));
EXPECT_EQ(200, response.getStatusCode());
}
TEST_F(EchoHandlerFixture, ReplaysBodyProperly) {
EXPECT_CALL(stats, recordRequest()).WillOnce(Return());
EXPECT_CALL(stats, getRequestCount()).WillOnce(Return(5));
HTTPMessage response;
folly::fbstring body;
EXPECT_CALL(*responseHandler, sendHeaders(_)).WillOnce(
DoAll(SaveArg<0>(&response), Return()));
EXPECT_CALL(*responseHandler, sendBody(_)).WillRepeatedly(
DoAll(
Invoke([&] (std::shared_ptr<folly::IOBuf> b) {
body += b->moveToFbString();
}),
Return()));
EXPECT_CALL(*responseHandler, sendEOM()).WillOnce(Return());
// Since we know we dont touch request, its ok to pass `nullptr` here.
handler->onRequest(nullptr);
handler->onBody(folly::IOBuf::copyBuffer("part1"));
handler->onBody(folly::IOBuf::copyBuffer("part2"));
handler->onEOM();
handler->requestComplete();
EXPECT_EQ("5", response.getHeaders().getSingleOrEmpty("Request-Number"));
EXPECT_EQ(200, response.getStatusCode());
EXPECT_EQ("part1part2", body);
}

View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/httpserver/HTTPServer.h"
#include "proxygen/httpserver/ResponseBuilder.h"
#include "proxygen/lib/utils/TestUtils.h"
#include <boost/thread.hpp>
#include <folly/io/async/AsyncServerSocket.h>
#include <folly/io/async/EventBaseManager.h>
#include <gtest/gtest.h>
using namespace proxygen;
using namespace testing;
using apache::thrift::async::TAsyncSSLSocket;
using apache::thrift::transport::SSLContext;
using folly::AsyncServerSocket;
using folly::EventBaseManager;
using folly::SocketAddress;
namespace {
const std::string kTestDir = getContainingDirectory(__FILE__).str();
}
class ServerThread {
private:
boost::barrier barrier_{2};
std::thread t_;
HTTPServer* server_{nullptr};
public:
explicit ServerThread(HTTPServer* server) : server_(server) {}
~ServerThread() {
if (server_) {
server_->stop();
}
t_.join();
}
bool start() {
bool throws = false;
t_ = std::thread([&] () {
server_->start(
[&] () {
barrier_.wait();
},
[&] (std::exception_ptr ex) {
throws = true;
server_ = nullptr;
barrier_.wait();
});
});
barrier_.wait();
return !throws;
}
};
TEST(MultiBind, HandlesListenFailures) {
SocketAddress addr("127.0.0.1", 0);
auto evb = EventBaseManager::get()->getEventBase();
AsyncServerSocket::UniquePtr socket(new AsyncServerSocket(evb));
socket->bind(addr);
// Get the ephemeral port
socket->getAddress(&addr);
int port = addr.getPort();
std::vector<HTTPServer::IPConfig> ips = {
{
folly::SocketAddress("127.0.0.1", port),
HTTPServer::Protocol::HTTP
}
};
HTTPServerOptions options;
options.threads = 4;
auto server = folly::make_unique<HTTPServer>(std::move(options));
// We have to bind both the sockets before listening on either
server->bind(ips);
// On kernel 2.6 trying to listen on a FD that another socket
// has bound to fails. While in kernel 3.2 only when one socket tries
// to listen on a FD that another socket is listening on fails.
try {
socket->listen(1024);
} catch (const std::exception& ex) {
return;
}
ServerThread st(server.get());
EXPECT_FALSE(st.start());
}
TEST(SSL, SSLTest) {
HTTPServer::IPConfig cfg{
folly::SocketAddress("127.0.0.1", 0),
HTTPServer::Protocol::HTTP};
SSLContextConfig sslCfg;
sslCfg.isDefault = true;
sslCfg.setCertificate(
kTestDir + "certs/test_cert1.pem",
kTestDir + "certs/test_key1.pem",
"");
cfg.sslConfigs.push_back(sslCfg);
HTTPServerOptions options;
options.threads = 4;
auto server = folly::make_unique<HTTPServer>(std::move(options));
std::vector<HTTPServer::IPConfig> ips{cfg};
server->bind(ips);
ServerThread st(server.get());
EXPECT_TRUE(st.start());
// Make an SSL connection to the server
class Cb : public apache::thrift::async::TAsyncSocket::ConnectCallback {
public:
explicit Cb(TAsyncSSLSocket* sock) : sock_(sock) {}
void connectSuccess() noexcept override {
success = true;
sock_->close();
}
void connectError(const apache::thrift::transport::TTransportException&)
noexcept override {
success = false;
}
bool success{false};
TAsyncSSLSocket* sock_{nullptr};
};
folly::EventBase evb;
auto ctx = std::make_shared<SSLContext>();
TAsyncSSLSocket::UniquePtr sock(new TAsyncSSLSocket(ctx, &evb));
Cb cb(sock.get());
sock->connect(&cb, server->addresses().front().address, 1000);
evb.loop();
EXPECT_TRUE(cb.success);
}

View File

@ -0,0 +1,11 @@
SUBDIRS = .
check_PROGRAMS = HTTPServerTests
HTTPServerTests_SOURCES = \
HTTPServerTest.cpp
HTTPServerTests_LDADD = \
../libproxygenhttpserver.la \
../../lib/test/libtestmain.la
TESTS = HTTPServerTests

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDWTCCAkGgAwIBAgIJAP6wIodJOj4eMA0GCSqGSIb3DQEBBQUAMEMxCzAJBgNV
BAYTAlVTMQ0wCwYDVQQKDARBc294MSUwIwYDVQQDDBxBc294IENlcnRpZmljYXRp
b24gQXV0aG9yaXR5MB4XDTE0MDkxOTIyMTM0NloXDTQyMDIwNDIyMTM0NlowQzEL
MAkGA1UEBhMCVVMxDTALBgNVBAoMBEFzb3gxJTAjBgNVBAMMHEFzb3ggQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQC2Sk5jEnq4NcoVCVDKrepazJfntSPcgL7DIQnGA3jH4+PA6oCUhZZtKw8R6EL2
rHpqwh1jQoZKEgxnj9Uf2VuS7cjRoCJxNh8l+XYqNZfNwUrno6UlHzu6Yd9ki6E2
PqK5xLVM/aem1/fAjlcSFZfua4BvtAqW3JqpafWpigSp8n7QckQSDW9tA8Fg2EX0
CG+x3TieYEiA2Vr5493sir/cmDY4D1aBiPPlkRSeW21ZMLN0JDUFc2NgfquFTyfg
vP4EKj/XZvwo1oR6fUsPIUr9S3LL2tiJEtIiiQPl/kKp8f6NiWMZKh630BfPcFkq
fpTj3XjsIavD7t3e2jQ/73C9AgMBAAGjUDBOMB0GA1UdDgQWBBR1I164CbUAkL2q
gbLyppcm6ODFmjAfBgNVHSMEGDAWgBR1I164CbUAkL2qgbLyppcm6ODFmjAMBgNV
HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCwIQ692yKqSKjGwLthRWyYtwAC
wH4YTX9LERF+CwzkpKcFlkg0g4rfGHP3qwRn0F9x485Hny8Yq9My7MWko9Oqu5Im
N62ixU7LJE+S0MOHbCcfCgVVaaUL7y0Nx3zYyPdURmyRjou534O9/BVjZkU+1S+L
iljuShPcJc3TxNc26s912RkTN5QZkVUOmamP+by5cK+yb3ja2ymIFipO9VuuBixo
s9WcsgaDWeKCaOIp3xLd5Tnm5Q5R1QbEe4okr+5CLF49C23Oei2sR6/VsxQwG2rC
cmdgbTFUDrz2lmwxh/YI0G60QFllCPVWkJFxyNJJ+0duxUfT4jLRzUKNgGrw
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAtkpOYxJ6uDXKFQlQyq3qWsyX57Uj3IC+wyEJxgN4x+PjwOqA
lIWWbSsPEehC9qx6asIdY0KGShIMZ4/VH9lbku3I0aAicTYfJfl2KjWXzcFK56Ol
JR87umHfZIuhNj6iucS1TP2nptf3wI5XEhWX7muAb7QKltyaqWn1qYoEqfJ+0HJE
Eg1vbQPBYNhF9Ahvsd04nmBIgNla+ePd7Iq/3Jg2OA9WgYjz5ZEUnlttWTCzdCQ1
BXNjYH6rhU8n4Lz+BCo/12b8KNaEen1LDyFK/Utyy9rYiRLSIokD5f5CqfH+jYlj
GSoet9AXz3BZKn6U49147CGrw+7d3to0P+9wvQIDAQABAoIBAHKltTs2KhylJ92n
KDrwus40ku1VzaInDtMmekEhedsuBtYUJp5CjmNGi4nVrBf8TlnKkDUXZ+I6C7cu
jPok+CUmjADbWA4f3eNCTAEsB7eOdA/PqlP4mtYULC3Oa6v0JN/1SZmMht62QcnH
PBfRoOaAkhyu/WH4iQU38RuaBGjlW+29AM4h3oP807Su/7A8Wb5lki02WTSmzQf6
l2t68aHX3Lnt4VPyHE8XFiLt52flqxYf/LklMRx9IA8OGzXc6nJuq0iomW+f8ZCI
Ciz9e+U476WfcbBXXQl8ailXGI3ZAPAHuNS5qV/swHzdWQHTTUJhdJLhA7lBpWEf
IX9kLzkCgYEA7ErlT3sidqNx0Hr0/JbIKfG5hXaZ/f5fpkxTr4b4OLI/vPaV2shC
qw20ellNpAkymRjNhGnj6alxPdP6tX/PkAvdze6hSEHJa20PFpxVIIJlO1HGlMx+
klpDT/Z6pjclYNal1ojpIXkCLlWK1bN3R6IUAe+dV/LmC9REKIGCTlsCgYEAxX5m
/bzAFoBJKfEWf8nqllnGqETgIC66VpDfDPtW2NI3ZBcjLnlRgyGNJTtaKmoiO1NO
uOX6z21aSnnUFCW5fgXbMpMstz4bUDoaAXzsYen7XZVWkFSdaGiackA5Dm/iBrvk
BzaQ0sDI6njnYKoRTpaMcXTmEgqptAAyx56kGMcCgYEA0+4o1ay/MGFQB4kAijxC
szwXBVlmrKSl7WWv+VK490EIYddYeK38/aaBJOtL88A8HYxdaFIBFOXgp2+lAXzt
EWlTOwy4ozI+EZfzXHhC8bGCUj36OiNfsqw6i1Gql8IGSGC8xTpuvpLmHeCjcSBR
73GzODlNikBVjG6J4zqlQNkCgYEAvtGk/WFUT+lfx9CTpqEXsnHHymnSDAZaMK4F
deubPB/ROTpJ2euKYKMYV3MDaZvmu1+A3pIHRkgoR3FzAox4r1VFN5aQS/UMOvYI
jot/chO7te5HF2lKNclsARwghNyBjXQZnQaR47A18KclGHb6Be7cf/stGR2IXs0a
Q1n6v50CgYEA2oA29lP/x4e5iULI5uwCA2H/fzFkm9oEwwOBqjjwS6CBipQH9WZB
1jI4ddvW9kzcXWO6l2mdeT9b+jFtgIUBqeN2pgD4RXmtuP0xbkUDZgHmWv2aOSgH
IEC8l9oRus88p+5yf7mQ+/WF37Oe/RezDuM6/+ehtoUTtJ1g4XIXICc=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC5zCCAc8CAQowDQYJKoZIhvcNAQEFBQAwQzELMAkGA1UEBhMCVVMxDTALBgNV
BAoMBEFzb3gxJTAjBgNVBAMMHEFzb3ggQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMTQwOTE5MjIxMzQ2WhcNNDIwMjA0MjIxMzQ2WjAwMQswCQYDVQQGEwJVUzEN
MAsGA1UECgwEQXNveDESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAprvPkBYr6LTFOldRjTQ9zKf86tBu9kG1a4CLu4c1
2twWTf04b8lfpG5qUMt13IeI5CH9ygjkLz6gZjsGDICVokG5P5fd9k+3eIv6m0K5
rgNUXeSYJJTbfIhEw99fdI4tpu5irtnWLGGsDApF1O2NDLYh6U0+eB1OWOGhqrSU
AMPibief2jtLsETaRZrYSFknPfgrNjzcIfhnAv3rMnkEc55knV8l7UZCLgUaRPfS
4ZcTe1VJghHPCbbfQ6AEcHZaXhOlX0voAXesB5RVuyPMuhQzBfasBstjITIpdbQI
AlnFuF/vo8JRhqJKjOWek6DJyH7yjw9ZtvXsMTJCun9M7wIDAQABMA0GCSqGSIb3
DQEBBQUAA4IBAQCdghgh4hK0HUgvr+Ue2xUgAkEhQK7nvBlxw42l64zWNIkVrg3C
sGBx1/ZV7sVrrz5P8LkoZmKcgSoaZQRhiZ9P+nBj4hUz8oFYJ2xTl2Bo1UmEoz+r
z63WerLLb48HQLrGJN/V1Uodjb/eVRwY16qw0JoaRg3BGbO2k19jeNIfpp00atic
xvgxZsHuRrax4PkL6ObrASILj78AOzPmKOlMk2cbS+Ol4WJNzbqFDQaR3QXv4WSR
6td3LlJtSyMWjMnkYOOidLYsSQ5bVnWbnP/bj/apRXxX9wi7ez739Gqc4bylJgW5
Ym+TCytFhaK6z05whWCDcD6CrXyFGer/Cqfv
-----END CERTIFICATE-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC5zCCAc8CAQowDQYJKoZIhvcNAQEFBQAwQzELMAkGA1UEBhMCVVMxDTALBgNV
BAoMBEFzb3gxJTAjBgNVBAMMHEFzb3ggQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMTQwOTE5MjIxMzQ2WhcNNDIwMjA0MjIxMzQ2WjAwMQswCQYDVQQGEwJVUzEN
MAsGA1UECgwEQXNveDESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA8uWxaUPDq8Ydzmofy2hD8XQnfniY+VnkIe53aMIT
JV+iMU1G8cguXxwIK6P+iyNDMMOG6tuObng/9yfS7Zt7Ov4F4tf/S0qHNK2yfZZj
n0QISkwtFboA0nso60y0QhlKeySsVnd8Bry53kNAb5CVO9u/RH/YHP7ykqUFGln5
tX9gpCN0Iw1LUZvABH4rxDP/9mYrKRDgBkYfKJ2xLxG0IyIdM9P0F8RLs/lqENVQ
w1X4TNkFL94VYEKl/5OiepY4cx2zGpqlSYcNXEqfR9wBdCpH0+mMf8FFxE8m7W26
9BefNn5zg7U2dJXIm1YV0lnjRt92P1RiUvKOkm4Og7WpowIDAQABMA0GCSqGSIb3
DQEBBQUAA4IBAQCT/OwW7fWCWrfOgkDQNERhE2RPz2s/39I6jHYJfnQaboPkSukB
ESFvk10bhQpJww5C7XHH180EneY48VqIBeSUwmr+KL2YkETwktj5hg4i/KvaW5In
Gb38GB2o10OrMNmM2TNi4eLb81FNZtdm01npH97My6XAkbxHr3e3nNInFUETxmhE
HCIp45Ug2I7vxtXMVqI8WcCBLuKXTkQqBCDXMwov83GllzAkczbkQKdeMgouEtou
Z9zooiMOiPQCg6/3xG0idBIyCRH79pPR079o2q09hY2wDy211e3tAnPygWhSivtO
P9I6H4OY2ZDGgT6HLbmT03RfVbPulsuaiBG+
-----END CERTIFICATE-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC5zCCAc8CAQowDQYJKoZIhvcNAQEFBQAwQzELMAkGA1UEBhMCVVMxDTALBgNV
BAoMBEFzb3gxJTAjBgNVBAMMHEFzb3ggQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMTQwOTE5MjIxMzQ2WhcNNDIwMjA0MjIxMzQ2WjAwMQswCQYDVQQGEwJVUzEN
MAsGA1UECgwEQXNveDESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAvGL/otOV3fqmBgLFBctGT25NID4silHADcZfRs4p
vJDb8d9PrKyKkz3HfWzaY/5MwNbkuyTfiqX1hm5fSGCFTp7wS569s1s3neixTtTQ
zDgW+m2oMIOapY8OL9rgU5TCUXqy3lv4MsFbcj2z9Gbnp+kLbsLGlxdO1MGzEwKJ
GsfCjmbtIFc4+6gtmm6SwY9WZ6y64OUmyBxbYKZmaYn8XM8l6NvXVeo3MzGkQS8m
jQCWfP+QSiCwm/GjsG/uRGChp9rvKDbf3D2pB79el4q00AWi9pH/rheuO8NJpuuq
Sd4QffzbDU6qrcux3Nb2N5cDPi4pq4xawgbLOcaUaQj4CQIDAQABMA0GCSqGSIb3
DQEBBQUAA4IBAQB/LT0Ufq79Km7V7+bmrpH0wD1n5tCXC411lz+G2rJnNLuYPJSI
xXZLXv8r3zv43hf4BonhsgFDk7UuXVAFDIk5N9VWvnVgZ80q51NLl7UOr6XMwgMw
NtCarnDFpvZRcsWAnG/4MKPOh7IZibf/3hHqPpeU2gY7Puxre/17RAXA8dwPx048
XGz/V9kyMSQ7Rv19LoPqxPWCP9/tdYhyEjh+ywBaZtmmMyutnlrrL1CJ4Pcai9w2
otb/n2dTjvaJtV8Nh0LWQnltD3IFHQAXwD8ipNQKQErfhhpAeWiZp1SlrmheIKj6
Zu2u5yeK3oy7FIwa17+6dYmQGKiPdhPMkHt/
-----END CERTIFICATE-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC5zCCAc8CAQowDQYJKoZIhvcNAQEFBQAwQzELMAkGA1UEBhMCVVMxDTALBgNV
BAoMBEFzb3gxJTAjBgNVBAMMHEFzb3ggQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMTQwOTE5MjIxMzQ3WhcNNDIwMjA0MjIxMzQ3WjAwMQswCQYDVQQGEwJVUzEN
MAsGA1UECgwEQXNveDESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAtMslNMNsNTMtnUgSJ5s1WDkUNUwU0uM0lTW9rdYW
T0ZUmYBDCG5IQotW7oQkJOcaBhXna6RDpXW9zMW0qae080whJwzXwVBUqJtt6UMr
MDPtgITzL8b8ZOVgPFWlPnMX1/hY48iL74J5dDziE/KlNvkGk+B9pthdge6syqaV
kKoGAuhrORoYS3z4/5yXAJJKjvxxZSUmSdjamuD9Th43lJyqbATeFfRmQKbJGUez
T9NUC4MdCISKxMbd5e1zC0XBSSGSR1oxS+lqq+xslkY8OfJ+gs9FQnq7goCxhG7k
J46ys59dZtDit0t4k53InzoBN5pBoh3NUMXGPGzLgtjoaQIDAQABMA0GCSqGSIb3
DQEBBQUAA4IBAQBz8q9EIIlkTcBq+HmlLoqjOeL4DG7BsV6PaZHGeqana/jD+QGF
fHY8hYbW/B7eRiAkKPyUI5/N8hrrYz+pnPIKxZXB/RNf6jwMmOLne7MB6dtacCeW
shNIA123SkOaCKpan6zzeKoPt7W/jKf14MNVYru58gJYwEs6WIGBt+TPEV4Rx7gj
VsGRfU5pVFlWTe//nQmWa5IvKgpxIbJf9/2A7iuHQtl0n5slKp7EEk7BV+X2YeGM
Z4W27CiRGSMvmP9Dq+r61ZNDS9xD3azbThcYNL9p55YfTOjFmQvFOdWGDLDA4CDE
qEbhmsYv74Q0oOEcHgH5dxxKhZ4Kh1s2vc6E
-----END CERTIFICATE-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC5zCCAc8CAQowDQYJKoZIhvcNAQEFBQAwQzELMAkGA1UEBhMCVVMxDTALBgNV
BAoMBEFzb3gxJTAjBgNVBAMMHEFzb3ggQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMTQwOTE5MjIxMzQ3WhcNNDIwMjA0MjIxMzQ3WjAwMQswCQYDVQQGEwJVUzEN
MAsGA1UECgwEQXNveDESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA3+ThdcTL2XjqQ0KeOGiV9HyDHCXl7U63XjYzBzQe
HA/ZEPlqCTfd2iOWbVpIFgDOyxDwJ+tXv9QuJnpj8sQso/BVN1m4lrT/MpjB1O+X
AUcRSlgjwXBEEkGTqi2FgQIMIhyKtlIq7+uhY7pXbwQrzweEbhhWiYC32sIGVCN2
qyXIYokLODXYFiHkMGx2BrLmvaUbFH7zOnjvGUeYLDDcS0/u9l/mpKeePsiVTZ/d
diLNEfzfapD60h0e0o0f6Nf5DScllCQLoVEIgMc7IcySUI/CMuWgCA+IPumFFSEg
HldFZhIONszJtYkUCAiDKOAA7ro9nY9HtPYoAxSbiv6xqQIDAQABMA0GCSqGSIb3
DQEBBQUAA4IBAQCvI81KpgGROxFFVII5EIPuWhH0DsTBlfjO/fzrjhL6dqphDZpA
SaD19XSia/NiPQO/4GhkFmx3w7SmNisJ33ecBcmR1HszmtVsJVoUOU1hDmLaxyJc
vhSRpCyIN9FPTACZmgWbRN6L90dQRoKPwYiLBwDX7Y20c0QF+Nvf6SwJBSqVHh5Y
hvwzhtYZwBR1Ufhk8g+CuGtn6ogsLx37AoG2Ysdhx7kqrSvPvahLw1Kgap5XObIZ
e/YvDqWpPC+ImXguFhSKTqL6m7cdDC43tKV8iSbkAdweAvs9XpgqoX0IZ9xKtGai
0+TX2AcoBPvQHCujYWnV2N6roDbYes8vLC9X
-----END CERTIFICATE-----

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICHDCCAQQCAQowDQYJKoZIhvcNAQEFBQAwQzELMAkGA1UEBhMCVVMxDTALBgNV
BAoMBEFzb3gxJTAjBgNVBAMMHEFzb3ggQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMTQwOTE5MjIxMzQ3WhcNNDIwMjA0MjIxMzQ3WjAwMQswCQYDVQQGEwJVUzEN
MAsGA1UECgwEQXNveDESMBAGA1UEAwwJMTI3LjAuMC4xMFkwEwYHKoZIzj0CAQYI
KoZIzj0DAQcDQgAEz2PAexkUGpUHzTaRNL8OtwoYurZpsCRaxTEvw0GTUOv5bpe0
ituqBHvSfQEdSeN2jfdHOmeQ82KTSpqiNbW2ljANBgkqhkiG9w0BAQUFAAOCAQEA
VQ0Pf2YpNwXIxFPKmGykZKlVH+L4m0La6B7luho+orMKdKOKuIoDJZJQbGYwFn10
kJ/hbVNeaYffB5b763ECPzuHIYokaPRGuXyixSlI5d4rzy/+GRV1ehSrRf3hXzR5
0SKPg1yxkwQZKbTD1R2YT3FzBQ7G5fueWhzh5DXFjgoT4eBvOZ+J9zySyL3AiEg7
tMLinp+vQC/wKt4tFCSqGdfpU/Q/irF70LX+8/iMVbq2mmtWYNCWcrWF4Rv0WMPO
+bB/C9/Mk9w4+J/N0caiCq4v4XJuABDHCEqZF1/FtuyZSTAaLbALdpKnLAk8nVkf
+a7gK3RK4Wl/4CTi9joQKw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICHDCCAQQCAQowDQYJKoZIhvcNAQEFBQAwQzELMAkGA1UEBhMCVVMxDTALBgNV
BAoMBEFzb3gxJTAjBgNVBAMMHEFzb3ggQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMTQwOTE5MjIxMzQ3WhcNNDIwMjA0MjIxMzQ3WjAwMQswCQYDVQQGEwJVUzEN
MAsGA1UECgwEQXNveDESMBAGA1UEAwwJMTI3LjAuMC4xMFkwEwYHKoZIzj0CAQYI
KoZIzj0DAQcDQgAEhPeKFAokkp4KmIVE7WqKc8w2k0YOwudHXXZ2fAQNxZcGHjml
5Mj9evH8Ezd9OoJ/u7cnuFKorBce4ypHZNA8zzANBgkqhkiG9w0BAQUFAAOCAQEA
L85cwJHc0QlvTqnngGS3F+1yElCROVUbdqLBXw1lyToNH280VtiPKEeyCzT4F76D
GCVRS7keLiR/IkhBpZkYi+YYTG649rvfsOI5ab74lyv0PiBrLTekEO3xi6yLK3bm
4Eo3mNIFE7qzUBgizWZyE1ri119P5K3+TRUz/TIafjKfkxUw4w8I3CnZzFzm3YF7
w47jvAws1rEO4UTT7paTp0g0AIP1JIuk4dN8wLL07fSxWZCNCQqTwwrT0J1xXdH5
jsgAxXPQ5GwetoP6pFs6F/hwxR1RZs99EmICvBeLnkWLZG70do+EOfI2paya80Gd
lcir1YxqwEwt2J1Xm5hmxQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICHDCCAQQCAQowDQYJKoZIhvcNAQEFBQAwQzELMAkGA1UEBhMCVVMxDTALBgNV
BAoMBEFzb3gxJTAjBgNVBAMMHEFzb3ggQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMTQwOTE5MjIxMzQ3WhcNNDIwMjA0MjIxMzQ3WjAwMQswCQYDVQQGEwJVUzEN
MAsGA1UECgwEQXNveDESMBAGA1UEAwwJMTI3LjAuMC4xMFkwEwYHKoZIzj0CAQYI
KoZIzj0DAQcDQgAEMLrfx4XVO8FwVtZ372TciaxM+Wc/DFUfnZLmw97OHHks6SAR
B/bnpVu23QsD/muYSiB6vvzLYxCJuYguH6NWvTANBgkqhkiG9w0BAQUFAAOCAQEA
Ik+T3frtQsOIWv8xN/wMR3p+qZCvqOUadEC4jO3b04w4BuVSKEMerkRu6Y+n8ZQZ
p1LkRtwUc8+5B9Gf8HIQijsD6BXCtpJB7M0WnyyUBvRVseNn2VhiCsNYEVEbeC/T
YYMI5l+sr5j4VS3smj+2YsjGo3mHMGXfQ4RMmpdLsDo/GEHxZ21DD8bS+41aYrZ7
JGTC1yn0iiM33brRvyD49sad4AJ1CIvD5DHmkiwC85AW1tBvpolTA9xpfjPQDgM+
MGMte40zM/HVWDMRTUWNhuPSp7XVZu7glxK90AYfsHRqb/DXoR0Df8qUQygt1et0
JqwbV1NafTD2CGt9SFhNNQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIG6+DnEONCjOb4bmys60IISRCPxn26Iga3ie+tKLy6x+oAoGCCqGSM49
AwEHoUQDQgAEz2PAexkUGpUHzTaRNL8OtwoYurZpsCRaxTEvw0GTUOv5bpe0ituq
BHvSfQEdSeN2jfdHOmeQ82KTSpqiNbW2lg==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDjTZCEXwIl57daCOp7/0kv9HBjJIM+DBMuYGQx7j/vzoAoGCCqGSM49
AwEHoUQDQgAEhPeKFAokkp4KmIVE7WqKc8w2k0YOwudHXXZ2fAQNxZcGHjml5Mj9
evH8Ezd9OoJ/u7cnuFKorBce4ypHZNA8zw==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIFvllTFRRZbSME7guGQMnzS05hbmMAWTxSP4CwknRo6IoAoGCCqGSM49
AwEHoUQDQgAEMLrfx4XVO8FwVtZ372TciaxM+Wc/DFUfnZLmw97OHHks6SARB/bn
pVu23QsD/muYSiB6vvzLYxCJuYguH6NWvQ==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAprvPkBYr6LTFOldRjTQ9zKf86tBu9kG1a4CLu4c12twWTf04
b8lfpG5qUMt13IeI5CH9ygjkLz6gZjsGDICVokG5P5fd9k+3eIv6m0K5rgNUXeSY
JJTbfIhEw99fdI4tpu5irtnWLGGsDApF1O2NDLYh6U0+eB1OWOGhqrSUAMPibief
2jtLsETaRZrYSFknPfgrNjzcIfhnAv3rMnkEc55knV8l7UZCLgUaRPfS4ZcTe1VJ
ghHPCbbfQ6AEcHZaXhOlX0voAXesB5RVuyPMuhQzBfasBstjITIpdbQIAlnFuF/v
o8JRhqJKjOWek6DJyH7yjw9ZtvXsMTJCun9M7wIDAQABAoIBAQCGJrJ4Yf5uO5Q8
vqjVDeVzVu4+F/pPlMrddg33knCYaWBg246fEs0rRdOwsiNgjoRr2ZWTCthd0uvH
lVHmmUbLyEm+iviCB9281hOK/ILdKbyl1xk6xbJbXmDFoGHzK7o7h65KtOaHywZc
oZ9SFNfaFGjwh7/tcNbq2I/1A1nZynTko6iLVpgV0kkQCpaweFYQMXWv/ELkFHeL
7tFIA50XFXbNDqnAuaW6XrIrW33ZeOJfruF2OG+QVWyTBgczk0fodDZJFS5MbDXu
UB+W1nDhakZFbugtDSXMd3BMnLZFdsa12FYTMNG050w2OTHOF5ILX+IFwzbnlboX
fbralUKRAoGBANnjttZWcNtM4L8JkUJwXsDgTD/0d+iu5gCmSs0p9E4wGbWlu+In
cNE6CV4Dy+GMk5GzXFR+GV/IlPvVzSOmbFsFdBi8L3c/1IrPbQv8dEosKm6BvV9O
0zIBaPuzgU2yyBFxdpfsHynAZoLdY3rq6IJdNmJrmDcVgEfBVOExU7EVAoGBAMPl
hqNmGi3VwHPQ2iiuM48ijPbuS0hK3dUjx2A4otOYAro86Q4egcdtyBOONhBwD89h
I6BUo+vReV6ikI8LQfoplBaRos7qJ2e9SOxmRIJGAZPkGlFF0uljxKZ2Hdtmruae
mJOZqKCa38sTnqWyXV/xCXE5X94EXuJP17L2Bt7zAoGAOG7gFheBV2tL8m657qlI
AVCWryHURLG35IctbIHnQrD2l7N7PBHXCHmtn2oATkSom94GleOrEsHSxH8ViJw8
CD8bWKS07n/bvrAGoEocnHFf9AsqTxsNXDA9TqOpY8RgSRRIEQUY9Sld45sPfvCE
k+8sfMU9QVcSSINsRn8OHBkCgYEAgzBGD01EQOfB/42hW9b1fmjEAGYrElnY33Eb
hyvGl29YfEJoTOVPQjAZ6ka1nCJ/5ACIrEmikT1yS1cQ+kquv4pyuv6DCpCzHP0d
Rfti699YFSOQIFdjXJtMybGWYyUMAjO5uDcSP6QYNVaJSyv87lBsY1/p/LPumx6f
NCEhDtMCgYBpcK4f2E+JjaGHABX5OS5Soegtgj7JjZv/M3N2OLvX2xrlkxAEPlJ5
nvaVjikBcOsj3/+LDrBMDoEbG2JFaopiue8pW+tZWfbhJw0pf02f+hgHjCaR+1Ny
Qqd+ERH7vFjwzc3UuZay1NbU9/wVMNsL7jWKnvsKKCk9PxG2OzP2iQ==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA8uWxaUPDq8Ydzmofy2hD8XQnfniY+VnkIe53aMITJV+iMU1G
8cguXxwIK6P+iyNDMMOG6tuObng/9yfS7Zt7Ov4F4tf/S0qHNK2yfZZjn0QISkwt
FboA0nso60y0QhlKeySsVnd8Bry53kNAb5CVO9u/RH/YHP7ykqUFGln5tX9gpCN0
Iw1LUZvABH4rxDP/9mYrKRDgBkYfKJ2xLxG0IyIdM9P0F8RLs/lqENVQw1X4TNkF
L94VYEKl/5OiepY4cx2zGpqlSYcNXEqfR9wBdCpH0+mMf8FFxE8m7W269BefNn5z
g7U2dJXIm1YV0lnjRt92P1RiUvKOkm4Og7WpowIDAQABAoIBAQClLFkmcfRfnQur
0DLqyW5ahVhOAohUGDKweE8vJ7qJUa0jxZ8Wz7/o4VEVDCIOT95jDLN0hfUnXhJx
ad1fwrlb9l3eUm/CrV2gDvYvvNLl/Qd+LqsB+UiR2TqMN87km/owH1IqQnpOwovK
fwUUWMeCuv4oAJ/fp+cgnaMItmK+bhr12OvTS2X739op+6c6fNZmtXAyQSckTs71
TTErNVu9lEs4epBZ8YRvb8dYIAEWgqnHOOz+RRmGyrQevIV+9TBK3L+bgx6OAfDq
TNdmdMeHHpXW1TI4JxYf2K+9esS+FL6Hf8zHsRNtRYu9pS6jkYw0REtW7N7E8roB
dZSsI8NRAoGBAP9ngY6TJAS6jV7n+CID6iyVF+7OCU8LiTsHr9nBa6sxL1VFED5o
Gxb7z4UyjVWaSaKyGMnuQx4k5t814nKmYnBRCuVPDhnn2ztNC6ixjV6s06Itc0t8
8KQy/FdsYAnMvZ+je3+jIWR/jK3lH6K6gzThk/oeAlukC5bZjchF8l01AoGBAPN2
uCbob2i3Hyn3mbAmoNXIqGSXIUrvyh5i9u0gwKMU/QNBWHtm1erUszZnPYIOaFhC
TsrA1E5xOiTqwKkd6Lg+fX7g6eCV+c+rJbl7wYtyKVeTw5m6wzjsoUqacLjnVCK6
WLaZn/IWtujDmmMYwbFiP13frGiUFEvOVtASQ753AoGBAOZSlG5b2QZ+qZClxon+
V8beqVeM7K4g7B+Uvgu0twEJ+PJ/trdgsNVYPnuS7Av/eFpFG7+2o0Zi5uTyNgVI
cMty+k1yrnfENFtVDqeRfribSLsfG7M+t9CLvi6kqDMONQ7qoiunlCyKLfaAAriA
VGRy7TyIpX25AU6HYKn0Ei3lAoGBAIFSwqcIOIWrIAau2xhSrIRivfAQx0KC9R4G
+5siFrGJ6IveHh4OlfrTWQ7A8E8xUAPx1OCmZR/1zSjm+cfbd07HAupulk0R3UyO
YM+SCVEFJyi5+OCj8CdAqrxyJQZS+sInsg/ssqVpo2co381bzSdoRLico3w5jD5o
MHz99rYpAoGAMBsrsbhhOvPMTF8NPu4oEksEUTSc8WzbMaOj1gppMKLhrXvzjCqP
ffpgEKcTADCiQ+I/L5amAUm314zcAie8dI/dBSe8dxgGHmtMnOgskMxbFO55C62y
pbYlw7w0FtUo0DKPjhW5w3cYRgji4QfbVZeqAaNyIafWMhuJP1H2v7E=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAvGL/otOV3fqmBgLFBctGT25NID4silHADcZfRs4pvJDb8d9P
rKyKkz3HfWzaY/5MwNbkuyTfiqX1hm5fSGCFTp7wS569s1s3neixTtTQzDgW+m2o
MIOapY8OL9rgU5TCUXqy3lv4MsFbcj2z9Gbnp+kLbsLGlxdO1MGzEwKJGsfCjmbt
IFc4+6gtmm6SwY9WZ6y64OUmyBxbYKZmaYn8XM8l6NvXVeo3MzGkQS8mjQCWfP+Q
SiCwm/GjsG/uRGChp9rvKDbf3D2pB79el4q00AWi9pH/rheuO8NJpuuqSd4Qffzb
DU6qrcux3Nb2N5cDPi4pq4xawgbLOcaUaQj4CQIDAQABAoIBAEbdgsZwi2bGW9/U
+OJ2FgvZNUAUw1KA8Br+bWbINOEPKP2ygFk3SqWazsk6wmhtB9hevgo8E68nQNYB
/OvQCzWJCmGX5Eps3N4U42YBHk5EJzirOWKOjwUKbE7KKiqmwfY+jrygohwNqmWu
GlysvSXnv3o54NSDWw8jkkZrZMHij9OMvbWE6gzaAyk3JIRtnVVvuOEflbDwRyzb
0BPygswDHaZFHVSYwiQSj4v7yPNSpiLSV8qHwmhRqeJM4Vknjhspf/ojFDt4pkx3
k/lyga0si8YTzeFYHsdzIRAiAqbTy5PH7KQ/oGOH1BZ7gynFd71GiR3sMa1VhCho
ukZcBIECgYEA48hE7fT04aqWeqBRKrvDVRGHFXlXnlIj2/KVSFXUxuLyyVGpkEro
d93pxV8nOI4wVkvmgHA/TVgJFDSMaz7kGvFm+/IJi0XYwpGapepoAM5+M0GTKmIO
OFor6EwGof7EAqUwA1txccFiLYNq1VszPkp6Vc9A5FC6z+oeMga665kCgYEA07ld
ZCxX6NT6ZaI1xTGLO0I4JbkK7wpMRpifBG5AWTkghCkYjL6JgyNYt7f+GLmWLYwh
b4Aqi2t99HMAmfWpefbEFTvbSrzTuNpp9UxxpFxDZmrI0Lyh0K89WDQz3vdZX1CW
F9Cj0pi8DdT8wHjx2aEip0GEHL0r8QgaFV0utfECgYAwhRBpv6wKBdRX+p5Pg/9v
t3QW1uobB1QsHdg+uEnCs8UTrRl5avtpzRNmv4YTPfZ9610GLuLNDtugOjFPBu5/
7rwJHt15lT8+8tUGUpAk29d2A5ndhVWSG2MO8GLZTvNhvN4lWO0pVhAbscqn9+1K
b4LvlF42RBwG/c/AyD44wQKBgQCiaYJTkpSI/b9q8UiwEPZOKdjqTw0tG0gxq2fo
PS7Ngr4Hw4KajJrt5tJqdBDOJKcoH2OSpc19D6MaPpHKiKFN9taptzXcGgeLuLgp
PE+8li35xZZwURMMAzFalQjpU/LR4/6PHDRfG1y+e5C7kMttd2ceyGowpYisp9ti
Yg2v8QKBgENPVPXCNOBLjdBj0W8Jg4aEE08547wEZLGg+4yp/U4SNyEoFO3WYlHb
P0V2+LqYEYKf23VCxTOoG4r1Yt04G0cWHZAyII298IK5iLXGbEChx6XgWhcpHQdg
2OyiBd591Ymwx80CuQM8vRfHtlfL5+47fyUYLlPBFS+1QreMDaAa
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpgIBAAKCAQEAtMslNMNsNTMtnUgSJ5s1WDkUNUwU0uM0lTW9rdYWT0ZUmYBD
CG5IQotW7oQkJOcaBhXna6RDpXW9zMW0qae080whJwzXwVBUqJtt6UMrMDPtgITz
L8b8ZOVgPFWlPnMX1/hY48iL74J5dDziE/KlNvkGk+B9pthdge6syqaVkKoGAuhr
ORoYS3z4/5yXAJJKjvxxZSUmSdjamuD9Th43lJyqbATeFfRmQKbJGUezT9NUC4Md
CISKxMbd5e1zC0XBSSGSR1oxS+lqq+xslkY8OfJ+gs9FQnq7goCxhG7kJ46ys59d
ZtDit0t4k53InzoBN5pBoh3NUMXGPGzLgtjoaQIDAQABAoIBAQCDca77TZm+lBb1
4xBP+2gtXFJ07Xu2V7pDQW9GlABkYp3ffIE//+5Jc0Ug5LHioqMpCYPXnvYPtiro
VlWjg2rr+M2htpSm1+C1NUwT6+HaieaDKiYk9mdKc/oj46DgZo1PWCBIe+ZBnHrx
J9K27hNwhF1j5jN0uppBfWXjcHoNUlYOl1+goszxiX0GIFn9xq0EDIDFLbKuw75l
qTDoMFTBGdTgSNjpL2D7WZonlpbd7rOjzOAvELC/Vd8ytt9bzG5IvsLeFU5JXFY2
KY4hAv8tbH8vMx6UIr5VfNrO9kNqJkTYSruE1rmTP0QX12y/EEWEG37OO1Onk4pz
Ka6oaUABAoGBAOyU/KTD4J1SIuVuPqHt8/DsknPdXPAhKaPb77E2d4B0wG3Pdk6G
IQgEhVikK/pGnQk4lhug1fTNWUujVTgD1op0l9rHHwyus5aMj9cJDP2naUmBNLXI
jjw54i3gYv4v69D7p54sYV2PRMup6tjvUXTr4riD2ErDb/hBkMZu5GlBAoGBAMOh
8V9YJ97tMkhwBvYdRkPp+H4JgnPmXzJlq+Ojl/IsgStHSwekaZP9k//WrFJ0+w4I
vIAjKgfeSoGJe2vtlPvq0YUkor+zKO+0FIYFexd05tb8LPf4HImkHEcPEMkUz9id
PD1W4KgtDOlOpPxJ+K9CbWtSkanoawbaFccWO80pAoGBAMnDtkN+mg0Rg5lPkDLh
kO2jlLMj0qMoZS7N33mvDfOLnqEIEcmeMoV39ZFHUgo8NqkqjTo6zL3ec41CWudO
vqWOEgQPVbenNpnqfrHRkjaxk+WQP7fYtxU9+FtPxp0pmV+9f5IyH0d/bBiVbShd
0YZ5tf3O06PPUarTn/jbkgkBAoGBAKapmBA58f2g5W2Awu15ExtYgDIft9s1L3Sn
2UAdZp0R/Rj5q5nfH3LMXQFfyX6V2iuilbQ0QOJjJeYlUdgolvvmmIhtJZla8E0F
hVaH5M2e8enE/CpkXSuFe/GtjAdCi69mhKNdGBcuCgnYzgWAnzPvy3fa5+1v633y
3Qq2jkvBAoGBAOPJU8nE88jiu/nHnq591z8gQmsWWPBOuhjqrcvgBlsu62G02G17
0j0iyp3ITvZIBgCl2kr/c+69DcE2TNRaFC9CePMWr233cyJ/Z26x866RK59WOcyT
/+upj6UI5/vfrGDEiDYy3r8VgniAVmADVfXQdmxiH+KFuFPJjHfQwoPg
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA3+ThdcTL2XjqQ0KeOGiV9HyDHCXl7U63XjYzBzQeHA/ZEPlq
CTfd2iOWbVpIFgDOyxDwJ+tXv9QuJnpj8sQso/BVN1m4lrT/MpjB1O+XAUcRSlgj
wXBEEkGTqi2FgQIMIhyKtlIq7+uhY7pXbwQrzweEbhhWiYC32sIGVCN2qyXIYokL
ODXYFiHkMGx2BrLmvaUbFH7zOnjvGUeYLDDcS0/u9l/mpKeePsiVTZ/ddiLNEfzf
apD60h0e0o0f6Nf5DScllCQLoVEIgMc7IcySUI/CMuWgCA+IPumFFSEgHldFZhIO
NszJtYkUCAiDKOAA7ro9nY9HtPYoAxSbiv6xqQIDAQABAoIBADvLpgdM8uB28aHb
BeSs0ffE1UNMP9ccIc7wjLpMygnWlbN52T7zA7ZBsOgtp3jw6EnFkU4oBXx/D4BJ
Sa5JhF6YTjoFJU5ispFxyLq07lByCYUgexQrhpKVnvlhRPmKcXEezFEbKsp+1NCM
sNM+evDE4jKBH4ODtBahoQ0Rl5znb3UawODBsFLZabUNK/+qO/cS+veDI2y0bfg0
hlTxzEJRCWlQcG0/IYEOIo/69qihtUYi622SLDOxGRf6j4HHEAIQvGmccWLhRNtE
5va8dxn2lilNn9/zc4pLJrR9EcPjP3mMsrFwrFMJU5KdRaJS31bIEdx/ZPLD/ty+
Og+TxnECgYEA8c5qQ61zR+yNfcRiNRq3irYyJI1/QtTzxEY/M3P03R6TeXHpStkH
A4bh2CYHUUxqDDH+4lzPG1gTg8J0U4WHcilsB7n9Vs9m+KKW4mmtHoZxf0m6ZCNA
6NQHeDcJTa6Pnh3qkG0eqqtBmyovg944EpAx5Hb5J/8xHfEi/yYAdz0CgYEA7QlN
GbfqwR9joO8inrEfiP/unHPsw4tcJVwK4vrH81rvggv9mKfJDJCe+XEODv/BD/mZ
fAtwB2DrkSFyzNJgZjpKrdc2phY5GucZgwmoKhydIENmc8lC78be71fBhkeDn1BI
qOOIn5uFZ5RW3ZHgpC0iaOjJnZ+crUWDlPei6t0CgYA4ftG1HkFg/JPSXp/TaHqZ
nhf5ElY5dye9I+yASQdc4lfyd/rZ0fshh9IcfkYXaJMeZk/281gwO1PT5QrouLn8
olDrPTlDnxGf5dz66VXZW/AInWE/JD12KJPWMoWp8K79cl/rfpM7rOeXKTQQy0qu
i6Icju+HuMfxBvX2Rxq0wQKBgF/Vz99jFb3pM/3AUa35jPd2CoIk+IgDE0ljkl88
55TDomxg7rJGvCmhWzUc+YMk8mjEEMMvWKcTD4sJDAI12JXcYY8xoT27ZHE1GIJ7
aAtGsFx4A7cymyaYsE/ymiLxTQ0fh5EJFZb6aRB80DYbIckfGndyDvn0q4L+xPl4
udzdAoGBAIiihXYJA6HeLv2GYRpsTUxH+/HHNvla9YAC/Fkr7nSPWvLqtFWtU58N
ASPlRk3/+YTGnEZen6m8xKXm5LI1pMMQFXx/kGBOMKko5JcDbyTtkw50B/JzFCCV
akwQOl/AmIx2LAJJUDcVqJMPkTwoSQoMhTXZDqEjszVBv4gzBFwH
-----END RSA PRIVATE KEY-----

13
proxygen/lib/Makefile.am Normal file
View File

@ -0,0 +1,13 @@
SUBDIRS = test utils ssl services http
lib_LTLIBRARIES = libproxygenlib.la
libproxygenlib_la_SOURCES =
nodist_EXTRA_libproxygenlib_la_SOURCES = dummy.cpp
libproxygenlib_la_LIBADD = \
http/libproxygenhttp.la \
services/libproxygenservices.la \
ssl/libproxygenssl.la \
utils/libutils.la
libproxygenlib_la_LDFLAGS=-static

View File

@ -0,0 +1,61 @@
%{
// Copyright 2004-present Facebook. All rights reserved.
#include "proxygen/lib/http/HTTPCommonHeaders.h"
#include <cstring>
#include <glog/logging.h>
namespace proxygen {
%}
%language=C++
%compare-lengths
%ignore-case
%struct-type
%readonly-tables
%global-table
%enum
%define class-name HTTPCommonHeadersInternal
struct HTTPCommonHeaderName { const char* name; uint8_t code; };
/* the following is a placeholder for the build script to generate a list
* of enum values from the list in HTTPCommonHeaders.txt
*/
%%%%%
HTTPHeaderCode HTTPCommonHeaders::hash(const char* name, unsigned len) {
const HTTPCommonHeaderName* match =
HTTPCommonHeadersInternal::in_word_set(name, len);
return (match == nullptr) ? HTTP_HEADER_OTHER : HTTPHeaderCode(match->code);
}
std::string* HTTPCommonHeaders::headerNames_;
void HTTPCommonHeaders::initHeaderNames() {
DCHECK_LE(MAX_HASH_VALUE, 255);
headerNames_ = new std::string[256];
for (int j = MIN_HASH_VALUE; j <= MAX_HASH_VALUE; ++j) {
uint8_t code = wordlist[j].code;
const uint8_t OFFSET = 2; // first 2 values are reserved for special cases
if (code >= OFFSET && code < TOTAL_KEYWORDS + OFFSET
&& wordlist[j].name[0] != '\0') {
DCHECK_EQ(headerNames_[code], std::string());
// this would mean a duplicate header code in the .gperf file
headerNames_[code] = wordlist[j].name;
}
}
}
} // proxygen

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <cstdint>
#include <string>
namespace proxygen {
/**
* Codes (hashes) of common HTTP header names
*/
enum HTTPHeaderCode : uint8_t {
// code reserved to indicate the absence of an HTTP header
HTTP_HEADER_NONE = 0,
// code for any HTTP header name not in the list of common headers
HTTP_HEADER_OTHER = 1,
/* the following is a placeholder for the build script to generate a list
* of enum values from the list in HTTPCommonHeaders.txt
*
* enum name of Some-Header is HTTP_HEADER_SOME_HEADER,
* so an example fragment of the generated list could be:
* ...
* HTTP_HEADER_WARNING = 65,
* HTTP_HEADER_WWW_AUTHENTICATE = 66,
* HTTP_HEADER_X_BACKEND = 67,
* HTTP_HEADER_X_BLOCKID = 68,
* ...
*/
%%%%%
};
class HTTPCommonHeaders {
public:
// Perfect hash function to match common HTTP header names
static HTTPHeaderCode hash(const char* name, unsigned len);
inline static HTTPHeaderCode hash(const std::string& name) {
return hash(name.data(), name.length());
}
static void initHeaderNames();
inline static const std::string* getPointerToHeaderName(HTTPHeaderCode code) {
return headerNames_ + code;
}
private:
static std::string* headerNames_;
};
} // proxygen

View File

@ -0,0 +1,82 @@
Accept
Accept-Charset
Accept-Datetime
Accept-Encoding
Accept-Language
Accept-Ranges
Access-Control-Allow-Credentials
Access-Control-Allow-Headers
Access-Control-Allow-Methods
Access-Control-Allow-Origin
Access-Control-Expose-Headers
Access-Control-Max-Age
Access-Control-Request-Headers
Access-Control-Request-Method
Age
Allow
Authorization
Cache-Control
Connection
Content-Disposition
Content-Encoding
Content-Language
Content-Length
Content-Location
Content-MD5
Content-Range
Content-Type
Cookie
DNT
Date
ETag
Expect
Expires
From
Front-End-Https
Host
If-Match
If-Modified-Since
If-None-Match
If-Range
If-Unmodified-Since
Keep-Alive
Last-Modified
Link
Location
Max-Forwards
Origin
P3P
Pragma
Proxy-Authenticate
Proxy-Authorization
Proxy-Connection
Range
Referer
Refresh
Retry-After
Server
Set-Cookie
Strict-Transport-Security
TE
Timestamp
Trailer
Transfer-Encoding
Upgrade
User-Agent
VIP
Vary
Via
WWW-Authenticate
Warning
X-Accel-Redirect
X-Content-Security-Policy-Report-Only
X-Content-Type-Options
X-Forwarded-For
X-Forwarded-Proto
X-Frame-Options
X-Powered-By
X-Real-IP
X-Requested-With
X-UA-Compatible
X-Wap-Profile
X-XSS-Protection

View File

@ -0,0 +1,167 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/HTTPConnector.h"
#include "proxygen/lib/http/codec/HTTP1xCodec.h"
#include "proxygen/lib/http/codec/SPDYCodec.h"
#include "proxygen/lib/http/session/HTTPTransaction.h"
#include "proxygen/lib/http/session/HTTPUpstreamSession.h"
#include "proxygen/lib/ssl/SSLUtil.h"
#include <thrift/lib/cpp/async/TAsyncSSLSocket.h>
using namespace apache::thrift::async;
using namespace apache::thrift::transport;
using namespace folly;
using namespace std;
namespace proxygen {
namespace {
unique_ptr<HTTPCodec> makeCodec(const string& chosenProto,
bool forceHTTP1xCodecTo1_1) {
auto spdyVersion = SPDYCodec::getVersion(chosenProto);
if (spdyVersion) {
return folly::make_unique<SPDYCodec>(TransportDirection::UPSTREAM,
*spdyVersion);
} else {
if (!chosenProto.empty() &&
!HTTP1xCodec::supportsNextProtocol(chosenProto)) {
LOG(ERROR) << "Chosen upstream protocol " <<
"\"" << chosenProto << "\" is unimplemented. " <<
"Attempting to use HTTP/1.1";
}
return folly::make_unique<HTTP1xCodec>(TransportDirection::UPSTREAM,
forceHTTP1xCodecTo1_1);
}
}
}
HTTPConnector::HTTPConnector(
Callback* callback,
TAsyncTimeoutSet* timeoutSet,
const string& plaintextProtocol,
bool forceHTTP1xCodecTo1_1):
cb_(CHECK_NOTNULL(callback)),
timeoutSet_(timeoutSet),
plaintextProtocol_(plaintextProtocol),
forceHTTP1xCodecTo1_1_(forceHTTP1xCodecTo1_1) {}
HTTPConnector::~HTTPConnector() {
reset();
}
void HTTPConnector::reset() {
if (socket_) {
auto cb = cb_;
cb_ = nullptr;
socket_.reset(); // This invokes connectError() but will be ignored
cb_ = cb;
}
}
void HTTPConnector::connect(
EventBase* eventBase,
const folly::SocketAddress& connectAddr,
chrono::milliseconds timeoutMs,
const TAsyncSocket::OptionMap& socketOptions,
const folly::SocketAddress& bindAddr) {
DCHECK(!isBusy());
transportInfo_ = TransportInfo();
transportInfo_.ssl = false;
socket_.reset(new TAsyncSocket(eventBase));
connectStart_ = getCurrentTime();
socket_->connect(this, connectAddr, timeoutMs.count(),
socketOptions, bindAddr);
}
void HTTPConnector::connectSSL(
EventBase* eventBase,
const folly::SocketAddress& connectAddr,
const shared_ptr<SSLContext>& context,
SSL_SESSION* session,
chrono::milliseconds timeoutMs,
const TAsyncSocket::OptionMap& socketOptions,
const folly::SocketAddress& bindAddr) {
DCHECK(!isBusy());
transportInfo_ = TransportInfo();
transportInfo_.ssl = true;
auto sslSock = new TAsyncSSLSocket(context, eventBase);
if (session) {
sslSock->setSSLSession(session, true /* take ownership */);
}
socket_.reset(sslSock);
connectStart_ = getCurrentTime();
socket_->connect(this, connectAddr, timeoutMs.count(),
socketOptions, bindAddr);
}
std::chrono::milliseconds HTTPConnector::timeElapsed() {
if (timePointInitialized(connectStart_)) {
return millisecondsSince(connectStart_);
}
return std::chrono::milliseconds(0);
}
// Callback interface
void HTTPConnector::connectSuccess() noexcept {
if (!cb_) {
return;
}
folly::SocketAddress localAddress;
folly::SocketAddress peerAddress;
socket_->getLocalAddress(&localAddress);
socket_->getPeerAddress(&peerAddress);
std::unique_ptr<HTTPCodec> codec;
transportInfo_.acceptTime = getCurrentTime();
if (transportInfo_.ssl) {
TAsyncSSLSocket* sslSocket = static_cast<TAsyncSSLSocket*>(socket_.get());
const char* npnProto;
unsigned npnProtoLen;
sslSocket->getSelectedNextProtocol(
reinterpret_cast<const unsigned char**>(&npnProto), &npnProtoLen);
transportInfo_.sslNextProtocol = string(npnProto, npnProtoLen);
transportInfo_.sslSetupTime = millisecondsSince(connectStart_);
transportInfo_.sslCipher = sslSocket->getNegotiatedCipherName();
transportInfo_.sslVersion = sslSocket->getSSLVersion();
transportInfo_.sslResume = SSLUtil::getResumeState(sslSocket);
codec = makeCodec(transportInfo_.sslNextProtocol, forceHTTP1xCodecTo1_1_);
} else {
codec = makeCodec(plaintextProtocol_, forceHTTP1xCodecTo1_1_);
}
HTTPUpstreamSession* session = new HTTPUpstreamSession(
timeoutSet_,
std::move(socket_), localAddress, peerAddress,
std::move(codec), transportInfo_, nullptr);
cb_->connectSuccess(session);
}
void HTTPConnector::connectError(const TTransportException& ex) noexcept {
socket_.reset();
if (cb_) {
cb_->connectError(ex);
}
}
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/lib/services/TransportInfo.h"
#include "proxygen/lib/utils/Time.h"
#include <thrift/lib/cpp/async/TAsyncSocket.h>
#include <thrift/lib/cpp/async/TAsyncTimeoutSet.h>
#include <thrift/lib/cpp/transport/TSSLSocket.h>
namespace proxygen {
class HTTPUpstreamSession;
/**
* This class establishes new connections to HTTP or HTTPS servers. It
* can be reused, even to connect to different addresses, but it can only
* service setting up one connection at a time.
*/
class HTTPConnector:
private apache::thrift::async::TAsyncSocket::ConnectCallback {
public:
/**
* This class defines the pure virtual interface on which to receive the
* result on.
*/
class Callback {
public:
virtual ~Callback() {}
virtual void connectSuccess(HTTPUpstreamSession* session) = 0;
virtual void connectError(
const apache::thrift::transport::TTransportException& ex) = 0;
};
/**
* Construct a HTTPConnector. The constructor arguments are those
* parameters HTTPConnector needs to keep a copy of through the
* connection process.
*
* @param callback The interface on which to receive the result.
* Whatever object is passed here MUST outlive this
* connector and MUST NOT be null.
* @param timeoutSet The timeout set to be used for the transactions
* that are opened on the session.
* @param plaintextProto An optional protocol string to specify the
* next protocol to use for unsecure connections.
* If omitted, http/1.1 will be assumed.
* @param forceHTTP1xCodecTo11 If true and this connector creates
* a session using an HTTP1xCodec, that codec will
* only serialize messages as HTTP/1.1.
*/
HTTPConnector(Callback* callback,
apache::thrift::async::TAsyncTimeoutSet* timeoutSet,
const std::string& plaintextProto = "",
bool forceHTTP1xCodecTo11 = false);
/**
* Clients may delete the connector at any time to cancel it. No
* callbacks will be received.
*/
~HTTPConnector();
/**
* Reset the object so that it can begin a new connection. No callbacks
* will be invoked as a result of executing this function. After this
* function returns, isBusy() will return false.
*/
void reset();
/**
* Begin the process of getting a plaintext connection to the server
* specified by 'connectAddr'. This function immediately starts async
* work and may invoke functions on Callback immediately.
*
* @param eventBase The event base to put events on.
* @param connectAddr The address to connect to.
* @param timeoutMs Optional. If this value is greater than zero, then a
* connect error will be given if no connection is
* established within this amount of time.
* @param socketOptions Optional socket options to set on the connection.
* @param bindAddr Optional address to bind to locally.
*/
void connect(
folly::EventBase* eventBase,
const folly::SocketAddress& connectAddr,
std::chrono::milliseconds timeoutMs = std::chrono::milliseconds(0),
const apache::thrift::async::TAsyncSocket::OptionMap& socketOptions =
apache::thrift::async::TAsyncSocket::emptyOptionMap,
const folly::SocketAddress& bindAddr =
apache::thrift::async::TAsyncSocket::anyAddress);
/**
* Begin the process of getting a secure connection to the server
* specified by 'connectAddr'. This function immediately starts async
* work and may invoke functions on Callback immediately.
*
* @param eventBase The event base to put events on.
* @param connectAddr The address to connect to.
* @param ctx SSL context to use. Must not be null.
* @param session Optional ssl session to use.
* @param timeoutMs Optional. If this value is greater than zero, then a
* connect error will be given if no connection is
* established within this amount of time.
* @param socketOptions Optional socket options to set on the connection.
* @param bindAddr Optional address to bind to locally.
*/
void connectSSL(
folly::EventBase* eventBase,
const folly::SocketAddress& connectAddr,
const std::shared_ptr<apache::thrift::transport::SSLContext>& ctx,
SSL_SESSION* session = nullptr,
std::chrono::milliseconds timeoutMs = std::chrono::milliseconds(0),
const apache::thrift::async::TAsyncSocket::OptionMap& socketOptions =
apache::thrift::async::TAsyncSocket::emptyOptionMap,
const folly::SocketAddress& bindAddr =
apache::thrift::async::TAsyncSocket::anyAddress);
/**
* @returns the number of milliseconds since connecting began, or
* zero if connecting hasn't started yet.
*/
std::chrono::milliseconds timeElapsed();
/**
* @returns true iff this connector is busy setting up a connection. If
* this is false, it is safe to call connect() or connectSSL() on it again.
*/
bool isBusy() const { return socket_.get(); }
private:
void connectSuccess() noexcept override;
void connectError(const apache::thrift::transport::TTransportException& ex)
noexcept override;
Callback* cb_;
apache::thrift::async::TAsyncTimeoutSet* timeoutSet_;
apache::thrift::async::TAsyncSocket::UniquePtr socket_;
TransportInfo transportInfo_;
std::string plaintextProtocol_;
TimePoint connectStart_;
bool forceHTTP1xCodecTo1_1_;
};
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/HTTPConstants.h"
namespace proxygen {
#define CONNECTION_CLOSE_REASON_STRING(e, r) r,
const char* connectionCloseStrings[] = {
CONNECTION_CLOSE_REASON_GEN(CONNECTION_CLOSE_REASON_STRING)
};
#undef CONNECTION_CLOSE_REASON_STRING
const char* getConnectionCloseReasonStringByIndex(unsigned int index) {
if (index >= (unsigned int)ConnectionCloseReason::kMAX_REASON) {
index = (unsigned int)ConnectionCloseReason::kMAX_REASON - 1;
}
return connectionCloseStrings[index];
}
const char* getConnectionCloseReasonString(ConnectionCloseReason r) {
return connectionCloseStrings[(unsigned int)r];
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
namespace proxygen {
//enum class ConnectionCloseReason : unsigned int {
// SHUTDOWN, // shutdown...probably due to the short shutdown
// // time we won't be able to see any of this
// READ_EOF, // received FIN from client so the connection is
// // not reusable any more
// GOAWAY, // session closed due to ingress goaway
// SESSION_PARSE_ERROR, // http/spdy parse error
// REMOTE_ERROR, // The various 5xx error
// TRANSACTION_ABORT, // transaction sendAbort()
// TIMEOUT, // read/write timeout excluding shutdown
// IO_READ_ERROR, // read error
// IO_WRITE_ERROR, // write error
// REQ_NOTREUSABLE, // client request is not reusable (http 1.0 or
// // Connection: close)
// ERR_RESP, // various 4xx error
// UNKNOWN, // this probably indicate some bug that the close
// // reason is not accounted for
// kMAX_REASON
//};
#define CONNECTION_CLOSE_REASON_GEN(x) \
x(SHUTDOWN, "shutdown") \
x(READ_EOF, "read_eof") \
x(GOAWAY, "goaway") \
x(SESSION_PARSE_ERROR, "session_parse_err") \
x(REMOTE_ERROR, "remote_err") \
x(TRANSACTION_ABORT, "transaction_abort") \
x(TIMEOUT, "timeout") \
x(IO_READ_ERROR, "io_read_err") \
x(IO_WRITE_ERROR, "io_write_err") \
x(REQ_NOTREUSABLE, "req_not_reusable") \
x(ERR_RESP, "err_resp") \
x(UNKNOWN, "unknown") \
x(FLOW_CONTROL, "flow_control") \
x(kMAX_REASON, "unset")
#define CONNECTION_CLOSE_REASON_ENUM(e, r) e,
enum class ConnectionCloseReason {
CONNECTION_CLOSE_REASON_GEN(CONNECTION_CLOSE_REASON_ENUM)
};
#undef CONNECTION_CLOSE_REASON_ENUM
extern const char* getConnectionCloseReasonStringByIndex(unsigned int i);
extern const char* getConnectionCloseReasonString(ConnectionCloseReason r);
/**
* Protocol to which the HTTPTransaction was upgraded
*/
enum class UpgradeProtocol: int {
// We only support changing to TCP after CONNECT requests
TCP
};
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/HTTPException.h"
namespace proxygen {
std::string HTTPException::describe() const {
std::stringstream ss;
ss << *this;
return ss.str();
}
std::ostream& operator<<(std::ostream& os, const HTTPException& ex) {
os << "what=\"" << ex.what()
<< "\", direction=" << static_cast<int>(ex.getDirection())
<< ", proxygenError=" << getErrorString(ex.getProxygenError())
<< ", codecStatusCode=" << (ex.hasCodecStatusCode() ?
getErrorCodeString(ex.getCodecStatusCode()) :
"-1")
<< ", httpStatusCode=" << ex.getHttpStatusCode();
return os;
}
}

View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/lib/http/HTTPMessage.h"
#include "proxygen/lib/http/ProxygenErrorEnum.h"
#include "proxygen/lib/http/codec/ErrorCode.h"
#include "proxygen/lib/utils/Exception.h"
#include <folly/Memory.h>
#include <folly/io/IOBufQueue.h>
namespace proxygen {
/**
* This class encapsulates the various errors that can occur on an
* http session. Errors can occur at various levels: the connection can
* be closed for reads and/or writes, the message body may fail to parse,
* or various protocol constraints may be violated.
*/
class HTTPException : public proxygen::Exception {
public:
/**
* Indicates which direction of the data flow was affected by this
* exception. For instance, if a class receives HTTPException(INGRESS),
* then it should consider ingress callbacks finished (whether or not
* the underlying transport actually shut down). Likewise for
* HTTPException(EGRESS), the class should consider the write stream
* shut down. INGRESS_AND_EGRESS indicates both directions are finished.
*/
enum class Direction {
INGRESS = 0,
EGRESS,
INGRESS_AND_EGRESS,
};
explicit HTTPException(Direction dir, const std::string& msg)
: Exception(msg),
dir_(dir) {}
template<typename... Args>
explicit HTTPException(Direction dir, Args&&... args)
: Exception(std::forward<Args>(args)...),
dir_(dir) {}
HTTPException(const HTTPException& ex) :
Exception(static_cast<const Exception&>(ex)),
dir_(ex.dir_),
proxygenError_(ex.proxygenError_),
httpStatusCode_(ex.httpStatusCode_),
codecStatusCode_(ex.codecStatusCode_),
errno_(ex.errno_) {
if (ex.currentIngressBuf_) {
currentIngressBuf_ = std::move(ex.currentIngressBuf_->clone());
}
if (ex.partialMsg_) {
partialMsg_ = folly::make_unique<HTTPMessage>(*ex.partialMsg_.get());
}
}
/**
* Returns a string representation of this exception. This function is
* intended for debugging and logging only. For the true exception
* string, use what()
*/
std::string describe() const;
Direction getDirection() const {
return dir_;
}
bool isIngressException() const {
return dir_ == Direction::INGRESS ||
dir_ == Direction::INGRESS_AND_EGRESS;
}
bool isEgressException() const {
return dir_ == Direction::EGRESS ||
dir_ == Direction::INGRESS_AND_EGRESS;
}
// Accessors for ProxygenError
bool hasProxygenError() const {
return (proxygenError_ != kErrorNone);
}
void setProxygenError(ProxygenError proxygenError) {
proxygenError_ = proxygenError;
}
ProxygenError getProxygenError() const {
return proxygenError_;
}
// Accessors for HTTP error codes
bool hasHttpStatusCode() const {
return (httpStatusCode_ != 0);
}
void setHttpStatusCode(uint32_t statusCode) {
httpStatusCode_ = statusCode;
}
uint32_t getHttpStatusCode() const {
return httpStatusCode_;
}
// Accessors for Codec specific status codes
bool hasCodecStatusCode() const {
return codecStatusCode_.hasValue();
}
void setCodecStatusCode(ErrorCode statusCode) {
codecStatusCode_ = statusCode;
}
ErrorCode getCodecStatusCode() const {
CHECK(hasCodecStatusCode());
return *codecStatusCode_;
}
// Accessors for errno
bool hasErrno() const {
return (errno_ != 0);
}
void setErrno(uint32_t errno) {
errno_ = errno;
}
uint32_t getErrno() const {
return errno_;
}
void setCurrentIngressBuf(std::unique_ptr<folly::IOBuf> buf) {
currentIngressBuf_ = std::move(buf);
}
std::unique_ptr<folly::IOBuf> moveCurrentIngressBuf() {
return std::move(currentIngressBuf_);
}
void setPartialMsg(std::unique_ptr<HTTPMessage> partialMsg) {
partialMsg_ = std::move(partialMsg);
}
std::unique_ptr<HTTPMessage> movePartialMsg() {
return std::move(partialMsg_);
}
private:
Direction dir_;
ProxygenError proxygenError_{kErrorNone};
uint32_t httpStatusCode_{0};
folly::Optional<ErrorCode> codecStatusCode_;
uint32_t errno_{0};
// current ingress buffer, may be compressed
std::unique_ptr<folly::IOBuf> currentIngressBuf_;
// partial message that is being parsed
std::unique_ptr<HTTPMessage> partialMsg_;
};
std::ostream& operator<<(std::ostream& os, const HTTPException& ex);
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <stdint.h>
namespace proxygen {
/**
* A structure that encapsulates byte counters related to the HTTP headers.
*/
struct HTTPHeaderSize {
/**
* The number of bytes used to represent the header after compression or
* before decompression. If header compression is not supported, the value
* is set to 0.
*/
uint32_t compressed{0};
/**
* The number of bytes used to represent the serialized header before
* compression or after decompression, in plain-text format.
*/
uint32_t uncompressed{0};
};
}

View File

@ -0,0 +1,308 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#define PROXYGEN_HTTPHEADERS_IMPL
#include "proxygen/lib/http/HTTPHeaders.h"
#include <glog/logging.h>
#include <vector>
using std::bitset;
using std::string;
using std::vector;
namespace proxygen {
const string empty_string;
bitset<256>& HTTPHeaders::perHopHeaderCodes() {
static bitset<256> perHopHeaderCodes;
return perHopHeaderCodes;
}
void
HTTPHeaders::initGlobals() {
HTTPCommonHeaders::initHeaderNames();
auto& perHopHeaders = perHopHeaderCodes();
perHopHeaders[HTTP_HEADER_CONNECTION] = true;
perHopHeaders[HTTP_HEADER_KEEP_ALIVE] = true;
perHopHeaders[HTTP_HEADER_PROXY_AUTHENTICATE] = true;
perHopHeaders[HTTP_HEADER_PROXY_AUTHORIZATION] = true;
perHopHeaders[HTTP_HEADER_PROXY_CONNECTION] = true;
perHopHeaders[HTTP_HEADER_TE] = true;
perHopHeaders[HTTP_HEADER_TRAILER] = true;
perHopHeaders[HTTP_HEADER_TRANSFER_ENCODING] = true;
perHopHeaders[HTTP_HEADER_UPGRADE] = true;
}
HTTPHeaders::HTTPHeaders() :
deletedCount_(0) {
codes_.reserve(kInitialVectorReserve);
headerNames_.reserve(kInitialVectorReserve);
headerValues_.reserve(kInitialVectorReserve);
}
void HTTPHeaders::add(folly::StringPiece name, folly::StringPiece value) {
CHECK(name.size());
const HTTPHeaderCode code = HTTPCommonHeaders::hash(name.data(), name.size());
codes_.push_back(code);
headerNames_.push_back((code == HTTP_HEADER_OTHER)
? new std::string(name.data(), name.size())
: HTTPCommonHeaders::getPointerToHeaderName(code));
headerValues_.emplace_back(value.data(), value.size());
}
void HTTPHeaders::rawAdd(const std::string& name, const std::string& value) {
add(name, value);
}
void HTTPHeaders::addFromCodec(const char* str, size_t len, string&& value) {
const HTTPHeaderCode code = HTTPCommonHeaders::hash(str, len);
codes_.push_back(code);
headerNames_.push_back((code == HTTP_HEADER_OTHER)
? new string(str, len)
: HTTPCommonHeaders::getPointerToHeaderName(code));
headerValues_.emplace_back(std::move(value));
}
bool HTTPHeaders::exists(folly::StringPiece name) const {
const HTTPHeaderCode code = HTTPCommonHeaders::hash(name.data(),
name.size());
if (code != HTTP_HEADER_OTHER) {
return exists(code);
} else {
ITERATE_OVER_STRINGS(name, { return true; });
return false;
}
}
bool HTTPHeaders::exists(HTTPHeaderCode code) const {
return memchr((void*)codes_.data(), code, codes_.size()) != nullptr;
}
size_t HTTPHeaders::getNumberOfValues(HTTPHeaderCode code) const {
size_t count = 0;
ITERATE_OVER_CODES(code, {
++count;
});
return count;
}
size_t HTTPHeaders::getNumberOfValues(folly::StringPiece name) const {
size_t count = 0;
forEachValueOfHeader(name, [&] (folly::StringPiece value) -> bool {
++count;
return false;
});
return count;
}
bool HTTPHeaders::remove(folly::StringPiece name) {
const HTTPHeaderCode code = HTTPCommonHeaders::hash(name.data(),
name.size());
if (code != HTTP_HEADER_OTHER) {
return remove(code);
} else {
bool removed = false;
ITERATE_OVER_STRINGS(name, {
delete headerNames_[pos];
codes_[pos] = HTTP_HEADER_NONE;
removed = true;
++deletedCount_;
});
return removed;
}
}
bool HTTPHeaders::remove(HTTPHeaderCode code) {
bool removed = false;
ITERATE_OVER_CODES(code, {
codes_[pos] = HTTP_HEADER_NONE;
removed = true;
++deletedCount_;
});
return removed;
}
void HTTPHeaders::disposeOfHeaderNames() {
for (size_t i = 0; i < codes_.size(); ++i) {
if (codes_[i] == HTTP_HEADER_OTHER) {
delete headerNames_[i];
}
}
}
HTTPHeaders::~HTTPHeaders () {
disposeOfHeaderNames();
}
HTTPHeaders::HTTPHeaders(const HTTPHeaders& hdrs) :
codes_(hdrs.codes_),
headerNames_(hdrs.headerNames_),
headerValues_(hdrs.headerValues_),
deletedCount_(hdrs.deletedCount_) {
for (size_t i = 0; i < codes_.size(); ++i) {
if (codes_[i] == HTTP_HEADER_OTHER) {
headerNames_[i] = new string(*hdrs.headerNames_[i]);
}
}
}
HTTPHeaders::HTTPHeaders(HTTPHeaders&& hdrs) noexcept :
codes_(std::move(hdrs.codes_)),
headerNames_(std::move(hdrs.headerNames_)),
headerValues_(std::move(hdrs.headerValues_)),
deletedCount_(hdrs.deletedCount_) {
hdrs.removeAll();
}
HTTPHeaders& HTTPHeaders::operator= (const HTTPHeaders& hdrs) {
if (this != &hdrs) {
disposeOfHeaderNames();
codes_ = hdrs.codes_;
headerNames_ = hdrs.headerNames_;
headerValues_ = hdrs.headerValues_;
deletedCount_ = hdrs.deletedCount_;
for (size_t i = 0; i < codes_.size(); ++i) {
if (codes_[i] == HTTP_HEADER_OTHER) {
headerNames_[i] = new string(*hdrs.headerNames_[i]);
}
}
}
return *this;
}
HTTPHeaders& HTTPHeaders::operator= (HTTPHeaders&& hdrs) {
if (this != &hdrs) {
codes_ = std::move(hdrs.codes_);
headerNames_ = std::move(hdrs.headerNames_);
headerValues_ = std::move(hdrs.headerValues_);
deletedCount_ = hdrs.deletedCount_;
hdrs.removeAll();
}
return *this;
}
void HTTPHeaders::removeAll() {
disposeOfHeaderNames();
codes_.clear();
headerNames_.clear();
headerValues_.clear();
deletedCount_ = 0;
}
size_t HTTPHeaders::size() const {
return codes_.size() - deletedCount_;
}
bool
HTTPHeaders::transferHeaderIfPresent(folly::StringPiece name,
HTTPHeaders& strippedHeaders) {
bool transferred = false;
const HTTPHeaderCode code = HTTPCommonHeaders::hash(name.data(),
name.size());
if (code == HTTP_HEADER_OTHER) {
ITERATE_OVER_STRINGS(name, {
strippedHeaders.codes_.push_back(HTTP_HEADER_OTHER);
// in the next line, ownership of pointer goes to strippedHeaders
strippedHeaders.headerNames_.push_back(headerNames_[pos]);
strippedHeaders.headerValues_.push_back(headerValues_[pos]);
codes_[pos] = HTTP_HEADER_NONE;
transferred = true;
++deletedCount_;
});
} else { // code != HTTP_HEADER_OTHER
ITERATE_OVER_CODES(code, {
strippedHeaders.codes_.push_back(code);
strippedHeaders.headerNames_.push_back(headerNames_[pos]);
strippedHeaders.headerValues_.push_back(headerValues_[pos]);
codes_[pos] = HTTP_HEADER_NONE;
transferred = true;
++deletedCount_;
});
}
return transferred;
}
void
HTTPHeaders::stripPerHopHeaders(HTTPHeaders& strippedHeaders) {
int len;
forEachValueOfHeader(HTTP_HEADER_CONNECTION, [&]
(const string& stdStr) -> bool {
// Remove all headers specified in Connection header
// look for multiple values separated by commas
char const* str = stdStr.c_str();
// skip leading whitespace
while (isLWS(*str)) str++;
while (*str != 0) {
char const* pos = strchr(str, ',');
if (pos == nullptr) {
// last (or only) token, done
// count chars in the token
len = 0;
while (str[len] != 0 && !isLWS(str[len])) len++;
if (len > 0) {
string hdr(str, len);
if (transferHeaderIfPresent(hdr, strippedHeaders)) {
VLOG(3) << "Stripped connection-named hop-by-hop header " << hdr;
}
}
break;
}
len = pos - str;
// strip trailing whitespace
while (len > 0 && isLWS(str[len - 1])) len--;
if (len > 0) {
// non-empty token
string hdr(str, len);
if (transferHeaderIfPresent(hdr, strippedHeaders)) {
VLOG(3) << "Stripped connection-named hop-by-hop header " << hdr;
}
} // else empty token, no-op
str = pos + 1;
// skip whitespace
while (isLWS(*str)) str++;
}
return false; // continue processing "connection" headers
});
// Strip hop-by-hop headers
auto& perHopHeaders = perHopHeaderCodes();
for (size_t i = 0; i < codes_.size(); ++i) {
if (perHopHeaders[codes_[i]]) {
strippedHeaders.codes_.push_back(codes_[i]);
strippedHeaders.headerNames_.push_back(headerNames_[i]);
strippedHeaders.headerValues_.push_back(headerValues_[i]);
codes_[i] = HTTP_HEADER_NONE;
++deletedCount_;
VLOG(3) << "Stripped hop-by-hop header " << *headerNames_[i];
}
}
}
void HTTPHeaders::copyTo(HTTPHeaders& hdrs) const {
for (size_t i = 0; i < codes_.size(); ++i) {
if (codes_[i] != HTTP_HEADER_NONE) {
hdrs.codes_.push_back(codes_[i]);
hdrs.headerNames_.push_back((codes_[i] == HTTP_HEADER_OTHER) ?
new string(*headerNames_[i]) : headerNames_[i]);
hdrs.headerValues_.push_back(headerValues_[i]);
}
}
}
}

View File

@ -0,0 +1,423 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <folly/FBVector.h>
#include <folly/Range.h>
#include "proxygen/lib/http/HTTPCommonHeaders.h"
#include "proxygen/lib/utils/UtilInl.h"
#include <bitset>
#include <cstring>
#include <string>
namespace proxygen {
extern const std::string empty_string;
/**
* Return true if the character is linear whitespace, as defined by the LWS
* definition in RFC 2616, and false otherwise
*/
inline bool isLWS(char c) {
// Technically \r and \n are only allowed in LWS if they appear together.
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
return true;
}
return false;
}
/**
* A collection of HTTP headers.
*
* This is broken out from HTTPMessage, as it's convenient for other things to
* be able to use collections of HTTP headers that are easy to work with. The
* structure is optimized for real-life header collection sizes.
*
* Headers are stored as Name/Value pairs, in the order they are received on
* the wire. We hash the names of all common HTTP headers (using a static
* perfect hash function generated using gperf from HTTPCommonHeaders.gperf)
* into 1-byte hashes (we call them "codes") and only store these. We search
* them using memchr, which has an x86_64 assembly implementation with
* complexity O(n/16) ;)
*
* Instead of creating strings with header names, we point to a static array
* of strings in HTTPCommonHeaders. If the header name is not in our set of
* common header names (this is considered unlikely, because we intend this set
* to be very complete), then we create a new string with its name (we own that
* pointer then). For such headers, we store the code HTTP_HEADER_OTHER.
*
* The code HTTP_HEADER_NONE signifies a header that has been removed.
*
* Most methods which take a header name have two versions: one accepting
* a string, and one accepting a code. It is recommended to use the latter
* if possible, as in:
* headers.add(HTTP_HEADER_LOCATION, location);
* rather than:
* headers.add("Location", location);
*/
class HTTPHeaders {
public:
/*
* separator used to concatenate multiple values of the same header
* check out sections 4.2 and 14.45 from rfc2616
*/
const std::string COMBINE_SEPARATOR = ", ";
HTTPHeaders();
~HTTPHeaders();
HTTPHeaders (const HTTPHeaders&);
HTTPHeaders& operator= (const HTTPHeaders&);
HTTPHeaders (HTTPHeaders&&) noexcept;
HTTPHeaders& operator= (HTTPHeaders&&);
/**
* Add the header 'name' with value 'value'; if other instances of this
* header name exist, they will be retained.
*/
void add(folly::StringPiece name, folly::StringPiece value);
template <typename T> // T = string
void add(folly::StringPiece name, T&& value);
template <typename T> // T = string
void add(HTTPHeaderCode code, T&& value);
void rawAdd(const std::string& name, const std::string& value);
void addFromCodec(const char* str, size_t len, std::string&& value);
/**
* For the header 'name', set its value to the single header 'value',
* removing any other instances of this header.
*/
void set(folly::StringPiece name, const std::string& value) {
// this could be somewhat optimized but probably not an issue yet
remove(name);
add(name, value);
}
void set(HTTPHeaderCode code, const std::string& value) {
remove(code);
add(code, value);
}
void rawSet(const std::string& name, const std::string& value) {
set(name, value);
}
/**
* Do we have an instance of the given header?
*/
bool exists(folly::StringPiece name) const;
bool exists(HTTPHeaderCode code) const;
bool rawExists(std::string& name) const {
return exists(name);
}
/**
* combine all the value for this header into a string
*/
template <typename T>
std::string combine(const T& header) const;
/**
* Process the list of all headers, in the order that they were seen:
* for each header:value pair, the function/functor/lambda-expression
* given as the second parameter will be executed. It should take two
* const string & parameters and return void. Example use:
* hdrs.forEach([&] (const string& header, const string& val) {
* std::cout << header << ": " << val;
* });
*/
template <typename LAMBDA> // (const string &, const string &) -> void
inline void forEach(LAMBDA func) const;
/**
* Process the list of all headers, in the order that they were seen:
* for each header:value pair, the function/functor/lambda-expression
* given as the second parameter will be executed. It should take one
* HTTPHeaderCode (code) parameter, two const string & parameters and
* return void. Example use:
* hdrs.forEachWithCode([&] (HTTPHeaderCode code,
* const string& header,
* const string& val) {
* std::cout << header << "(" << code << "): " << val;
* });
*/
template <typename LAMBDA>
inline void forEachWithCode(LAMBDA func) const;
/**
* Process the list of all headers, in the order that they were seen:
* for each header:value pair, the function/functor/lambda-expression
* given as the parameter will be executed to determine whether the
* header should be removed. Example use:
*
* hdrs.removeByPredicate([&] (HTTPHeaderCode code,
* const string& header,
* const string& val) {
* return boost::regex_match(header, "^X-Fb-.*");
* });
*
* return true only if one or more headers are removed.
*/
template <typename LAMBDA> // (const string &, const string &) -> bool
inline bool removeByPredicate(LAMBDA func);
/**
* Returns the value of the header if it's found in the message and is the
* only value under the given name. If either of these is violated, returns
* empty_string.
*/
template <typename T> // either uint8_t or string
const std::string & getSingleOrEmpty(const T& nameOrCode) const;
const std::string rawGet(const std::string& header) const {
return getSingleOrEmpty(header);
}
/**
* Get the number of values corresponding to a given header name.
*/
size_t getNumberOfValues(HTTPHeaderCode code) const;
size_t getNumberOfValues(folly::StringPiece name) const;
/**
* Process the ordered list of values for the given header name:
* for each value, the function/functor/lambda-expression given as the second
* parameter will be executed. It should take one const string & parameter
* and return bool (false to keep processing, true to stop it). Example use:
* hdrs.forEachValueOfHeader("someheader", [&] (const string& val) {
* std::cout << val;
* return false;
* });
* This method returns true if processing was stopped (by func returning
* true), and false otherwise.
*/
template <typename LAMBDA> // const string & -> bool
inline bool forEachValueOfHeader(folly::StringPiece name, LAMBDA func) const;
template <typename LAMBDA> // const string & -> bool
inline bool forEachValueOfHeader(HTTPHeaderCode code, LAMBDA func) const;
/**
* Remove all instances of the given header, returning true if anything was
* removed and false if this header didn't exist in our set.
*/
bool remove(folly::StringPiece name);
bool remove(HTTPHeaderCode code);
void rawRemove(const std::string& name) {
remove(name);
}
/**
* Remove all headers.
*/
void removeAll();
/**
* Remove per-hop-headers and headers named in the Connection header
* and place the value in strippedHeaders
*/
void stripPerHopHeaders(HTTPHeaders& strippedHeaders);
/**
* Get the total number of headers.
*/
size_t size() const;
/**
* Copy all headers from this to hdrs.
*/
void copyTo(HTTPHeaders& hdrs) const;
/**
* Determines whether header with a given code is a per-hop header,
* which should be stripped by stripPerHopHeaders().
*/
static std::bitset<256>& perHopHeaderCodes();
private:
// vector storing the 1-byte hashes of header names
folly::fbvector<HTTPHeaderCode> codes_;
/**
* Vector storing pointers to header names; we own those pointers which
* correspond to HTTP_HEADER_OTHER codes.
*/
folly::fbvector<const std::string *> headerNames_;
folly::fbvector<std::string> headerValues_;
size_t deletedCount_;
/**
* The initial capacity of the three vectors, reserved right after
* construction.
*/
static const size_t kInitialVectorReserve = 16;
/**
* Moves the named header and values from this group to the destination
* group. No-op if the header doesn't exist. Returns true if header(s) were
* moved.
*/
bool transferHeaderIfPresent(folly::StringPiece name, HTTPHeaders& dest);
static void initGlobals() __attribute__ ((__constructor__));
// deletes the strings in headerNames_ that we own
void disposeOfHeaderNames();
};
// Implementation follows - it has to be in the .h because of the templates
template <typename T> // T = string
void HTTPHeaders::add(folly::StringPiece name, T&& value) {
assert(name.size());
const HTTPHeaderCode code = HTTPCommonHeaders::hash(name.data(), name.size());
codes_.push_back(code);
headerNames_.push_back((code == HTTP_HEADER_OTHER)
? new std::string(name.data(), name.size())
: HTTPCommonHeaders::getPointerToHeaderName(code));
headerValues_.emplace_back(std::forward<T>(value));
}
template <typename T> // T = string
void HTTPHeaders::add(HTTPHeaderCode code, T&& value) {
codes_.push_back(code);
headerNames_.push_back(HTTPCommonHeaders::getPointerToHeaderName(code));
headerValues_.emplace_back(std::forward<T>(value));
}
// iterate over the positions (in vector) of all headers with given code
#define ITERATE_OVER_CODES(Code, Block) { \
const HTTPHeaderCode* ptr = codes_.data(); \
while(true) { \
ptr = (HTTPHeaderCode*) memchr((void*)ptr, (Code), \
codes_.size() - (ptr - codes_.data())); \
if (ptr == nullptr) break; \
const int pos = ptr - codes_.data(); \
{Block} \
ptr++; \
} \
}
// iterate over the positions of all headers with given name
#define ITERATE_OVER_STRINGS(String, Block) \
ITERATE_OVER_CODES(HTTP_HEADER_OTHER, { \
if (caseInsensitiveEqual((String), *headerNames_[pos])) { \
{Block} \
} \
})
template <typename LAMBDA> // (const string &, const string &) -> void
void HTTPHeaders::forEach(LAMBDA func) const {
for (size_t i = 0; i < codes_.size(); ++i) {
if (codes_[i] != HTTP_HEADER_NONE) {
func(*headerNames_[i], headerValues_[i]);
}
}
}
template <typename LAMBDA>
void HTTPHeaders::forEachWithCode(LAMBDA func) const {
for (size_t i = 0; i < codes_.size(); ++i) {
if (codes_[i] != HTTP_HEADER_NONE) {
func(codes_[i], *headerNames_[i], headerValues_[i]);
}
}
}
template <typename LAMBDA> // const string & -> bool
bool HTTPHeaders::forEachValueOfHeader(folly::StringPiece name,
LAMBDA func) const {
const HTTPHeaderCode code = HTTPCommonHeaders::hash(name.data(), name.size());
if (code != HTTP_HEADER_OTHER) {
return forEachValueOfHeader(code, func);
} else {
ITERATE_OVER_STRINGS(name, {
if (func(headerValues_[pos])) {
return true;
}
});
return false;
}
}
template <typename LAMBDA> // const string & -> bool
bool HTTPHeaders::forEachValueOfHeader(HTTPHeaderCode code,
LAMBDA func) const {
ITERATE_OVER_CODES(code, {
if (func(headerValues_[pos])) {
return true;
}
});
return false;
}
template <typename T>
std::string HTTPHeaders::combine(const T& header) const {
std::string combined = "";
forEachValueOfHeader(header, [&] (const std::string& value) -> bool {
if (combined.empty()) {
combined.append(value);
} else {
combined.append(COMBINE_SEPARATOR).append(value);
}
return false;
});
return combined;
}
// LAMBDA: (HTTPHeaderCode, const string&, const string&) -> bool
template <typename LAMBDA>
bool HTTPHeaders::removeByPredicate(LAMBDA func) {
bool removed = false;
for (size_t i = 0; i < codes_.size(); ++i) {
if (codes_[i] == HTTP_HEADER_NONE ||
!func(codes_[i], *headerNames_[i], headerValues_[i])) {
continue;
}
if (codes_[i] == HTTP_HEADER_OTHER) {
delete headerNames_[i];
headerNames_[i] = nullptr;
}
codes_[i] = HTTP_HEADER_NONE;
++deletedCount_;
removed = true;
}
return removed;
}
template <typename T> // either uint8_t or string
const std::string & HTTPHeaders::getSingleOrEmpty(const T& nameOrCode) const {
const std::string* res = nullptr;
forEachValueOfHeader(nameOrCode, [&] (const std::string& value) -> bool {
if (res != nullptr) {
// a second value is found
res = nullptr;
return true; // stop processing
} else {
// the first value is found
res = &value;
return false;
}
});
if (res == nullptr) {
return empty_string;
} else {
return *res;
}
}
#ifndef PROXYGEN_HTTPHEADERS_IMPL
#undef ITERATE_OVER_CODES
#undef ITERATE_OVER_STRINGS
#endif // PROXYGEN_HTTPHEADERS_IMPL
}

View File

@ -0,0 +1,831 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/HTTPMessage.h"
#include <array>
#include <boost/algorithm/string.hpp>
#include <folly/Format.h>
#include <folly/Range.h>
#include <string>
#include <utility>
#include <vector>
using folly::IOBuf;
using folly::Optional;
using folly::StringPiece;
using std::pair;
using std::string;
using std::unique_ptr;
namespace {
const string kHeaderStr_ = "header.";
const string kQueryStr_ = "query.";
const string kCookieStr = "cookie.";
/**
* Create a C locale once and pass it to all the boost string methods
* that would otherwise create and destruct a temporary locale object
* per call. (Performance profiling showed that we were spending
* approximately 1% of our total CPU time on temporary locale objects.)
*/
std::locale defaultLocale;
}
namespace proxygen {
std::mutex HTTPMessage::mutexDump_;
const pair<uint8_t, uint8_t> HTTPMessage::kHTTPVersion10(1, 0);
const pair<uint8_t, uint8_t> HTTPMessage::kHTTPVersion11(1, 1);
void HTTPMessage::stripPerHopHeaders() {
// Some code paths end up recyling a single HTTPMessage instance for multiple
// requests, and adding their own per-hop headers each time. In that case, we
// don't want to accumulate these headers.
strippedPerHopHeaders_.removeAll();
if (!trailersAllowed_) {
// Because stripPerHopHeaders can be called multiple times, don't
// let subsequent instances clear this flag
trailersAllowed_ = checkForHeaderToken(HTTP_HEADER_TE, "trailers", false);
}
headers_.stripPerHopHeaders(strippedPerHopHeaders_);
}
HTTPMessage::HTTPMessage() :
startTime_(getCurrentTime()),
seqNo_(-1),
localIP_(),
versionStr_("1.0"),
fields_(),
version_(1,0),
sslVersion_(0), sslCipher_(nullptr), spdy_(0), pri_(0),
parsedCookies_(false), parsedQueryParams_(false),
chunked_(false), upgraded_(false), wantsKeepalive_(true),
trailersAllowed_(false), secure_(false) {
}
HTTPMessage::~HTTPMessage() {
}
HTTPMessage::HTTPMessage(const HTTPMessage& message) :
startTime_(message.startTime_),
seqNo_(message.seqNo_),
dstAddress_(message.dstAddress_),
dstIP_(message.dstIP_),
dstPort_(message.dstPort_),
localIP_(message.localIP_),
versionStr_(message.versionStr_),
fields_(message.fields_),
cookies_(message.cookies_),
queryParams_(message.queryParams_),
version_(message.version_),
headers_(message.headers_),
strippedPerHopHeaders_(message.headers_),
sslVersion_(message.sslVersion_),
sslCipher_(message.sslCipher_),
spdy_(message.spdy_),
parsedCookies_(message.parsedCookies_),
parsedQueryParams_(message.parsedQueryParams_),
chunked_(message.chunked_),
upgraded_(message.upgraded_),
wantsKeepalive_(message.wantsKeepalive_),
trailersAllowed_(message.trailersAllowed_),
secure_(message.secure_) {
if (message.trailers_) {
trailers_.reset(new HTTPHeaders(*message.trailers_.get()));
}
}
HTTPMessage& HTTPMessage::operator=(const HTTPMessage& message) {
if (&message == this) {
return *this;
}
startTime_ = message.startTime_;
seqNo_ = message.seqNo_;
dstAddress_ = message.dstAddress_;
dstIP_ = message.dstIP_;
dstPort_ = message.dstPort_;
localIP_ = message.localIP_;
versionStr_ = message.versionStr_;
fields_ = message.fields_;
cookies_ = message.cookies_;
queryParams_ = message.queryParams_;
version_ = message.version_;
headers_ = message.headers_;
strippedPerHopHeaders_ = message.headers_;
sslVersion_ = message.sslVersion_;
sslCipher_ = message.sslCipher_;
spdy_ = message.spdy_;
parsedCookies_ = message.parsedCookies_;
parsedQueryParams_ = message.parsedQueryParams_;
chunked_ = message.chunked_;
upgraded_ = message.upgraded_;
wantsKeepalive_ = message.wantsKeepalive_;
trailersAllowed_ = message.trailersAllowed_;
secure_ = message.secure_;
if (message.trailers_) {
trailers_.reset(new HTTPHeaders(*message.trailers_.get()));
} else {
trailers_.reset();
}
return *this;
}
void HTTPMessage::setMethod(HTTPMethod method) {
Request& req = request();
req.method_ = method;
}
void HTTPMessage::setMethod(folly::StringPiece method) {
VLOG(9) << "setMethod: " << method;
Request& req = request();
boost::optional<HTTPMethod> result = stringToMethod(method);
if (result) {
req.method_ = *result;
} else {
req.method_ = method.str();
auto& storedMethod = boost::get<std::string>(req.method_);
std::transform(storedMethod.begin(), storedMethod.end(),
storedMethod.begin(), ::toupper);
}
}
boost::optional<HTTPMethod> HTTPMessage::getMethod() const {
const auto& req = request();
if (req.method_.which() == 2) {
return boost::get<HTTPMethod>(req.method_);
}
return boost::none;
}
/**
* @Returns a string representation of the request method (fpreq)
*/
const std::string& HTTPMessage::getMethodString() const {
const auto& req = request();
if (req.method_.which() == 1) {
return boost::get<std::string>(req.method_);
} else if (req.method_.which() == 2) {
return methodToString(boost::get<HTTPMethod>(req.method_));
}
return empty_string;
}
void HTTPMessage::setHTTPVersion(uint8_t maj, uint8_t min) {
version_.first = maj;
version_.second = min;
versionStr_ = folly::to<string>(maj, ".", min);
}
const pair<uint8_t, uint8_t>& HTTPMessage::getHTTPVersion() const {
return version_;
}
int HTTPMessage::processMaxForwards() {
if (getMethod() == HTTPMethod::TRACE || getMethod() == HTTPMethod::OPTIONS) {
const string& value = headers_.getSingleOrEmpty(HTTP_HEADER_MAX_FORWARDS);
if (value.length() > 0) {
int64_t max_forwards = 0;
try {
max_forwards = folly::to<int64_t>(value);
} catch (const std::range_error& ex) {
return 400;
}
if (max_forwards < 0) {
return 400;
} else if (max_forwards == 0) {
return 501;
} else {
headers_.set(HTTP_HEADER_MAX_FORWARDS,
folly::to<string>(max_forwards - 1));
}
}
}
return 0;
}
bool HTTPMessage::isHTTP1_0() const {
return version_ == kHTTPVersion10;
}
bool HTTPMessage::isHTTP1_1() const {
return version_ == kHTTPVersion11;
}
string HTTPMessage::formatDateHeader() {
const auto now = std::chrono::system_clock::to_time_t(
std::chrono::system_clock::now());
char buff[1024];
tm timeTupple;
gmtime_r(&now, &timeTupple);
strftime(buff, 1024, "%a, %d %b %Y %H:%M:%S %Z", &timeTupple);
return std::string(buff);
}
void HTTPMessage::ensureHostHeader() {
if (!headers_.exists(HTTP_HEADER_HOST)) {
headers_.add(HTTP_HEADER_HOST,
getDstAddress().getFamily() == AF_INET6
? '[' + getDstIP() + ']' : getDstIP());
}
}
void HTTPMessage::setStatusCode(uint16_t status) {
response().status_ = status;
response().statusStr_ = folly::to<string>(status);
}
uint16_t HTTPMessage::getStatusCode() const {
return response().status_;
}
void
HTTPMessage::constructDirectResponse(const pair<uint8_t,uint8_t>& version,
const int statusCode,
const string& statusMsg,
int contentLength) {
setStatusCode(statusCode);
setStatusMessage(statusMsg);
constructDirectResponse(version, contentLength);
}
void
HTTPMessage::constructDirectResponse(const pair<uint8_t,uint8_t>& version,
int contentLength) {
setHTTPVersion(version.first, version.second);
headers_.set(HTTP_HEADER_CONTENT_LENGTH, folly::to<string>(contentLength));
if (!headers_.exists(HTTP_HEADER_CONTENT_TYPE)) {
headers_.add(HTTP_HEADER_CONTENT_TYPE, "text/plain");
}
setIsChunked(false);
setIsUpgraded(false);
}
void HTTPMessage::parseCookies() const {
DCHECK(!parsedCookies_);
parsedCookies_ = true;
headers_.forEachValueOfHeader(HTTP_HEADER_COOKIE,
[&](const string& headerval) {
splitNameValuePieces(headerval, ';', '=',
[this](StringPiece cookieName, StringPiece cookieValue) {
cookies_.emplace(cookieName, cookieValue);
});
return false; // continue processing "cookie" headers
});
}
void HTTPMessage::unparseCookies() {
cookies_.clear();
parsedCookies_ = false;
}
const StringPiece HTTPMessage::getCookie(const string& name) const {
// Parse the cookies if we haven't done so yet
if (!parsedCookies_) {
parseCookies();
}
auto it = cookies_.find(name);
if (it == cookies_.end()) {
return StringPiece();
} else {
return it->second;
}
}
void HTTPMessage::parseQueryParams() const {
DCHECK(!parsedQueryParams_);
const Request& req = request();
parsedQueryParams_ = true;
if (req.query_.empty()) {
return;
}
splitNameValue(req.query_, '&', '=',
[this] (string&& paramName, string&& paramValue) {
auto it = queryParams_.find(paramName);
if (it == queryParams_.end()) {
queryParams_.emplace(std::move(paramName), std::move(paramValue));
} else {
// We have some unit tests that make sure we always return the last
// value when there are duplicate parameters. I don't think this really
// matters, but for now we might as well maintain the same behavior.
it->second = std::move(paramValue);
}
});
}
void HTTPMessage::unparseQueryParams() {
queryParams_.clear();
parsedQueryParams_ = false;
}
const string* HTTPMessage::getQueryParamPtr(const string& name) const {
// Parse the query parameters if we haven't done so yet
if (!parsedQueryParams_) {
parseQueryParams();
}
auto it = queryParams_.find(name);
if (it == queryParams_.end()) {
return nullptr;
}
return &it->second;
}
bool HTTPMessage::hasQueryParam(const string& name) const {
return getQueryParamPtr(name) != nullptr;
}
const string& HTTPMessage::getQueryParam(const string& name) const {
const string* ret = getQueryParamPtr(name);
return ret ? *ret : empty_string;
}
int HTTPMessage::getIntQueryParam(const std::string& name) const {
return folly::to<int>(getQueryParam(name));
}
int HTTPMessage::getIntQueryParam(const std::string& name, int defval) const {
try {
return getIntQueryParam(name);
} catch (const std::exception& ex) {
}
return defval;
}
std::string HTTPMessage::getDecodedQueryParam(const std::string& name) const {
auto val = getQueryParam(name);
std::string result;
try {
folly::uriUnescape(val, result, folly::UriEscapeMode::QUERY);
} catch (const std::exception& ex) {
LOG(WARNING) << "Invalid escaped query param: " << folly::exceptionStr(ex);
}
return result;
}
const std::map<std::string, std::string>& HTTPMessage::getQueryParams() const {
// Parse the query parameters if we haven't done so yet
if (!parsedQueryParams_) {
parseQueryParams();
}
return queryParams_;
}
bool HTTPMessage::setQueryString(const std::string& query) {
ParseURL u(request().url_);
if (u.valid()) {
// Recreate the URL by just changing the query string
request().url_ = createUrl(u.scheme(),
u.authority(),
u.path(),
query, // new query string
u.fragment());
request().query_ = query;
return true;
}
VLOG(4) << "Error parsing URL during setQueryString: " << request().url_;
return false;
}
bool HTTPMessage::removeQueryParam(const std::string& name) {
// Parse the query parameters if we haven't done so yet
if (!parsedQueryParams_) {
parseQueryParams();
}
if (!queryParams_.erase(name)) {
// Query param was not found.
return false;
}
auto query = createQueryString(queryParams_, request().query_.length());
return setQueryString(query);
}
bool HTTPMessage::setQueryParam(const std::string& name,
const std::string& value) {
// Parse the query parameters if we haven't done so yet
if (!parsedQueryParams_) {
parseQueryParams();
}
queryParams_[name] = value;
auto query = createQueryString(queryParams_, request().query_.length());
return setQueryString(query);
}
std::string HTTPMessage::createQueryString(
const std::map<std::string, std::string>& params, uint32_t maxLength) {
std::string query;
query.reserve(maxLength);
for (auto it = params.begin(); it != params.end(); it++) {
if (it != params.begin()) {
query.append("&");
}
query.append(it->first + "=" + it->second);
}
query.shrink_to_fit();
return query;
}
std::string HTTPMessage::createUrl(const folly::StringPiece scheme,
const folly::StringPiece authority,
const folly::StringPiece path,
const folly::StringPiece query,
const folly::StringPiece fragment) {
std::string url;
url.reserve(scheme.size() + authority.size() + path.size() + query.size() +
fragment.size() + 5); // 5 chars for ://,? and #
if (!scheme.empty()) {
folly::toAppend(scheme.str(), "://", &url);
}
folly::toAppend(authority, path, &url);
if (!query.empty()) {
folly::toAppend('?', query, &url);
}
if (!fragment.empty()) {
folly::toAppend('#', fragment, &url);
}
url.shrink_to_fit();
return url;
}
void HTTPMessage::splitNameValuePieces(
const string& input,
char pairDelim,
char valueDelim,
std::function<void(StringPiece, StringPiece)> callback) {
StringPiece sp(input);
while (!sp.empty()) {
size_t pairDelimPos = sp.find(pairDelim);
StringPiece keyValue;
if (pairDelimPos == string::npos) {
keyValue = sp;
sp.advance(sp.size());
} else {
keyValue = sp.subpiece(0, pairDelimPos);
// Skip '&' char
sp.advance(pairDelimPos + 1);
}
if (keyValue.empty()) {
continue;
}
size_t valueDelimPos = keyValue.find(valueDelim);
if (valueDelimPos == string::npos) {
// Key only query param
callback(trim(keyValue), StringPiece());
} else {
auto name = keyValue.subpiece(0, valueDelimPos);
auto value = keyValue.subpiece(valueDelimPos + 1);
callback(trim(name), trim(value));
}
}
}
StringPiece HTTPMessage::trim(StringPiece sp) {
// TODO: use a library function from boost?
for (; !sp.empty() && sp.front() == ' '; sp.pop_front()) {
}
for (; !sp.empty() && sp.back() == ' '; sp.pop_back()) {
}
return sp;
}
void HTTPMessage::splitNameValue(
const string& input,
char pairDelim,
char valueDelim,
std::function<void(string&&, string&&)> callback) {
folly::StringPiece sp(input);
while (!sp.empty()) {
size_t pairDelimPos = sp.find(pairDelim);
folly::StringPiece keyValue;
if (pairDelimPos == string::npos) {
keyValue = sp;
sp.advance(sp.size());
} else {
keyValue = sp.subpiece(0, pairDelimPos);
// Skip '&' char
sp.advance(pairDelimPos + 1);
}
if (keyValue.empty()) {
continue;
}
size_t valueDelimPos = keyValue.find(valueDelim);
if (valueDelimPos == string::npos) {
// Key only query param
string name = keyValue.str();
string value;
boost::trim(name, defaultLocale);
callback(std::move(name), std::move(value));
} else {
string name = keyValue.subpiece(0, valueDelimPos).str();
string value = keyValue.subpiece(valueDelimPos + 1).str();
boost::trim(name, defaultLocale);
boost::trim(value, defaultLocale);
callback(std::move(name), std::move(value));
}
}
}
void HTTPMessage::dumpMessage(int vlogLevel) const {
VLOG(vlogLevel) << "Version: " << versionStr_
<< ", chunked: " << chunked_
<< ", upgraded: " << upgraded_;
// Common fields to both requests and responses.
std::vector<std::pair<const char*, const std::string*>> fields {{
{"local_ip", &localIP_},
{"version", &versionStr_},
{"dst_ip", &dstIP_},
{"dst_port", &dstPort_},
}};
if (fields_.type() == typeid(Request)) {
// Request fields.
const Request& req = request();
fields.push_back(make_pair("client_ip", &req.clientIP_));
fields.push_back(make_pair("client_port", &req.clientPort_));
fields.push_back(make_pair("method", &getMethodString()));
fields.push_back(make_pair("path", &req.path_));
fields.push_back(make_pair("query", &req.query_));
fields.push_back(make_pair("url", &req.url_));
} else if (fields_.type() == typeid(Response)) {
// Response fields.
const Response& resp = response();
fields.push_back(make_pair("status", &resp.statusStr_));
fields.push_back(make_pair("status_msg", &resp.statusMsg_));
}
VLOG(vlogLevel) << "Fields for message: ";
for (auto field : fields) {
if (!field.second->empty()) {
VLOG(vlogLevel) << " " << field.first
<< ":" << stripCntrlChars(*field.second);
}
}
VLOG(vlogLevel) << "Headers for message: ";
headers_.forEach([&] (const string& h, const string& v) {
VLOG(vlogLevel) << " " << stripCntrlChars(h) << ": " << stripCntrlChars(v);
});
}
void
HTTPMessage::atomicDumpMessage(int vlogLevel) const {
std::lock_guard<std::mutex> g(mutexDump_);
dumpMessage(vlogLevel);
}
void HTTPMessage::dumpMessageToSink(google::LogSink* logSink) const {
LOG_TO_SINK(logSink, INFO) << "Version: " << versionStr_
<< ", chunked: " << chunked_
<< ", upgraded: " << upgraded_;
// Common fields to both requests and responses.
std::vector<std::pair<const char*, const std::string*>> fields {{
{"local_ip", &localIP_},
{"version", &versionStr_},
{"dst_ip", &dstIP_},
{"dst_port", &dstPort_},
}};
if (fields_.type() == typeid(Request)) {
// Request fields.
const Request& req = request();
fields.push_back(make_pair("client_ip", &req.clientIP_));
fields.push_back(make_pair("client_port", &req.clientPort_));
fields.push_back(make_pair("method", &getMethodString()));
fields.push_back(make_pair("path", &req.path_));
fields.push_back(make_pair("query", &req.query_));
fields.push_back(make_pair("url", &req.url_));
} else if (fields_.type() == typeid(Response)) {
// Response fields.
const Response& resp = response();
fields.push_back(make_pair("status", &resp.statusStr_));
fields.push_back(make_pair("status_msg", &resp.statusMsg_));
}
LOG_TO_SINK(logSink, INFO) << "Fields for message: ";
for (auto field : fields) {
if (!field.second->empty()) {
LOG_TO_SINK(logSink, INFO) << " " << field.first
<< ":" << folly::backslashify(*field.second);
}
}
LOG_TO_SINK(logSink, INFO) << "Headers for message: ";
headers_.forEach([&logSink] (const string& h, const string& v) {
LOG_TO_SINK(logSink, INFO) << " " << folly::backslashify(h)
<< ": " << folly::backslashify(v);
});
}
bool HTTPMessage::computeKeepalive() const {
if (version_.first < 1) {
return false;
}
// RFC 2616 isn't explicitly clear about whether "close" is case-sensitive.
// Section 2.1 states that literal tokens in the BNF are case-insensitive
// unless stated otherwise. The "close" token isn't explicitly mentioned
// in the BNF, but other header fields such as the character set and
// content coding are explicitly called out as being case insensitive.
//
// We'll treat the "close" token case-insensitively. This is the most
// conservative approach, since disabling keepalive when it was requested
// is better than enabling keepalive for a client that didn't expect it.
//
// Note that we only perform ASCII lowering here. This is good enough,
// since the token we are looking for is ASCII.
if (checkForHeaderToken(HTTP_HEADER_CONNECTION, "close", false)) {
// The Connection header contained a "close" token, so keepalive
// is disabled.
return false;
}
if (version_ == kHTTPVersion10) {
// HTTP 1.0 persistent connections require a Connection: Keep-Alive
// header to be present for the connection to be persistent.
if (checkForHeaderToken(HTTP_HEADER_CONNECTION, "keep-alive", false)) {
return true;
}
return false;
}
// It's a keepalive connection.
return true;
}
bool HTTPMessage::checkForHeaderToken(const HTTPHeaderCode headerCode,
char const* token,
bool caseSensitive) const {
StringPiece tokenPiece(token);
// Search through all of the headers with this name.
// forEachValueOfHeader will return true iff it was "broken" prematurely
// with "return true" in the lambda-function
return headers_.forEachValueOfHeader(headerCode, [&] (const string& value) {
string lower;
// Use StringPiece, since it implements a faster find() than std::string
StringPiece headerValue;
if (caseSensitive) {
headerValue.reset(value);
} else {
// TODO: We only perform ASCII lowering right now. Technically the
// headers could contain data in other encodings, if encoded according
// to RFC 2047 (encoded strings will start with "=?").
lower = value;
boost::to_lower(lower, defaultLocale);
headerValue.reset(lower);
}
// Look for the specified token
size_t idx = 0;
size_t end = headerValue.size();
while (idx < end) {
idx = headerValue.find(tokenPiece, idx);
if (idx == string::npos) {
break;
}
// Search backwards to make sure we found the value at the beginning
// of a token.
bool at_token_start = false;
size_t prev = idx;
while (true) {
if (prev == 0) {
at_token_start = true;
break;
}
--prev;
char c = headerValue[prev];
if (c == ',') {
at_token_start = true;
break;
}
if (!isLWS(c)) {
// not at a token start
break;
}
}
if (!at_token_start) {
idx += 1;
continue;
}
// Search forwards to see if we found the value at the end of a token
bool at_token_end = false;
size_t next = idx + tokenPiece.size();
while (true) {
if (next >= end) {
at_token_end = true;
break;
}
char c = headerValue[next];
if (c == ',') {
at_token_end = true;
break;
}
if (!isLWS(c)) {
// not at a token end
break;
}
++next;
}
if (at_token_end) {
// We found the token we're looking for
return true;
}
idx += 1;
}
return false; // keep processing
});
}
const char* HTTPMessage::getDefaultReason(uint16_t status) {
switch (status) {
case 100: return "Continue";
case 101: return "Switching Protocols";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Request Entity Too Large";
case 414: return "Request-URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 418: return "I'm a teapot";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
}
// Note: Some Microsoft clients behave badly if the reason string
// is left empty. Therefore return a non-empty string here.
return "-";
}
} // proxygen

View File

@ -0,0 +1,724 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/lib/http/HTTPHeaderSize.h"
#include "proxygen/lib/http/HTTPHeaders.h"
#include "proxygen/lib/http/HTTPMethod.h"
#include "proxygen/lib/utils/ParseURL.h"
#include "proxygen/lib/utils/Time.h"
#include <array>
#include <boost/variant.hpp>
#include <folly/Conv.h>
#include <folly/Optional.h>
#include <folly/SocketAddress.h>
#include <folly/io/IOBufQueue.h>
#include <glog/logging.h>
#include <map>
#include <mutex>
#include <string>
namespace proxygen {
/**
* An HTTP request or response minus the body.
*
* Some of the methods on this class will assert if called from the wrong
* context since they only make sense for a request or response. Make sure
* you know what type of HTTPMessage this is before calling such methods.
*
* All header names stored in this class are case-insensitive.
*/
class HTTPMessage {
public:
HTTPMessage();
~HTTPMessage();
HTTPMessage(const HTTPMessage& message);
HTTPMessage& operator=(const HTTPMessage& message);
/**
* Is this a chunked message? (fpreq, fpresp)
*/
void setIsChunked(bool chunked) { chunked_ = chunked; }
bool getIsChunked() const { return chunked_; }
/**
* Is this an upgraded message? (fpreq, fpresp)
*/
void setIsUpgraded(bool upgraded) { upgraded_ = upgraded; }
bool getIsUpgraded() const { return upgraded_; }
/**
* Set/Get client address
*/
void setClientAddress(const folly::SocketAddress& addr) {
request().clientAddress_ = addr;
request().clientIP_ = addr.getAddressStr();
request().clientPort_ = folly::to<std::string>(addr.getPort());
}
const folly::SocketAddress& getClientAddress() const {
return request().clientAddress_;
}
const std::string& getClientIP() const {
return request().clientIP_;
}
const std::string& getClientPort() const {
return request().clientPort_;
}
/**
* Set/Get destination (vip) address
*/
void setDstAddress(const folly::SocketAddress& addr) {
dstAddress_ = addr;
dstIP_ = addr.getAddressStr();
dstPort_ = folly::to<std::string>(addr.getPort());
}
const folly::SocketAddress& getDstAddress() const {
return dstAddress_;
}
const std::string& getDstIP() const {
return dstIP_;
}
const std::string& getDstPort() const {
return dstPort_;
}
/**
* Set/Get the local IP address
*/
template <typename T> // T = string
void setLocalIp(T&& ip) {
localIP_ = std::forward<T>(ip);
}
const std::string& getLocalIp() const {
return localIP_;
}
/**
* Access the method (fpreq)
*/
void setMethod(HTTPMethod method);
void setMethod(folly::StringPiece method);
void rawSetMethod(const std::string& method) {
setMethod(method);
}
/**
* @Returns an HTTPMethod enum value representing the method if it is a
* standard request method, or else "none" if it is an extension method
* (fpreq)
*/
boost::optional<HTTPMethod> getMethod() const;
/**
* @Returns a string representation of the request method (fpreq)
*/
const std::string& getMethodString() const;
/**
* Access the URL component (fpreq)
*
* The <url> component from the initial "METHOD <url> HTTP/..." line. When
* valid, this is a full URL, not just a path.
*/
template <typename T> // T = string
ParseURL setURL(T&& url) {
VLOG(9) << "setURL: " << url;
// Set the URL, path, and query string parameters
ParseURL u(url);
if (u.valid()) {
VLOG(9) << "set path: " << u.path() << " query:" << u.query();
request().path_ = u.path().str();
request().query_ = u.query().str();
unparseQueryParams();
} else {
VLOG(4) << "Error in parsing URL: " << url;
}
request().url_ = std::forward<T>(url);
return u;
}
// The template function above doesn't work with char*,
// so explicitly convert to a string first.
void setURL(const char* url) {
setURL(std::string(url));
}
const std::string& getURL() const {
return request().url_;
}
void rawSetURL(const std::string& url) {
setURL(url);
}
/**
* Access the path component (fpreq)
*/
const std::string& getPath() const {
return request().path_;
}
/**
* Access the query component (fpreq)
*/
const std::string& getQueryString() const {
return request().query_;
}
/**
* Version constants
*/
static const std::pair<uint8_t, uint8_t> kHTTPVersion10;
static const std::pair<uint8_t, uint8_t> kHTTPVersion11;
/**
* Access the HTTP version number (fpreq, fpres)
*/
void setHTTPVersion(uint8_t major, uint8_t minor);
const std::pair<uint8_t, uint8_t>& getHTTPVersion() const;
/**
* Access the HTTP status message string (res)
*/
template <typename T> // T = string
void setStatusMessage(T&& msg) {
response().statusMsg_ = std::forward<T>(msg);
}
const std::string& getStatusMessage() const {
return response().statusMsg_;
}
void rawSetStatusMessage(std::string msg) {
setStatusMessage(msg);
}
/**
* Get/Set the HTTP version string (like "1.1").
* XXX: Note we only support X.Y format while setting version.
*/
const std::string& getVersionString() const {
return versionStr_;
}
void setVersionString(const std::string& ver) {
if (ver.size() != 3 ||
ver[1] != '.' ||
!isdigit(ver[0]) ||
!isdigit(ver[2])) {
return;
}
setHTTPVersion(ver[0] - '0', ver[2] - '0');
}
/**
* Access the headers (fpreq, fpres)
*/
HTTPHeaders& getHeaders() { return headers_; }
const HTTPHeaders& getHeaders() const { return headers_; }
/**
* Access the trailers
*/
HTTPHeaders* getTrailers() { return trailers_.get(); }
const HTTPHeaders* getTrailers() const { return trailers_.get(); }
/**
* Set the trailers, replacing any that might already be present
*/
void setTrailers(std::unique_ptr<HTTPHeaders>&& trailers) {
trailers_ = std::move(trailers);
}
/**
* Decrements Max-Forwards header, when present on OPTIONS or TRACE methods.
*
* Returns HTTP status code.
*/
int processMaxForwards();
/**
* Returns true if the version of this message is HTTP/1.0
*/
bool isHTTP1_0() const;
/**
* Returns true if the version of this message is HTTP/1.1
*/
bool isHTTP1_1() const;
/**
* Returns true if this is a 1xx response.
*/
bool is1xxResponse() const { return (getStatusCode() / 100) == 1; }
/**
* Formats the current time appropriately for a Date header
*/
static std::string formatDateHeader();
/**
* Ensures this HTTPMessage contains a host header, adding a default one
* with the destination address if necessary.
*/
void ensureHostHeader();
/**
* Indicates if this request wants the connection to be kept-alive
* (default true). Not all codecs respect this option.
*/
void setWantsKeepalive(bool wantsKeepaliveVal) {
wantsKeepalive_ = wantsKeepaliveVal;
}
bool wantsKeepalive() const {
return wantsKeepalive_;
}
/**
* Returns true if trailers are allowed on this message. Trailers
* are not allowed on responses unless the client is expecting them.
*/
bool trailersAllowed() const { return trailersAllowed_; }
/**
* Accessor to set whether trailers are allowed in the response
*/
void setTrailersAllowed(bool trailersAllowedVal) {
trailersAllowed_ = trailersAllowedVal;
}
/**
* Returns true if this message has trailers that need to be serialized
*/
bool hasTrailers() const {
return trailersAllowed_ && trailers_ && trailers_->size() > 0;
}
/**
* Access the status code (fpres)
*/
void setStatusCode(uint16_t status);
uint16_t getStatusCode() const;
/**
* Fill in the fields for a response message header that the server will
* send directly to the client.
*
* @param version HTTP version (major, minor)
* @param statusCode HTTP status code to respond with
* @param msg textual message to embed in "message" status field
* @param contentLength the length of the data to be written out through
* this message
*/
void constructDirectResponse(const std::pair<uint8_t, uint8_t>& version,
const int statusCode,
const std::string& statusMsg,
int contentLength = 0);
/**
* Fill in the fields for a response message header that the server will
* send directly to the client. This function assumes the status code and
* status message have already been set on this HTTPMessage object
*
* @param version HTTP version (major, minor)
* @param contentLength the length of the data to be written out through
* this message
*/
void constructDirectResponse(const std::pair<uint8_t, uint8_t>& version,
int contentLength = 0);
/**
* Check if query parameter with the specified name exists.
*/
bool hasQueryParam(const std::string& name) const;
/**
* Get the query parameter with the specified name.
*
* Returns a pointer to the query parameter value, or nullptr if there is no
* parameter with the specified name. The returned value is only valid as
* long as this HTTPMessage object.
*/
const std::string* getQueryParamPtr(const std::string& name) const;
/**
* Get the query parameter with the specified name.
*
* Returns a reference to the query parameter value, or
* proxygen::empty_string if there is no parameter with the
* specified name. The returned value is only valid as long as this
* HTTPMessage object.
*/
const std::string& getQueryParam(const std::string& name) const;
/**
* Get the query parameter with the specified name as int.
*
* If the conversion fails, throws exception.
*/
int getIntQueryParam(const std::string& name) const;
/**
* Get the query parameter with the specified name as int.
*
* Returns the query parameter if it can be parsed as int otherwise the
* default value.
*/
int getIntQueryParam(const std::string& name, int defval) const;
/**
* Get the query parameter with the specified name after percent decoding.
*
* Returns empty string if parameter is missing or folly::uriUnescape
* query param
*/
std::string getDecodedQueryParam(const std::string& name) const;
/**
* Get all the query parameters.
*
* Returns a reference to the query parameters map. The returned
* value is only valid as long as this
* HTTPMessage object.
*/
const std::map<std::string, std::string>& getQueryParams() const;
/**
* Set the query string to the specified value, and recreate the url_.
*
* Returns true if the query string was changed successfully.
*/
bool setQueryString(const std::string& query);
/**
* Remove the query parameter with the specified name.
*
* Returns true if the query parameter was present and deleted.
*/
bool removeQueryParam(const std::string& name);
/**
* Sets the query parameter with the specified name to the specified value.
*
* Returns true if the query parameter was successfully set.
*/
bool setQueryParam(const std::string& name, const std::string& value);
/**
* Get the cookie with the specified name.
*
* Returns a StringPiece to the cookie value, or an empty StringPiece if
* there is no cookie with the specified name. The returned cookie is
* only valid as long as the Cookie Header in HTTPMessage object exists.
* Applications should make sure they call unparseCookies() when editing
* the Cookie Header, so that the StringPiece references are cleared.
*/
const folly::StringPiece getCookie(const std::string& name) const;
/**
* Print the message out.
*/
void dumpMessage(int verbosity) const;
/**
* Print the message out, serializes through mutex
* Used in shutdown path
*/
void atomicDumpMessage(int verbosity) const;
/**
* Print the message out to logSink.
*/
void dumpMessageToSink(google::LogSink* logSink) const;
/**
* Interact with headers that are defined to be per-hop.
*
* It is expected that during request processing, stripPerHopHeaders() will
* be called before the message is proxied to the other connection.
*/
void stripPerHopHeaders();
const HTTPHeaders& getStrippedPerHopHeaders() const {
return strippedPerHopHeaders_;
}
void setSecure(bool secure) { secure_ = secure; }
bool isSecure() const { return secure_; }
int getSecureVersion() const { return sslVersion_; }
const char* getSecureCipher() const { return sslCipher_; }
void setSecureInfo(int ver, const char* cipher) {
// cipher is a static const char* provided and managed by openssl lib
sslVersion_ = ver; sslCipher_ = cipher;
}
void setSPDY(uint16_t spdyVersion) { spdy_ = spdyVersion; }
bool isSPDY() const { return spdy_; }
uint16_t getSPDYVersion() const { return spdy_; }
/* Setter and getter for the SPDY priority value.
*
* Negative values of pri are interpreted much like negative array
* indexes in python, so -1 will be the largest numerical priority
* value for this SPDY version (i.e. 3 for SPDY/2 or 7 for SPDY/3),
* -2 the second largest (i.e. 2 for SPDY/2 or 6 for SPDY/3).
*/
void setPriority(int8_t pri) { pri_ = pri; }
uint8_t getPriority() const {
int8_t pri = pri_;
if (pri < 0) {
pri += (getSPDYVersion() > 2) ? 8 : 4;
}
return pri;
}
/**
* get and setter for transaction sequence number
*/
void setSeqNo(int32_t seqNo) { seqNo_ = seqNo; }
int32_t getSeqNo() const { return seqNo_; }
/**
* getter and setter for size in serialized form
*/
void setIngressHeaderSize(const HTTPHeaderSize& size) {
size_ = size;
}
const HTTPHeaderSize& getIngressHeaderSize() const {
return size_;
}
/**
* Get the time when the first byte of the message arrived
*/
TimePoint getStartTime() const { return startTime_; }
/**
* Check if a particular token value is present in a header that consists of
* a list of comma separated tokens. (e.g., a header with a #rule
* body as specified in the RFC 2616 BNF notation.)
*/
bool checkForHeaderToken(const HTTPHeaderCode headerCode,
char const* token,
bool caseSensitive) const;
/**
* Forget about the parsed cookies.
*
* Ideally HTTPMessage should automatically forget about the current parsed
* cookie state whenever a Cookie header is changed. However, at the moment
* callers have to explicitly call unparseCookies() after modifying the
* cookie headers.
*/
void unparseCookies();
/**
* Get the default reason string for a status code.
*
* This returns the reason string for the specified status code as specified
* in RFC 2616. For unknown status codes, the string "-" is returned.
*/
static const char* getDefaultReason(uint16_t status);
/**
* Computes whether the state of this message is compatible with keepalive.
* Changing the headers, version, etc can change the result.
*/
bool computeKeepalive() const;
/**
* @returns true if this HTTPMessage represents an HTTP request
*/
bool isRequest() const {
return fields_.which() == 1;
}
/**
* @returns true if this HTTPMessage represents an HTTP response
*/
bool isResponse() const {
return fields_.which() == 2;
}
/**
* Assuming input contains
* <name><valueDelim><value>(<pairDelim><name><valueDelim><value>),
* invoke callback once with each name,value pair.
*/
static void splitNameValuePieces(
const std::string& input,
char pairDelim,
char valueDelim,
std::function<void(folly::StringPiece, folly::StringPiece)> callback);
static void splitNameValue(
const std::string& input,
char pairDelim,
char valueDelim,
std::function<void(std::string&&, std::string&&)> callback);
/**
* Form the URL from the individual components.
* url -> {scheme}://{authority}{path}?{query}#{fragment}
*/
static std::string createUrl(const folly::StringPiece scheme,
const folly::StringPiece authority,
const folly::StringPiece path,
const folly::StringPiece query,
const folly::StringPiece fragment);
/**
* Create a query string from the query parameters map
* containing the name-value pairs.
*/
static std::string createQueryString(
const std::map<std::string, std::string>& params, uint32_t maxSize);
protected:
// Message start time, in msec since the epoch.
TimePoint startTime_;
int32_t seqNo_;
private:
void parseCookies() const;
void parseQueryParams() const;
void unparseQueryParams();
/**
* Trims whitespace from the beggining and end of the StringPiece.
*/
static folly::StringPiece trim(folly::StringPiece sp);
/** The 12 standard fields for HTTP messages. Use accessors.
* An HTTPMessage is either a Request or Response.
* Once an accessor for either is used, that fixes the type of HTTPMessage.
* If an access is then used for the other type, a DCHECK will fail.
*/
struct Request {
folly::SocketAddress clientAddress_;
std::string clientIP_;
std::string clientPort_;
mutable boost::variant<boost::blank, std::string, HTTPMethod> method_;
std::string path_;
std::string query_;
std::string url_;
};
struct Response {
uint16_t status_;
std::string statusStr_;
std::string statusMsg_;
};
folly::SocketAddress dstAddress_;
std::string dstIP_;
std::string dstPort_;
std::string localIP_;
std::string versionStr_;
mutable boost::variant<boost::blank, Request, Response> fields_;
Request& request() {
DCHECK(fields_.which() == 0 || fields_.which() == 1) << fields_.which();
if (fields_.which() == 0) {
fields_ = Request();
}
return boost::get<Request>(fields_);
}
const Request& request() const {
DCHECK(fields_.which() == 0 || fields_.which() == 1) << fields_.which();
if (fields_.which() == 0) {
fields_ = Request();
}
return boost::get<const Request>(fields_);
}
Response& response() {
DCHECK(fields_.which() == 0 || fields_.which() == 2) << fields_.which();
if (fields_.which() == 0) {
fields_ = Response();
}
return boost::get<Response>(fields_);
}
const Response& response() const {
DCHECK(fields_.which() == 0 || fields_.which() == 2) << fields_.which();
if (fields_.which() == 0) {
fields_ = Response();
}
return boost::get<const Response>(fields_);
}
/*
* Cookies and query parameters
* These are mutable since we parse them lazily in getCookie() and
* getQueryParam()
*/
mutable std::map<folly::StringPiece, folly::StringPiece> cookies_;
// TODO: use StringPiece for queryParams_ and delete splitNameValue()
mutable std::map<std::string, std::string> queryParams_;
std::pair<uint8_t, uint8_t> version_;
HTTPHeaders headers_;
HTTPHeaders strippedPerHopHeaders_;
HTTPHeaderSize size_;
std::unique_ptr<HTTPHeaders> trailers_;
int sslVersion_;
const char* sslCipher_;
uint16_t spdy_; // == 0 - no SPDY; > 0 - SPDY Version
uint8_t pri_;
mutable bool parsedCookies_:1;
mutable bool parsedQueryParams_:1;
bool chunked_:1;
bool upgraded_:1;
bool wantsKeepalive_:1;
bool trailersAllowed_:1;
// Whether the message is received in HTTPS.
bool secure_:1;
// used by atomicDumpMessage
static std::mutex mutexDump_;
};
/**
* Returns a std::string that has the control characters removed from the
* input string.
*/
template<typename Str>
std::string stripCntrlChars(const Str& str) {
std::string res;
res.reserve(str.length());
for (size_t i = 0; i < str.size(); ++i) {
if (!(str[i] <= 0x1F || str[i] == 0x7F)) {
res += str[i];
}
}
return res;
}
} // proxygen

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/lib/http/session/HTTPTransaction.h"
#include "proxygen/lib/utils/DestructorCheck.h"
#include <folly/Memory.h>
namespace proxygen {
class HTTPMessageFilter: public HTTPTransaction::Handler,
public DestructorCheck {
public:
void setNextTransactionHandler(HTTPTransaction::Handler* next) {
CHECK(next);
nextTransactionHandler_ = next;
}
HTTPTransaction::Handler* getNextTransactionHandler() {
return nextTransactionHandler_;
}
// These HTTPTransaction::Handler callbacks may be overwritten
// The default behavior is to pass the call through.
void onHeadersComplete(std::unique_ptr<HTTPMessage> msg) noexcept override {
nextTransactionHandler_->onHeadersComplete(std::move(msg));
}
void onBody(std::unique_ptr<folly::IOBuf> chain) noexcept override {
nextTransactionHandler_->onBody(std::move(chain));
}
void onChunkHeader(size_t length) noexcept override {
nextTransactionHandler_->onChunkHeader(length);
}
void onChunkComplete() noexcept override {
nextTransactionHandler_->onChunkComplete();
}
void onTrailers(std::unique_ptr<HTTPHeaders> trailers) noexcept override {
nextTransactionHandler_->onTrailers(std::move(trailers));
}
void onEOM() noexcept override {
nextTransactionHandler_->onEOM();
}
void onUpgrade(UpgradeProtocol protocol) noexcept override {
nextTransactionHandler_->onUpgrade(protocol);
}
void onError(const HTTPException& error) noexcept override {
nextTransactionHandler_->onError(error);
}
// These HTTPTransaction::Handler callbacks cannot be overrwritten
void setTransaction(HTTPTransaction* txn) noexcept override final {
nextTransactionHandler_->setTransaction(txn);
}
void detachTransaction() noexcept override final {
nextTransactionHandler_->detachTransaction();
}
void onEgressPaused() noexcept override final {
nextTransactionHandler_->onEgressPaused();
}
void onEgressResumed() noexcept override final {
nextTransactionHandler_->onEgressResumed();
}
void onPushedTransaction(HTTPTransaction* txn) noexcept override final {
nextTransactionHandler_->onPushedTransaction(txn);
}
protected:
void nextOnHeadersComplete(std::unique_ptr<HTTPMessage> msg) {
nextTransactionHandler_->onHeadersComplete(std::move(msg));
}
void nextOnBody(std::unique_ptr<folly::IOBuf> chain) {
nextTransactionHandler_->onBody(std::move(chain));
}
void nextOnChunkHeader(size_t length) {
nextTransactionHandler_->onChunkHeader(length);
}
void nextOnChunkComplete() {
nextTransactionHandler_->onChunkComplete();
}
void nextOnTrailers(std::unique_ptr<HTTPHeaders> trailers) {
nextTransactionHandler_->onTrailers(std::move(trailers));
}
void nextOnEOM() {
nextTransactionHandler_->onEOM();
}
void nextOnError(const HTTPException& ex) {
nextTransactionHandler_->onError(ex);
}
HTTPTransaction::Handler* nextTransactionHandler_{nullptr};
};
} // proxygen

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/HTTPMethod.h"
#include "proxygen/lib/http/HTTPHeaders.h"
#include <folly/Foreach.h>
#include <vector>
#define HTTP_METHOD_STR(method) #method
namespace {
static const std::vector<std::string> kMethodStrings = {
HTTP_METHOD_GEN(HTTP_METHOD_STR)
};
}
namespace proxygen {
boost::optional<HTTPMethod> stringToMethod(folly::StringPiece method) {
FOR_EACH_ENUMERATE(index, cur, kMethodStrings) {
if (caseInsensitiveEqual(*cur, method)) {
return HTTPMethod(index);
}
}
return boost::none;
}
const std::string& methodToString(HTTPMethod method) {
return kMethodStrings[static_cast<unsigned>(method)];
}
std::ostream& operator <<(std::ostream& out, HTTPMethod method) {
out << methodToString(method);
return out;
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <boost/optional/optional.hpp>
#include <folly/Range.h>
#include <string>
namespace proxygen {
// Ordered by frequency to minimize time spent in iteration
#define HTTP_METHOD_GEN(x) \
x(GET), \
x(POST), \
x(OPTIONS), \
x(DELETE), \
x(HEAD), \
x(CONNECT), \
x(PUT), \
x(TRACE)
#define HTTP_METHOD_ENUM(method) method
/**
* See the definitions in RFC2616 5.1.1 for the source of this
* list. Today, proxygen only understands the methods defined in 5.1.1 and
* is not aware of any extension methods. If you wish to support extension
* methods, you must handle those separately from this enum.
*/
enum class HTTPMethod {
HTTP_METHOD_GEN(HTTP_METHOD_ENUM)
};
#undef HTTP_METHOD_ENUM
/**
* Returns the HTTPMethod that matches the method. Although RFC2616 5.1.1
* says methods are case-sensitive, we ignore case here since most
* programmers probably really meant "GET" not "get". If the method is not
* recognized, the return value will be None
*/
extern boost::optional<HTTPMethod> stringToMethod(folly::StringPiece method);
/**
* Returns a string representation of the method. If EXTENSION_METHOD is
* passed, then an empty string is returned
*/
extern const std::string& methodToString(HTTPMethod method);
std::ostream& operator<<(std::ostream& os, HTTPMethod method);
}

View File

@ -0,0 +1,133 @@
SUBDIRS = . codec session test
BUILT_SOURCES = HTTPCommonHeaders.h HTTPCommonHeaders.cpp
CommonHeadersGen: HTTPCommonHeaders.template.h HTTPCommonHeaders.template.gperf HTTPCommonHeaders.txt
INSTALLED_GPERF=1 FBCODE_DIR=$(top_srcdir)/.. INSTALL_DIR=$(srcdir) ruby generate_http_gperfs.rb
touch CommonHeadersGen
HTTPCommonHeaders.h: CommonHeadersGen
HTTPCommonHeaders.cpp: CommonHeadersGen
noinst_LTLIBRARIES = libproxygenhttp.la
libproxygenhttpdir = $(includedir)/proxygen/lib/http
nobase_libproxygenhttp_HEADERS = \
HTTPCommonHeaders.h \
HTTPConnector.h \
HTTPConstants.h \
HTTPException.h \
HTTPHeaderSize.h \
HTTPHeaders.h \
HTTPMessage.h \
HTTPMessageFilters.h \
HTTPMethod.h \
ProxygenErrorEnum.h \
RFC2616.h \
Window.h \
codec/CodecDictionaries.h \
codec/CodecProtocol.h \
codec/ErrorCode.h \
codec/FlowControlFilter.h \
codec/HTTP1xCodec.h \
codec/HTTPChecks.h \
codec/HTTPCodec.h \
codec/HTTPCodecFilter.h \
codec/HTTPSettings.h \
codec/SPDYCodec.h \
codec/SPDYConstants.h \
codec/SPDYUtil.h \
codec/SPDYVersion.h \
codec/SPDYVersionSettings.h \
codec/SettingsId.h \
codec/TransportDirection.h \
codec/compress/GzipHeaderCodec.h \
codec/compress/HPACKCodec.h \
codec/compress/HPACKConstants.h \
codec/compress/HPACKContext.h \
codec/compress/HPACKDecodeBuffer.h \
codec/compress/HPACKDecoder.h \
codec/compress/HPACKEncodeBuffer.h \
codec/compress/HPACKEncoder.h \
codec/compress/HPACKHeader.h \
codec/compress/Header.h \
codec/compress/HeaderCodec.h \
codec/compress/HeaderPiece.h \
codec/compress/HeaderTable.h \
codec/compress/Huffman.h \
codec/compress/Logging.h \
codec/compress/StaticHeaderTable.h \
session/ByteEventTracker.h \
session/ByteEvents.h \
session/CodecErrorResponseHandler.h \
session/HTTPDirectResponseHandler.h \
session/HTTPDownstreamSession.h \
session/HTTPErrorPage.h \
session/HTTPEvent.h \
session/HTTPSession.h \
session/HTTPSessionAcceptor.h \
session/HTTPSessionController.h \
session/HTTPSessionStats.h \
session/HTTPTransaction.h \
session/HTTPTransactionEgressSM.h \
session/HTTPTransactionIngressSM.h \
session/HTTPUpstreamSession.h \
session/SimpleController.h \
session/TransportFilter.h
libproxygenhttp_la_SOURCES = \
HTTPCommonHeaders.cpp \
codec/CodecProtocol.cpp \
codec/compress/GzipHeaderCodec.cpp \
codec/compress/HeaderTable.cpp \
codec/compress/HPACKCodec.cpp \
codec/compress/HPACKContext.cpp \
codec/compress/HPACKDecodeBuffer.cpp \
codec/compress/HPACKDecoder.cpp \
codec/compress/HPACKEncodeBuffer.cpp \
codec/compress/HPACKEncoder.cpp \
codec/compress/HPACKHeader.cpp \
codec/compress/Huffman.cpp \
codec/compress/Logging.cpp \
codec/compress/StaticHeaderTable.cpp \
codec/ErrorCode.cpp \
codec/FlowControlFilter.cpp \
codec/HTTP1xCodec.cpp \
codec/HTTPChecks.cpp \
codec/HTTPCodecFilter.cpp \
codec/HTTPSettings.cpp \
codec/SPDYCodec.cpp \
codec/SPDYConstants.cpp \
codec/SPDYUtil.cpp \
codec/SettingsId.cpp \
codec/TransportDirection.cpp \
HTTPConnector.cpp \
HTTPConstants.cpp \
HTTPException.cpp \
HTTPHeaders.cpp \
HTTPMessage.cpp \
HTTPMethod.cpp \
ProxygenErrorEnum.cpp \
RFC2616.cpp \
session/ByteEvents.cpp \
session/CodecErrorResponseHandler.cpp \
session/HTTPDirectResponseHandler.cpp \
session/HTTPDownstreamSession.cpp \
session/HTTPErrorPage.cpp \
session/HTTPEvent.cpp \
session/HTTPSessionAcceptor.cpp \
session/HTTPSession.cpp \
session/HTTPTransaction.cpp \
session/HTTPTransactionEgressSM.cpp \
session/HTTPTransactionIngressSM.cpp \
session/HTTPUpstreamSession.cpp \
session/ByteEventTracker.cpp \
session/SimpleController.cpp \
session/TransportFilter.cpp \
Window.cpp
libproxygenhttp_la_LIBADD = \
../services/libproxygenservices.la \
../ssl/libproxygenssl.la \
../utils/libutils.la

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/ProxygenErrorEnum.h"
#define PROXYGEN_ERROR_STR(error) #error
namespace {
static const char* errorStrings[] = {
PROXYGEN_ERROR_GEN(PROXYGEN_ERROR_STR)
};
}
namespace proxygen {
const char* getErrorString(ProxygenError error) {
if (error < kErrorNone || error >= kErrorMax) {
return errorStrings[kErrorMax];
} else {
return errorStrings[error];
}
}
const char* getErrorStringByIndex(int i) {
return errorStrings[i];
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
namespace proxygen {
// TODO #5101452 remove all references to "read" and "write" since that
// should be indicated by the direction field of HTTPException
// Max must be the last one.
#define PROXYGEN_ERROR_GEN(x) \
x(None), \
x(Message), \
x(Connect), \
x(ConnectTimeout), \
x(Read), \
x(Write), \
x(Timeout), \
x(Handshake), \
x(NoServer), \
x(MaxRedirects), \
x(InvalidRedirect), \
x(ResponseAction), \
x(MaxConnects), \
x(Dropped), \
x(Connection), \
x(ConnectionReset), \
x(ParseHeader), \
x(ParseBody), \
x(EOF), \
x(ClientRenegotiation), \
x(Unknown), \
x(BadDecompress), \
x(SSL), \
x(StreamAbort), \
x(StreamUnacknowledged), \
x(WriteTimeout), \
x(AddressPrivate), \
x(AddressFamilyNotSupported), \
x(DNSNoResults), \
x(MalformedInput), \
x(UnsupportedExpectation), \
x(MethodNotSupported), \
x(UnsupportedScheme), \
x(Shutdown), \
x(IngressStateTransition), \
x(ClientSilent), \
x(Max)
// Increase this if you add more error types and Max exceeds 63
#define PROXYGEN_ERROR_BITSIZE 6
#define PROXYGEN_ERROR_ENUM(error) kError##error
enum ProxygenError {
PROXYGEN_ERROR_GEN(PROXYGEN_ERROR_ENUM)
};
#undef PROXYGEN_ERROR_ENUM
extern const char* getErrorString(ProxygenError error);
extern const char* getErrorStringByIndex(int i);
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/RFC2616.h"
#include "proxygen/lib/http/HTTPHeaders.h"
#include <folly/String.h>
#include <folly/ThreadLocal.h>
namespace proxygen { namespace RFC2616 {
BodyAllowed isRequestBodyAllowed(boost::optional<HTTPMethod> method) {
if (method == HTTPMethod::TRACE) {
return BodyAllowed::NOT_ALLOWED;
}
if (method == HTTPMethod::OPTIONS || method == HTTPMethod::POST ||
method == HTTPMethod::PUT || method == HTTPMethod::CONNECT) {
return BodyAllowed::DEFINED;
}
return BodyAllowed::NOT_DEFINED;
}
bool responseBodyMustBeEmpty(unsigned status) {
return (status == 304 || status == 204 ||
(100 <= status && status < 200));
}
bool bodyImplied(const HTTPHeaders& headers) {
return headers.exists(HTTP_HEADER_TRANSFER_ENCODING) ||
headers.exists(HTTP_HEADER_CONTENT_LENGTH);
}
bool parseQvalues(folly::StringPiece value, std::vector<TokenQPair> &output) {
bool result = true;
static folly::ThreadLocal<std::vector<folly::StringPiece>> tokens;
tokens->clear();
folly::split(",", value, *tokens, true /*ignore empty*/);
for (auto& token: *tokens) {
auto pos = token.find(';');
double qvalue = 1.0;
if (pos != std::string::npos) {
auto qpos = token.find("q=", pos);
if (qpos != std::string::npos) {
folly::StringPiece qvalueStr(token.data() + qpos + 2,
token.size() - (qpos + 2));
try {
qvalue = folly::to<double>(&qvalueStr);
} catch (const std::range_error&) {
// q=<some garbage>
result = false;
}
// we could validate that the remainder of qvalueStr was all whitespace,
// for now we just discard it
} else {
// ; but no q=
result = false;
}
token.reset(token.start(), pos);
}
// strip leading whitespace
while (token.size() > 0 && isspace(token[0])) {
token.reset(token.start() + 1, token.size() - 1);
}
if (token.size() == 0) {
// empty token
result = false;
} else {
output.emplace_back(token, qvalue);
}
}
return result && output.size() > 0;
}
}}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/lib/http/HTTPMethod.h"
#include <folly/Range.h>
#include <string>
namespace proxygen {
class HTTPHeaders;
namespace RFC2616 {
/**
* This file contains functions for determining when certain tricky parts of RFC
* 2616 arise.
*/
/**
* The HTTP request as defined in RFC 2616 may or may not have a body. In some
* cases they MUST NOT have a body. In other cases, the body has no semantic
* meaning and so is not defined. Finally, for some methods, the body is well
* defined. Please see Section 9 and 4.3 for details on this.
*/
enum class BodyAllowed {
DEFINED,
NOT_DEFINED,
NOT_ALLOWED,
};
BodyAllowed isRequestBodyAllowed(boost::optional<HTTPMethod> method);
/**
* Some status codes imply that there MUST NOT be a response body. See section
* 4.3: "All 1xx (informational), 204 (no content), and 304 (not modified)
* responses MUST NOT include a message-body."
* @param status The code to test (100 <= status <= 999)
*/
bool responseBodyMustBeEmpty(unsigned status);
/**
* Returns true if the headers imply that a body will follow. Note that in some
* situations a body may come even if this function returns false (e.g. a 1.0
* response body's length can be given implicitly by closing the connection).
*/
bool bodyImplied(const HTTPHeaders& headers);
/**
* Parse a string containing tokens and qvalues, such as the RFC strings for
* Accept-Charset, Accept-Encoding and Accept-Language. It won't work for
* complex Accept: headers because it doesn't return parameters or
* accept-extension.
*
* See RFC sections 14.2, 14.3, 14.4 for definitions of these header values
*
* TODO: optionally sort by qvalue descending
*
* Return true if the string was well formed according to the RFC. Note it can
* return false but still populate output with best-effort parsing.
*/
typedef std::pair<folly::StringPiece, double> TokenQPair;
bool parseQvalues(folly::StringPiece value, std::vector<TokenQPair> &output);
}}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/Window.h"
#include <glog/logging.h>
#include <limits>
namespace proxygen {
Window::Window(uint32_t capacity) {
CHECK(setCapacity(capacity));
}
int32_t Window::getSize() const {
return capacity_ - outstanding_;
}
uint32_t Window::getNonNegativeSize() const {
auto size = getSize();
return size > 0 ? size : 0;
}
uint32_t Window::getCapacity() const {
// This conversion is safe since we always ensure capacity_ > 0
return static_cast<uint32_t>(capacity_);
}
uint32_t Window::getOutstanding() const {
return outstanding_ < 0 ? 0 : outstanding_;
}
bool Window::reserve(const uint32_t amount, bool strict) {
if (amount > std::numeric_limits<int32_t>::max()) {
VLOG(3) << "Cannot shrink window by more than 2^31 - 1. "
<< "Attempted decrement of " << amount;
return false;
}
const uint32_t limit = std::numeric_limits<int32_t>::max() - outstanding_;
if (outstanding_ > 0 && amount > limit) {
VLOG(3) << "Overflow detected. Window change failed.";
return false;
}
const int32_t newOutstanding = outstanding_ + amount;
if (strict && newOutstanding > capacity_) {
VLOG(3) << "Outstanding bytes (" << newOutstanding << ") exceeded "
<< "window capacity (" << capacity_ << ")";
return false;
}
outstanding_ = newOutstanding;
return true;
}
bool Window::free(const uint32_t amount) {
if (amount > std::numeric_limits<int32_t>::max()) {
VLOG(3) << "Cannot expand window by more than 2^31 - 1. "
<< "Attempted increment of " << amount;
return false;
}
const uint32_t limit = outstanding_ - std::numeric_limits<int32_t>::min();
if (outstanding_ < 0 && amount > limit) {
VLOG(3) << "Underflow detected. Window change failed.";
return false;
}
const int32_t newOutstanding = outstanding_ - amount;
if (newOutstanding < capacity_ - std::numeric_limits<int32_t>::max()) {
VLOG(3) << "Window exceeded 2^31 - 1. Window change failed.";
return false;
}
outstanding_ = newOutstanding;
return true;
}
bool Window::setCapacity(const uint32_t capacity) {
if (capacity > std::numeric_limits<int32_t>::max()) {
VLOG(3) << "Cannot set initial window > 2^31 -1.";
return false;
}
if (capacity > uint32_t(capacity_) &&
(capacity - capacity_ >
uint32_t(std::numeric_limits<int32_t>::max() - getSize()))) {
VLOG(3) << "Increasing the capacity overflowed the window";
return false;
}
capacity_ = static_cast<int32_t>(capacity);
return true;
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <cstdint>
namespace proxygen {
/**
* A class that implements SPDY & HTTP/2 window management. This class
* should be used for both connection and stream level flow control.
*/
class Window {
public:
/**
* Constructs a new Window.
* @param capacity The initial capacity of this Window. The initial size will
* also be equal to this. This parameter must not be > 2^31 -1
*/
explicit Window(uint32_t capacity);
/**
* Returns the number of bytes available to be consumed in this
* window. This could become a negative number if the initial window is
* set to a smaller number.
*/
int32_t getSize() const;
/**
* Returns the number of bytes available to be consumed in this
* window. If that number went negative somehow, this function clamps
* the return value to zero.
*/
uint32_t getNonNegativeSize() const;
/**
* Returns the size of the initial window. That is, the total number of
* bytes allowed to be outstanding on this window.
*/
uint32_t getCapacity() const;
/**
* Returns the number of bytes reserved in this window. If multiple
* calls to free() caused this number to go negative, this function
* returns 0.
*/
uint32_t getOutstanding() const;
/**
* @param amount The amount to reduce the window size by. Increases
* bytes outstanding by amount.
* @param strict If true, reserve() will return false if there is
* no space remaining in the window. If false,
* reserve() will return true until the integer
* overflows.
*/
bool reserve(uint32_t amount, bool strict = true);
/**
* Increment the window size by amount. Decrease bytes outstanding by
* amount. The window size could become greater than the capacity here.
*/
bool free(uint32_t amount);
/**
* Sets a new initial window size. This will also apply the delta
* between the current window size and the new window size. Returns false
* iff applying the new initial window fails.
*/
bool setCapacity(uint32_t capacity);
private:
int32_t outstanding_{0};
int32_t capacity_{0}; // always positive
};
}

View File

@ -0,0 +1,218 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
namespace proxygen {
/**
* Compression dictionary for SPDYv2; note that the trailing null is included.
* From http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2
*/
const char kSPDYv2Dictionary[] =
"optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
"languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
"f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
"-agent10010120020120220320420520630030130230330430530630740040140240340440"
"5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
"glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
"ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
"sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
"oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
"ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
"pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
"ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
".1statusversionurl";
/**
* Compression dictionary for SPDYv3. Copied from:
* http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3
*/
const unsigned char kSPDYv3Dictionary[] = {
0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // - - - - o p t i
0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // o n s - - - - h
0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // e a d - - - - p
0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // o s t - - - - p
0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // u t - - - - d e
0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // l e t e - - - -
0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // t r a c e - - -
0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // - a c c e p t -
0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p
0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t - c h a r s e
0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t - - - - a c c
0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e p t - e n c o
0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // d i n g - - - -
0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // a c c e p t - l
0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // a n g u a g e -
0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p
0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t - r a n g e s
0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // - - - - a g e -
0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // - - - a l l o w
0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // - - - - a u t h
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // o r i z a t i o
0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n - - - - c a c
0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // h e - c o n t r
0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // o l - - - - c o
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // n n e c t i o n
0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // e n t - b a s e
0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e n t - e n c o
0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // d i n g - - - -
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // c o n t e n t -
0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // l a n g u a g e
0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // e n t - l e n g
0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // t h - - - - c o
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // n t e n t - l o
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // c a t i o n - -
0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n
0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t - m d 5 - - -
0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // - c o n t e n t
0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // - r a n g e - -
0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n
0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t - t y p e - -
0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // - - d a t e - -
0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // - - e t a g - -
0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // - - e x p e c t
0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // - - - - e x p i
0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // r e s - - - - f
0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // r o m - - - - h
0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // o s t - - - - i
0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f - m a t c h -
0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // - - - i f - m o
0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // d i f i e d - s
0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // i n c e - - - -
0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // i f - n o n e -
0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // m a t c h - - -
0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // - i f - r a n g
0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e - - - - i f -
0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // u n m o d i f i
0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // e d - s i n c e
0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // - - - - l a s t
0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // - m o d i f i e
0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d - - - - l o c
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // a t i o n - - -
0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // - m a x - f o r
0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // w a r d s - - -
0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // - p r a g m a -
0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // - - - p r o x y
0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // - a u t h e n t
0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // i c a t e - - -
0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // - p r o x y - a
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // u t h o r i z a
0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // t i o n - - - -
0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // r a n g e - - -
0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // - r e f e r e r
0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // - - - - r e t r
0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y - a f t e r -
0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // - - - s e r v e
0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r - - - - t e -
0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // - - - t r a i l
0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // e r - - - - t r
0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // a n s f e r - e
0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // n c o d i n g -
0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // - - - u p g r a
0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // d e - - - - u s
0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // e r - a g e n t
0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // - - - - v a r y
0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // - - - - v i a -
0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // - - - w a r n i
0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // n g - - - - w w
0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w - a u t h e n
0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // t i c a t e - -
0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // - - m e t h o d
0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // - - - - g e t -
0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // - - - s t a t u
0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s - - - - 2 0 0
0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // - O K - - - - v
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // e r s i o n - -
0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // - - H T T P - 1
0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // - 1 - - - - u r
0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l - - - - p u b
0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // l i c - - - - s
0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // e t - c o o k i
0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e - - - - k e e
0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p - a l i v e -
0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // - - - o r i g i
0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n 1 0 0 1 0 1 2
0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 0 1 2 0 2 2 0 5
0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 2 0 6 3 0 0 3 0
0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 2 3 0 3 3 0 4 3
0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 0 5 3 0 6 3 0 7
0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 4 0 2 4 0 5 4 0
0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 6 4 0 7 4 0 8 4
0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 0 9 4 1 0 4 1 1
0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 4 1 2 4 1 3 4 1
0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 4 4 1 5 4 1 6 4
0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 1 7 5 0 2 5 0 4
0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 5 0 5 2 0 3 - N
0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // o n - A u t h o
0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // r i t a t i v e
0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // - I n f o r m a
0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // t i o n 2 0 4 -
0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // N o - C o n t e
0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // n t 3 0 1 - M o
0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // v e d - P e r m
0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // a n e n t l y 4
0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 0 0 - B a d - R
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // e q u e s t 4 0
0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1 - U n a u t h
0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // o r i z e d 4 0
0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3 - F o r b i d
0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // d e n 4 0 4 - N
0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // o t - F o u n d
0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 5 0 0 - I n t e
0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // r n a l - S e r
0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // v e r - E r r o
0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r 5 0 1 - N o t
0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // - I m p l e m e
0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // n t e d 5 0 3 -
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // S e r v i c e -
0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // U n a v a i l a
0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // b l e J a n - F
0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // e b - M a r - A
0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // p r - M a y - J
0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // u n - J u l - A
0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // u g - S e p t -
0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // O c t - N o v -
0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // D e c - 0 0 - 0
0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0 - 0 0 - M o n
0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // - - T u e - - W
0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // e d - - T h u -
0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // - F r i - - S a
0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t - - S u n - -
0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // G M T c h u n k
0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // e d - t e x t -
0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // h t m l - i m a
0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // g e - p n g - i
0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // m a g e - j p g
0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // - i m a g e - g
0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // i f - a p p l i
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x
0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // m l - a p p l i
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x
0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // h t m l - x m l
0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // - t e x t - p l
0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // a i n - t e x t
0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // - j a v a s c r
0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // i p t - p u b l
0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // i c p r i v a t
0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // e m a x - a g e
0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // - g z i p - d e
0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // f l a t e - s d
0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // c h c h a r s e
0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t - u t f - 8 c
0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // h a r s e t - i
0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // s o - 8 8 5 9 -
0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1 - u t f - - -
0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // - e n q - 0 -
};
} //proxygen

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/codec/CodecProtocol.h"
#include <glog/logging.h>
namespace proxygen {
namespace {
static const std::string http_1_1 = "http/1.1";
static const std::string spdy_2 = "spdy/2";
static const std::string spdy_3 = "spdy/3";
static const std::string spdy_3_1 = "spdy/3.1";
static const std::string spdy_3_1_hpack = "spdy/3.1-hpack";
static const std::string http_2 = "http/2";
static const std::string empty = "";
}
extern const std::string& getCodecProtocolString(CodecProtocol proto) {
switch (proto) {
case CodecProtocol::HTTP_1_1: return http_1_1;
case CodecProtocol::SPDY_2: return spdy_2;
case CodecProtocol::SPDY_3: return spdy_3;
case CodecProtocol::SPDY_3_1: return spdy_3_1;
case CodecProtocol::SPDY_3_1_HPACK: return spdy_3_1_hpack;
case CodecProtocol::HTTP_2: return http_2;
}
LOG(FATAL) << "Unreachable";
return empty;
}
extern bool isValidCodecProtocolStr(const std::string& protocolStr) {
return protocolStr == http_1_1 ||
protocolStr == spdy_2 ||
protocolStr == spdy_3 ||
protocolStr == spdy_3_1 ||
protocolStr == spdy_3_1_hpack ||
protocolStr == http_2;
}
extern CodecProtocol getCodecProtocolFromStr(const std::string& protocolStr) {
if (protocolStr == http_1_1) {
return CodecProtocol::HTTP_1_1;
} else if (protocolStr == spdy_2) {
return CodecProtocol::SPDY_2;
} else if (protocolStr == spdy_3) {
return CodecProtocol::SPDY_3;
} else if (protocolStr == spdy_3_1) {
return CodecProtocol::SPDY_3_1;
} else if (protocolStr == spdy_3_1_hpack) {
return CodecProtocol::SPDY_3_1_HPACK;
} else if (protocolStr == http_2) {
return CodecProtocol::HTTP_2;
} else {
// return default protocol
return CodecProtocol::HTTP_1_1;
}
}
extern bool isSpdyCodecProtocol(CodecProtocol protocol) {
return protocol == CodecProtocol::SPDY_2 ||
protocol == CodecProtocol::SPDY_3 ||
protocol == CodecProtocol::SPDY_3_1 ||
protocol == CodecProtocol::SPDY_3_1_HPACK;
}
extern uint8_t maxProtocolPriority(CodecProtocol protocol) {
// Priorities per protocol
switch (protocol) {
case CodecProtocol::SPDY_2:
// SPDY 2 Max Priority
return 3;
case CodecProtocol::SPDY_3:
case CodecProtocol::SPDY_3_1:
case CodecProtocol::SPDY_3_1_HPACK:
// SPDY 3 Max Priority
return 7;
case CodecProtocol::HTTP_1_1:
case CodecProtocol::HTTP_2:
//HTTP doesn't support priorities
return 0;
}
return 0;
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <cstdint>
#include <string>
namespace proxygen {
enum class CodecProtocol : uint8_t {
HTTP_1_1,
SPDY_2,
SPDY_3,
SPDY_3_1,
SPDY_3_1_HPACK,
HTTP_2,
};
/**
* Returns a debugging name to refer to the given protocol.
*/
extern const std::string& getCodecProtocolString(CodecProtocol);
/**
* Check if given debugging name refers to a valid protocol.
*/
extern bool isValidCodecProtocolStr(const std::string& protocolStr);
/**
* Get the protocol from the given debugging name.
* If it's an invalid string, return the default protocol.
*/
extern CodecProtocol getCodecProtocolFromStr(const std::string& protocolStr);
/**
* Check if the given protocol is SPDY.
*/
extern bool isSpdyCodecProtocol(CodecProtocol protocol);
/**
* Returns the maximum priority for the given protocol.
*/
extern uint8_t maxProtocolPriority(CodecProtocol);
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/codec/ErrorCode.h"
#include <glog/logging.h>
namespace proxygen {
const uint8_t kMaxErrorCode = 12;
const char* getErrorCodeString(ErrorCode error) {
switch (error) {
case ErrorCode::NO_ERROR: return "NO_ERROR";
case ErrorCode::PROTOCOL_ERROR: return "PROTOCOL_ERROR";
case ErrorCode::INTERNAL_ERROR: return "INTERNAL_ERROR";
case ErrorCode::FLOW_CONTROL_ERROR: return "FLOW_CONTROL_ERROR";
case ErrorCode::SETTINGS_TIMEOUT: return "SETTINGS_TIMEOUT";
case ErrorCode::STREAM_CLOSED: return "STREAM_CLOSED";
case ErrorCode::FRAME_SIZE_ERROR: return "FRAME_SIZE_ERROR";
case ErrorCode::REFUSED_STREAM: return "REFUSED_STREAM";
case ErrorCode::CANCEL: return "CANCEL";
case ErrorCode::COMPRESSION_ERROR: return "COMPRESSION_ERROR";
case ErrorCode::CONNECT_ERROR: return "CONNECT_ERROR";
case ErrorCode::ENHANCE_YOUR_CALM: return "ENHANCE_YOUR_CALM";
case ErrorCode::INADEQUATE_SECURITY: return "INADEQUATE_SECURITY";
case ErrorCode::_SPDY_INVALID_STREAM: return "_SPDY_INVALID_STREAM";
}
LOG(FATAL) << "Unreachable";
return "";
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <cstdint>
#define RETURN_IF_ERROR(err) \
if (err != ErrorCode::NO_ERROR) { \
VLOG(4) << "Returning with error=" << getErrorCodeString(err); \
return err; \
}
namespace proxygen {
// Error codes are 32-bit fields that are used in RST_STREAM and GOAWAY
// frames to convey the reasons for the stream or connection error.
// We only need <1 byte to represent it in memory
enum class ErrorCode: uint8_t {
NO_ERROR = 0,
PROTOCOL_ERROR = 1,
INTERNAL_ERROR = 2,
FLOW_CONTROL_ERROR = 3,
SETTINGS_TIMEOUT = 4,
STREAM_CLOSED = 5,
FRAME_SIZE_ERROR = 6,
REFUSED_STREAM = 7,
CANCEL = 8,
COMPRESSION_ERROR = 9,
CONNECT_ERROR = 10,
ENHANCE_YOUR_CALM = 11,
INADEQUATE_SECURITY = 12,
// This code is *NOT* to be used outside of SPDYCodec. Delete this
// when we deprecate SPDY.
_SPDY_INVALID_STREAM = 100,
};
extern const uint8_t kMaxErrorCode;
/**
* Returns a string representation of the error code.
*/
extern const char* getErrorCodeString(ErrorCode error);
}

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/codec/FlowControlFilter.h"
#include "proxygen/lib/http/codec/SPDYConstants.h"
namespace proxygen {
namespace {
HTTPException getException() {
HTTPException ex(HTTPException::Direction::INGRESS_AND_EGRESS);
ex.setCodecStatusCode(ErrorCode::FLOW_CONTROL_ERROR);
return ex;
}
}
FlowControlFilter::FlowControlFilter(Callback& callback,
folly::IOBufQueue& writeBuf,
HTTPCodec* codec,
uint32_t recvCapacity):
notify_(callback),
recvWindow_(spdy::kInitialWindow),
sendWindow_(spdy::kInitialWindow),
error_(false),
sendsBlocked_(false) {
if (recvCapacity < spdy::kInitialWindow) {
VLOG(4) << "Ignoring low conn-level recv window size of " << recvCapacity;
} else if (recvCapacity > spdy::kInitialWindow) {
auto delta = recvCapacity - spdy::kInitialWindow;
VLOG(4) << "Incrementing default conn-level recv window by " << delta;
CHECK(recvWindow_.setCapacity(recvCapacity));
codec->generateWindowUpdate(writeBuf, 0, delta);
}
}
bool FlowControlFilter::ingressBytesProcessed(folly::IOBufQueue& writeBuf,
uint32_t delta) {
toAck_ += delta;
if (toAck_ > recvWindow_.getCapacity() / 2) {
CHECK(recvWindow_.free(toAck_));
call_->generateWindowUpdate(writeBuf, 0, toAck_);
toAck_ = 0;
return true;
}
return false;
}
uint32_t FlowControlFilter::getAvailableSend() const {
return sendWindow_.getNonNegativeSize();
}
bool FlowControlFilter::isReusable() const {
if (error_) {
return false;
}
return call_->isReusable();
}
void FlowControlFilter::onBody(StreamID stream,
std::unique_ptr<folly::IOBuf> chain) {
if (!recvWindow_.reserve(chain->computeChainDataLength())) {
error_ = true;
HTTPException ex = getException();
callback_->onError(0, ex, false);
} else {
callback_->onBody(stream, std::move(chain));
}
}
void FlowControlFilter::onWindowUpdate(StreamID stream, uint32_t amount) {
if (!stream) {
bool success = sendWindow_.free(amount);
if (!success) {
LOG(WARNING) << "Remote side sent connection-level WINDOW_UPDATE "
<< "that could not be applied. Aborting session.";
// If something went wrong applying the flow control change, abort
// the entire session.
error_ = true;
HTTPException ex = getException();
callback_->onError(stream, ex, false);
}
if (sendsBlocked_ && sendWindow_.getNonNegativeSize()) {
sendsBlocked_ = false;
notify_.onConnectionSendWindowOpen();
}
// Don't forward.
} else {
callback_->onWindowUpdate(stream, amount);
}
}
size_t FlowControlFilter::generateBody(folly::IOBufQueue& writeBuf,
StreamID stream,
std::unique_ptr<folly::IOBuf> chain,
bool eom) {
bool success = sendWindow_.reserve(chain->computeChainDataLength());
// In the future, maybe make this DCHECK
CHECK(success) << "Session-level send window underflowed! "
<< "Too much data sent without WINDOW_UPDATES!";
if (sendWindow_.getNonNegativeSize() == 0) {
// Need to inform when the send window is no longer full
sendsBlocked_ = true;
}
return call_->generateBody(writeBuf, stream, std::move(chain), eom);
}
size_t FlowControlFilter::generateWindowUpdate(folly::IOBufQueue& writeBuf,
StreamID stream,
uint32_t delta) {
CHECK(stream) << " someone tried to manually manipulate a conn-level window";
return call_->generateWindowUpdate(writeBuf, stream, delta);
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/lib/http/Window.h"
#include "proxygen/lib/http/codec/HTTPCodecFilter.h"
namespace folly {
class IOBufQueue;
}
namespace proxygen {
/**
* This class implements the logic for managing per-session flow
* control. Not every codec is interested in per-session flow control, so
* this filter can only be added in that case or else it is an error.
*/
class FlowControlFilter:
public PassThroughHTTPCodecFilter {
public:
class Callback {
public:
virtual ~Callback() {}
/**
* Notification channel to alert when the send window is no longer full.
*/
virtual void onConnectionSendWindowOpen() = 0;
};
/**
* Construct a flow control filter.
* @param callback A channel to be notified when the window is not
* full anymore.
* @param writeBuf The buffer to write egress on. This constructor
* may generate a window update frame on this buffer.
* @param codec The codec implementation.
* @param recvCapacity The initial size of the conn-level recv window.
* It must be >= 65536.
*/
explicit
FlowControlFilter(Callback& callback,
folly::IOBufQueue& writeBuf,
HTTPCodec* codec,
uint32_t recvCapacity);
/**
* Notify the flow control filter that some ingress bytes were
* processed. If the number of bytes to acknowledge exceeds half the
* receive window's capacity, a WINDOW_UPDATE frame will be written.
* @param writeBuf The buffer to write egress on. This function may
* generate a WINDOW_UPDATE on this buffer.
* @param delta The number of bytes that were processed.
* @returns true iff we wrote a WINDOW_UPDATE frame to the write buf.
*/
bool ingressBytesProcessed(folly::IOBufQueue& writeBuf, uint32_t delta);
/**
* @returns the number of bytes available in the connection-level send window
*/
uint32_t getAvailableSend() const;
// Filter functions
bool isReusable() const override;
void onBody(StreamID stream, std::unique_ptr<folly::IOBuf> chain) override;
void onWindowUpdate(StreamID stream, uint32_t amount) override;
size_t generateBody(folly::IOBufQueue& writeBuf,
StreamID stream,
std::unique_ptr<folly::IOBuf> chain,
bool eom) override;
size_t generateWindowUpdate(folly::IOBufQueue& writeBuf,
StreamID stream,
uint32_t delta) override;
private:
Callback& notify_;
Window recvWindow_;
Window sendWindow_;
uint32_t toAck_{0};
bool error_:1;
bool sendsBlocked_:1;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,196 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "proxygen/external/http_parser/http_parser.h"
#include "proxygen/lib/http/HTTPMessage.h"
#include "proxygen/lib/http/codec/HTTPCodec.h"
#include "proxygen/lib/http/codec/TransportDirection.h"
#include <string>
namespace proxygen {
struct HTTPHeaderSize;
class HTTP1xCodec : public HTTPCodec {
public:
explicit HTTP1xCodec(TransportDirection direction,
bool forceUpstream1_1 = false);
~HTTP1xCodec() override;
// HTTPCodec API
CodecProtocol getProtocol() const override {
return CodecProtocol::HTTP_1_1;
}
TransportDirection getTransportDirection() const override {
return transportDirection_;
}
StreamID createStream() override;
void setCallback(Callback* callback) override { callback_ = callback; }
bool isBusy() const override;
void setParserPaused(bool paused) override;
size_t onIngress(const folly::IOBuf& buf) override;
void onIngressEOF() override;
bool isReusable() const override;
bool isWaitingToDrain() const override { return false; }
// If the session has been upgraded we will send EOF (or RST if needed)
// on egress complete
bool closeOnEgressComplete() const override { return egressUpgrade_; }
bool supportsParallelRequests() const override { return false; }
bool supportsPushTransactions() const override { return false; }
void generateHeader(folly::IOBufQueue& writeBuf,
StreamID txn,
const HTTPMessage& msg,
StreamID assocStream = 0,
HTTPHeaderSize* size = nullptr) override;
size_t generateBody(folly::IOBufQueue& writeBuf,
StreamID txn,
std::unique_ptr<folly::IOBuf> chain,
bool eom) override;
size_t generateChunkHeader(folly::IOBufQueue& writeBuf,
StreamID txn,
size_t length) override;
size_t generateChunkTerminator(folly::IOBufQueue& writeBuf,
StreamID txn) override;
size_t generateTrailers(folly::IOBufQueue& writeBuf,
StreamID txn,
const HTTPHeaders& trailers) override;
size_t generateEOM(folly::IOBufQueue& writeBuf,
StreamID txn) override;
size_t generateRstStream(folly::IOBufQueue& writeBuf,
StreamID txn,
ErrorCode statusCode) override;
size_t generateGoaway(folly::IOBufQueue& writeBuf,
StreamID lastStream,
ErrorCode statusCode) override;
/**
* @returns true if the codec supports the given NPN protocol.
*/
static bool supportsNextProtocol(const std::string& npn);
private:
/** Simple state model used to track the parsing of HTTP headers */
enum class HeaderParseState : uint8_t {
kParsingHeaderIdle,
kParsingHeaderStart,
kParsingHeaderName,
kParsingHeaderValue,
kParsingHeadersComplete,
kParsingTrailerName,
kParsingTrailerValue
};
/** Used to keep track of whether a client requested keep-alive. This is
* only useful to support HTTP 1.0 keep-alive for a downstream connection
* where keep-alive is disabled unless the client requested it. */
enum class KeepaliveRequested : uint8_t {
UNSET,
YES, // incoming message requested keepalive
NO, // incoming message disabled keepalive
};
void addDateHeader(folly::IOBufQueue& writeBuf, size_t& len);
/** Check whether we're currently parsing ingress message headers */
bool isParsingHeaders() const {
return (headerParseState_ > HeaderParseState::kParsingHeaderIdle) &&
(headerParseState_ < HeaderParseState::kParsingHeadersComplete);
}
/** Check whether we're currently parsing ingress header-or-trailer name */
bool isParsingHeaderOrTrailerName() const {
return (headerParseState_ == HeaderParseState::kParsingHeaderName) ||
(headerParseState_ == HeaderParseState::kParsingTrailerName);
}
/** Invoked when a parsing error occurs. It will send an exception to
the callback object to report the error and do any other cleanup
needed. It optionally takes a message to pass to the generated
HTTPException passed to callback_. */
void onParserError(const char* what = nullptr);
/** Push out header name-value pair to hdrs and clear currentHeader*_ */
void pushHeaderNameAndValue(HTTPHeaders& hdrs);
// Parser callbacks
int onMessageBegin();
int onURL(const char* buf, size_t len);
int onReason(const char* buf, size_t len);
int onHeaderField(const char* buf, size_t len);
int onHeaderValue(const char* buf, size_t len);
int onHeadersComplete(size_t len);
int onBody(const char* buf, size_t len);
int onChunkHeader(size_t len);
int onChunkComplete();
int onMessageComplete();
HTTPCodec::Callback* callback_;
StreamID ingressTxnID_;
StreamID egressTxnID_;
http_parser parser_;
const folly::IOBuf* currentIngressBuf_;
std::unique_ptr<HTTPMessage> msg_;
std::unique_ptr<HTTPHeaders> trailers_;
std::string currentHeaderName_;
folly::StringPiece currentHeaderNameStringPiece_;
std::string currentHeaderValue_;
std::string url_;
std::string reason_;
HTTPHeaderSize headerSize_;
HeaderParseState headerParseState_;
TransportDirection transportDirection_;
KeepaliveRequested keepaliveRequested_; // only used in DOWNSTREAM mode
bool forceUpstream1_1_:1; // Use HTTP/1.1 upstream even if msg is 1.0
bool parserActive_:1;
bool pendingEOF_:1;
bool parserPaused_:1;
bool parserError_:1;
bool requestPending_:1;
bool responsePending_:1;
bool egressChunked_:1;
bool inChunk_:1;
bool lastChunkWritten_:1;
bool keepalive_:1;
bool disableKeepalivePending_:1;
// TODO: replace the 2 booleans below with an enum "request method"
bool connectRequest_:1;
bool headRequest_:1;
bool expectNoResponseBody_:1;
bool mayChunkEgress_:1;
bool is1xxResponse_:1;
bool inRecvLastChunk_:1;
bool ingressUpgrade_:1;
bool ingressUpgradeComplete_:1;
bool egressUpgrade_:1;
bool headersComplete_:1;
// C-callable wrappers for the http_parser callbacks
static int onMessageBeginCB(http_parser* parser);
static int onPathCB(http_parser* parser, const char* buf, size_t len);
static int onQueryStringCB(http_parser* parser, const char* buf, size_t len);
static int onUrlCB(http_parser* parser, const char* buf, size_t len);
static int onReasonCB(http_parser* parser, const char* buf, size_t len);
static int onHeaderFieldCB(http_parser* parser, const char* buf, size_t len);
static int onHeaderValueCB(http_parser* parser, const char* buf, size_t len);
static int onHeadersCompleteCB(http_parser* parser,
const char* buf, size_t len);
static int onBodyCB(http_parser* parser, const char* buf, size_t len);
static int onChunkHeaderCB(http_parser* parser);
static int onChunkCompleteCB(http_parser* parser);
static int onMessageCompleteCB(http_parser* parser);
static void initParserSettings() __attribute__ ((__constructor__));
static http_parser_settings kParserSettings;
};
} // proxygen

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "proxygen/lib/http/codec/HTTPChecks.h"
#include "proxygen/lib/http/RFC2616.h"
namespace proxygen {
void HTTPChecks::onHeadersComplete(StreamID stream,
std::unique_ptr<HTTPMessage> msg) {
if (msg->isRequest() && (RFC2616::isRequestBodyAllowed(msg->getMethod())
== RFC2616::BodyAllowed::NOT_ALLOWED) &&
RFC2616::bodyImplied(msg->getHeaders())) {
HTTPException ex(HTTPException::Direction::INGRESS);
ex.setProxygenError(kErrorParseHeader);
// setting the status code means that the error is at the HTTP layer and
// that parsing succeeded.
ex.setHttpStatusCode(400);
callback_->onError(stream, ex, true);
return;
}
callback_->onHeadersComplete(stream, std::move(msg));
}
void HTTPChecks::generateHeader(folly::IOBufQueue& writeBuf,
StreamID stream,
const HTTPMessage& msg,
StreamID assocStream,
HTTPHeaderSize* sizeOut) {
if (msg.isRequest() && RFC2616::bodyImplied(msg.getHeaders())) {
CHECK(RFC2616::isRequestBodyAllowed(msg.getMethod()) !=
RFC2616::BodyAllowed::NOT_ALLOWED);
// We could also add a "strict" mode that disallows sending body on GET
// requests here too.
}
call_->generateHeader(writeBuf, stream, msg, assocStream, sizeOut);
}
}

Some files were not shown because too many files have changed in this diff Show More