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:
parent
227f8bb2ee
commit
97546cd0f2
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal 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
9
.travis.yml
Normal 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
44
CONTRIBUTING.md
Normal 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.
|
BIN
CoreProxygenArchitecture.png
Normal file
BIN
CoreProxygenArchitecture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
30
LICENSE
Normal file
30
LICENSE
Normal 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
23
PATENTS
Normal 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 Facebook’s 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
141
README.md
Normal 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: [](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.
|
||||
|
||||

|
||||
|
||||
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
1
proxygen/Makefile.am
Normal file
@ -0,0 +1 @@
|
||||
SUBDIRS = lib httpserver
|
1
proxygen/VERSION
Normal file
1
proxygen/VERSION
Normal file
@ -0,0 +1 @@
|
||||
1
|
264
proxygen/configure.ac
Normal file
264
proxygen/configure.ac
Normal 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
64
proxygen/deps.sh
Executable 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
|
4
proxygen/external/http_parser/CONTRIBUTIONS
vendored
Normal file
4
proxygen/external/http_parser/CONTRIBUTIONS
vendored
Normal 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
|
23
proxygen/external/http_parser/LICENSE-MIT
vendored
Normal file
23
proxygen/external/http_parser/LICENSE-MIT
vendored
Normal 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
171
proxygen/external/http_parser/README.md
vendored
Normal 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
|
1
proxygen/external/http_parser/http_parser.c
vendored
Symbolic link
1
proxygen/external/http_parser/http_parser.c
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
http_parser_cpp.cpp
|
319
proxygen/external/http_parser/http_parser.h
vendored
Normal file
319
proxygen/external/http_parser/http_parser.h
vendored
Normal 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
|
2381
proxygen/external/http_parser/http_parser_cpp.cpp
vendored
Normal file
2381
proxygen/external/http_parser/http_parser_cpp.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3142
proxygen/external/http_parser/test.c
vendored
Normal file
3142
proxygen/external/http_parser/test.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
125
proxygen/httpserver/Filters.h
Normal file
125
proxygen/httpserver/Filters.h
Normal 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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
205
proxygen/httpserver/HTTPServer.cpp
Normal file
205
proxygen/httpserver/HTTPServer.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
142
proxygen/httpserver/HTTPServer.h
Normal file
142
proxygen/httpserver/HTTPServer.h
Normal 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_;
|
||||
};
|
||||
|
||||
}
|
92
proxygen/httpserver/HTTPServerAcceptor.cpp
Normal file
92
proxygen/httpserver/HTTPServerAcceptor.cpp
Normal 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_();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
48
proxygen/httpserver/HTTPServerAcceptor.h
Normal file
48
proxygen/httpserver/HTTPServerAcceptor.h
Normal 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};
|
||||
};
|
||||
|
||||
}
|
86
proxygen/httpserver/HTTPServerOptions.h
Normal file
86
proxygen/httpserver/HTTPServerOptions.h
Normal 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};
|
||||
};
|
||||
|
||||
}
|
27
proxygen/httpserver/Makefile.am
Normal file
27
proxygen/httpserver/Makefile.am
Normal 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
|
78
proxygen/httpserver/Mocks.h
Normal file
78
proxygen/httpserver/Mocks.h
Normal 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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
99
proxygen/httpserver/RequestHandler.h
Normal file
99
proxygen/httpserver/RequestHandler.h
Normal 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};
|
||||
};
|
||||
|
||||
}
|
186
proxygen/httpserver/RequestHandlerAdaptor.cpp
Normal file
186
proxygen/httpserver/RequestHandlerAdaptor.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
74
proxygen/httpserver/RequestHandlerAdaptor.h
Normal file
74
proxygen/httpserver/RequestHandlerAdaptor.h
Normal 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};
|
||||
};
|
||||
|
||||
}
|
76
proxygen/httpserver/RequestHandlerFactory.h
Normal file
76
proxygen/httpserver/RequestHandlerFactory.h
Normal 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_;
|
||||
};
|
||||
|
||||
}
|
198
proxygen/httpserver/ResponseBuilder.h
Normal file
198
proxygen/httpserver/ResponseBuilder.h
Normal 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};
|
||||
};
|
||||
|
||||
}
|
84
proxygen/httpserver/ResponseHandler.h
Normal file
84
proxygen/httpserver/ResponseHandler.h
Normal 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};
|
||||
};
|
||||
|
||||
}
|
192
proxygen/httpserver/ScopedHTTPServer.h
Normal file
192
proxygen/httpserver/ScopedHTTPServer.h
Normal 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)));
|
||||
}
|
||||
|
||||
}
|
36
proxygen/httpserver/SignalHandler.cpp
Normal file
36
proxygen/httpserver/SignalHandler.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
37
proxygen/httpserver/SignalHandler.h
Normal file
37
proxygen/httpserver/SignalHandler.h
Normal 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};
|
||||
};
|
||||
|
||||
}
|
59
proxygen/httpserver/filters/DirectResponseHandler.h
Normal file
59
proxygen/httpserver/filters/DirectResponseHandler.h
Normal 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_;
|
||||
};
|
||||
|
||||
}
|
106
proxygen/httpserver/filters/RejectConnectFilter.h
Normal file
106
proxygen/httpserver/filters/RejectConnectFilter.h
Normal 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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
1
proxygen/httpserver/samples/Makefile.am
Normal file
1
proxygen/httpserver/samples/Makefile.am
Normal file
@ -0,0 +1 @@
|
||||
SUBDIRS = echo
|
56
proxygen/httpserver/samples/echo/EchoHandler.cpp
Normal file
56
proxygen/httpserver/samples/echo/EchoHandler.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
47
proxygen/httpserver/samples/echo/EchoHandler.h
Normal file
47
proxygen/httpserver/samples/echo/EchoHandler.h
Normal 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_;
|
||||
};
|
||||
|
||||
}
|
86
proxygen/httpserver/samples/echo/EchoServer.cpp
Normal file
86
proxygen/httpserver/samples/echo/EchoServer.cpp
Normal 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;
|
||||
}
|
41
proxygen/httpserver/samples/echo/EchoStats.h
Normal file
41
proxygen/httpserver/samples/echo/EchoStats.h
Normal 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};
|
||||
};
|
||||
|
||||
}
|
10
proxygen/httpserver/samples/echo/Makefile.am
Normal file
10
proxygen/httpserver/samples/echo/Makefile.am
Normal file
@ -0,0 +1,10 @@
|
||||
SUBDIRS = .
|
||||
|
||||
noinst_PROGRAMS = echo_server
|
||||
|
||||
echo_server_SOURCES = \
|
||||
EchoHandler.cpp \
|
||||
EchoServer.cpp
|
||||
|
||||
echo_server_LDADD = \
|
||||
../../libproxygenhttpserver.la
|
97
proxygen/httpserver/samples/echo/test/EchoHandlerTest.cpp
Normal file
97
proxygen/httpserver/samples/echo/test/EchoHandlerTest.cpp
Normal 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);
|
||||
}
|
154
proxygen/httpserver/tests/HTTPServerTest.cpp
Normal file
154
proxygen/httpserver/tests/HTTPServerTest.cpp
Normal 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);
|
||||
}
|
11
proxygen/httpserver/tests/Makefile.am
Normal file
11
proxygen/httpserver/tests/Makefile.am
Normal file
@ -0,0 +1,11 @@
|
||||
SUBDIRS = .
|
||||
|
||||
check_PROGRAMS = HTTPServerTests
|
||||
HTTPServerTests_SOURCES = \
|
||||
HTTPServerTest.cpp
|
||||
|
||||
HTTPServerTests_LDADD = \
|
||||
../libproxygenhttpserver.la \
|
||||
../../lib/test/libtestmain.la
|
||||
|
||||
TESTS = HTTPServerTests
|
20
proxygen/httpserver/tests/certs/ca_cert.pem
Normal file
20
proxygen/httpserver/tests/certs/ca_cert.pem
Normal 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-----
|
27
proxygen/httpserver/tests/certs/ca_key.pem
Normal file
27
proxygen/httpserver/tests/certs/ca_key.pem
Normal 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-----
|
18
proxygen/httpserver/tests/certs/test_cert1.pem
Normal file
18
proxygen/httpserver/tests/certs/test_cert1.pem
Normal 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-----
|
18
proxygen/httpserver/tests/certs/test_cert2.pem
Normal file
18
proxygen/httpserver/tests/certs/test_cert2.pem
Normal 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-----
|
18
proxygen/httpserver/tests/certs/test_cert3.pem
Normal file
18
proxygen/httpserver/tests/certs/test_cert3.pem
Normal 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-----
|
18
proxygen/httpserver/tests/certs/test_cert4.pem
Normal file
18
proxygen/httpserver/tests/certs/test_cert4.pem
Normal 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-----
|
18
proxygen/httpserver/tests/certs/test_cert5.pem
Normal file
18
proxygen/httpserver/tests/certs/test_cert5.pem
Normal 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-----
|
14
proxygen/httpserver/tests/certs/test_ecdsa_cert1.pem
Normal file
14
proxygen/httpserver/tests/certs/test_ecdsa_cert1.pem
Normal 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-----
|
14
proxygen/httpserver/tests/certs/test_ecdsa_cert2.pem
Normal file
14
proxygen/httpserver/tests/certs/test_ecdsa_cert2.pem
Normal 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-----
|
14
proxygen/httpserver/tests/certs/test_ecdsa_cert3.pem
Normal file
14
proxygen/httpserver/tests/certs/test_ecdsa_cert3.pem
Normal 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-----
|
5
proxygen/httpserver/tests/certs/test_ecdsa_key1.pem
Normal file
5
proxygen/httpserver/tests/certs/test_ecdsa_key1.pem
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIG6+DnEONCjOb4bmys60IISRCPxn26Iga3ie+tKLy6x+oAoGCCqGSM49
|
||||
AwEHoUQDQgAEz2PAexkUGpUHzTaRNL8OtwoYurZpsCRaxTEvw0GTUOv5bpe0ituq
|
||||
BHvSfQEdSeN2jfdHOmeQ82KTSpqiNbW2lg==
|
||||
-----END EC PRIVATE KEY-----
|
5
proxygen/httpserver/tests/certs/test_ecdsa_key2.pem
Normal file
5
proxygen/httpserver/tests/certs/test_ecdsa_key2.pem
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIDjTZCEXwIl57daCOp7/0kv9HBjJIM+DBMuYGQx7j/vzoAoGCCqGSM49
|
||||
AwEHoUQDQgAEhPeKFAokkp4KmIVE7WqKc8w2k0YOwudHXXZ2fAQNxZcGHjml5Mj9
|
||||
evH8Ezd9OoJ/u7cnuFKorBce4ypHZNA8zw==
|
||||
-----END EC PRIVATE KEY-----
|
5
proxygen/httpserver/tests/certs/test_ecdsa_key3.pem
Normal file
5
proxygen/httpserver/tests/certs/test_ecdsa_key3.pem
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIFvllTFRRZbSME7guGQMnzS05hbmMAWTxSP4CwknRo6IoAoGCCqGSM49
|
||||
AwEHoUQDQgAEMLrfx4XVO8FwVtZ372TciaxM+Wc/DFUfnZLmw97OHHks6SARB/bn
|
||||
pVu23QsD/muYSiB6vvzLYxCJuYguH6NWvQ==
|
||||
-----END EC PRIVATE KEY-----
|
27
proxygen/httpserver/tests/certs/test_key1.pem
Normal file
27
proxygen/httpserver/tests/certs/test_key1.pem
Normal 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-----
|
27
proxygen/httpserver/tests/certs/test_key2.pem
Normal file
27
proxygen/httpserver/tests/certs/test_key2.pem
Normal 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-----
|
27
proxygen/httpserver/tests/certs/test_key3.pem
Normal file
27
proxygen/httpserver/tests/certs/test_key3.pem
Normal 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-----
|
27
proxygen/httpserver/tests/certs/test_key4.pem
Normal file
27
proxygen/httpserver/tests/certs/test_key4.pem
Normal 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-----
|
27
proxygen/httpserver/tests/certs/test_key5.pem
Normal file
27
proxygen/httpserver/tests/certs/test_key5.pem
Normal 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
13
proxygen/lib/Makefile.am
Normal 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
|
61
proxygen/lib/http/HTTPCommonHeaders.template.gperf
Normal file
61
proxygen/lib/http/HTTPCommonHeaders.template.gperf
Normal 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
|
61
proxygen/lib/http/HTTPCommonHeaders.template.h
Normal file
61
proxygen/lib/http/HTTPCommonHeaders.template.h
Normal 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
|
82
proxygen/lib/http/HTTPCommonHeaders.txt
Normal file
82
proxygen/lib/http/HTTPCommonHeaders.txt
Normal 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
|
167
proxygen/lib/http/HTTPConnector.cpp
Normal file
167
proxygen/lib/http/HTTPConnector.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
152
proxygen/lib/http/HTTPConnector.h
Normal file
152
proxygen/lib/http/HTTPConnector.h
Normal 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_;
|
||||
};
|
||||
|
||||
}
|
32
proxygen/lib/http/HTTPConstants.cpp
Normal file
32
proxygen/lib/http/HTTPConstants.cpp
Normal 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];
|
||||
}
|
||||
|
||||
}
|
67
proxygen/lib/http/HTTPConstants.h
Normal file
67
proxygen/lib/http/HTTPConstants.h
Normal 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
|
||||
};
|
||||
|
||||
}
|
31
proxygen/lib/http/HTTPException.cpp
Normal file
31
proxygen/lib/http/HTTPException.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
169
proxygen/lib/http/HTTPException.h
Normal file
169
proxygen/lib/http/HTTPException.h
Normal 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);
|
||||
|
||||
}
|
34
proxygen/lib/http/HTTPHeaderSize.h
Normal file
34
proxygen/lib/http/HTTPHeaderSize.h
Normal 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};
|
||||
};
|
||||
|
||||
}
|
308
proxygen/lib/http/HTTPHeaders.cpp
Normal file
308
proxygen/lib/http/HTTPHeaders.cpp
Normal 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
423
proxygen/lib/http/HTTPHeaders.h
Normal file
423
proxygen/lib/http/HTTPHeaders.h
Normal 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
|
||||
|
||||
}
|
831
proxygen/lib/http/HTTPMessage.cpp
Normal file
831
proxygen/lib/http/HTTPMessage.cpp
Normal 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
|
724
proxygen/lib/http/HTTPMessage.h
Normal file
724
proxygen/lib/http/HTTPMessage.h
Normal 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
|
98
proxygen/lib/http/HTTPMessageFilters.h
Normal file
98
proxygen/lib/http/HTTPMessageFilters.h
Normal 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
|
45
proxygen/lib/http/HTTPMethod.cpp
Normal file
45
proxygen/lib/http/HTTPMethod.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
59
proxygen/lib/http/HTTPMethod.h
Normal file
59
proxygen/lib/http/HTTPMethod.h
Normal 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);
|
||||
|
||||
}
|
133
proxygen/lib/http/Makefile.am
Normal file
133
proxygen/lib/http/Makefile.am
Normal 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
|
34
proxygen/lib/http/ProxygenErrorEnum.cpp
Normal file
34
proxygen/lib/http/ProxygenErrorEnum.cpp
Normal 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];
|
||||
}
|
||||
|
||||
}
|
72
proxygen/lib/http/ProxygenErrorEnum.h
Normal file
72
proxygen/lib/http/ProxygenErrorEnum.h
Normal 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);
|
||||
|
||||
}
|
81
proxygen/lib/http/RFC2616.cpp
Normal file
81
proxygen/lib/http/RFC2616.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}}
|
73
proxygen/lib/http/RFC2616.h
Normal file
73
proxygen/lib/http/RFC2616.h
Normal 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);
|
||||
|
||||
}}
|
96
proxygen/lib/http/Window.cpp
Normal file
96
proxygen/lib/http/Window.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
84
proxygen/lib/http/Window.h
Normal file
84
proxygen/lib/http/Window.h
Normal 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
|
||||
};
|
||||
|
||||
}
|
218
proxygen/lib/http/codec/CodecDictionaries.h
Normal file
218
proxygen/lib/http/codec/CodecDictionaries.h
Normal 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
|
92
proxygen/lib/http/codec/CodecProtocol.cpp
Normal file
92
proxygen/lib/http/codec/CodecProtocol.cpp
Normal 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;
|
||||
}
|
||||
}
|
51
proxygen/lib/http/codec/CodecProtocol.h
Normal file
51
proxygen/lib/http/codec/CodecProtocol.h
Normal 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);
|
||||
}
|
39
proxygen/lib/http/codec/ErrorCode.cpp
Normal file
39
proxygen/lib/http/codec/ErrorCode.cpp
Normal 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 "";
|
||||
}
|
||||
|
||||
}
|
52
proxygen/lib/http/codec/ErrorCode.h
Normal file
52
proxygen/lib/http/codec/ErrorCode.h
Normal 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);
|
||||
|
||||
}
|
124
proxygen/lib/http/codec/FlowControlFilter.cpp
Normal file
124
proxygen/lib/http/codec/FlowControlFilter.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
97
proxygen/lib/http/codec/FlowControlFilter.h
Normal file
97
proxygen/lib/http/codec/FlowControlFilter.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
1071
proxygen/lib/http/codec/HTTP1xCodec.cpp
Normal file
1071
proxygen/lib/http/codec/HTTP1xCodec.cpp
Normal file
File diff suppressed because it is too large
Load Diff
196
proxygen/lib/http/codec/HTTP1xCodec.h
Normal file
196
proxygen/lib/http/codec/HTTP1xCodec.h
Normal 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
|
49
proxygen/lib/http/codec/HTTPChecks.cpp
Normal file
49
proxygen/lib/http/codec/HTTPChecks.cpp
Normal 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
Loading…
x
Reference in New Issue
Block a user