├── .bazelrc ├── .bazelversion ├── .gitignore ├── BUILD.bazel ├── LICENSE ├── README.md ├── WORKSPACE ├── bazel ├── BUILD.bazel ├── extra_deps.bzl └── net_http_deps.bzl ├── docs ├── README.md └── contributing.md ├── net_http ├── client │ └── test_client │ │ ├── internal │ │ ├── BUILD │ │ ├── README.md │ │ ├── evhttp_connection.cc │ │ └── evhttp_connection.h │ │ ├── public │ │ ├── BUILD │ │ ├── README.md │ │ ├── httpclient.h │ │ └── httpclient_interface.h │ │ └── testing │ │ ├── BUILD │ │ └── evhttp_echo_client.cc ├── compression │ ├── BUILD │ ├── README.md │ ├── gzip_zlib.cc │ ├── gzip_zlib.h │ └── gzip_zlib_test.cc ├── internal │ ├── BUILD │ ├── fixed_thread_pool.h │ ├── net_logging.cc │ ├── net_logging.h │ └── testing │ │ ├── BUILD │ │ └── net_logging_example.cc ├── public │ ├── BUILD │ ├── header_names.cc │ ├── header_names.h │ └── response_code_enum.h ├── server │ ├── .DS_Store │ ├── internal │ │ ├── BUILD │ │ ├── evhttp_request.cc │ │ ├── evhttp_request.h │ │ ├── evhttp_request_test.cc │ │ ├── evhttp_server.cc │ │ ├── evhttp_server.h │ │ ├── evhttp_server_test.cc │ │ └── server_support.h │ ├── public │ │ ├── BUILD │ │ ├── httpserver.h │ │ ├── httpserver_interface.h │ │ └── server_request_interface.h │ └── testing │ │ ├── BUILD │ │ └── evhttp_echo_server.cc └── socket │ └── testing │ ├── BUILD │ ├── ev_fetch_client.cc │ └── ev_print_req_server.cc └── third_party ├── libevent └── BUILD └── rapidjson └── BUILD /.bazelrc: -------------------------------------------------------------------------------- 1 | # Processor native optimizations (depends on build host capabilities). 2 | build:nativeopt --copt=-march=native 3 | build:nativeopt --host_copt=-march=native 4 | build:nativeopt --copt=-O3 5 | 6 | build --keep_going 7 | build --verbose_failures=true 8 | build --spawn_strategy=standalone 9 | build --genrule_strategy=standalone 10 | 11 | build --cxxopt='-std=c++14' 12 | build --host_cxxopt='-std=c++14' 13 | -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 6.4.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-bin 2 | bazel-net_http 3 | bazel-out 4 | bazel-testlogs 5 | MODULE.bazel* 6 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/net_http/55ac2986f5d3b64b21b642b49bf29ac6797d9457/BUILD.bazel -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lightweight C++ HTTP frameworks 2 | 3 | This project is currently hosted at tensorflow_serving/util/net_http. 4 | 5 | We are in the process of moving the library to this new repo as a standalone project. 6 | 7 | ## Project contacts 8 | 9 | If you have any questions, please send them to [web|awk]@google.com 10 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name="net_http") 2 | 3 | load("//bazel:net_http_deps.bzl", "net_http_deps") 4 | 5 | net_http_deps() 6 | 7 | load("//bazel:extra_deps.bzl", "net_http_extra_deps") 8 | 9 | net_http_extra_deps() 10 | -------------------------------------------------------------------------------- /bazel/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | licenses(["notice"]) # Apache v2 16 | 17 | package(default_visibility = ["//:__subpackages__"]) 18 | -------------------------------------------------------------------------------- /bazel/extra_deps.bzl: -------------------------------------------------------------------------------- 1 | load("@rules_foreign_cc//foreign_cc:repositories.bzl", "rules_foreign_cc_dependencies") 2 | 3 | def net_http_extra_deps(): 4 | rules_foreign_cc_dependencies() 5 | -------------------------------------------------------------------------------- /bazel/net_http_deps.bzl: -------------------------------------------------------------------------------- 1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 2 | load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") 3 | 4 | ## Allowing other projects to load dependencies 5 | def net_http_deps(): 6 | # ===== Abseil dependency ===== 7 | 8 | maybe( 9 | http_archive, 10 | name = "com_google_absl", 11 | sha256 = "8eeec9382fc0338ef5c60053f3a4b0e0708361375fe51c9e65d0ce46ccfe55a7", 12 | urls = ["https://github.com/abseil/abseil-cpp/archive/b971ac5250ea8de900eae9f95e06548d14cd95fe.tar.gz"], 13 | strip_prefix = "abseil-cpp-b971ac5250ea8de900eae9f95e06548d14cd95fe", 14 | ) 15 | 16 | # ===== Google Test dependency ===== 17 | maybe ( 18 | http_archive, 19 | name = "com_google_googletest", 20 | sha256 = "1f357c27ca988c3f7c6b4bf68a9395005ac6761f034046e9dde0896e3aba00e4", 21 | urls = ["https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip"], 22 | strip_prefix = "googletest-1.14.0", 23 | ) 24 | 25 | # ===== Bazel skylib dependency ===== 26 | maybe (http_archive, 27 | name = "bazel_skylib", 28 | sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506", 29 | urls = [ 30 | "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", 31 | "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", 32 | ], 33 | ) 34 | 35 | # ===== Bazel package rules dependency ===== 36 | maybe ( 37 | http_archive, 38 | name = "rules_pkg", 39 | sha256 = "451e08a4d78988c06fa3f9306ec813b836b1d076d0f055595444ba4ff22b867f", 40 | url = "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.1/rules_pkg-0.7.1.tar.gz", 41 | ) 42 | 43 | maybe ( 44 | http_archive, 45 | name = "com_google_protobuf", 46 | sha256 = "a79d19dcdf9139fa4b81206e318e33d245c4c9da1ffed21c87288ed4380426f9", 47 | strip_prefix = "protobuf-3.11.4", 48 | # latest, as of 2020-02-21 49 | urls = [ 50 | "https://mirror.bazel.build/github.com/protocolbuffers/protobuf/archive/v3.11.4.tar.gz", 51 | "https://github.com/protocolbuffers/protobuf/archive/v3.11.4.tar.gz", 52 | ], 53 | ) 54 | 55 | maybe ( 56 | http_archive, 57 | name = "zlib", 58 | build_file = "@com_google_protobuf//:third_party/zlib.BUILD", 59 | sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1", 60 | strip_prefix = "zlib-1.2.11", 61 | urls = [ 62 | "https://mirror.bazel.build/zlib.net/zlib-1.2.11.tar.gz", 63 | "https://zlib.net/zlib-1.2.11.tar.gz", 64 | ], 65 | ) 66 | 67 | maybe ( 68 | http_archive, 69 | name = "rules_foreign_cc", 70 | sha256 = "2a4d07cd64b0719b39a7c12218a3e507672b82a97b98c6a89d38565894cf7c51", 71 | strip_prefix = "rules_foreign_cc-0.9.0", 72 | url = "https://github.com/bazelbuild/rules_foreign_cc/archive/refs/tags/0.9.0.tar.gz", 73 | ) 74 | 75 | maybe ( 76 | http_archive, 77 | name = "com_github_libevent_libevent", 78 | url = "https://github.com/libevent/libevent/archive/release-2.1.12-stable.zip", 79 | strip_prefix = "libevent-release-2.1.12-stable", 80 | sha256 = "8836ad722ab211de41cb82fe098911986604f6286f67d10dfb2b6787bf418f49", 81 | build_file = "@net_http//third_party/libevent:BUILD", 82 | ) 83 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | Too install on linux ubuntu machine: 4 | 5 | ### First install Bazel: 6 | 7 | 1. Install dependency: 8 | `sudo apt-get update && sudo apt-get install apt-transport-https curl gnupg build-essential cmake git unzip pkg-config` 9 | 10 | 2. Add Bazel's APT Repository: 11 | `curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor -o /usr/share/keyrings/bazel-archive-keyring.gpg` 12 | 13 | 3. Now add Bazel's APT repository to the system: 14 | `echo "deb [arch=amd64 signed-by=/usr/share/keyrings/bazel-archive-keyring.gpg] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list` 15 | 16 | 4. Install Bazel: 17 | `sudo apt-get update && sudo apt-get install bazel` 18 | or In our case we want to use Bazel 5.4.0: 19 | `sudo apt-get install bazel-` 20 | 21 | 5. Verify Installation: 22 | `bazel --version` 23 | 24 | ### Then Install libvert: 25 | ``` 26 | sudo apt-get update 27 | sudo apt-get install -y autoconf automake libtool 28 | ``` 29 | 30 | ## To Build: 31 | 32 | Under the project root dir: 33 | `bazel build //net_http/server/public:http_server` -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We would love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our Community Guidelines 22 | 23 | This project follows [Google's Open Source Community 24 | Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code Reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests) 32 | for this purpose. 33 | -------------------------------------------------------------------------------- /net_http/client/test_client/internal/BUILD: -------------------------------------------------------------------------------- 1 | # Description: a lightweight http client 2 | 3 | package(default_visibility = [ 4 | "//net_http:__subpackages__", 5 | ]) 6 | 7 | licenses(["notice"]) 8 | 9 | cc_library( 10 | name = "evhttp_client", 11 | srcs = [ 12 | "evhttp_connection.cc", 13 | ], 14 | hdrs = [ 15 | "evhttp_connection.h", 16 | ], 17 | deps = [ 18 | "//net_http/client/test_client/public:http_client_api", 19 | "//net_http/internal:net_logging", 20 | "//net_http/public:shared_files", 21 | "//net_http/server/public:http_server_api", 22 | "@com_github_libevent_libevent//:libevent", 23 | "@com_google_absl//absl/strings", 24 | "@com_google_absl//absl/synchronization", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /net_http/client/test_client/internal/README.md: -------------------------------------------------------------------------------- 1 | The client library is still under development, and has yet to be finalized. 2 | 3 | It should be primarily used for writing tests for users of the 4 | ServerRequestInterface and HTTPServerInterface APIs to verify basic 5 | functionality, and the current state should be considered experimental. 6 | -------------------------------------------------------------------------------- /net_http/client/test_client/internal/evhttp_connection.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // libevent based client implementation 17 | 18 | #include "net_http/client/test_client/internal/evhttp_connection.h" 19 | 20 | #include "absl/strings/str_cat.h" 21 | #include "net_http/internal/net_logging.h" 22 | #include "net_http/public/response_code_enum.h" 23 | 24 | namespace net_http { 25 | 26 | TestEvHTTPConnection::~TestEvHTTPConnection() { 27 | if (evcon_ != nullptr) { 28 | evhttp_connection_free(evcon_); 29 | } 30 | if (http_uri_ != nullptr) { 31 | evhttp_uri_free(http_uri_); 32 | } 33 | 34 | event_base_free(ev_base_); 35 | } 36 | 37 | // This needs be called with any async SendRequest() 38 | void TestEvHTTPConnection::Terminate() { 39 | event_base_loopexit(ev_base_, nullptr); 40 | if (loop_exit_ != nullptr) { 41 | loop_exit_->WaitForNotification(); 42 | } 43 | } 44 | 45 | std::unique_ptr TestEvHTTPConnection::Connect( 46 | absl::string_view url) { 47 | std::string url_str(url.data(), url.size()); 48 | struct evhttp_uri* http_uri = evhttp_uri_parse(url_str.c_str()); 49 | if (http_uri == nullptr) { 50 | NET_LOG(ERROR, "Failed to connect : event_base_new()"); 51 | return nullptr; 52 | } 53 | 54 | const char* host = evhttp_uri_get_host(http_uri); 55 | if (host == nullptr) { 56 | NET_LOG(ERROR, "url must have a host %.*s", static_cast(url.size()), 57 | url.data()); 58 | return nullptr; 59 | } 60 | 61 | int port = evhttp_uri_get_port(http_uri); 62 | if (port == -1) { 63 | port = 80; 64 | } 65 | 66 | auto result = Connect(host, port); 67 | evhttp_uri_free(http_uri); 68 | 69 | return result; 70 | } 71 | 72 | std::unique_ptr TestEvHTTPConnection::Connect( 73 | absl::string_view host, int port) { 74 | std::unique_ptr result(new TestEvHTTPConnection()); 75 | 76 | result->ev_base_ = event_base_new(); 77 | if (result->ev_base_ == nullptr) { 78 | NET_LOG(ERROR, "Failed to connect : event_base_new()"); 79 | return nullptr; 80 | } 81 | 82 | // blocking call (DNS resolution) 83 | std::string host_str(host.data(), host.size()); 84 | result->evcon_ = evhttp_connection_base_bufferevent_new( 85 | result->ev_base_, nullptr, nullptr, host_str.c_str(), 86 | static_cast(port)); 87 | if (result->evcon_ == nullptr) { 88 | NET_LOG(ERROR, 89 | "Failed to connect : evhttp_connection_base_bufferevent_new()"); 90 | return nullptr; 91 | } 92 | 93 | evhttp_connection_set_retries(result->evcon_, 0); 94 | 95 | // TODO(wenboz): make this an option (default to 5s) 96 | evhttp_connection_set_timeout(result->evcon_, 5); 97 | 98 | return result; 99 | } 100 | 101 | namespace { 102 | 103 | // Copy ev response data to ClientResponse. 104 | void PopulateResponse(evhttp_request* req, TestClientResponse* response) { 105 | response->status = 106 | static_cast(evhttp_request_get_response_code(req)); 107 | 108 | struct evkeyvalq* headers = evhttp_request_get_input_headers(req); 109 | struct evkeyval* header; 110 | for (header = headers->tqh_first; header; header = header->next.tqe_next) { 111 | response->headers.emplace_back(header->key, header->value); 112 | } 113 | 114 | char buffer[1024]; 115 | int nread; 116 | 117 | while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req), buffer, 118 | sizeof(buffer))) > 0) { 119 | absl::StrAppend(&response->body, 120 | absl::string_view(buffer, static_cast(nread))); 121 | } 122 | } 123 | 124 | evhttp_cmd_type GetMethodEnum(absl::string_view method, bool with_body) { 125 | if (method.compare("GET") == 0) { 126 | return EVHTTP_REQ_GET; 127 | } else if (method.compare("POST") == 0) { 128 | return EVHTTP_REQ_POST; 129 | } else if (method.compare("HEAD") == 0) { 130 | return EVHTTP_REQ_HEAD; 131 | } else if (method.compare("PUT") == 0) { 132 | return EVHTTP_REQ_PUT; 133 | } else if (method.compare("DELETE") == 0) { 134 | return EVHTTP_REQ_DELETE; 135 | } else if (method.compare("OPTIONS") == 0) { 136 | return EVHTTP_REQ_OPTIONS; 137 | } else if (method.compare("TRACE") == 0) { 138 | return EVHTTP_REQ_TRACE; 139 | } else if (method.compare("CONNECT") == 0) { 140 | return EVHTTP_REQ_CONNECT; 141 | } else if (method.compare("PATCH") == 0) { 142 | return EVHTTP_REQ_PATCH; 143 | } else { 144 | if (with_body) { 145 | return EVHTTP_REQ_POST; 146 | } else { 147 | return EVHTTP_REQ_GET; 148 | } 149 | } 150 | } 151 | 152 | void ResponseDone(evhttp_request* req, void* ctx) { 153 | TestClientResponse* response = reinterpret_cast(ctx); 154 | 155 | if (req == nullptr) { 156 | // TODO(wenboz): make this a util and check safety 157 | int errcode = EVUTIL_SOCKET_ERROR(); 158 | NET_LOG(ERROR, "socket error = %s (%d)", 159 | evutil_socket_error_to_string(errcode), errcode); 160 | return; 161 | } 162 | 163 | PopulateResponse(req, response); 164 | 165 | if (response->done != nullptr) { 166 | response->done(); 167 | } 168 | } 169 | 170 | // Returns false if there is any error. 171 | bool GenerateEvRequest(evhttp_connection* evcon, 172 | const TestClientRequest& request, 173 | TestClientResponse* response) { 174 | evhttp_request* evreq = evhttp_request_new(ResponseDone, response); 175 | if (evreq == nullptr) { 176 | NET_LOG(ERROR, "Failed to send request : evhttp_request_new()"); 177 | return false; 178 | } 179 | 180 | evkeyvalq* output_headers = evhttp_request_get_output_headers(evreq); 181 | for (auto header : request.headers) { 182 | std::string key(header.first.data(), header.first.size()); 183 | std::string value(header.second.data(), header.second.size()); 184 | evhttp_add_header(output_headers, key.c_str(), value.c_str()); 185 | } 186 | 187 | evhttp_add_header(output_headers, "Connection", "close"); 188 | 189 | if (!request.body.empty()) { 190 | evbuffer* output_buffer = evhttp_request_get_output_buffer(evreq); 191 | 192 | std::string body(request.body.data(), request.body.size()); 193 | evbuffer_add(output_buffer, body.c_str(), request.body.size()); 194 | 195 | char length_header[16]; 196 | evutil_snprintf(length_header, sizeof(length_header) - 1, "%lu", 197 | request.body.size()); 198 | evhttp_add_header(output_headers, "Content-Length", length_header); 199 | } 200 | 201 | std::string uri(request.uri_path.data(), request.uri_path.size()); 202 | int r = evhttp_make_request( 203 | evcon, evreq, GetMethodEnum(request.method, !request.body.empty()), 204 | uri.c_str()); 205 | if (r != 0) { 206 | NET_LOG(ERROR, "evhttp_make_request() failed"); 207 | return false; 208 | } 209 | 210 | return true; 211 | } 212 | 213 | } // namespace 214 | 215 | // Sends the request and has the connection closed 216 | bool TestEvHTTPConnection::BlockingSendRequest(const TestClientRequest& request, 217 | TestClientResponse* response) { 218 | if (!GenerateEvRequest(evcon_, request, response)) { 219 | NET_LOG(ERROR, "Failed to generate the ev_request"); 220 | return false; 221 | } 222 | 223 | // inline loop blocking 224 | event_base_dispatch(ev_base_); 225 | return true; 226 | } 227 | 228 | bool TestEvHTTPConnection::SendRequest(const TestClientRequest& request, 229 | TestClientResponse* response) { 230 | if (this->executor_ == nullptr) { 231 | NET_LOG(ERROR, "EventExecutor is not configured."); 232 | return false; 233 | } 234 | 235 | if (!GenerateEvRequest(evcon_, request, response)) { 236 | NET_LOG(ERROR, "Failed to generate the ev_request"); 237 | return false; 238 | } 239 | 240 | executor_->Schedule([this]() { 241 | loop_exit_.reset(new absl::Notification()); 242 | event_base_dispatch(ev_base_); 243 | loop_exit_->Notify(); 244 | }); 245 | 246 | return true; 247 | } 248 | 249 | void TestEvHTTPConnection::SetExecutor( 250 | std::unique_ptr executor) { 251 | this->executor_ = std::move(executor); 252 | } 253 | 254 | } // namespace net_http 255 | -------------------------------------------------------------------------------- /net_http/client/test_client/internal/evhttp_connection.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef NET_HTTP_CLIENT_TEST_CLIENT_INTERNAL_EVHTTP_CONNECTION_H_ 17 | #define NET_HTTP_CLIENT_TEST_CLIENT_INTERNAL_EVHTTP_CONNECTION_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "absl/strings/string_view.h" 26 | #include "absl/synchronization/notification.h" 27 | #include "event2/buffer.h" 28 | #include "event2/bufferevent.h" 29 | #include "event2/event.h" 30 | #include "event2/http.h" 31 | #include "event2/keyvalq_struct.h" 32 | #include "event2/util.h" 33 | 34 | // TODO(wenboz): move EventExecutor to common 35 | #include "net_http/client/test_client/public/httpclient_interface.h" 36 | #include "net_http/server/public/httpserver_interface.h" 37 | namespace net_http { 38 | 39 | // The following types may be moved to an API interface in future. 40 | 41 | class TestEvHTTPConnection final : public TestHTTPClientInterface { 42 | public: 43 | TestEvHTTPConnection() = default; 44 | 45 | ~TestEvHTTPConnection() override; 46 | 47 | TestEvHTTPConnection(const TestEvHTTPConnection& other) = delete; 48 | TestEvHTTPConnection& operator=(const TestEvHTTPConnection& other) = delete; 49 | 50 | // Terminates the connection. 51 | void Terminate() override; 52 | 53 | // Returns a new connection given an absolute URL. 54 | // Always treat the URL scheme as "http" for now. 55 | // Returns nullptr if any error 56 | static std::unique_ptr Connect(absl::string_view url); 57 | 58 | // Returns a new connection to the specified host:port. 59 | // Returns nullptr if any error 60 | static std::unique_ptr Connect(absl::string_view host, 61 | int port); 62 | 63 | // Returns a new connection to the specified port of localhost. 64 | // Returns nullptr if any error 65 | static std::unique_ptr ConnectLocal(int port) { 66 | return Connect("localhost", port); 67 | } 68 | 69 | // Sends a request and blocks the caller till a response is received 70 | // or any error has happened. 71 | // Returns false if any error. 72 | bool BlockingSendRequest(const TestClientRequest& request, 73 | TestClientResponse* response) override; 74 | 75 | // Sends a request and returns immediately. The response will be handled 76 | // asynchronously via the response->done callback. 77 | // Returns false if any error in sending the request, or if the executor 78 | // has not been configured. 79 | bool SendRequest(const TestClientRequest& request, 80 | TestClientResponse* response) override; 81 | 82 | // Sets the executor for processing requests asynchronously. 83 | void SetExecutor(std::unique_ptr executor) override; 84 | 85 | private: 86 | struct event_base* ev_base_; 87 | struct evhttp_uri* http_uri_; 88 | struct evhttp_connection* evcon_; 89 | 90 | std::unique_ptr executor_; 91 | 92 | std::unique_ptr loop_exit_; 93 | }; 94 | 95 | } // namespace net_http 96 | 97 | #endif // NET_HTTP_CLIENT_TEST_CLIENT_INTERNAL_EVHTTP_CONNECTION_H_ 98 | -------------------------------------------------------------------------------- /net_http/client/test_client/public/BUILD: -------------------------------------------------------------------------------- 1 | # Description: APIs for experimental testing of net_http server instances 2 | 3 | package(default_visibility = ["//visibility:public",]) 4 | 5 | 6 | package_group( 7 | name = "http_client_users", 8 | packages = ["//..."], 9 | ) 10 | 11 | licenses(["notice"]) 12 | 13 | cc_library( 14 | name = "http_client_api", 15 | srcs = [], 16 | hdrs = [ 17 | "httpclient_interface.h", 18 | ], 19 | deps = [ 20 | "//net_http/public:shared_files", 21 | "//net_http/server/public:http_server_api", 22 | ], 23 | ) 24 | 25 | cc_library( 26 | name = "http_client", 27 | hdrs = [ 28 | "httpclient.h", 29 | ], 30 | deps = [ 31 | ":http_client_api", 32 | "//net_http/client/test_client/internal:evhttp_client", 33 | "@com_google_absl//absl/memory", 34 | ], 35 | ) 36 | -------------------------------------------------------------------------------- /net_http/client/test_client/public/README.md: -------------------------------------------------------------------------------- 1 | The client library is still under development, and has yet to be finalized. 2 | 3 | It should be primarily used for writing tests for users of the 4 | ServerRequestInterface and HTTPServerInterface APIs to verify basic 5 | functionality, and the current state should be considered experimental. 6 | -------------------------------------------------------------------------------- /net_http/client/test_client/public/httpclient.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef NET_HTTP_CLIENT_TEST_CLIENT_PUBLIC_HTTPCLIENT_H_ 17 | #define NET_HTTP_CLIENT_TEST_CLIENT_PUBLIC_HTTPCLIENT_H_ 18 | 19 | #include "absl/memory/memory.h" 20 | #include "net_http/client/test_client/internal/evhttp_connection.h" 21 | #include "net_http/client/test_client/public/httpclient_interface.h" 22 | 23 | // Factory to manage internal dependency 24 | // NOTE: This API is not yet finalized, and should in its current state be 25 | // considered experimental 26 | namespace net_http { 27 | 28 | // Creates a connection to a server implemented based on the libevents library. 29 | // Returns nullptr if there is any error. 30 | inline std::unique_ptr CreateEvHTTPConnection( 31 | absl::string_view host, int port) { 32 | auto connection = absl::make_unique(); 33 | connection = connection->Connect(host, port); 34 | if (!connection) { 35 | return nullptr; 36 | } 37 | 38 | return std::move(connection); 39 | } 40 | 41 | } // namespace net_http 42 | 43 | #endif // NET_HTTP_CLIENT_TEST_CLIENT_PUBLIC_HTTPCLIENT_H_ 44 | 45 | -------------------------------------------------------------------------------- /net_http/client/test_client/public/httpclient_interface.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef NET_HTTP_CLIENT_TEST_CLIENT_PUBLIC_HTTPCLIENT_INTERFACE_H_ 17 | #define NET_HTTP_CLIENT_TEST_CLIENT_PUBLIC_HTTPCLIENT_INTERFACE_H_ 18 | 19 | #include "net_http/public/response_code_enum.h" 20 | #include "net_http/server/public/httpserver_interface.h" 21 | 22 | // API for the HTTP Client 23 | // NOTE: This API is not yet finalized, and should be considered experimental. 24 | 25 | namespace net_http 26 | { 27 | 28 | // Data to be copied 29 | struct TestClientRequest 30 | { 31 | typedef std::pair HeaderKeyValue; 32 | 33 | absl::string_view uri_path; 34 | absl::string_view method; // must be in upper-case 35 | std::vector headers; 36 | absl::string_view body; 37 | }; 38 | 39 | // Caller allocates the data for output 40 | struct TestClientResponse 41 | { 42 | typedef std::pair HeaderKeyValue; 43 | 44 | HTTPStatusCode status = HTTPStatusCode::UNDEFINED; 45 | std::vector headers; 46 | std::string body; 47 | 48 | std::function done; // callback 49 | }; 50 | 51 | // This interface class specifies the API contract for the HTTP client. 52 | class TestHTTPClientInterface 53 | { 54 | public: 55 | TestHTTPClientInterface(const TestHTTPClientInterface &other) = delete; 56 | TestHTTPClientInterface &operator=(const TestHTTPClientInterface &other) = 57 | delete; 58 | 59 | virtual ~TestHTTPClientInterface() = default; 60 | 61 | // Terminates the connection. 62 | virtual void Terminate() = 0; 63 | 64 | // Sends a request and blocks the caller till a response is received 65 | // or any error has happened. 66 | // Returns false if any error. 67 | virtual bool BlockingSendRequest(const TestClientRequest &request, 68 | TestClientResponse *response) = 0; 69 | 70 | // Sends a request and returns immediately. The response will be handled 71 | // asynchronously via the response->done callback. 72 | // Returns false if any error in sending the request, or if the executor 73 | // has not been configured. 74 | virtual bool SendRequest(const TestClientRequest &request, 75 | TestClientResponse *response) = 0; 76 | 77 | // Sets the executor for processing requests asynchronously. 78 | virtual void SetExecutor(std::unique_ptr executor) = 0; 79 | 80 | protected: 81 | TestHTTPClientInterface() = default; 82 | }; 83 | 84 | } // namespace net_http 85 | 86 | #endif // NET_HTTP_CLIENT_TEST_CLIENT_PUBLIC_HTTPCLIENT_INTERFACE_H_ 87 | -------------------------------------------------------------------------------- /net_http/client/test_client/testing/BUILD: -------------------------------------------------------------------------------- 1 | # Description: net_http/client/test_client/testing 2 | 3 | package(default_visibility = ["//visibility:private"]) 4 | 5 | licenses(["notice"]) 6 | 7 | cc_binary( 8 | name = "evhttp_echo_client", 9 | srcs = ["evhttp_echo_client.cc"], 10 | deps = [ 11 | "//net_http/client/test_client/internal:evhttp_client", 12 | ], 13 | ) 14 | -------------------------------------------------------------------------------- /net_http/client/test_client/testing/evhttp_echo_client.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // A test client to print the response from the evhttp_echo_server 17 | // URI: /print 18 | 19 | #include 20 | 21 | #include "net_http/client/test_client/internal/evhttp_connection.h" 22 | 23 | namespace { 24 | 25 | using net_http::TestClientRequest; 26 | using net_http::TestClientResponse; 27 | using net_http::TestEvHTTPConnection; 28 | 29 | bool SendRequest(const char* url) { 30 | auto connection = TestEvHTTPConnection::Connect(url); 31 | if (connection == nullptr) { 32 | std::cerr << "Fail to connect to %s" << url; 33 | } 34 | 35 | TestClientRequest request = {url, "GET", {}, ""}; 36 | TestClientResponse response = {}; 37 | 38 | if (!connection->BlockingSendRequest(request, &response)) { 39 | std::cerr << "Request failed."; 40 | return false; 41 | } 42 | 43 | std::cout << "Response received: " << std::endl 44 | << "Status: " << static_cast(response.status) << std::endl; 45 | 46 | for (const auto& keyval : response.headers) { 47 | std::cout << keyval.first << " : " << keyval.second << std::endl; 48 | } 49 | 50 | std::cout << std::endl << response.body << std::endl; 51 | return true; 52 | } 53 | 54 | } // namespace 55 | 56 | int main(int argc, char** argv) { 57 | if (argc < 2) { 58 | std::cerr << "Usage: http-client " << std::endl; 59 | return 1; 60 | } 61 | 62 | return SendRequest(argv[1]); 63 | } 64 | -------------------------------------------------------------------------------- /net_http/compression/BUILD: -------------------------------------------------------------------------------- 1 | # Description: compression support libraries 2 | 3 | package(default_visibility = [ 4 | "//net_http:__subpackages__", 5 | ]) 6 | 7 | licenses(["notice"]) 8 | 9 | # C++ lib based on zlib for gzip support 10 | cc_library( 11 | name = "gzip_zlib", 12 | srcs = [ 13 | "gzip_zlib.cc", 14 | ], 15 | hdrs = [ 16 | "gzip_zlib.h", 17 | ], 18 | deps = [ 19 | "//net_http/internal:net_logging", 20 | "@com_google_absl//absl/base", 21 | "@com_google_absl//absl/base:core_headers", 22 | "@com_google_absl//absl/strings", 23 | "@zlib", 24 | ], 25 | ) 26 | 27 | cc_test( 28 | name = "gzip_zlib_test", 29 | size = "large", 30 | srcs = ["gzip_zlib_test.cc"], 31 | deps = [ 32 | ":gzip_zlib", 33 | # "//core/test_util:test_main", 34 | "@com_google_googletest//:gtest_main", 35 | "@com_google_absl//absl/strings", 36 | # "@com_google_googletest//:gtest", 37 | ], 38 | ) 39 | -------------------------------------------------------------------------------- /net_http/compression/README.md: -------------------------------------------------------------------------------- 1 | Compression support 2 | =================== 3 | 4 | This package provides C++ wrappers for compression libraries such as gzip, br. 5 | 6 | APIs are subject to change but usage outside net_http is expected. 7 | 8 | gzip_zlib.h 9 | --------------------- 10 | 11 | Minimum APIs and implementation to support gzip Content-Encoding via zlib. 12 | -------------------------------------------------------------------------------- /net_http/compression/gzip_zlib.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef NET_HTTP_COMPRESSION_GZIP_ZLIB_H_ 17 | #define NET_HTTP_COMPRESSION_GZIP_ZLIB_H_ 18 | 19 | #include 20 | 21 | #include 22 | namespace net_http 23 | { 24 | 25 | class GZipHeader 26 | { 27 | public: 28 | GZipHeader() { Reset(); } 29 | ~GZipHeader() {} 30 | 31 | // Wipe the slate clean and start from scratch. 32 | void Reset() 33 | { 34 | state_ = IN_HEADER_ID1; 35 | flags_ = 0; 36 | extra_length_ = 0; 37 | } 38 | 39 | enum Status 40 | { 41 | INCOMPLETE_HEADER, 42 | COMPLETE_HEADER, 43 | INVALID_HEADER, 44 | }; 45 | 46 | // If the bytes we've seen so far do not yet constitute a complete gzip 47 | // header, return INCOMPLETE_HEADER. If these bytes do not constitute a valid 48 | // gzip header, return INVALID_HEADER. When we've seen a complete 49 | // gzip header, return COMPLETE_HEADER and set the pointer pointed 50 | // to by header_end to the first byte beyond the gzip header. 51 | Status ReadMore(const char *inbuf, int inbuf_len, const char **header_end); 52 | 53 | private: 54 | enum 55 | { // flags (see RFC) 56 | FLAG_FTEXT = 0x01, // bit 0 set: file probably ascii text 57 | FLAG_FHCRC = 0x02, // bit 1 set: header CRC present 58 | FLAG_FEXTRA = 0x04, // bit 2 set: extra field present 59 | FLAG_FNAME = 0x08, // bit 3 set: original file name present 60 | FLAG_FCOMMENT = 0x10, // bit 4 set: file comment present 61 | FLAG_RESERVED = 0xE0, // bits 5..7: reserved 62 | }; 63 | 64 | enum State 65 | { 66 | // The first 10 bytes are the fixed-size header: 67 | IN_HEADER_ID1, 68 | IN_HEADER_ID2, 69 | IN_HEADER_CM, 70 | IN_HEADER_FLG, 71 | IN_HEADER_MTIME_BYTE_0, 72 | IN_HEADER_MTIME_BYTE_1, 73 | IN_HEADER_MTIME_BYTE_2, 74 | IN_HEADER_MTIME_BYTE_3, 75 | IN_HEADER_XFL, 76 | IN_HEADER_OS, 77 | 78 | IN_XLEN_BYTE_0, 79 | IN_XLEN_BYTE_1, 80 | IN_FEXTRA, 81 | 82 | IN_FNAME, 83 | 84 | IN_FCOMMENT, 85 | 86 | IN_FHCRC_BYTE_0, 87 | IN_FHCRC_BYTE_1, 88 | 89 | IN_DONE, 90 | }; 91 | 92 | int state_; // our current State in the parsing FSM: an int so we can ++ 93 | uint8_t flags_; // the flags byte of the header ("FLG" in the RFC) 94 | uint16_t extra_length_; // how much of the "extra field" we have yet to read 95 | }; 96 | 97 | class ZLib 98 | { 99 | public: 100 | ZLib(); 101 | ~ZLib(); 102 | 103 | // The max length of the buffer to store uncompressed data 104 | static constexpr int64_t kMaxUncompressedBytes = 100 * 1024 * 1024; // 100MB 105 | 106 | // Wipe a ZLib object to a virgin state. This differs from Reset() 107 | // in that it also breaks any dictionary, gzip, etc, state. 108 | void Reinit(); 109 | 110 | // Call this to make a zlib buffer as good as new. Here's the only 111 | // case where they differ: 112 | // CompressChunk(a); CompressChunk(b); CompressChunkDone(); vs 113 | // CompressChunk(a); Reset(); CompressChunk(b); CompressChunkDone(); 114 | // You'll want to use Reset(), then, when you interrupt a compress 115 | // (or uncompress) in the middle of a chunk and want to start over. 116 | void Reset(); 117 | 118 | // By default UncompressAtMostOrAll will return Z_OK upon hitting the end of 119 | // the input stream. This function modifies that behavior by returning 120 | // Z_STREAM_END instead. This is useful when getting multiple compressed 121 | // documents in a single stream. Returning Z_STREAM_END will indicate the end 122 | // of a document. 123 | void SetDontHideStreamEnd(); 124 | 125 | // Sets the compression level to be used 126 | void SetCompressionLevel(int level) { settings_.compression_level_ = level; } 127 | 128 | // Sets the size of the window (history buffer) used by the compressor. 129 | // The size is expressed in bits (log base 2 of the desired size). 130 | void SetCompressionWindowSizeInBits(int bits) 131 | { 132 | settings_.window_bits_ = bits; 133 | } 134 | 135 | // Controls the amount of memory used by the compresser. 136 | // Legal value are 1 through 9. See zlib.h for more info. 137 | void SetCompressionMemLevel(int level) { settings_.mem_level_ = level; } 138 | 139 | // According to the zlib manual, when you Compress, the destination 140 | // buffer must have size at least src + .1%*src + 12. This function 141 | // helps you calculate that. Augment this to account for a potential 142 | // gzip header and footer, plus a few bytes of slack. 143 | static uLong MinCompressbufSize(uLong uncompress_size) 144 | { 145 | return uncompress_size + uncompress_size / 1000 + 40; 146 | } 147 | 148 | // The minimum size of footers written by CompressChunkDone(). 149 | int MinFooterSize() const; 150 | 151 | // Compresses the source buffer into the destination buffer. 152 | // sourceLen is the byte length of the source buffer. 153 | // Upon entry, destLen is the total size of the destination buffer, 154 | // which must be of size at least MinCompressbufSize(sourceLen). 155 | // Upon exit, destLen is the actual size of the compressed buffer. 156 | // 157 | // This function can be used to compress a whole file at once if the 158 | // input file is mmap'ed. 159 | // 160 | // Returns Z_OK if success, Z_MEM_ERROR if there was not 161 | // enough memory, Z_BUF_ERROR if there was not enough room in the 162 | // output buffer. Note that if the output buffer is exactly the same 163 | // size as the compressed result, we still return Z_BUF_ERROR. 164 | // (check CL#1936076) 165 | // 166 | // If the values of *destLen or sourceLen do not fit in an unsigned int, 167 | // Z_BUF_ERROR is returned. 168 | int Compress(Bytef *dest, uLongf *destLen, const Bytef *source, 169 | uLong sourceLen); 170 | 171 | // Uncompresses the source buffer into the destination buffer. 172 | // The destination buffer must be long enough to hold the entire 173 | // decompressed contents. 174 | // 175 | // Returns Z_OK on success, otherwise, it returns a zlib error code. 176 | // 177 | // If the values of *destLen or sourceLen do not fit in an unsigned int, 178 | // Z_BUF_ERROR is returned. 179 | int Uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, 180 | uLong sourceLen); 181 | 182 | // Get the uncompressed size from the gzip footer. Returns 0 if source is too 183 | // short (len < 5). 184 | uLongf GzipUncompressedLength(const Bytef *source, uLong len); 185 | 186 | // Special helper function to help uncompress gzipped documents: 187 | // We'll allocate (via std::allocator) a destination buffer exactly big 188 | // enough to hold the gzipped content. We set dest and destLen. 189 | // If we don't return Z_OK, *dest will be NULL, otherwise you 190 | // should free() it when you're done with it. 191 | // Returns Z_OK on success, otherwise, it returns a zlib error code. 192 | // Its the responsibility of the user to set *destLen to the 193 | // expected maximum size of the uncompressed data. The size of the 194 | // uncompressed data is read from the compressed buffer gzip footer. 195 | // This value cannot be trusted, so we compare it to the expected 196 | // maximum size supplied by the user, returning Z_MEM_ERROR if its 197 | // greater than the expected maximum size. 198 | int UncompressGzipAndAllocate(Bytef **dest, uLongf *destLen, 199 | const Bytef *source, uLong sourceLen); 200 | 201 | // Streaming compression and decompression methods. 202 | // {Unc,C}ompressAtMost() decrements sourceLen by the amount of data that was 203 | // consumed: if it returns Z_BUF_ERROR, set the source of the next 204 | // {Unc,C}ompressAtMost() to the unconsumed data. 205 | 206 | // Compresses data one chunk at a time -- ie you can call this more 207 | // than once. This is useful for a webserver, for instance, which 208 | // might want to use chunked encoding with compression. To get this 209 | // to work you need to call start and finish routines. 210 | // 211 | // Returns Z_OK if success, Z_MEM_ERROR if there was not 212 | // enough memory, Z_BUF_ERROR if there was not enough room in the 213 | // output buffer. 214 | 215 | int CompressAtMost(Bytef *dest, uLongf *destLen, const Bytef *source, 216 | uLong *sourceLen); 217 | 218 | // Emits gzip footer information, as needed. 219 | // destLen should be at least MinFooterSize() long. 220 | // Returns Z_OK, Z_MEM_ERROR, and Z_BUF_ERROR as in CompressChunk(). 221 | int CompressChunkDone(Bytef *dest, uLongf *destLen); 222 | 223 | // Uncompress data one chunk at a time -- ie you can call this 224 | // more than once. To get this to work you need to call per-chunk 225 | // and "done" routines. 226 | // 227 | // Returns Z_OK if success, Z_MEM_ERROR if there was not 228 | // enough memory, Z_BUF_ERROR if there was not enough room in the 229 | // output buffer. 230 | 231 | int UncompressAtMost(Bytef *dest, uLongf *destLen, const Bytef *source, 232 | uLong *sourceLen); 233 | 234 | // Checks gzip footer information, as needed. Mostly this just 235 | // makes sure the checksums match. Whenever you call this, it 236 | // will assume the last 8 bytes from the previous UncompressChunk 237 | // call are the footer. Returns true iff everything looks ok. 238 | bool UncompressChunkDone(); 239 | 240 | // Only meaningful for chunked compressing/uncompressing. It's true 241 | // after initialization or reset and before the first chunk of 242 | // user data is received. 243 | bool first_chunk() const { return first_chunk_; } 244 | 245 | // Convenience method to check if a bytestream has a header. This 246 | // is intended as a quick test: "Is this likely a GZip file?" 247 | static bool HasGzipHeader(const char *source, int sourceLen); 248 | 249 | // Have we parsed the complete gzip footer? When this result is true, it is 250 | // time to call IsGzipFooterValid() / UncompressChunkDone(). 251 | bool IsGzipFooterComplete() const; 252 | 253 | // Have we parsed the complete gzip footer, and does it match the 254 | // length and CRC checksum of the content that we have uncompressed 255 | // so far? 256 | bool IsGzipFooterValid() const; 257 | 258 | // Accessor for the uncompressed size 259 | uLong uncompressed_size() const { return uncompressed_size_; } 260 | 261 | private: 262 | int InflateInit(); // sets up the zlib inflate structure 263 | int DeflateInit(); // sets up the zlib deflate structure 264 | 265 | // These init the zlib data structures for compressing/uncompressing 266 | int CompressInit(Bytef *dest, uLongf *destLen, const Bytef *source, 267 | uLong *sourceLen); 268 | int UncompressInit(Bytef *dest, uLongf *destLen, const Bytef *source, 269 | uLong *sourceLen); 270 | // Initialization method to be called if we hit an error while 271 | // uncompressing. On hitting an error, call this method before 272 | // returning the error. 273 | void UncompressErrorInit(); 274 | // Helper functions to write gzip-specific data 275 | int WriteGzipHeader(); 276 | int WriteGzipFooter(Bytef *dest, uLongf destLen); 277 | 278 | // Helper function for both Compress and CompressChunk 279 | int CompressChunkOrAll(Bytef *dest, uLongf *destLen, const Bytef *source, 280 | uLong sourceLen, int flush_mode); 281 | int CompressAtMostOrAll(Bytef *dest, uLongf *destLen, const Bytef *source, 282 | uLong *sourceLen, int flush_mode); 283 | 284 | // Likewise for UncompressAndUncompressChunk 285 | int UncompressChunkOrAll(Bytef *dest, uLongf *destLen, const Bytef *source, 286 | uLong sourceLen, int flush_mode); 287 | 288 | int UncompressAtMostOrAll(Bytef *dest, uLongf *destLen, const Bytef *source, 289 | uLong *sourceLen, int flush_mode); 290 | 291 | // Initialization method to be called if we hit an error while 292 | // compressing. On hitting an error, call this method before 293 | // returning the error. 294 | void CompressErrorInit(); 295 | 296 | struct Settings 297 | { 298 | // compression level 299 | int compression_level_; 300 | 301 | // log base 2 of the window size used in compression 302 | int window_bits_; 303 | 304 | // specifies the amount of memory to be used by compressor (1-9) 305 | int mem_level_; 306 | 307 | // Controls behavior of UncompressAtMostOrAll with regards to returning 308 | // Z_STREAM_END. See comments for SetDontHideStreamEnd. 309 | bool dont_hide_zstream_end_; 310 | }; 311 | 312 | // "Current" settings. These will be used whenever we next configure zlib. 313 | // For example changing compression level or header mode will be recorded 314 | // in these, but don't usually get applied immediately but on next compress. 315 | Settings settings_; 316 | 317 | // Settings last used to initialise and configure zlib. These are needed 318 | // to know if the current desired configuration in settings_ is sufficiently 319 | // compatible with the previous configuration and we can just reconfigure the 320 | // underlying zlib objects, or have to recreate them from scratch. 321 | Settings init_settings_; 322 | 323 | z_stream comp_stream_; // Zlib stream data structure 324 | bool comp_init_; // True if we have initialized comp_stream_ 325 | z_stream uncomp_stream_; // Zlib stream data structure 326 | bool uncomp_init_; // True if we have initialized uncomp_stream_ 327 | 328 | // These are used only in gzip compression mode 329 | uLong crc_; // stored in gzip footer, fitting 4 bytes 330 | uLong uncompressed_size_; 331 | 332 | GZipHeader *gzip_header_; // our gzip header state 333 | 334 | Byte gzip_footer_[8]; // stored footer, used to uncompress 335 | int gzip_footer_bytes_; // num of footer bytes read so far, or -1 336 | 337 | // These are used only with chunked compression. 338 | bool first_chunk_; // true if we need to emit headers with this chunk 339 | }; 340 | 341 | } // namespace net_http 342 | 343 | #endif // NET_HTTP_COMPRESSION_GZIP_ZLIB_H_ 344 | -------------------------------------------------------------------------------- /net_http/internal/BUILD: -------------------------------------------------------------------------------- 1 | # Description: shared code for net_http 2 | 3 | package(default_visibility = [ 4 | "//net_http:__subpackages__", 5 | ]) 6 | 7 | licenses(["notice"]) 8 | 9 | cc_library( 10 | name = "fixed_thread_pool", 11 | testonly = 1, 12 | hdrs = ["fixed_thread_pool.h"], 13 | deps = [ 14 | "@com_google_absl//absl/base:core_headers", 15 | "@com_google_absl//absl/synchronization", 16 | ], 17 | ) 18 | 19 | cc_library( 20 | name = "net_logging", 21 | srcs = ["net_logging.cc"], 22 | hdrs = ["net_logging.h"], 23 | deps = [ 24 | "@com_google_absl//absl/base:config", 25 | "@com_google_absl//absl/base:core_headers", 26 | "@com_google_absl//absl/base:log_severity", 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /net_http/internal/fixed_thread_pool.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef NET_HTTP_INTERNAL_FIXED_THREAD_POOL_H_ 17 | #define NET_HTTP_INTERNAL_FIXED_THREAD_POOL_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include // NOLINT(build/c++11) 23 | #include 24 | 25 | #include "absl/base/thread_annotations.h" 26 | #include "absl/synchronization/mutex.h" 27 | namespace net_http 28 | { 29 | 30 | // A simple fixed-size ThreadPool implementation for tests. 31 | // The initial version is copied from 32 | // absl/synchronization/internal/thread_pool.h 33 | class FixedThreadPool 34 | { 35 | public: 36 | explicit FixedThreadPool(int num_threads) 37 | { 38 | for (int i = 0; i < num_threads; ++i) 39 | { 40 | threads_.push_back(std::thread(&FixedThreadPool::WorkLoop, this)); 41 | } 42 | } 43 | 44 | FixedThreadPool(const FixedThreadPool &) = delete; 45 | FixedThreadPool &operator=(const FixedThreadPool &) = delete; 46 | 47 | ~FixedThreadPool() 48 | { 49 | { 50 | absl::MutexLock l(&mu_); 51 | for (int i = 0; i < threads_.size(); ++i) 52 | { 53 | queue_.push(nullptr); // Shutdown signal. 54 | } 55 | } 56 | for (auto &t : threads_) 57 | { 58 | t.join(); 59 | } 60 | } 61 | 62 | // Schedule a function to be run on a ThreadPool thread immediately. 63 | void Schedule(std::function func) 64 | { 65 | assert(func != nullptr); 66 | absl::MutexLock l(&mu_); 67 | queue_.push(std::move(func)); 68 | } 69 | 70 | private: 71 | bool WorkAvailable() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) 72 | { 73 | return !queue_.empty(); 74 | } 75 | 76 | void WorkLoop() 77 | { 78 | while (true) 79 | { 80 | std::function func; 81 | { 82 | absl::MutexLock l(&mu_); 83 | mu_.Await(absl::Condition(this, &FixedThreadPool::WorkAvailable)); 84 | func = std::move(queue_.front()); 85 | queue_.pop(); 86 | } 87 | if (func == nullptr) 88 | { // Shutdown signal. 89 | break; 90 | } 91 | func(); 92 | } 93 | } 94 | 95 | absl::Mutex mu_; 96 | std::queue> queue_ ABSL_GUARDED_BY(mu_); 97 | std::vector threads_; 98 | }; 99 | 100 | } // namespace net_http 101 | 102 | #endif // NET_HTTP_INTERNAL_FIXED_THREAD_POOL_H_ 103 | -------------------------------------------------------------------------------- /net_http/internal/net_logging.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "net_http/internal/net_logging.h" 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "absl/base/attributes.h" 26 | #include "absl/base/config.h" 27 | #include "absl/base/log_severity.h" 28 | 29 | #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ 30 | defined(__Fuchsia__) || defined(__native_client__) || \ 31 | defined(__EMSCRIPTEN__) 32 | #include 33 | 34 | #define NET_HAVE_POSIX_WRITE 1 35 | #define NET_LOW_LEVEL_WRITE_SUPPORTED 1 36 | #else 37 | #undef NET_HAVE_POSIX_WRITE 38 | #endif 39 | 40 | #if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__) 41 | #include 42 | #define NET_HAVE_SYSCALL_WRITE 1 43 | #define NET_LOW_LEVEL_WRITE_SUPPORTED 1 44 | #else 45 | #undef NET_HAVE_SYSCALL_WRITE 46 | #endif 47 | 48 | #ifdef _WIN32 49 | #include 50 | 51 | #define NET_HAVE_RAW_IO 1 52 | #define NET_LOW_LEVEL_WRITE_SUPPORTED 1 53 | #else 54 | #undef NET_HAVE_RAW_IO 55 | #endif 56 | 57 | #ifdef NET_LOW_LEVEL_WRITE_SUPPORTED 58 | static const char kTruncated[] = " ... (message truncated)\n"; 59 | 60 | inline static bool VADoNetLog(char **buf, int *size, const char *format, 61 | va_list ap) ABSL_PRINTF_ATTRIBUTE(3, 0); 62 | inline static bool VADoNetLog(char **buf, int *size, const char *format, 63 | va_list ap) 64 | { 65 | int n = vsnprintf(*buf, *size, format, ap); 66 | bool result = true; 67 | if (n < 0 || n > *size) 68 | { 69 | result = false; 70 | if (static_cast(*size) > sizeof(kTruncated)) 71 | { 72 | n = *size - sizeof(kTruncated); // room for truncation message 73 | } 74 | else 75 | { 76 | n = 0; // no room for truncation message 77 | } 78 | } 79 | *size -= n; 80 | *buf += n; 81 | return result; 82 | } 83 | #endif // NET_LOW_LEVEL_WRITE_SUPPORTED 84 | 85 | static constexpr int kLogBufSize = 10000; // absl defaults to 3000 86 | 87 | namespace 88 | { 89 | 90 | bool DoNetLog(char **buf, int *size, const char *format, ...) 91 | ABSL_PRINTF_ATTRIBUTE(3, 4); 92 | bool DoNetLog(char **buf, int *size, const char *format, ...) 93 | { 94 | va_list ap; 95 | va_start(ap, format); 96 | int n = vsnprintf(*buf, *size, format, ap); 97 | va_end(ap); 98 | if (n < 0 || n > *size) 99 | return false; 100 | *size -= n; 101 | *buf += n; 102 | return true; 103 | } 104 | 105 | void NetLogVA(absl::LogSeverity severity, const char *file, int line, 106 | const char *format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0); 107 | void NetLogVA(absl::LogSeverity severity, const char *file, int line, 108 | const char *format, va_list ap) 109 | { 110 | char buffer[kLogBufSize]; 111 | char *buf = buffer; 112 | int size = sizeof(buffer); 113 | #ifdef NET_LOW_LEVEL_WRITE_SUPPORTED 114 | bool enabled = true; 115 | #else 116 | bool enabled = false; 117 | #endif 118 | 119 | #ifdef ABSL_MIN_LOG_LEVEL 120 | if (severity < static_cast(ABSL_MIN_LOG_LEVEL) && 121 | severity < absl::LogSeverity::kFatal) 122 | { 123 | enabled = false; 124 | } 125 | #endif 126 | 127 | if (enabled) 128 | { 129 | DoNetLog(&buf, &size, "[%s : %d] NET_LOG: ", file, line); 130 | } 131 | 132 | #ifdef NET_LOW_LEVEL_WRITE_SUPPORTED 133 | if (enabled) 134 | { 135 | bool no_chop = VADoNetLog(&buf, &size, format, ap); 136 | if (no_chop) 137 | { 138 | DoNetLog(&buf, &size, "\n"); 139 | } 140 | else 141 | { 142 | DoNetLog(&buf, &size, "%s", kTruncated); 143 | } 144 | net_http::SafeWriteToStderr(buffer, strlen(buffer)); 145 | } 146 | #else 147 | static_cast(format); 148 | static_cast(ap); 149 | #endif 150 | 151 | if (severity == absl::LogSeverity::kFatal) 152 | { 153 | abort(); 154 | } 155 | } 156 | 157 | } // namespace 158 | namespace net_http 159 | { 160 | 161 | void SafeWriteToStderr(const char *s, size_t len) 162 | { 163 | #if defined(NET_HAVE_SYSCALL_WRITE) 164 | syscall(SYS_write, STDERR_FILENO, s, len); 165 | #elif defined(NET_HAVE_POSIX_WRITE) 166 | write(STDERR_FILENO, s, len); 167 | #elif defined(NET_HAVE_RAW_IO) 168 | _write(/* stderr */ 2, s, len); 169 | #else 170 | (void)s; 171 | (void)len; 172 | #endif 173 | } 174 | 175 | void NetLog(absl::LogSeverity severity, const char *file, int line, 176 | const char *format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); 177 | void NetLog(absl::LogSeverity severity, const char *file, int line, 178 | const char *format, ...) 179 | { 180 | va_list ap; 181 | va_start(ap, format); 182 | NetLogVA(severity, file, line, format, ap); 183 | va_end(ap); 184 | } 185 | 186 | } // namespace net_http 187 | -------------------------------------------------------------------------------- /net_http/internal/net_logging.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2019 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef NET_HTTP_INTERNAL_NET_LOGGING_H_ 17 | #define NET_HTTP_INTERNAL_NET_LOGGING_H_ 18 | 19 | #include 20 | 21 | #include "absl/base/attributes.h" 22 | #include "absl/base/log_severity.h" 23 | #include "absl/base/macros.h" 24 | #include "absl/base/port.h" 25 | 26 | // This initial version is a minimum fork from absl/base/internal/raw_logging.h 27 | // Hooks are not supported. 28 | // 29 | // TODO(wenboz): finalize the log support for net_http 30 | // * make logging pluggable (by TF serving or by adapting external libraries 31 | 32 | #define NET_LOG(severity, ...) \ 33 | do \ 34 | { \ 35 | constexpr const char *net_logging_internal_basename = \ 36 | net_http::Basename(__FILE__, \ 37 | sizeof(__FILE__) - 1); \ 38 | net_http::NetLog(NET_LOGGING_INTERNAL_##severity, \ 39 | net_logging_internal_basename, \ 40 | __LINE__, __VA_ARGS__); \ 41 | } while (0) 42 | 43 | #define NET_CHECK(condition, message) \ 44 | do \ 45 | { \ 46 | if (ABSL_PREDICT_FALSE(!(condition))) \ 47 | { \ 48 | NET_LOG(FATAL, "Check %s failed: %s", #condition, message); \ 49 | } \ 50 | } while (0) 51 | 52 | #define NET_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo 53 | #define NET_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning 54 | #define NET_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError 55 | #define NET_LOGGING_INTERNAL_FATAL ::absl::LogSeverity::kFatal 56 | #define NET_LOGGING_INTERNAL_LEVEL(severity) \ 57 | ::absl::NormalizeLogSeverity(severity) 58 | namespace net_http 59 | { 60 | 61 | void NetLog(absl::LogSeverity severity, const char *file, int line, 62 | const char *format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); 63 | 64 | void SafeWriteToStderr(const char *s, size_t len); 65 | 66 | constexpr const char *Basename(const char *fname, int offset) 67 | { 68 | return offset == 0 || fname[offset - 1] == '/' || fname[offset - 1] == '\\' 69 | ? fname + offset 70 | : Basename(fname, offset - 1); 71 | } 72 | 73 | } // namespace net_http 74 | 75 | #endif // NET_HTTP_INTERNAL_NET_LOGGING_H_ 76 | -------------------------------------------------------------------------------- /net_http/internal/testing/BUILD: -------------------------------------------------------------------------------- 1 | # Description: net_http/internal/testing 2 | 3 | package(default_visibility = ["//visibility:private"]) 4 | 5 | licenses(["notice"]) 6 | 7 | cc_binary( 8 | name = "net_logging_example", 9 | srcs = ["net_logging_example.cc"], 10 | deps = [ 11 | "//net_http/internal:net_logging", 12 | ], 13 | ) 14 | -------------------------------------------------------------------------------- /net_http/internal/testing/net_logging_example.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2019 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include 17 | #include 18 | 19 | #include "net_http/internal/net_logging.h" 20 | 21 | int main(int argc, char** argv) { 22 | NET_LOG(INFO, "started!"); 23 | 24 | size_t size = 100; 25 | NET_LOG(ERROR, "read less than specified bytes : %zu", size); 26 | 27 | const char* url = "/url"; 28 | NET_LOG(WARNING, "%s: read less than specified bytes : %zu", url, size); 29 | 30 | NET_LOG(FATAL, "aborted!"); 31 | 32 | return 0; // unexpected 33 | } 34 | -------------------------------------------------------------------------------- /net_http/public/BUILD: -------------------------------------------------------------------------------- 1 | # Description: shared files 2 | 3 | package(default_visibility = ["//visibility:public",]) 4 | 5 | package_group( 6 | name = "http_server_clients", 7 | packages = ["//..."], 8 | ) 9 | 10 | licenses(["notice"]) 11 | 12 | # C++ lib based on zlib for gzip support 13 | cc_library( 14 | name = "shared_files", 15 | srcs = [ 16 | "header_names.cc", 17 | ], 18 | hdrs = [ 19 | "header_names.h", 20 | "response_code_enum.h", 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /net_http/public/header_names.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "net_http/public/header_names.h" 17 | namespace net_http { 18 | 19 | // Standard header names 20 | 21 | const char HTTPHeaders::ACCEPT[] = "Accept"; 22 | const char HTTPHeaders::ACCEPT_CHARSET[] = "Accept-Charset"; 23 | const char HTTPHeaders::ACCEPT_ENCODING[] = "Accept-Encoding"; 24 | const char HTTPHeaders::ACCEPT_LANGUAGE[] = "Accept-Language"; 25 | const char HTTPHeaders::ACCEPT_RANGES[] = "Accept-Ranges"; 26 | const char HTTPHeaders::ACCESS_CONTROL_ALLOW_CREDENTIALS[] = 27 | "Access-Control-Allow-Credentials"; 28 | const char HTTPHeaders::ACCESS_CONTROL_ALLOW_HEADERS[] = 29 | "Access-Control-Allow-Headers"; 30 | const char HTTPHeaders::ACCESS_CONTROL_ALLOW_METHODS[] = 31 | "Access-Control-Allow-Methods"; 32 | const char HTTPHeaders::ACCESS_CONTROL_ALLOW_ORIGIN[] = 33 | "Access-Control-Allow-Origin"; 34 | const char HTTPHeaders::ACCESS_CONTROL_EXPOSE_HEADERS[] = 35 | "Access-Control-Expose-Headers"; 36 | const char HTTPHeaders::ACCESS_CONTROL_MAX_AGE[] = "Access-Control-Max-Age"; 37 | const char HTTPHeaders::ACCESS_CONTROL_REQUEST_HEADERS[] = 38 | "Access-Control-Request-Headers"; 39 | const char HTTPHeaders::ACCESS_CONTROL_REQUEST_METHOD[] = 40 | "Access-Control-Request-Method"; 41 | const char HTTPHeaders::AGE[] = "Age"; 42 | const char HTTPHeaders::ALLOW[] = "Allow"; 43 | const char HTTPHeaders::AUTHORIZATION[] = "Authorization"; 44 | const char HTTPHeaders::CACHE_CONTROL[] = "Cache-Control"; 45 | const char HTTPHeaders::CONNECTION[] = "Connection"; 46 | const char HTTPHeaders::CONTENT_DISPOSITION[] = "Content-Disposition"; 47 | const char HTTPHeaders::CONTENT_ENCODING[] = "Content-Encoding"; 48 | const char HTTPHeaders::CONTENT_LANGUAGE[] = "Content-Language"; 49 | const char HTTPHeaders::CONTENT_LENGTH[] = "Content-Length"; 50 | const char HTTPHeaders::CONTENT_LOCATION[] = "Content-Location"; 51 | const char HTTPHeaders::CONTENT_RANGE[] = "Content-Range"; 52 | const char HTTPHeaders::CONTENT_SECURITY_POLICY[] = "Content-Security-Policy"; 53 | const char HTTPHeaders::CONTENT_SECURITY_POLICY_REPORT_ONLY[] = 54 | "Content-Security-Policy-Report-Only"; 55 | const char HTTPHeaders::X_CONTENT_SECURITY_POLICY[] = 56 | "X-Content-Security-Policy"; 57 | const char HTTPHeaders::X_CONTENT_SECURITY_POLICY_REPORT_ONLY[] = 58 | "X-Content-Security-Policy-Report-Only"; 59 | const char HTTPHeaders::X_WEBKIT_CSP[] = "X-WebKit-CSP"; 60 | const char HTTPHeaders::X_WEBKIT_CSP_REPORT_ONLY[] = "X-WebKit-CSP-Report-Only"; 61 | const char HTTPHeaders::CONTENT_TYPE[] = "Content-Type"; 62 | const char HTTPHeaders::CONTENT_MD5[] = "Content-MD5"; 63 | const char HTTPHeaders::X_CONTENT_TYPE_OPTIONS[] = "X-Content-Type-Options"; 64 | const char HTTPHeaders::COOKIE[] = "Cookie"; 65 | const char HTTPHeaders::COOKIE2[] = "Cookie2"; 66 | const char HTTPHeaders::DATE[] = "Date"; 67 | const char HTTPHeaders::DAV[] = "DAV"; 68 | const char HTTPHeaders::DEPTH[] = "Depth"; 69 | const char HTTPHeaders::DESTINATION[] = "Destination"; 70 | const char HTTPHeaders::DNT[] = "DNT"; 71 | const char HTTPHeaders::EARLY_DATA[] = "Early-Data"; 72 | const char HTTPHeaders::ETAG[] = "ETag"; 73 | const char HTTPHeaders::EXPECT[] = "Expect"; 74 | const char HTTPHeaders::EXPIRES[] = "Expires"; 75 | const char HTTPHeaders::FOLLOW_ONLY_WHEN_PRERENDER_SHOWN[] = 76 | "Follow-Only-When-Prerender-Shown"; 77 | const char HTTPHeaders::FORWARDED[] = "Forwarded"; 78 | const char HTTPHeaders::FROM[] = "From"; 79 | const char HTTPHeaders::HOST[] = "Host"; 80 | const char HTTPHeaders::HTTP2_SETTINGS[] = "HTTP2-Settings"; 81 | const char HTTPHeaders::IF[] = "If"; 82 | const char HTTPHeaders::IF_MATCH[] = "If-Match"; 83 | const char HTTPHeaders::IF_MODIFIED_SINCE[] = "If-Modified-Since"; 84 | const char HTTPHeaders::IF_NONE_MATCH[] = "If-None-Match"; 85 | const char HTTPHeaders::IF_UNMODIFIED_SINCE[] = "If-Unmodified-Since"; 86 | const char HTTPHeaders::IF_RANGE[] = "If-Range"; 87 | const char HTTPHeaders::KEEP_ALIVE[] = "Keep-Alive"; 88 | const char HTTPHeaders::LABEL[] = "Label"; 89 | const char HTTPHeaders::LAST_MODIFIED[] = "Last-Modified"; 90 | const char HTTPHeaders::LINK[] = "Link"; 91 | const char HTTPHeaders::LOCATION[] = "Location"; 92 | const char HTTPHeaders::LOCK_TOKEN[] = "Lock-Token"; 93 | const char HTTPHeaders::MAX_FORWARDS[] = "Max-Forwards"; 94 | const char HTTPHeaders::MS_AUTHOR_VIA[] = "MS-Author-Via"; 95 | const char HTTPHeaders::ORIGIN[] = "Origin"; 96 | const char HTTPHeaders::OVERWRITE_HDR[] = "Overwrite"; 97 | const char HTTPHeaders::PRAGMA[] = "Pragma"; 98 | const char HTTPHeaders::P3P[] = "P3P"; 99 | const char HTTPHeaders::PING_FROM[] = "Ping-From"; 100 | const char HTTPHeaders::PING_TO[] = "Ping-To"; 101 | const char HTTPHeaders::PROXY_CONNECTION[] = "Proxy-Connection"; 102 | const char HTTPHeaders::PROXY_AUTHENTICATE[] = "Proxy-Authenticate"; 103 | const char HTTPHeaders::PROXY_AUTHORIZATION[] = "Proxy-Authorization"; 104 | const char HTTPHeaders::PUBLIC_KEY_PINS[] = "Public-Key-Pins"; 105 | const char HTTPHeaders::PUBLIC_KEY_PINS_REPORT_ONLY[] = 106 | "Public-Key-Pins-Report-Only"; 107 | const char HTTPHeaders::RANGE[] = "Range"; 108 | const char HTTPHeaders::REFERER[] = "Referer"; 109 | const char HTTPHeaders::REFERRER_POLICY[] = "Referrer-Policy"; 110 | const char HTTPHeaders::REFERRER_POLICY_NO_REFERRER[] = "no-referrer"; 111 | const char HTTPHeaders::REFERRER_POLICY_NO_REFFERER_WHEN_DOWNGRADE[] = 112 | "no-referrer-when-downgrade"; 113 | const char HTTPHeaders::REFERRER_POLICY_SAME_ORIGIN[] = "same-origin"; 114 | const char HTTPHeaders::REFERRER_POLICY_ORIGIN[] = "origin"; 115 | const char HTTPHeaders::REFERRER_POLICY_STRICT_ORIGIN[] = "strict-origin"; 116 | const char HTTPHeaders::REFERRER_POLICY_ORIGIN_WHEN_CROSS_ORIGIN[] = 117 | "origin-when-cross-origin"; 118 | const char HTTPHeaders::REFERRER_POLICY_STRICT_ORIGIN_WHEN_CROSS_ORIGIN[] = 119 | "strict-origin-when-cross-origin"; 120 | const char HTTPHeaders::REFERRER_POLICY_UNSAFE_URL[] = "unsafe-url"; 121 | const char HTTPHeaders::REFRESH[] = "Refresh"; 122 | const char HTTPHeaders::RETRY_AFTER[] = "Retry-After"; 123 | const char HTTPHeaders::SEC_METADATA[] = "Sec-Metadata"; 124 | const char HTTPHeaders::SEC_TOKEN_BINDING[] = "Sec-Token-Binding"; 125 | const char HTTPHeaders::SEC_PROVIDED_TOKEN_BINDING_ID[] = 126 | "Sec-Provided-Token-Binding-ID"; 127 | const char HTTPHeaders::SEC_REFERRED_TOKEN_BINDING_ID[] = 128 | "Sec-Referred-Token-Binding-ID"; 129 | const char HTTPHeaders::SERVER[] = "Server"; 130 | const char HTTPHeaders::SERVER_TIMING[] = "Server-Timing"; 131 | const char HTTPHeaders::SERVICE_WORKER[] = "Service-Worker"; 132 | const char HTTPHeaders::SERVICE_WORKER_ALLOWED[] = "Service-Worker-Allowed"; 133 | const char HTTPHeaders::SERVICE_WORKER_NAVIGATION_PRELOAD[] = 134 | "Service-Worker-Navigation-Preload"; 135 | const char HTTPHeaders::SET_COOKIE[] = "Set-Cookie"; 136 | const char HTTPHeaders::SET_COOKIE2[] = "Set-Cookie2"; 137 | const char HTTPHeaders::STATUS_URI[] = "Status-URI"; 138 | const char HTTPHeaders::STRICT_TRANSPORT_SECURITY[] = 139 | "Strict-Transport-Security"; 140 | const char HTTPHeaders::TIMEOUT[] = "Timeout"; 141 | const char HTTPHeaders::TIMING_ALLOW_ORIGIN[] = "Timing-Allow-Origin"; 142 | const char HTTPHeaders::TK[] = "Tk"; 143 | const char HTTPHeaders::TRAILER[] = "Trailer"; 144 | const char HTTPHeaders::TRAILERS[] = "Trailers"; 145 | const char HTTPHeaders::TRANSFER_ENCODING[] = "Transfer-Encoding"; 146 | const char HTTPHeaders::TRANSFER_ENCODING_ABBRV[] = "TE"; 147 | const char HTTPHeaders::UPGRADE[] = "Upgrade"; 148 | const char HTTPHeaders::USER_AGENT[] = "User-Agent"; 149 | const char HTTPHeaders::VARY[] = "Vary"; 150 | const char HTTPHeaders::VIA[] = "Via"; 151 | const char HTTPHeaders::WARNING[] = "Warning"; 152 | const char HTTPHeaders::WWW_AUTHENTICATE[] = "WWW-Authenticate"; 153 | 154 | } // namespace net_http 155 | -------------------------------------------------------------------------------- /net_http/public/header_names.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef NET_HTTP_PUBLIC_HEADER_NAMES_H_ 17 | #define NET_HTTP_PUBLIC_HEADER_NAMES_H_ 18 | 19 | namespace net_http { 20 | 21 | // Standard HTTP Header Names 22 | // 23 | // http://www.iana.org/assignments/message-headers 24 | class HTTPHeaders { 25 | public: 26 | HTTPHeaders() = delete; 27 | 28 | static const char ACCEPT[]; 29 | static const char ACCEPT_CHARSET[]; 30 | static const char ACCEPT_ENCODING[]; 31 | static const char ACCEPT_LANGUAGE[]; 32 | static const char ACCEPT_RANGES[]; 33 | static const char ACCESS_CONTROL_ALLOW_CREDENTIALS[]; 34 | static const char ACCESS_CONTROL_ALLOW_HEADERS[]; 35 | static const char ACCESS_CONTROL_ALLOW_METHODS[]; 36 | static const char ACCESS_CONTROL_ALLOW_ORIGIN[]; 37 | static const char ACCESS_CONTROL_EXPOSE_HEADERS[]; 38 | static const char ACCESS_CONTROL_MAX_AGE[]; 39 | static const char ACCESS_CONTROL_REQUEST_HEADERS[]; 40 | static const char ACCESS_CONTROL_REQUEST_METHOD[]; 41 | static const char AGE[]; 42 | static const char ALLOW[]; 43 | static const char AUTHORIZATION[]; 44 | static const char CACHE_CONTROL[]; 45 | static const char CONNECTION[]; 46 | static const char CONTENT_DISPOSITION[]; 47 | static const char CONTENT_ENCODING[]; 48 | static const char CONTENT_LANGUAGE[]; 49 | static const char CONTENT_LENGTH[]; 50 | static const char CONTENT_LOCATION[]; 51 | static const char CONTENT_RANGE[]; 52 | // http://w3.org/TR/CSP/#content-security-policy-header-field 53 | static const char CONTENT_SECURITY_POLICY[]; 54 | // http://w3.org/TR/CSP/#content-security-policy-report-only-header-field 55 | static const char CONTENT_SECURITY_POLICY_REPORT_ONLY[]; 56 | // A nonstandard CSP header that was introduced for CSP v.1 57 | // https://www.w3.org/TR/2011/WD-CSP-20111129/ and used by the Firefox until 58 | // version 23 and the Internet Explorer version 10. 59 | // Please, use CONTENT_SECURITY_POLICY to pass the CSP. 60 | static const char X_CONTENT_SECURITY_POLICY[]; 61 | // A nonstandard CSP header that was introduced for CSP v.1 62 | // https://www.w3.org/TR/2011/WD-CSP-20111129/ and used by the Firefox until 63 | // version 23 and Internet Explorer version 10. 64 | // Please, use CONTENT_SECURITY_POLICY_REPORT_ONLY to pass the CSP. 65 | static const char X_CONTENT_SECURITY_POLICY_REPORT_ONLY[]; 66 | // A nonstandard CSP header that was introduced for CSP v.1 67 | // https://www.w3.org/TR/2011/WD-CSP-20111129/ and used by the Chrome until 68 | // version 25. Please, use CONTENT_SECURITY_POLICY to pass the CSP. 69 | static const char X_WEBKIT_CSP[]; 70 | // A nonstandard CSP header that was introduced for CSP v.1 71 | // https://www.w3.org/TR/2011/WD-CSP-20111129/ and used by the Chrome until 72 | // version 25. 73 | // Please, use CONTENT_SECURITY_POLICY_REPORT_ONLY to pass the CSP. 74 | static const char X_WEBKIT_CSP_REPORT_ONLY[]; 75 | static const char CONTENT_TYPE[]; 76 | static const char CONTENT_MD5[]; 77 | // A header, introduced by Microsoft, to modify how browsers 78 | // interpret Content-Type: 79 | // http://blogs.msdn.com/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx 80 | static const char X_CONTENT_TYPE_OPTIONS[]; 81 | static const char COOKIE[]; 82 | static const char COOKIE2[]; 83 | static const char DATE[]; 84 | static const char DAV[]; 85 | static const char DEPTH[]; 86 | static const char DESTINATION[]; 87 | static const char DNT[]; 88 | // https://tools.ietf.org/html/rfc8470 89 | static const char EARLY_DATA[]; 90 | static const char ETAG[]; 91 | static const char EXPECT[]; 92 | static const char EXPIRES[]; 93 | static const char FOLLOW_ONLY_WHEN_PRERENDER_SHOWN[]; 94 | // Supersedes X-Forwarded-For (https://tools.ietf.org/html/rfc7239). 95 | static const char FORWARDED[]; 96 | static const char FROM[]; 97 | static const char HOST[]; 98 | // http://httpwg.org/specs/rfc7540.html#Http2SettingsHeader 99 | static const char HTTP2_SETTINGS[]; 100 | static const char IF[]; 101 | static const char IF_MATCH[]; 102 | static const char IF_MODIFIED_SINCE[]; 103 | static const char IF_NONE_MATCH[]; 104 | static const char IF_RANGE[]; 105 | static const char IF_UNMODIFIED_SINCE[]; 106 | static const char KEEP_ALIVE[]; 107 | static const char LABEL[]; 108 | static const char LAST_MODIFIED[]; 109 | static const char LINK[]; 110 | static const char LOCATION[]; 111 | static const char LOCK_TOKEN[]; 112 | static const char MAX_FORWARDS[]; 113 | static const char MS_AUTHOR_VIA[]; 114 | static const char ORIGIN[]; 115 | static const char OVERWRITE_HDR[]; 116 | static const char P3P[]; 117 | // http://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing 118 | static const char PING_FROM[]; 119 | // http://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing 120 | static const char PING_TO[]; 121 | static const char PRAGMA[]; 122 | static const char PROXY_CONNECTION[]; 123 | static const char PROXY_AUTHENTICATE[]; 124 | static const char PROXY_AUTHORIZATION[]; 125 | // http://tools.ietf.org/html/draft-ietf-websec-key-pinning 126 | static const char PUBLIC_KEY_PINS[]; 127 | static const char PUBLIC_KEY_PINS_REPORT_ONLY[]; 128 | static const char RANGE[]; 129 | static const char REFERER[]; 130 | // https://www.w3.org/TR/referrer-policy/ 131 | static const char REFERRER_POLICY[]; 132 | static const char REFERRER_POLICY_NO_REFERRER[]; 133 | static const char REFERRER_POLICY_NO_REFFERER_WHEN_DOWNGRADE[]; 134 | static const char REFERRER_POLICY_SAME_ORIGIN[]; 135 | static const char REFERRER_POLICY_ORIGIN[]; 136 | static const char REFERRER_POLICY_STRICT_ORIGIN[]; 137 | static const char REFERRER_POLICY_ORIGIN_WHEN_CROSS_ORIGIN[]; 138 | static const char REFERRER_POLICY_STRICT_ORIGIN_WHEN_CROSS_ORIGIN[]; 139 | static const char REFERRER_POLICY_UNSAFE_URL[]; 140 | static const char REFRESH[]; 141 | static const char RETRY_AFTER[]; 142 | // https://github.com/mikewest/sec-metadata 143 | static const char SEC_METADATA[]; 144 | // https://tools.ietf.org/html/draft-ietf-tokbind-https 145 | static const char SEC_TOKEN_BINDING[]; 146 | // https://tools.ietf.org/html/draft-ietf-tokbind-ttrp 147 | static const char SEC_PROVIDED_TOKEN_BINDING_ID[]; 148 | static const char SEC_REFERRED_TOKEN_BINDING_ID[]; 149 | static const char SERVER[]; 150 | // https://www.w3.org/TR/server-timing/ 151 | static const char SERVER_TIMING[]; 152 | // https://www.w3.org/TR/service-workers/#update-algorithm 153 | static const char SERVICE_WORKER[]; 154 | static const char SERVICE_WORKER_ALLOWED[]; 155 | // https://developers.google.com/web/updates/2017/02/navigation-preload 156 | static const char SERVICE_WORKER_NAVIGATION_PRELOAD[]; 157 | static const char SET_COOKIE[]; 158 | static const char SET_COOKIE2[]; 159 | static const char STATUS_URI[]; 160 | // HSTS http://tools.ietf.org/html/draft-ietf-websec-strict-transport-sec 161 | static const char STRICT_TRANSPORT_SECURITY[]; 162 | static const char TIMEOUT[]; 163 | // http://www.w3.org/TR/2011/WD-resource-timing-20110524/#cross-origin-resources 164 | static const char TIMING_ALLOW_ORIGIN[]; 165 | // http://www.w3.org/2011/tracking-protection/drafts/tracking-dnt.html#response-header-field 166 | static const char TK[]; 167 | static const char TRAILER[]; 168 | static const char TRAILERS[]; 169 | static const char TRANSFER_ENCODING[]; 170 | static const char TRANSFER_ENCODING_ABBRV[]; 171 | static const char UPGRADE[]; 172 | static const char USER_AGENT[]; 173 | static const char VARY[]; 174 | static const char VIA[]; 175 | static const char WARNING[]; 176 | static const char WWW_AUTHENTICATE[]; 177 | }; 178 | 179 | } // namespace net_http 180 | 181 | #endif // NET_HTTP_PUBLIC_HEADER_NAMES_H_ 182 | -------------------------------------------------------------------------------- /net_http/public/response_code_enum.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #ifndef NET_HTTP_PUBLIC_RESPONSE_CODE_ENUM_H_ 17 | #define NET_HTTP_PUBLIC_RESPONSE_CODE_ENUM_H_ 18 | namespace net_http { 19 | 20 | enum class HTTPStatusCode { 21 | // These are the status codes we can give back to the client. 22 | // http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml#http-status-codes-1 23 | UNDEFINED = 0, // Illegal value for initialization 24 | FIRST_CODE = 100, 25 | 26 | // Informational 27 | CONTINUE = 100, // Continue 28 | SWITCHING = 101, // Switching Protocols 29 | PROCESSING = 102, // Processing (RFC 2518, sec 10.1) 30 | 31 | // Success 32 | OK = 200, // OK 33 | CREATED = 201, // Created 34 | ACCEPTED = 202, // Accepted 35 | PROVISIONAL = 203, // Non-Authoritative Information 36 | NO_CONTENT = 204, // No Content 37 | RESET_CONTENT = 205, // Reset Content 38 | PART_CONTENT = 206, // Partial Content 39 | MULTI_STATUS = 207, // Multi-Status (RFC 2518, sec 10.2) 40 | ALREADY_REPORTED = 208, // Already Reported (RFC 5842) 41 | IM_USED = 226, // IM Used (RFC 3229) 42 | 43 | // Redirect 44 | MULTIPLE = 300, // Multiple Choices 45 | MOVED_PERM = 301, // Moved Permanently 46 | MOVED_TEMP = 302, // Found. For historical reasons, 47 | // a user agent MAY change the method 48 | // from POST to GET for the subsequent 49 | // request (RFC 7231, sec 6.4.3). 50 | SEE_OTHER = 303, // See Other 51 | NOT_MODIFIED = 304, // Not Modified 52 | USE_PROXY = 305, // Use Proxy 53 | TEMP_REDIRECT = 307, // Similar to 302, except that user 54 | // agents MUST NOT change the request 55 | // method (RFC 7231, sec 6.4.7) 56 | RESUME_INCOMPLETE = 308, // Resume Incomplete 57 | 58 | // Client Error 59 | BAD_REQUEST = 400, // Bad Request 60 | UNAUTHORIZED = 401, // Unauthorized 61 | PAYMENT = 402, // Payment Required 62 | FORBIDDEN = 403, // Forbidden 63 | NOT_FOUND = 404, // Not Found 64 | METHOD_NA = 405, // Method Not Allowed 65 | NONE_ACC = 406, // Not Acceptable 66 | PROXY = 407, // Proxy Authentication Required 67 | REQUEST_TO = 408, // Request Time-out 68 | CONFLICT = 409, // Conflict 69 | GONE = 410, // Gone 70 | LEN_REQUIRED = 411, // Length Required 71 | PRECOND_FAILED = 412, // Precondition Failed 72 | ENTITY_TOO_BIG = 413, // Request Entity Too Large 73 | URI_TOO_BIG = 414, // Request-URI Too Large 74 | UNKNOWN_MEDIA = 415, // Unsupported Media Type 75 | BAD_RANGE = 416, // Requested range not satisfiable 76 | BAD_EXPECTATION = 417, // Expectation Failed 77 | IM_A_TEAPOT = 418, // I'm a Teapot (RFC 2324, 7168) 78 | MISDIRECTED_REQUEST = 421, // Misdirected Request (RFC 7540) 79 | UNPROC_ENTITY = 422, // Unprocessable Entity (RFC 2518, sec 10.3) 80 | LOCKED = 423, // Locked (RFC 2518, sec 10.4) 81 | FAILED_DEP = 424, // Failed Dependency (RFC 2518, sec 10.5) 82 | UPGRADE_REQUIRED = 426, // Upgrade Required (RFC 7231, sec 6.5.14) 83 | PRECOND_REQUIRED = 428, // Precondition Required (RFC 6585, sec 3) 84 | TOO_MANY_REQUESTS = 429, // Too Many Requests (RFC 6585, sec 4) 85 | HEADER_TOO_LARGE = 431, // Request Header Fields Too Large 86 | // (RFC 6585, sec 5) 87 | UNAVAILABLE_LEGAL = 451, // Unavailable For Legal Reasons (RFC 7725) 88 | CLIENT_CLOSED_REQUEST = 499, // Client Closed Request (Nginx) 89 | 90 | // Server Error 91 | ERROR = 500, // Internal Server Error 92 | NOT_IMP = 501, // Not Implemented 93 | BAD_GATEWAY = 502, // Bad Gateway 94 | SERVICE_UNAV = 503, // Service Unavailable 95 | GATEWAY_TO = 504, // Gateway Time-out 96 | BAD_VERSION = 505, // HTTP Version not supported 97 | VARIANT_NEGOTIATES = 506, // Variant Also Negotiates (RFC 2295) 98 | INSUF_STORAGE = 507, // Insufficient Storage (RFC 2518, sec 10.6) 99 | LOOP_DETECTED = 508, // Loop Detected (RFC 5842) 100 | NOT_EXTENDED = 510, // Not Extended (RFC 2774) 101 | NETAUTH_REQUIRED = 511, // Network Authentication Required 102 | // (RFC 6585, sec 6) 103 | LAST_CODE = 599, 104 | }; 105 | 106 | } // namespace net_http 107 | 108 | #endif // NET_HTTP_PUBLIC_RESPONSE_CODE_ENUM_H_ 109 | -------------------------------------------------------------------------------- /net_http/server/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/net_http/55ac2986f5d3b64b21b642b49bf29ac6797d9457/net_http/server/.DS_Store -------------------------------------------------------------------------------- /net_http/server/internal/BUILD: -------------------------------------------------------------------------------- 1 | # Description: implementations of HTTP Server 2 | 3 | package(default_visibility = [ 4 | "//net_http:__subpackages__", 5 | ]) 6 | 7 | licenses(["notice"]) 8 | 9 | cc_library( 10 | name = "evhttp_server", 11 | srcs = [ 12 | "evhttp_request.cc", 13 | "evhttp_server.cc", 14 | ], 15 | hdrs = [ 16 | "evhttp_request.h", 17 | "evhttp_server.h", 18 | "server_support.h", 19 | ], 20 | deps = [ 21 | "//net_http/compression:gzip_zlib", 22 | "//net_http/internal:net_logging", 23 | "//net_http/public:shared_files", 24 | "//net_http/server/public:http_server_api", 25 | "@com_github_libevent_libevent//:libevent", 26 | "@com_google_absl//absl/base", 27 | "@com_google_absl//absl/base:core_headers", 28 | "@com_google_absl//absl/memory", 29 | "@com_google_absl//absl/strings", 30 | "@com_google_absl//absl/synchronization", 31 | "@com_google_absl//absl/types:span", 32 | "@zlib", 33 | ], 34 | ) 35 | 36 | cc_test( 37 | name = "evhttp_server_test", 38 | size = "medium", 39 | srcs = ["evhttp_server_test.cc"], 40 | features = ["-layering_check"], 41 | deps = [ 42 | ":evhttp_server", 43 | # "//tensorflow_serving/core/test_util:test_main", 44 | "@com_google_googletest//:gtest_main", 45 | "//net_http/client/test_client/internal:evhttp_client", 46 | "//net_http/internal:fixed_thread_pool", 47 | "//net_http/server/public:http_server", 48 | "//net_http/server/public:http_server_api", 49 | "@com_google_absl//absl/memory", 50 | "@com_google_absl//absl/synchronization", 51 | ], 52 | ) 53 | 54 | cc_test( 55 | name = "evhttp_request_test", 56 | size = "medium", 57 | srcs = ["evhttp_request_test.cc"], 58 | features = ["-layering_check"], 59 | deps = [ 60 | ":evhttp_server", 61 | # "//tensorflow_serving/core/test_util:test_main", 62 | "@com_google_googletest//:gtest_main", 63 | "//net_http/client/test_client/internal:evhttp_client", 64 | "//net_http/compression:gzip_zlib", 65 | "//net_http/internal:fixed_thread_pool", 66 | "//net_http/server/public:http_server", 67 | "//net_http/server/public:http_server_api", 68 | "@com_google_absl//absl/memory", 69 | ], 70 | ) 71 | -------------------------------------------------------------------------------- /net_http/server/internal/evhttp_request.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // libevent based request implementation 17 | 18 | #include "net_http/server/internal/evhttp_request.h" 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "absl/strings/match.h" 30 | #include "absl/strings/string_view.h" 31 | #include "absl/types/span.h" 32 | #include "event2/buffer.h" 33 | #include "event2/event.h" 34 | #include "event2/http.h" 35 | #include "event2/keyvalq_struct.h" 36 | #include "net_http/compression/gzip_zlib.h" 37 | #include "net_http/internal/net_logging.h" 38 | #include "net_http/public/header_names.h" 39 | namespace net_http { 40 | 41 | ParsedEvRequest::~ParsedEvRequest() { 42 | if (decoded_uri) { 43 | evhttp_uri_free(decoded_uri); 44 | } 45 | 46 | if (request && evhttp_request_is_owned(request)) { 47 | evhttp_request_free(request); 48 | } 49 | } 50 | 51 | ParsedEvRequest::ParsedEvRequest(evhttp_request* request_in) 52 | : request(request_in) {} 53 | 54 | bool ParsedEvRequest::decode() { 55 | switch (evhttp_request_get_command(request)) { 56 | case EVHTTP_REQ_GET: 57 | method = "GET"; 58 | break; 59 | case EVHTTP_REQ_POST: 60 | method = "POST"; 61 | break; 62 | case EVHTTP_REQ_HEAD: 63 | method = "HEAD"; 64 | break; 65 | case EVHTTP_REQ_PUT: 66 | method = "PUT"; 67 | break; 68 | case EVHTTP_REQ_DELETE: 69 | method = "DELETE"; 70 | break; 71 | case EVHTTP_REQ_OPTIONS: 72 | method = "OPTIONS"; 73 | break; 74 | case EVHTTP_REQ_TRACE: 75 | method = "TRACE"; 76 | break; 77 | case EVHTTP_REQ_CONNECT: 78 | method = "CONNECT"; 79 | break; 80 | case EVHTTP_REQ_PATCH: 81 | method = "PATCH"; 82 | break; 83 | default: 84 | return false; 85 | } 86 | 87 | uri = evhttp_request_get_uri(request); 88 | 89 | decoded_uri = evhttp_uri_parse(uri); 90 | if (decoded_uri == nullptr) { 91 | return false; 92 | } 93 | 94 | // NB: need double-check "/" is OK 95 | path = evhttp_uri_get_path(decoded_uri); 96 | if (path == nullptr) { 97 | path = "/"; 98 | } 99 | 100 | path_and_query = path; 101 | const char* query = evhttp_uri_get_query(decoded_uri); 102 | if (query != nullptr) { 103 | path_and_query.push_back('?'); 104 | path_and_query.append(query); 105 | } 106 | 107 | const char* fragment = evhttp_uri_get_fragment(decoded_uri); 108 | if (fragment != nullptr) { 109 | path_and_query.push_back('#'); 110 | path_and_query.append(fragment); 111 | } 112 | 113 | headers = evhttp_request_get_input_headers(request); 114 | 115 | return true; 116 | } 117 | 118 | EvHTTPRequest::EvHTTPRequest(std::unique_ptr request, 119 | ServerSupport* server) 120 | : server_(server), 121 | parsed_request_(std::move(request)), 122 | output_buf(nullptr) {} 123 | 124 | EvHTTPRequest::~EvHTTPRequest() { 125 | if (output_buf != nullptr) { 126 | evbuffer_free(output_buf); 127 | } 128 | } 129 | 130 | absl::string_view EvHTTPRequest::uri_path() const { 131 | return parsed_request_->path_and_query; 132 | } 133 | 134 | absl::string_view EvHTTPRequest::http_method() const { 135 | return parsed_request_->method; 136 | } 137 | 138 | bool EvHTTPRequest::Initialize() { 139 | output_buf = evbuffer_new(); 140 | return output_buf != nullptr; 141 | } 142 | 143 | void EvHTTPRequest::WriteResponseBytes(const char* data, int64_t size) { 144 | assert(size >= 0); 145 | if (output_buf == nullptr) { 146 | NET_LOG(FATAL, "Request not initialized."); 147 | return; 148 | } 149 | 150 | int ret = evbuffer_add(output_buf, data, static_cast(size)); 151 | if (ret == -1) { 152 | NET_LOG(ERROR, "Failed to write %zu bytes data to output buffer", 153 | static_cast(size)); 154 | } 155 | } 156 | 157 | void EvHTTPRequest::WriteResponseString(absl::string_view data) { 158 | WriteResponseBytes(data.data(), static_cast(data.size())); 159 | } 160 | 161 | std::unique_ptr 162 | EvHTTPRequest::ReadRequestBytes(int64_t* size) { 163 | evbuffer* input_buf = 164 | evhttp_request_get_input_buffer(parsed_request_->request); 165 | if (input_buf == nullptr) { 166 | *size = 0; 167 | return nullptr; // no body 168 | } 169 | 170 | // possible a reentry after gzip uncompression 171 | if (evbuffer_get_length(input_buf) == 0) { 172 | *size = 0; 173 | return nullptr; // EOF 174 | } 175 | 176 | // Uncompress the entire body 177 | if (NeedUncompressGzipContent()) { 178 | return ReadRequestGzipBytes(input_buf, size); 179 | } 180 | 181 | auto buf_size = reinterpret_cast(size); 182 | 183 | *buf_size = evbuffer_get_contiguous_space(input_buf); 184 | assert(*buf_size > 0); 185 | 186 | char* block = std::allocator().allocate(*buf_size); 187 | int ret = evbuffer_remove(input_buf, block, *buf_size); 188 | 189 | if (ret != *buf_size) { 190 | NET_LOG(ERROR, "Unexpected: read less than specified num_bytes : %zu", 191 | *buf_size); 192 | std::allocator().deallocate(block, *buf_size); 193 | *buf_size = 0; 194 | return nullptr; // don't return corrupted buffer 195 | } 196 | 197 | return std::unique_ptr( 198 | block, ServerRequestInterface::BlockDeleter(*buf_size)); 199 | } 200 | 201 | std::unique_ptr 202 | EvHTTPRequest::ReadRequestGzipBytes(evbuffer* input_buf, int64_t* size) { 203 | std::vector> buf_list; 204 | 205 | size_t body_length = 0; 206 | while (true) { 207 | auto buf_size = evbuffer_get_contiguous_space(input_buf); 208 | 209 | if (buf_size == 0) { 210 | break; // EOF 211 | } 212 | 213 | char* block = std::allocator().allocate(buf_size); 214 | int ret = evbuffer_remove(input_buf, block, buf_size); 215 | if (ret != buf_size) { 216 | NET_LOG(ERROR, "Unexpected: read less than specified num_bytes : %zu", 217 | buf_size); 218 | std::allocator().deallocate(block, buf_size); 219 | for (auto buf : buf_list) { 220 | std::allocator().deallocate(buf.data(), buf.size()); 221 | } 222 | *size = 0; 223 | return nullptr; // don't return corrupted buffer 224 | } 225 | 226 | body_length += buf_size; 227 | buf_list.emplace_back(block, buf_size); 228 | } 229 | 230 | char* comp_body = std::allocator().allocate(body_length); 231 | 232 | size_t pos = 0; 233 | for (auto buf : buf_list) { 234 | memcpy(comp_body + pos, buf.data(), buf.size()); 235 | pos += buf.size(); 236 | std::allocator().deallocate(buf.data(), buf.size()); 237 | } 238 | 239 | char* uncomp_body; 240 | auto uncomp_size = reinterpret_cast(size); 241 | UncompressGzipBody(comp_body, body_length, 242 | reinterpret_cast(&uncomp_body), uncomp_size); 243 | 244 | std::allocator().deallocate(comp_body, body_length); 245 | 246 | if (uncomp_body != nullptr) { 247 | return std::unique_ptr( 248 | uncomp_body, ServerRequestInterface::BlockDeleter(*uncomp_size)); 249 | } else { 250 | NET_LOG(ERROR, "Failed to uncompress the gzipped body"); 251 | *uncomp_size = 0; 252 | return nullptr; 253 | } 254 | } 255 | 256 | bool EvHTTPRequest::NeedUncompressGzipContent() { 257 | if (handler_options_ != nullptr && 258 | handler_options_->auto_uncompress_input()) { 259 | auto content_encoding = GetRequestHeader(HTTPHeaders::CONTENT_ENCODING); 260 | return absl::StrContains(content_encoding, "gzip"); 261 | } 262 | 263 | return false; 264 | } 265 | 266 | void EvHTTPRequest::UncompressGzipBody(void* input, size_t input_size, 267 | void** uncompressed_input, 268 | size_t* uncompressed_input_size) { 269 | int64_t max = handler_options_->auto_uncompress_max_size() > 0 270 | ? handler_options_->auto_uncompress_max_size() 271 | : ZLib::kMaxUncompressedBytes; 272 | 273 | // our APIs don't need expose the actual content-length 274 | *uncompressed_input_size = static_cast(max); 275 | 276 | ZLib zlib; 277 | int err = zlib.UncompressGzipAndAllocate( 278 | reinterpret_cast(uncompressed_input), 279 | reinterpret_cast(uncompressed_input_size), 280 | static_cast(input), static_cast(input_size)); 281 | if (err != Z_OK) { 282 | NET_LOG(ERROR, "Got zlib error: %d", err); 283 | } 284 | } 285 | 286 | // Note: passing string_view incurs a copy of underlying std::string data 287 | // (stack) 288 | absl::string_view EvHTTPRequest::GetRequestHeader( 289 | absl::string_view header) const { 290 | std::string header_str(header.data(), header.size()); 291 | return absl::NullSafeStringView( 292 | evhttp_find_header(parsed_request_->headers, header_str.c_str())); 293 | } 294 | 295 | std::vector EvHTTPRequest::request_headers() const { 296 | auto result = std::vector(); 297 | auto ev_headers = parsed_request_->headers; 298 | 299 | for (evkeyval* header = ev_headers->tqh_first; header; 300 | header = header->next.tqe_next) { 301 | result.emplace_back(header->key); 302 | } 303 | 304 | return result; 305 | } 306 | 307 | void EvHTTPRequest::OverwriteResponseHeader(absl::string_view header, 308 | absl::string_view value) { 309 | evkeyvalq* ev_headers = 310 | evhttp_request_get_output_headers(parsed_request_->request); 311 | 312 | std::string header_str = std::string(header.data(), header.size()); 313 | const char* header_cstr = header_str.c_str(); 314 | 315 | evhttp_remove_header(ev_headers, header_cstr); 316 | evhttp_add_header(ev_headers, header_cstr, 317 | std::string(value.data(), value.size()).c_str()); 318 | } 319 | 320 | void EvHTTPRequest::AppendResponseHeader(absl::string_view header, 321 | absl::string_view value) { 322 | evkeyvalq* ev_headers = 323 | evhttp_request_get_output_headers(parsed_request_->request); 324 | 325 | int ret = evhttp_add_header(ev_headers, 326 | std::string(header.data(), header.size()).c_str(), 327 | std::string(value.data(), value.size()).c_str()); 328 | 329 | if (ret != 0) { 330 | NET_LOG(ERROR, 331 | "Unexpected: failed to set the request header" 332 | " %.*s: %.*s", 333 | static_cast(header.size()), header.data(), 334 | static_cast(value.size()), value.data()); 335 | } 336 | } 337 | 338 | void EvHTTPRequest::PartialReplyWithStatus(HTTPStatusCode status) { 339 | NET_LOG(FATAL, "PartialReplyWithStatus not implemented."); 340 | } 341 | 342 | void EvHTTPRequest::PartialReply() { 343 | NET_LOG(FATAL, "PartialReplyWithStatus not implemented."); 344 | } 345 | 346 | ServerRequestInterface::CallbackStatus 347 | EvHTTPRequest::PartialReplyWithFlushCallback(std::function callback) { 348 | NET_LOG(FATAL, "PartialReplyWithStatus not implemented."); 349 | return CallbackStatus::NOT_SCHEDULED; 350 | } 351 | 352 | void EvHTTPRequest::ReplyWithStatus(HTTPStatusCode status) { 353 | server_->ScheduleReply([this, status]() { EvSendReply(status); }); 354 | } 355 | 356 | void EvHTTPRequest::EvSendReply(HTTPStatusCode status) { 357 | evhttp_send_reply(parsed_request_->request, static_cast(status), nullptr, 358 | output_buf); 359 | server_->DecOps(); 360 | delete this; 361 | } 362 | 363 | void EvHTTPRequest::Reply() { ReplyWithStatus(HTTPStatusCode::OK); } 364 | 365 | // Treats this as 500 for now and let libevent decide what to do 366 | // with the connection. 367 | void EvHTTPRequest::Abort() { 368 | evhttp_send_error(parsed_request_->request, HTTP_INTERNAL, nullptr); 369 | server_->DecOps(); 370 | delete this; 371 | } 372 | 373 | } // namespace net_http 374 | -------------------------------------------------------------------------------- /net_http/server/internal/evhttp_request.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // libevent based request implementation 17 | 18 | #ifndef NET_HTTP_SERVER_INTERNAL_EVHTTP_REQUEST_H_ 19 | #define NET_HTTP_SERVER_INTERNAL_EVHTTP_REQUEST_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "net_http/server/internal/server_support.h" 26 | #include "net_http/server/public/httpserver_interface.h" 27 | #include "net_http/server/public/server_request_interface.h" 28 | 29 | struct evbuffer; 30 | struct evhttp_request; 31 | struct evhttp_uri; 32 | struct evkeyvalq; 33 | 34 | namespace net_http { 35 | 36 | // Headers only 37 | struct ParsedEvRequest { 38 | public: 39 | // Doesn't take the ownership 40 | explicit ParsedEvRequest(evhttp_request* request_in); 41 | ~ParsedEvRequest(); 42 | 43 | // Decode and cache the result; or return false if any parsing error 44 | bool decode(); 45 | 46 | evhttp_request* request; // raw request 47 | 48 | const char* method; // from enum 49 | 50 | const char* uri = nullptr; // from raw request 51 | evhttp_uri* decoded_uri = nullptr; // owned by this 52 | 53 | // TODO(wenboz): do we need escaped path for dispatching requests? 54 | // evhttp_uridecode(path) 55 | const char* path = nullptr; // owned by uri 56 | std::string path_and_query; 57 | 58 | evkeyvalq* headers = nullptr; // owned by raw request 59 | }; 60 | 61 | // Thread-compatible. See ServerRequestInterface on the exact contract 62 | // between the server runtime and application handlers. 63 | class EvHTTPRequest final : public ServerRequestInterface { 64 | public: 65 | virtual ~EvHTTPRequest(); 66 | 67 | EvHTTPRequest(const EvHTTPRequest& other) = delete; 68 | EvHTTPRequest& operator=(const EvHTTPRequest& other) = delete; 69 | 70 | // Doesn't own the server 71 | EvHTTPRequest(std::unique_ptr request, 72 | ServerSupport* server); 73 | 74 | absl::string_view uri_path() const override; 75 | 76 | absl::string_view http_method() const override; 77 | 78 | void WriteResponseBytes(const char* data, int64_t size) override; 79 | 80 | void WriteResponseString(absl::string_view data) override; 81 | 82 | std::unique_ptr 83 | ReadRequestBytes(int64_t* size) override; 84 | 85 | absl::string_view GetRequestHeader(absl::string_view header) const override; 86 | 87 | std::vector request_headers() const override; 88 | 89 | void OverwriteResponseHeader(absl::string_view header, 90 | absl::string_view value) override; 91 | void AppendResponseHeader(absl::string_view header, 92 | absl::string_view value) override; 93 | 94 | void PartialReplyWithStatus(HTTPStatusCode status) override; 95 | void PartialReply() override; 96 | 97 | CallbackStatus PartialReplyWithFlushCallback( 98 | std::function callback) override; 99 | 100 | void ReplyWithStatus(HTTPStatusCode status) override; 101 | void Reply() override; 102 | 103 | void Abort() override; 104 | 105 | // Initializes the resource and returns false if any error. 106 | bool Initialize(); 107 | 108 | // Keeps a reference to the registered RequestHandlerOptions 109 | void SetHandlerOptions(const RequestHandlerOptions& handler_options) { 110 | this->handler_options_ = &handler_options; 111 | } 112 | 113 | private: 114 | void EvSendReply(HTTPStatusCode status); 115 | 116 | // Returns true if the data needs be uncompressed 117 | bool NeedUncompressGzipContent(); 118 | 119 | // Must set uncompressed_input to nullptr if uncompression is failed 120 | void UncompressGzipBody(void* input, size_t input_size, 121 | void** uncompressed_input, 122 | size_t* uncompressed_input_size); 123 | 124 | std::unique_ptr 125 | ReadRequestGzipBytes(evbuffer* input_buf, int64_t* size); 126 | 127 | ServerSupport* server_; 128 | 129 | const RequestHandlerOptions* handler_options_; 130 | 131 | std::unique_ptr parsed_request_; 132 | 133 | evbuffer* output_buf; // owned by this 134 | }; 135 | 136 | } // namespace net_http 137 | 138 | #endif // NET_HTTP_SERVER_INTERNAL_EVHTTP_REQUEST_H_ 139 | -------------------------------------------------------------------------------- /net_http/server/internal/evhttp_request_test.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include "absl/memory/memory.h" 22 | #include "net_http/client/test_client/internal/evhttp_connection.h" 23 | #include "net_http/compression/gzip_zlib.h" 24 | #include "net_http/internal/fixed_thread_pool.h" 25 | #include "net_http/public/response_code_enum.h" 26 | #include "net_http/server/internal/evhttp_server.h" 27 | #include "net_http/server/public/httpserver.h" 28 | #include "net_http/server/public/httpserver_interface.h" 29 | #include "net_http/server/public/server_request_interface.h" 30 | 31 | namespace net_http { 32 | namespace { 33 | 34 | class MyExecutor final : public EventExecutor { 35 | public: 36 | explicit MyExecutor(int num_threads) : thread_pool_(num_threads) {} 37 | 38 | void Schedule(std::function fn) override { 39 | thread_pool_.Schedule(fn); 40 | } 41 | 42 | private: 43 | FixedThreadPool thread_pool_; 44 | }; 45 | 46 | class EvHTTPRequestTest : public ::testing::Test { 47 | public: 48 | void SetUp() override { InitServer(); } 49 | 50 | void TearDown() override { 51 | if (!server->is_terminating()) { 52 | server->Terminate(); 53 | server->WaitForTermination(); 54 | } 55 | } 56 | 57 | protected: 58 | std::unique_ptr server; 59 | 60 | private: 61 | void InitServer() { 62 | auto options = absl::make_unique(); 63 | options->AddPort(0); 64 | options->SetExecutor(absl::make_unique(4)); 65 | 66 | server = CreateEvHTTPServer(std::move(options)); 67 | 68 | ASSERT_TRUE(server != nullptr); 69 | } 70 | }; 71 | 72 | // Test basic GET with 404 73 | TEST_F(EvHTTPRequestTest, SimpleGETNotFound) { 74 | server->StartAcceptingRequests(); 75 | 76 | auto connection = 77 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 78 | ASSERT_TRUE(connection != nullptr); 79 | 80 | TestClientRequest request = {"/noop", "GET", {}, ""}; 81 | TestClientResponse response = {}; 82 | 83 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 84 | EXPECT_EQ(response.status, HTTPStatusCode::NOT_FOUND); 85 | EXPECT_FALSE(response.body.empty()); 86 | 87 | server->Terminate(); 88 | server->WaitForTermination(); 89 | } 90 | 91 | // Test basic GET with 200 92 | TEST_F(EvHTTPRequestTest, SimpleGETOK) { 93 | auto handler = [](ServerRequestInterface* request) { 94 | request->WriteResponseString("OK"); 95 | request->Reply(); 96 | }; 97 | server->RegisterRequestHandler("/ok", std::move(handler), 98 | RequestHandlerOptions()); 99 | server->StartAcceptingRequests(); 100 | 101 | auto connection = 102 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 103 | ASSERT_TRUE(connection != nullptr); 104 | 105 | TestClientRequest request = {"/ok", "GET", {}, ""}; 106 | TestClientResponse response = {}; 107 | 108 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 109 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 110 | EXPECT_EQ(response.body, "OK"); 111 | 112 | server->Terminate(); 113 | server->WaitForTermination(); 114 | } 115 | 116 | // Test basic POST with 200 117 | TEST_F(EvHTTPRequestTest, SimplePOST) { 118 | auto handler = [](ServerRequestInterface* request) { 119 | int64_t num_bytes; 120 | auto request_chunk = request->ReadRequestBytes(&num_bytes); 121 | while (request_chunk != nullptr) { 122 | request->WriteResponseBytes(request_chunk.get(), num_bytes); 123 | request_chunk = request->ReadRequestBytes(&num_bytes); 124 | } 125 | request->Reply(); 126 | }; 127 | server->RegisterRequestHandler("/ok", std::move(handler), 128 | RequestHandlerOptions()); 129 | server->StartAcceptingRequests(); 130 | 131 | auto connection = 132 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 133 | ASSERT_TRUE(connection != nullptr); 134 | 135 | TestClientRequest request = {"/ok", "POST", {}, "abcde"}; 136 | TestClientResponse response = {}; 137 | 138 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 139 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 140 | EXPECT_EQ(response.body, "abcde"); 141 | 142 | server->Terminate(); 143 | server->WaitForTermination(); 144 | } 145 | 146 | // Test request's uri_path() method. 147 | TEST_F(EvHTTPRequestTest, RequestUri) { 148 | static const char* const kUriPath[] = { 149 | "/", 150 | "/path", 151 | "/path/", 152 | "/path?query=value", 153 | "/path#fragment", 154 | "/path?param=value#fragment", 155 | "/path?param=value%20value", 156 | }; 157 | 158 | int counter = 0; 159 | auto handler = [&counter](ServerRequestInterface* request) { 160 | EXPECT_EQ(kUriPath[counter++], request->uri_path()); 161 | request->Reply(); 162 | }; 163 | server->RegisterRequestDispatcher( 164 | [&handler](ServerRequestInterface* request) -> RequestHandler { 165 | return handler; 166 | }, 167 | RequestHandlerOptions()); 168 | 169 | server->StartAcceptingRequests(); 170 | 171 | auto connection = 172 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 173 | ASSERT_TRUE(connection != nullptr); 174 | 175 | for (const char* path : kUriPath) { 176 | TestClientRequest request = {path, "GET", {}, ""}; 177 | TestClientResponse response = {}; 178 | 179 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 180 | } 181 | 182 | server->Terminate(); 183 | server->WaitForTermination(); 184 | } 185 | 186 | // Test request headers 187 | TEST_F(EvHTTPRequestTest, RequestHeaders) { 188 | auto handler = [](ServerRequestInterface* request) { 189 | EXPECT_GT(request->request_headers().size(), 2); 190 | EXPECT_EQ(request->GetRequestHeader("H1"), "v1"); 191 | EXPECT_EQ(request->GetRequestHeader("h1"), "v1"); 192 | EXPECT_EQ(request->GetRequestHeader("H2"), "v2"); 193 | request->WriteResponseString("OK"); 194 | request->Reply(); 195 | }; 196 | server->RegisterRequestHandler("/ok", std::move(handler), 197 | RequestHandlerOptions()); 198 | server->StartAcceptingRequests(); 199 | 200 | auto connection = 201 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 202 | ASSERT_TRUE(connection != nullptr); 203 | 204 | TestClientRequest request = {"/ok", 205 | "GET", 206 | {TestClientRequest::HeaderKeyValue("H1", "v1"), 207 | TestClientRequest::HeaderKeyValue("H2", "v2")}, 208 | ""}; 209 | TestClientResponse response = {}; 210 | 211 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 212 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 213 | EXPECT_EQ(response.body, "OK"); 214 | 215 | server->Terminate(); 216 | server->WaitForTermination(); 217 | } 218 | 219 | // Test response headers 220 | TEST_F(EvHTTPRequestTest, ResponseHeaders) { 221 | auto handler = [](ServerRequestInterface* request) { 222 | request->AppendResponseHeader("H1", "V1"); 223 | request->AppendResponseHeader("H2", "V2"); 224 | request->OverwriteResponseHeader("h2", "v2"); 225 | request->WriteResponseString("OK"); 226 | request->Reply(); 227 | }; 228 | server->RegisterRequestHandler("/ok", std::move(handler), 229 | RequestHandlerOptions()); 230 | server->StartAcceptingRequests(); 231 | 232 | auto connection = 233 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 234 | ASSERT_TRUE(connection != nullptr); 235 | 236 | TestClientRequest request = {"/ok", "GET", {}, ""}; 237 | TestClientResponse response = {}; 238 | 239 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 240 | for (auto keyvalue : response.headers) { 241 | if (keyvalue.first == "H1") { 242 | EXPECT_EQ(keyvalue.second, "V1"); 243 | } else if (keyvalue.first == "H2") { 244 | FAIL() << "H2 should have been overwritten by h2"; 245 | } else if (keyvalue.first == "h2") { 246 | EXPECT_EQ(keyvalue.second, "v2"); 247 | } 248 | } 249 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 250 | EXPECT_EQ(response.body, "OK"); 251 | 252 | server->Terminate(); 253 | server->WaitForTermination(); 254 | } 255 | 256 | // === gzip support ==== 257 | 258 | // Test invalid gzip body 259 | TEST_F(EvHTTPRequestTest, InvalidGzipPost) { 260 | auto handler = [](ServerRequestInterface* request) { 261 | int64_t num_bytes; 262 | auto request_body = request->ReadRequestBytes(&num_bytes); 263 | EXPECT_TRUE(request_body == nullptr); 264 | EXPECT_EQ(0, num_bytes); 265 | 266 | request->Reply(); 267 | }; 268 | server->RegisterRequestHandler("/ok", std::move(handler), 269 | RequestHandlerOptions()); 270 | server->StartAcceptingRequests(); 271 | 272 | auto connection = 273 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 274 | ASSERT_TRUE(connection != nullptr); 275 | 276 | TestClientRequest request = {"/ok", "POST", {}, "abcde"}; 277 | request.headers.emplace_back("Content-Encoding", "my_gzip"); 278 | TestClientResponse response = {}; 279 | 280 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 281 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 282 | 283 | server->Terminate(); 284 | server->WaitForTermination(); 285 | } 286 | 287 | // Test disabled gzip 288 | TEST_F(EvHTTPRequestTest, DisableGzipPost) { 289 | auto handler = [](ServerRequestInterface* request) { 290 | int64_t num_bytes; 291 | auto request_body = request->ReadRequestBytes(&num_bytes); 292 | EXPECT_EQ(5, num_bytes); 293 | 294 | request->Reply(); 295 | }; 296 | RequestHandlerOptions options; 297 | options.set_auto_uncompress_input(false); 298 | server->RegisterRequestHandler("/ok", std::move(handler), options); 299 | server->StartAcceptingRequests(); 300 | 301 | auto connection = 302 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 303 | ASSERT_TRUE(connection != nullptr); 304 | 305 | TestClientRequest request = {"/ok", "POST", {}, "abcde"}; 306 | request.headers.emplace_back("Content-Encoding", "my_gzip"); 307 | TestClientResponse response = {}; 308 | 309 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 310 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 311 | 312 | server->Terminate(); 313 | server->WaitForTermination(); 314 | } 315 | 316 | std::string CompressLargeString(const char* data, size_t size, 317 | size_t buf_size) { 318 | ZLib zlib; 319 | std::string buf(buf_size, '\0'); 320 | size_t compressed_size = buf.size(); 321 | zlib.Compress((Bytef*)buf.data(), &compressed_size, (Bytef*)data, size); 322 | 323 | return std::string(buf.data(), compressed_size); 324 | } 325 | 326 | std::string CompressString(const char* data, size_t size) { 327 | return CompressLargeString(data, size, 1024); 328 | } 329 | 330 | // Test valid gzip body 331 | TEST_F(EvHTTPRequestTest, ValidGzipPost) { 332 | constexpr char kBody[] = "abcdefg12345"; 333 | std::string compressed = CompressString(kBody, sizeof(kBody) - 1); 334 | 335 | auto handler = [&](ServerRequestInterface* request) { 336 | int64_t num_bytes; 337 | auto request_body = request->ReadRequestBytes(&num_bytes); 338 | 339 | std::string body_str(request_body.get(), static_cast(num_bytes)); 340 | EXPECT_EQ(body_str, std::string(kBody)); 341 | EXPECT_EQ(sizeof(kBody) - 1, num_bytes); 342 | 343 | EXPECT_EQ(nullptr, request->ReadRequestBytes(&num_bytes)); 344 | EXPECT_EQ(0, num_bytes); 345 | 346 | request->Reply(); 347 | }; 348 | server->RegisterRequestHandler("/ok", std::move(handler), 349 | RequestHandlerOptions()); 350 | server->StartAcceptingRequests(); 351 | 352 | auto connection = 353 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 354 | ASSERT_TRUE(connection != nullptr); 355 | 356 | TestClientRequest request = {"/ok", "POST", {}, compressed}; 357 | request.headers.emplace_back("Content-Encoding", "my_gzip"); 358 | TestClientResponse response = {}; 359 | 360 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 361 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 362 | 363 | server->Terminate(); 364 | server->WaitForTermination(); 365 | } 366 | 367 | // Test gzip exceeding the max uncompressed limit 368 | TEST_F(EvHTTPRequestTest, GzipExceedingLimit) { 369 | constexpr char kBody[] = "abcdefg12345"; 370 | constexpr int bodySize = sizeof(kBody) - 1; 371 | std::string compressed = CompressString(kBody, static_cast(bodySize)); 372 | 373 | auto handler = [&](ServerRequestInterface* request) { 374 | int64_t num_bytes; 375 | auto request_body = request->ReadRequestBytes(&num_bytes); 376 | 377 | std::string body_str(request_body.get(), static_cast(num_bytes)); 378 | EXPECT_TRUE(request_body == nullptr); 379 | EXPECT_EQ(0, num_bytes); 380 | 381 | request->Reply(); 382 | }; 383 | 384 | RequestHandlerOptions options; 385 | options.set_auto_uncompress_max_size(bodySize - 1); // not enough buffer 386 | server->RegisterRequestHandler("/ok", std::move(handler), options); 387 | server->StartAcceptingRequests(); 388 | 389 | auto connection = 390 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 391 | ASSERT_TRUE(connection != nullptr); 392 | 393 | TestClientRequest request = {"/ok", "POST", {}, compressed}; 394 | request.headers.emplace_back("Content-Encoding", "my_gzip"); 395 | TestClientResponse response = {}; 396 | 397 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 398 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 399 | 400 | server->Terminate(); 401 | server->WaitForTermination(); 402 | } 403 | 404 | std::string MakeRandomString(int64_t len) { 405 | std::random_device rd; 406 | std::mt19937_64 gen(rd()); 407 | std::uniform_int_distribution<> dis('a', 'z'); 408 | std::string s(len, '0'); 409 | for (char& c : s) { 410 | c = dis(gen); 411 | } 412 | return s; 413 | } 414 | 415 | // Test large gzip body 416 | TEST_F(EvHTTPRequestTest, LargeGzipPost) { 417 | constexpr int64_t uncompress_len = 1024 * 1024; 418 | std::string uncompressed = MakeRandomString(uncompress_len); 419 | std::string compressed = CompressLargeString( 420 | uncompressed.data(), uncompressed.size(), 2 * uncompress_len); 421 | 422 | auto handler = [&](ServerRequestInterface* request) { 423 | int64_t num_bytes; 424 | auto request_body = request->ReadRequestBytes(&num_bytes); 425 | 426 | std::string body_str(request_body.get(), static_cast(num_bytes)); 427 | EXPECT_EQ(body_str, uncompressed); 428 | EXPECT_EQ(uncompressed.size(), num_bytes); 429 | 430 | EXPECT_EQ(nullptr, request->ReadRequestBytes(&num_bytes)); 431 | EXPECT_EQ(0, num_bytes); 432 | 433 | request->Reply(); 434 | }; 435 | server->RegisterRequestHandler("/ok", std::move(handler), 436 | RequestHandlerOptions()); 437 | server->StartAcceptingRequests(); 438 | 439 | auto connection = 440 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 441 | ASSERT_TRUE(connection != nullptr); 442 | 443 | TestClientRequest request = {"/ok", "POST", {}, compressed}; 444 | request.headers.emplace_back("Content-Encoding", "my_gzip"); 445 | TestClientResponse response = {}; 446 | 447 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 448 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 449 | 450 | server->Terminate(); 451 | server->WaitForTermination(); 452 | } 453 | 454 | } // namespace 455 | } // namespace net_http 456 | -------------------------------------------------------------------------------- /net_http/server/internal/evhttp_server.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // libevent based server implementation 17 | 18 | #include "net_http/server/internal/evhttp_server.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "absl/base/call_once.h" 29 | #include "absl/memory/memory.h" 30 | #include "event2/event.h" 31 | #include "event2/http.h" 32 | #include "event2/thread.h" 33 | #include "event2/util.h" 34 | #include "net_http/internal/net_logging.h" 35 | 36 | namespace net_http { 37 | 38 | namespace { 39 | 40 | absl::once_flag libevent_init_once; 41 | 42 | void InitLibEvent() { 43 | if (evthread_use_pthreads() != 0) { 44 | NET_LOG(FATAL, "Server requires pthread support."); 45 | } 46 | // Ignore SIGPIPE and allow errors to propagate through error codes. 47 | signal(SIGPIPE, SIG_IGN); 48 | // TODO(wenboz): windows support needed? 49 | } 50 | 51 | void GlobalInitialize() { absl::call_once(libevent_init_once, &InitLibEvent); } 52 | 53 | } // namespace 54 | 55 | EvHTTPServer::EvHTTPServer(std::unique_ptr options) 56 | : server_options_(std::move(options)), accepting_requests_() {} 57 | 58 | // May crash the server if called before WaitForTermination() returns 59 | EvHTTPServer::~EvHTTPServer() { 60 | if (!is_terminating()) { 61 | NET_LOG(ERROR, "Server has not been terminated. Force termination now."); 62 | Terminate(); 63 | } 64 | 65 | if (ev_http_ != nullptr) { 66 | // this frees the socket handlers too 67 | evhttp_free(ev_http_); 68 | } 69 | 70 | if (ev_base_ != nullptr) { 71 | event_base_free(ev_base_); 72 | } 73 | } 74 | 75 | // Checks options. 76 | // TODO(wenboz): support multiple ports 77 | bool EvHTTPServer::Initialize() { 78 | if (server_options_->executor() == nullptr) { 79 | NET_LOG(FATAL, "Default EventExecutor is not configured."); 80 | return false; 81 | } 82 | 83 | if (server_options_->ports().empty()) { 84 | NET_LOG(FATAL, "Server port is not specified."); 85 | return false; 86 | } 87 | 88 | GlobalInitialize(); 89 | 90 | // This ev_base_ created per-server v.s. global 91 | ev_base_ = event_base_new(); 92 | if (ev_base_ == nullptr) { 93 | NET_LOG(FATAL, "Failed to create an event_base."); 94 | return false; 95 | } 96 | 97 | timeval tv_zero = {0, 0}; 98 | immediate_ = event_base_init_common_timeout(ev_base_, &tv_zero); 99 | 100 | ev_http_ = evhttp_new(ev_base_); 101 | if (ev_http_ == nullptr) { 102 | NET_LOG(FATAL, "Failed to create evhttp."); 103 | return false; 104 | } 105 | 106 | // By default libevents only allow GET, POST, HEAD, PUT, DELETE request 107 | // we have to manually turn OPTIONS and PATCH flag on documentation: 108 | // (http://www.wangafu.net/~nickm/libevent-2.0/doxygen/html/http_8h.html) 109 | evhttp_set_allowed_methods( 110 | ev_http_, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_HEAD | 111 | EVHTTP_REQ_PUT | EVHTTP_REQ_DELETE | EVHTTP_REQ_OPTIONS | 112 | EVHTTP_REQ_PATCH); 113 | evhttp_set_gencb(ev_http_, &DispatchEvRequestFn, this); 114 | 115 | return true; 116 | } 117 | 118 | // static function pointer 119 | void EvHTTPServer::DispatchEvRequestFn(evhttp_request* req, void* server) { 120 | EvHTTPServer* http_server = static_cast(server); 121 | http_server->DispatchEvRequest(req); 122 | } 123 | 124 | void EvHTTPServer::DispatchEvRequest(evhttp_request* req) { 125 | auto parsed_request = absl::make_unique(req); 126 | 127 | if (!parsed_request->decode()) { 128 | evhttp_send_error(req, HTTP_BADREQUEST, nullptr); 129 | return; 130 | } 131 | 132 | std::string path(parsed_request->path); 133 | 134 | bool dispatched = false; 135 | std::unique_ptr ev_request( 136 | new EvHTTPRequest(std::move(parsed_request), this)); 137 | 138 | if (!ev_request->Initialize()) { 139 | evhttp_send_error(req, HTTP_SERVUNAVAIL, nullptr); 140 | return; 141 | } 142 | 143 | { 144 | absl::MutexLock l(&request_mu_); 145 | 146 | auto handler_map_it = uri_handlers_.find(path); 147 | if (handler_map_it != uri_handlers_.end()) { 148 | ev_request->SetHandlerOptions(handler_map_it->second.options); 149 | IncOps(); 150 | dispatched = true; 151 | ScheduleHandlerReference(handler_map_it->second.handler, 152 | ev_request.release()); 153 | } 154 | 155 | if (!dispatched) { 156 | for (const auto& dispatcher : dispatchers_) { 157 | auto handler = dispatcher.dispatcher(ev_request.get()); 158 | if (handler == nullptr) { 159 | continue; 160 | } 161 | ev_request->SetHandlerOptions(dispatcher.options); 162 | IncOps(); 163 | dispatched = true; 164 | ScheduleHandler(std::move(handler), ev_request.release()); 165 | break; 166 | } 167 | } 168 | } 169 | 170 | if (!dispatched) { 171 | evhttp_send_error(req, HTTP_NOTFOUND, nullptr); 172 | return; 173 | } 174 | } 175 | 176 | void EvHTTPServer::ScheduleHandlerReference(const RequestHandler& handler, 177 | EvHTTPRequest* ev_request) { 178 | server_options_->executor()->Schedule( 179 | [&handler, ev_request]() { handler(ev_request); }); 180 | } 181 | 182 | // Exactly one copy of the handler argument 183 | // with the lambda passed by value to Schedule() 184 | void EvHTTPServer::ScheduleHandler(RequestHandler&& handler, 185 | EvHTTPRequest* ev_request) { 186 | server_options_->executor()->Schedule( 187 | [handler, ev_request]() { handler(ev_request); }); 188 | } 189 | 190 | namespace { 191 | 192 | void ResolveEphemeralPort(evhttp_bound_socket* listener, int* port) { 193 | sockaddr_storage ss = {}; 194 | ev_socklen_t socklen = sizeof(ss); 195 | 196 | evutil_socket_t fd = evhttp_bound_socket_get_fd(listener); 197 | if (getsockname(fd, reinterpret_cast(&ss), &socklen)) { 198 | NET_LOG(ERROR, "getsockname() failed"); 199 | return; 200 | } 201 | 202 | if (ss.ss_family == AF_INET) { 203 | *port = ntohs((reinterpret_cast(&ss))->sin_port); 204 | } else if (ss.ss_family == AF_INET6) { 205 | *port = ntohs((reinterpret_cast(&ss))->sin6_port); 206 | } else { 207 | NET_LOG(ERROR, "Unknown address family %d", ss.ss_family); 208 | } 209 | } 210 | 211 | } // namespace 212 | 213 | bool EvHTTPServer::StartAcceptingRequests() { 214 | if (ev_http_ == nullptr) { 215 | NET_LOG(FATAL, "Server has not been successfully initialized"); 216 | return false; 217 | } 218 | 219 | const int port = server_options_->ports().front(); 220 | 221 | // "::" => in6addr_any 222 | ev_uint16_t ev_port = static_cast(port); 223 | ev_listener_ = evhttp_bind_socket_with_handle(ev_http_, "::", ev_port); 224 | if (ev_listener_ == nullptr) { 225 | // in case ipv6 is not supported, fallback to inaddr_any 226 | ev_listener_ = evhttp_bind_socket_with_handle(ev_http_, nullptr, ev_port); 227 | if (ev_listener_ == nullptr) { 228 | NET_LOG(ERROR, "Couldn't bind to port %d", port); 229 | return false; 230 | } 231 | } 232 | 233 | // Listener counts as an active operation 234 | IncOps(); 235 | 236 | port_ = port; 237 | if (port_ == 0) { 238 | ResolveEphemeralPort(ev_listener_, &port_); 239 | } 240 | 241 | IncOps(); 242 | server_options_->executor()->Schedule([this]() { 243 | NET_LOG(INFO, "Entering the event loop ..."); 244 | int result = event_base_dispatch(ev_base_); 245 | NET_LOG(INFO, "event_base_dispatch() exits with value %d", result); 246 | 247 | DecOps(); 248 | }); 249 | 250 | accepting_requests_.Notify(); 251 | 252 | return true; 253 | } 254 | 255 | int EvHTTPServer::listen_port() const { return port_; } 256 | 257 | bool EvHTTPServer::is_accepting_requests() const { 258 | return accepting_requests_.HasBeenNotified(); 259 | } 260 | 261 | void EvHTTPServer::Terminate() { 262 | if (!is_accepting_requests()) { 263 | NET_LOG(ERROR, "Server is not running ..."); 264 | return; 265 | } 266 | 267 | if (is_terminating()) { 268 | NET_LOG(ERROR, "Server is already being terminated ..."); 269 | return; 270 | } 271 | 272 | terminating_.Notify(); 273 | 274 | // call exit-loop from the event loop 275 | this->EventLoopSchedule([this]() { 276 | // Stop the listener first, which will delete ev_listener_ 277 | // This may cause the loop to exit, so need be scheduled from within 278 | evhttp_del_accept_socket(ev_http_, ev_listener_); 279 | DecOps(); 280 | }); 281 | 282 | // Current shut-down behavior: 283 | // - we don't proactively delete/close any HTTP connections as part of 284 | // Terminate(). This is not an issue as we don't support read-streaming yet. 285 | // - we don't wait for all dispatched requests to run to completion 286 | // before we stop the event loop. 287 | // - and otherwise, this is meant to be a clean shutdown 288 | } 289 | 290 | bool EvHTTPServer::is_terminating() const { 291 | return terminating_.HasBeenNotified(); 292 | } 293 | 294 | void EvHTTPServer::IncOps() { 295 | absl::MutexLock l(&ops_mu_); 296 | num_pending_ops_++; 297 | } 298 | 299 | void EvHTTPServer::DecOps() { 300 | absl::MutexLock l(&ops_mu_); 301 | num_pending_ops_--; 302 | } 303 | 304 | void EvHTTPServer::WaitForTermination() { 305 | { 306 | absl::MutexLock l(&ops_mu_); 307 | ops_mu_.Await(absl::Condition( 308 | +[](int64_t* count) { return *count <= 1; }, &num_pending_ops_)); 309 | } 310 | 311 | int result = event_base_loopexit(ev_base_, nullptr); 312 | NET_LOG(INFO, "event_base_loopexit() exits with value %d", result); 313 | 314 | { 315 | absl::MutexLock l(&ops_mu_); 316 | ops_mu_.Await(absl::Condition( 317 | +[](int64_t* count) { return *count == 0; }, &num_pending_ops_)); 318 | } 319 | } 320 | 321 | bool EvHTTPServer::WaitForTerminationWithTimeout(absl::Duration timeout) { 322 | bool wait_result = true; 323 | 324 | { 325 | absl::MutexLock l(&ops_mu_); 326 | wait_result = ops_mu_.AwaitWithTimeout( 327 | absl::Condition( 328 | +[](int64_t* count) { return *count <= 1; }, &num_pending_ops_), 329 | timeout); 330 | } 331 | 332 | if (wait_result) { 333 | int result = event_base_loopexit(ev_base_, nullptr); 334 | NET_LOG(INFO, "event_base_loopexit() exits with value %d", result); 335 | 336 | // This should pass immediately 337 | { 338 | absl::MutexLock l(&ops_mu_); 339 | wait_result = ops_mu_.AwaitWithTimeout( 340 | absl::Condition( 341 | +[](int64_t* count) { return *count == 0; }, &num_pending_ops_), 342 | timeout); 343 | } 344 | } 345 | 346 | return wait_result; 347 | } 348 | 349 | EvHTTPServer::UriHandlerInfo::UriHandlerInfo( 350 | absl::string_view uri_in, RequestHandler handler_in, 351 | const RequestHandlerOptions& options_in) 352 | : uri(uri_in.data(), uri_in.size()), 353 | handler(std::move(handler_in)), 354 | options(options_in) {} 355 | 356 | EvHTTPServer::DispatcherInfo::DispatcherInfo( 357 | RequestDispatcher dispatcher_in, const RequestHandlerOptions& options_in) 358 | : dispatcher(std::move(dispatcher_in)), options(options_in) {} 359 | 360 | void EvHTTPServer::RegisterRequestHandler( 361 | absl::string_view uri, RequestHandler handler, 362 | const RequestHandlerOptions& options) { 363 | absl::MutexLock l(&request_mu_); 364 | auto result = uri_handlers_.emplace( 365 | std::piecewise_construct, std::forward_as_tuple(uri), 366 | std::forward_as_tuple(uri, handler, options)); 367 | 368 | if (!result.second) { 369 | NET_LOG(INFO, 370 | "Overwrite the existing handler registered under " 371 | "the URI path %.*s", 372 | static_cast(uri.size()), uri.data()); 373 | 374 | uri_handlers_.erase(result.first); 375 | if (!uri_handlers_ 376 | .emplace(std::piecewise_construct, std::forward_as_tuple(uri), 377 | std::forward_as_tuple(uri, handler, options)) 378 | .second) { 379 | NET_LOG(ERROR, "Failed to register an handler under the URI path %.*s", 380 | static_cast(uri.size()), uri.data()); 381 | } 382 | } 383 | } 384 | 385 | void EvHTTPServer::RegisterRequestDispatcher( 386 | RequestDispatcher dispatcher, const RequestHandlerOptions& options) { 387 | absl::MutexLock l(&request_mu_); 388 | dispatchers_.emplace_back(dispatcher, options); 389 | } 390 | 391 | namespace { 392 | 393 | void EvImmediateCallback(evutil_socket_t socket, int16_t flags, void* arg) { 394 | auto fn = static_cast*>(arg); 395 | (*fn)(); 396 | delete fn; 397 | } 398 | 399 | } // namespace 400 | 401 | bool EvHTTPServer::EventLoopSchedule(std::function fn) { 402 | auto scheduled_fn = new std::function(std::move(fn)); 403 | int result = event_base_once(ev_base_, -1, EV_TIMEOUT, EvImmediateCallback, 404 | static_cast(scheduled_fn), immediate_); 405 | return result == 0; 406 | } 407 | 408 | void EvHTTPServer::ScheduleReply(std::function fn) { 409 | server_options_->executor()->Schedule(fn); 410 | } 411 | 412 | } // namespace net_http 413 | -------------------------------------------------------------------------------- /net_http/server/internal/evhttp_server.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // libevent based server implementation 17 | 18 | #ifndef NET_HTTP_SERVER_INTERNAL_EVHTTP_SERVER_H_ 19 | #define NET_HTTP_SERVER_INTERNAL_EVHTTP_SERVER_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "absl/base/thread_annotations.h" 28 | #include "absl/synchronization/mutex.h" 29 | 30 | #include "absl/synchronization/notification.h" 31 | 32 | #include "net_http/server/internal/evhttp_request.h" 33 | #include "net_http/server/internal/server_support.h" 34 | #include "net_http/server/public/httpserver_interface.h" 35 | 36 | struct event_base; 37 | struct evhttp; 38 | struct evhttp_bound_socket; 39 | struct evhttp_request; 40 | 41 | namespace net_http { 42 | 43 | class EvHTTPServer final : public HTTPServerInterface, ServerSupport { 44 | public: 45 | virtual ~EvHTTPServer(); 46 | 47 | EvHTTPServer(const EvHTTPServer& other) = delete; 48 | EvHTTPServer& operator=(const EvHTTPServer& other) = delete; 49 | 50 | explicit EvHTTPServer(std::unique_ptr options); 51 | 52 | bool Initialize(); 53 | 54 | bool StartAcceptingRequests() override; 55 | 56 | bool is_accepting_requests() const override; 57 | 58 | int listen_port() const override; 59 | 60 | void Terminate() override; 61 | 62 | bool is_terminating() const override; 63 | 64 | void WaitForTermination() override; 65 | 66 | bool WaitForTerminationWithTimeout(absl::Duration timeout) override; 67 | 68 | void RegisterRequestHandler(absl::string_view uri, RequestHandler handler, 69 | const RequestHandlerOptions& options) override; 70 | 71 | void RegisterRequestDispatcher(RequestDispatcher dispatcher, 72 | const RequestHandlerOptions& options) override; 73 | 74 | void IncOps() override; 75 | void DecOps() override; 76 | 77 | bool EventLoopSchedule(std::function fn) override; 78 | void ScheduleReply(std::function fn) override; 79 | private: 80 | static void DispatchEvRequestFn(struct evhttp_request* req, void* server); 81 | 82 | void DispatchEvRequest(struct evhttp_request* req); 83 | 84 | void ScheduleHandlerReference(const RequestHandler& handler, 85 | EvHTTPRequest* ev_request) 86 | ABSL_EXCLUSIVE_LOCKS_REQUIRED(request_mu_); 87 | void ScheduleHandler(RequestHandler&& handler, EvHTTPRequest* ev_request) 88 | ABSL_EXCLUSIVE_LOCKS_REQUIRED(request_mu_); 89 | 90 | struct UriHandlerInfo { 91 | public: 92 | UriHandlerInfo(absl::string_view uri_in, RequestHandler handler_in, 93 | const RequestHandlerOptions& options_in); 94 | const std::string uri; 95 | const RequestHandler handler; 96 | const RequestHandlerOptions options; 97 | }; 98 | 99 | struct DispatcherInfo { 100 | public: 101 | DispatcherInfo(RequestDispatcher dispatcher_in, 102 | const RequestHandlerOptions& options_in); 103 | 104 | const RequestDispatcher dispatcher; 105 | const RequestHandlerOptions options; 106 | }; 107 | 108 | std::unique_ptr server_options_; 109 | 110 | // Started accepting requests. 111 | absl::Notification accepting_requests_; 112 | // Listener port 113 | int port_ = 0; 114 | 115 | // Started terminating the server, i.e. Terminate() has been called 116 | absl::Notification terminating_; 117 | 118 | // Tracks the # of pending operations 119 | mutable absl::Mutex ops_mu_; 120 | int64_t num_pending_ops_ ABSL_GUARDED_BY(ops_mu_) = 0; 121 | 122 | mutable absl::Mutex request_mu_; 123 | std::unordered_map uri_handlers_ 124 | ABSL_GUARDED_BY(request_mu_); 125 | std::vector dispatchers_ ABSL_GUARDED_BY(request_mu_); 126 | 127 | // ev instances 128 | event_base* ev_base_ = nullptr; 129 | evhttp* ev_http_ = nullptr; 130 | evhttp_bound_socket* ev_listener_ = nullptr; 131 | 132 | // Timeval used to register immediate callbacks, which are called 133 | // in the order that they are registered. 134 | const timeval* immediate_; 135 | }; 136 | 137 | } // namespace net_http 138 | 139 | #endif // NET_HTTP_SERVER_INTERNAL_EVHTTP_SERVER_H_ 140 | -------------------------------------------------------------------------------- /net_http/server/internal/evhttp_server_test.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include "net_http/server/internal/evhttp_server.h" 17 | 18 | #include 19 | 20 | #include 21 | #include "absl/memory/memory.h" 22 | #include "absl/synchronization/notification.h" 23 | #include "net_http/client/test_client/internal/evhttp_connection.h" 24 | #include "net_http/internal/fixed_thread_pool.h" 25 | #include "net_http/server/public/httpserver.h" 26 | #include "net_http/server/public/httpserver_interface.h" 27 | #include "net_http/server/public/server_request_interface.h" 28 | namespace net_http { 29 | namespace { 30 | 31 | class MyExecutor final : public EventExecutor { 32 | public: 33 | explicit MyExecutor(int num_threads) : thread_pool_(num_threads) {} 34 | 35 | void Schedule(std::function fn) override { 36 | thread_pool_.Schedule(fn); 37 | } 38 | 39 | private: 40 | FixedThreadPool thread_pool_; 41 | }; 42 | 43 | class EvHTTPServerTest : public ::testing::Test { 44 | public: 45 | void SetUp() override { InitServer(); } 46 | 47 | void TearDown() override { 48 | if (!server->is_terminating()) { 49 | server->Terminate(); 50 | server->WaitForTermination(); 51 | } 52 | } 53 | 54 | protected: 55 | std::unique_ptr server; 56 | 57 | private: 58 | void InitServer() { 59 | auto options = absl::make_unique(); 60 | options->AddPort(0); 61 | options->SetExecutor(absl::make_unique(4)); 62 | 63 | server = CreateEvHTTPServer(std::move(options)); 64 | 65 | ASSERT_TRUE(server != nullptr); 66 | } 67 | }; 68 | 69 | // Test StartAcceptingRequests 70 | TEST_F(EvHTTPServerTest, AcceptingTerminating) { 71 | EXPECT_FALSE(server->is_accepting_requests()); 72 | server->StartAcceptingRequests(); 73 | EXPECT_TRUE(server->is_accepting_requests()); 74 | 75 | server->Terminate(); 76 | EXPECT_TRUE(server->is_terminating()); 77 | 78 | server->WaitForTermination(); 79 | } 80 | 81 | // Test the path matching behavior 82 | TEST_F(EvHTTPServerTest, ExactPathMatching) { 83 | auto handler = [](ServerRequestInterface* request) { 84 | request->WriteResponseString("OK"); 85 | request->Reply(); 86 | }; 87 | server->RegisterRequestHandler("/ok", std::move(handler), 88 | RequestHandlerOptions()); 89 | server->StartAcceptingRequests(); 90 | 91 | auto connection = 92 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 93 | ASSERT_TRUE(connection != nullptr); 94 | 95 | TestClientRequest request = {"/ok?a=foo", "GET", {}, ""}; 96 | TestClientResponse response = {}; 97 | 98 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 99 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 100 | EXPECT_EQ(response.body, "OK"); 101 | 102 | // no canonicalization for the trailing "/" 103 | request = {"/ok/", "GET", {}, ""}; 104 | response = {}; 105 | 106 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 107 | EXPECT_EQ(response.status, HTTPStatusCode::NOT_FOUND); 108 | 109 | server->Terminate(); 110 | server->WaitForTermination(); 111 | } 112 | 113 | // Test RequestHandler overwriting 114 | TEST_F(EvHTTPServerTest, RequestHandlerOverwriting) { 115 | auto handler1 = [](ServerRequestInterface* request) { 116 | request->WriteResponseString("OK1"); 117 | request->Reply(); 118 | }; 119 | auto handler2 = [](ServerRequestInterface* request) { 120 | request->WriteResponseString("OK2"); 121 | request->Reply(); 122 | }; 123 | 124 | server->RegisterRequestHandler("/ok", std::move(handler1), 125 | RequestHandlerOptions()); 126 | server->RegisterRequestHandler("/ok", std::move(handler2), 127 | RequestHandlerOptions()); 128 | server->StartAcceptingRequests(); 129 | 130 | auto connection = 131 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 132 | ASSERT_TRUE(connection != nullptr); 133 | 134 | TestClientRequest request = {"/ok", "GET", {}, ""}; 135 | TestClientResponse response = {}; 136 | 137 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 138 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 139 | EXPECT_EQ(response.body, "OK2"); 140 | 141 | server->Terminate(); 142 | server->WaitForTermination(); 143 | } 144 | 145 | // Test single RequestDispatcher 146 | TEST_F(EvHTTPServerTest, SingleRequestDispather) { 147 | auto handler = [](ServerRequestInterface* request) { 148 | request->WriteResponseString("OK"); 149 | request->Reply(); 150 | }; 151 | 152 | auto dispatcher = [&handler](ServerRequestInterface* request) { 153 | return handler; 154 | }; 155 | 156 | server->RegisterRequestDispatcher(std::move(dispatcher), 157 | RequestHandlerOptions()); 158 | server->StartAcceptingRequests(); 159 | 160 | auto connection = 161 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 162 | ASSERT_TRUE(connection != nullptr); 163 | 164 | TestClientRequest request = {"/ok", "GET", {}, ""}; 165 | TestClientResponse response = {}; 166 | 167 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 168 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 169 | EXPECT_EQ(response.body, "OK"); 170 | 171 | server->Terminate(); 172 | server->WaitForTermination(); 173 | } 174 | 175 | // Test URI path precedes over RequestDispatcher 176 | TEST_F(EvHTTPServerTest, UriPrecedesOverRequestDispather) { 177 | auto handler1 = [](ServerRequestInterface* request) { 178 | request->WriteResponseString("OK1"); 179 | request->Reply(); 180 | }; 181 | 182 | server->RegisterRequestHandler("/ok", std::move(handler1), 183 | RequestHandlerOptions()); 184 | 185 | auto handler2 = [](ServerRequestInterface* request) { 186 | request->WriteResponseString("OK2"); 187 | request->Reply(); 188 | }; 189 | 190 | auto dispatcher = [&handler2](ServerRequestInterface* request) { 191 | return handler2; 192 | }; 193 | server->RegisterRequestDispatcher(std::move(dispatcher), 194 | RequestHandlerOptions()); 195 | 196 | server->StartAcceptingRequests(); 197 | 198 | auto connection = 199 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 200 | ASSERT_TRUE(connection != nullptr); 201 | 202 | TestClientRequest request = {"/ok", "GET", {}, ""}; 203 | TestClientResponse response = {}; 204 | 205 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 206 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 207 | EXPECT_EQ(response.body, "OK1"); 208 | 209 | request = {"/okxx", "GET", {}, ""}; 210 | response = {}; 211 | 212 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 213 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 214 | EXPECT_EQ(response.body, "OK2"); 215 | 216 | server->Terminate(); 217 | server->WaitForTermination(); 218 | } 219 | 220 | // Test RequestDispatcher in-order dispatching 221 | TEST_F(EvHTTPServerTest, InOrderRequestDispather) { 222 | auto dispatcher1 = [](ServerRequestInterface* request) { 223 | return [](ServerRequestInterface* request) { 224 | request->WriteResponseString("OK1"); 225 | request->Reply(); 226 | }; 227 | }; 228 | 229 | auto dispatcher2 = [](ServerRequestInterface* request) { 230 | return [](ServerRequestInterface* request) { 231 | request->WriteResponseString("OK2"); 232 | request->Reply(); 233 | }; 234 | }; 235 | 236 | server->RegisterRequestDispatcher(std::move(dispatcher1), 237 | RequestHandlerOptions()); 238 | server->RegisterRequestDispatcher(std::move(dispatcher2), 239 | RequestHandlerOptions()); 240 | 241 | server->StartAcceptingRequests(); 242 | 243 | auto connection = 244 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 245 | ASSERT_TRUE(connection != nullptr); 246 | 247 | TestClientRequest request = {"/ok", "GET", {}, ""}; 248 | TestClientResponse response = {}; 249 | 250 | EXPECT_TRUE(connection->BlockingSendRequest(request, &response)); 251 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 252 | EXPECT_EQ(response.body, "OK1"); 253 | 254 | server->Terminate(); 255 | server->WaitForTermination(); 256 | } 257 | 258 | // Test handler interaction 259 | TEST_F(EvHTTPServerTest, RequestHandlerInteraction) { 260 | absl::Notification handler1_start; 261 | auto handler1 = [&handler1_start](ServerRequestInterface* request) { 262 | handler1_start.WaitForNotification(); 263 | request->WriteResponseString("OK1"); 264 | request->Reply(); 265 | }; 266 | 267 | server->RegisterRequestHandler("/ok", std::move(handler1), 268 | RequestHandlerOptions()); 269 | 270 | server->StartAcceptingRequests(); 271 | 272 | auto connection = 273 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 274 | ASSERT_TRUE(connection != nullptr); 275 | connection->SetExecutor(absl::make_unique(4)); 276 | 277 | absl::Notification response_done; 278 | TestClientRequest request = {"/ok", "GET", {}, ""}; 279 | TestClientResponse response = {}; 280 | response.done = [&response_done]() { response_done.Notify(); }; 281 | 282 | EXPECT_TRUE(connection->SendRequest(request, &response)); 283 | EXPECT_FALSE( 284 | response_done.WaitForNotificationWithTimeout(absl::Milliseconds(50))); 285 | 286 | handler1_start.Notify(); 287 | response_done.WaitForNotification(); 288 | 289 | EXPECT_EQ(response.status, HTTPStatusCode::OK); 290 | EXPECT_EQ(response.body, "OK1"); 291 | 292 | connection->Terminate(); 293 | 294 | server->Terminate(); 295 | server->WaitForTermination(); 296 | } 297 | 298 | // Test active-request count during shutdown 299 | TEST_F(EvHTTPServerTest, ActiveRequestCountInShutdown) { 300 | absl::Notification handler_enter; 301 | absl::Notification handler_start; 302 | auto handler = [&handler_enter, 303 | &handler_start](ServerRequestInterface* request) { 304 | handler_enter.Notify(); 305 | handler_start.WaitForNotification(); 306 | request->WriteResponseString("OK1"); 307 | request->Reply(); 308 | }; 309 | 310 | server->RegisterRequestHandler("/ok", std::move(handler), 311 | RequestHandlerOptions()); 312 | 313 | server->StartAcceptingRequests(); 314 | 315 | auto connection = 316 | TestEvHTTPConnection::Connect("localhost", server->listen_port()); 317 | ASSERT_TRUE(connection != nullptr); 318 | connection->SetExecutor(absl::make_unique(4)); 319 | 320 | TestClientRequest request = {"/ok", "GET", {}, ""}; 321 | TestClientResponse response = {}; 322 | 323 | EXPECT_TRUE(connection->SendRequest(request, &response)); 324 | handler_enter.WaitForNotification(); 325 | 326 | server->Terminate(); 327 | EXPECT_FALSE(server->WaitForTerminationWithTimeout(absl::Milliseconds(50))); 328 | 329 | handler_start.Notify(); 330 | EXPECT_TRUE(server->WaitForTerminationWithTimeout(absl::Milliseconds(5000))); 331 | 332 | connection->Terminate(); 333 | 334 | // response.status etc are undefined as the server is terminated 335 | } 336 | 337 | } // namespace 338 | } // namespace net_http 339 | -------------------------------------------------------------------------------- /net_http/server/internal/server_support.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // This pure interface provides callbacks from a request object to the 17 | // server object so the two are properly decoupled. 18 | // This may turn out to be generally useful with no libevents specifics. 19 | 20 | #ifndef NET_HTTP_SERVER_INTERNAL_SERVER_SUPPORT_H_ 21 | #define NET_HTTP_SERVER_INTERNAL_SERVER_SUPPORT_H_ 22 | 23 | #include 24 | 25 | #include "net_http/server/public/server_request_interface.h" 26 | 27 | namespace net_http { 28 | 29 | class ServerSupport { 30 | public: 31 | virtual ~ServerSupport() = default; 32 | 33 | ServerSupport(const ServerSupport& other) = delete; 34 | ServerSupport& operator=(const ServerSupport& other) = delete; 35 | 36 | // book-keeping of active requests 37 | virtual void IncOps() = 0; 38 | virtual void DecOps() = 0; 39 | 40 | // Schedules the callback function to run immediately from the event loop. 41 | // Returns false if any error. 42 | virtual bool EventLoopSchedule(std::function fn) = 0; 43 | virtual void ScheduleReply(std::function fn) = 0; 44 | protected: 45 | ServerSupport() = default; 46 | }; 47 | 48 | } // namespace net_http 49 | 50 | #endif // NET_HTTP_SERVER_INTERNAL_SERVER_SUPPORT_H_ 51 | -------------------------------------------------------------------------------- /net_http/server/public/BUILD: -------------------------------------------------------------------------------- 1 | # Description: a lightweight http server and related utils to support Web clients 2 | 3 | package(default_visibility = ["//visibility:public",]) 4 | 5 | licenses(["notice"]) 6 | 7 | cc_library( 8 | name = "http_server_api", 9 | hdrs = [ 10 | "httpserver_interface.h", 11 | "server_request_interface.h", 12 | ], 13 | deps = [ 14 | "//net_http/public:shared_files", 15 | "@com_google_absl//absl/strings", 16 | "@com_google_absl//absl/time", 17 | ], 18 | ) 19 | 20 | cc_library( 21 | name = "http_server", 22 | hdrs = [ 23 | "httpserver.h", 24 | ], 25 | deps = [ 26 | ":http_server_api", 27 | "//net_http/server/internal:evhttp_server", 28 | "@com_google_absl//absl/memory", 29 | ], 30 | ) 31 | -------------------------------------------------------------------------------- /net_http/server/public/httpserver.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // The entry point to access different HTTP server implementations. 17 | 18 | #ifndef NET_HTTP_SERVER_PUBLIC_HTTPSERVER_H_ 19 | #define NET_HTTP_SERVER_PUBLIC_HTTPSERVER_H_ 20 | 21 | #include 22 | 23 | #include "absl/memory/memory.h" 24 | 25 | #include "net_http/server/internal/evhttp_server.h" 26 | #include "net_http/server/public/httpserver_interface.h" 27 | 28 | namespace net_http { 29 | 30 | // Creates a server implemented based on the libevents library. 31 | // Returns nullptr if there is any error. 32 | // 33 | // Must call WaitForTermination() or WaitForTerminationWithTimeout() before 34 | // the server is to be destructed. 35 | inline std::unique_ptr CreateEvHTTPServer( 36 | std::unique_ptr options) { 37 | auto server = absl::make_unique(std::move(options)); 38 | bool result = server->Initialize(); 39 | if (!result) { 40 | return nullptr; 41 | } 42 | 43 | return std::move(server); 44 | } 45 | 46 | } // namespace net_http 47 | 48 | #endif // NET_HTTP_SERVER_PUBLIC_HTTPSERVER_H_ 49 | -------------------------------------------------------------------------------- /net_http/server/public/httpserver_interface.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // APIs for the HTTP server. 17 | 18 | #ifndef NET_HTTP_SERVER_PUBLIC_HTTPSERVER_INTERFACE_H_ 19 | #define NET_HTTP_SERVER_PUBLIC_HTTPSERVER_INTERFACE_H_ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "absl/strings/string_view.h" 28 | #include "absl/time/time.h" 29 | 30 | #include "net_http/server/public/server_request_interface.h" 31 | 32 | namespace net_http { 33 | 34 | // A non-blocking executor for processing I/O polling or callback events. 35 | // TODO: move EventExecutor into ServerOptions 36 | class EventExecutor { 37 | public: 38 | virtual ~EventExecutor() = default; 39 | 40 | EventExecutor(const EventExecutor& other) = delete; 41 | EventExecutor& operator=(const EventExecutor& other) = delete; 42 | 43 | // Schedule the specified 'fn' for execution in this executor. 44 | // Must be non-blocking 45 | virtual void Schedule(std::function fn) = 0; 46 | 47 | protected: 48 | EventExecutor() = default; 49 | }; 50 | 51 | // Options to specify when a server instance is created. 52 | class ServerOptions { 53 | public: 54 | ServerOptions() = default; 55 | 56 | // At least one port has to be configured. 57 | // Port 0 will start the server using an ephemeral port. 58 | void AddPort(int port) { 59 | assert(port >= 0); 60 | ports_.emplace_back(port); 61 | } 62 | 63 | // The default executor for running I/O event polling. 64 | // This is a mandatory option. 65 | void SetExecutor(std::unique_ptr executor) { 66 | executor_ = std::move(executor); 67 | } 68 | 69 | const std::vector& ports() const { return ports_; } 70 | 71 | EventExecutor* executor() const { return executor_.get(); } 72 | 73 | private: 74 | std::vector ports_; 75 | std::unique_ptr executor_; 76 | }; 77 | 78 | // Options to specify when registering a handler (given a uri pattern). 79 | // This should be a value type. 80 | class RequestHandlerOptions { 81 | public: 82 | RequestHandlerOptions() = default; 83 | 84 | RequestHandlerOptions(const RequestHandlerOptions&) = default; 85 | RequestHandlerOptions& operator=(const RequestHandlerOptions&) = default; 86 | 87 | // Sets the max length of uncompressed data when uncompressing a request body 88 | inline RequestHandlerOptions& set_auto_uncompress_max_size(int64_t size) { 89 | auto_uncompress_max_size_ = size; 90 | return *this; 91 | } 92 | 93 | // The max length of uncompressed data when doing uncompress. Returns 0 if 94 | // not set. See Zlib::kMaxUncompressedBytes for the default config. 95 | inline int64_t auto_uncompress_max_size() const { 96 | return auto_uncompress_max_size_; 97 | } 98 | 99 | // The auto_uncompress_input option specifies whether the request 100 | // input data should be uncompressed if the request has the 101 | // Content-Encoding: .*gzip.* header. The option defaults to true. 102 | inline RequestHandlerOptions& set_auto_uncompress_input( 103 | bool should_uncompress) { 104 | auto_uncompress_input_ = should_uncompress; 105 | return *this; 106 | } 107 | 108 | inline bool auto_uncompress_input() const { return auto_uncompress_input_; } 109 | 110 | private: 111 | // To be added: compression, CORS rules, streaming control 112 | // thread executor, admission control, limits ... 113 | 114 | bool auto_uncompress_input_ = true; 115 | 116 | int64_t auto_uncompress_max_size_ = 0; 117 | }; 118 | 119 | // A request handler is registered by the application to handle a request 120 | // based on the request Uri path, available via ServerRequestInterface. 121 | // 122 | // Request handlers need be completely non-blocking. And handlers may add 123 | // callbacks to a thread-pool that is managed by the application itself. 124 | typedef std::function RequestHandler; 125 | 126 | // Returns a nullptr if the request is not handled by this dispatcher. 127 | typedef std::function 128 | RequestDispatcher; 129 | 130 | // This interface class specifies the API contract for the HTTP server. 131 | // 132 | // Requirements for implementations: 133 | // - must be thread-safe 134 | // - multiple HTTP server instances need be supported in a single process 135 | // - for a basic implementation, the application needs provide a dedicated 136 | // thread to handle all I/O events, i.e. the thread that calls 137 | // StartAcceptingRequests(). 138 | // - the arrival order of concurrent requests is insignificant because the 139 | // server runtime and I/O are completely event-driven. 140 | class HTTPServerInterface { 141 | public: 142 | virtual ~HTTPServerInterface() = default; 143 | 144 | HTTPServerInterface(const HTTPServerInterface& other) = delete; 145 | HTTPServerInterface& operator=(const HTTPServerInterface& other) = delete; 146 | 147 | // Starts to accept requests arrived on the network. 148 | // Returns false if the server runtime fails to initialize properly. 149 | virtual bool StartAcceptingRequests() = 0; 150 | 151 | // Returns true if StartAcceptingRequests() has been called. 152 | virtual bool is_accepting_requests() const = 0; 153 | 154 | // Returns the server listener port if any, or else returns 0. 155 | virtual int listen_port() const = 0; 156 | 157 | // Starts the server termination, and returns immediately. 158 | virtual void Terminate() = 0; 159 | 160 | // Returns true if Terminate() has been called. 161 | virtual bool is_terminating() const = 0; 162 | 163 | // Blocks the calling thread until the server is terminated and safe 164 | // to destroy. 165 | virtual void WaitForTermination() = 0; 166 | 167 | // Blocks the calling thread until the server is terminated and safe 168 | // to destroy, or until the specified timeout elapses. Returns true 169 | // if safe termination completed within the timeout, and false otherwise. 170 | virtual bool WaitForTerminationWithTimeout(absl::Duration timeout) = 0; 171 | 172 | // To be specified: lameduck 173 | 174 | // Registers a request handler with exact URI path matching. 175 | // Any existing handler under the same uri will be overwritten. 176 | // The server owns the handler after registration. 177 | // Handlers may be registered after the server has been started. 178 | virtual void RegisterRequestHandler(absl::string_view uri, 179 | RequestHandler handler, 180 | const RequestHandlerOptions& options) = 0; 181 | 182 | // Registers a request dispatcher, i.e. application-provided URI dispatching 183 | // logic, e.g. a regexp based one. 184 | // 185 | // For a given request, dispatchers are only invoked if there is no exact URI 186 | // path matching to any registered request handler. 187 | // 188 | // Dispatchers are invoked in order of registration, i.e. first registered 189 | // gets first pick. The server owns the dispatcher after registration. 190 | // Dispatchers may be registered after the server has been started. 191 | virtual void RegisterRequestDispatcher( 192 | RequestDispatcher dispatcher, const RequestHandlerOptions& options) = 0; 193 | 194 | // To be added: unregister (if needed) 195 | 196 | protected: 197 | HTTPServerInterface() = default; 198 | }; 199 | 200 | } // namespace net_http 201 | 202 | #endif // NET_HTTP_SERVER_PUBLIC_HTTPSERVER_INTERFACE_H_ 203 | -------------------------------------------------------------------------------- /net_http/server/public/server_request_interface.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // net_http::ServerRequestInterface defines a pure interface class for handling 17 | // an HTTP request on the server-side. It is designed as a minimum API 18 | // to ensure reusability and to work with different HTTP implementations. 19 | // 20 | // ServerRequestInterface is thread-compatible. Once the request object 21 | // has been dispatched to the application-specified handler, the HTTP server 22 | // runtime will not access the request object till Reply() is called. 23 | // 24 | // Streamed request/response APIs are to be added, which will introduce 25 | // additional API contract wrt the threading semantics. 26 | 27 | #ifndef NET_HTTP_SERVER_PUBLIC_SERVER_REQUEST_INTERFACE_H_ 28 | #define NET_HTTP_SERVER_PUBLIC_SERVER_REQUEST_INTERFACE_H_ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "absl/strings/string_view.h" 36 | #include "net_http/public/response_code_enum.h" 37 | 38 | namespace net_http { 39 | 40 | class ServerRequestInterface { 41 | public: 42 | // To be used with memory blocks returned via std::unique_ptr 43 | struct BlockDeleter { 44 | public: 45 | BlockDeleter() : size_(0) {} // nullptr 46 | explicit BlockDeleter(int64_t size) : size_(size) {} 47 | inline void operator()(char* ptr) const { 48 | // TODO: c++14 ::operator delete[](ptr, size_t) 49 | std::allocator().deallocate(ptr, static_cast(size_)); 50 | } 51 | 52 | private: 53 | int64_t size_; 54 | }; 55 | 56 | virtual ~ServerRequestInterface() = default; 57 | 58 | ServerRequestInterface(const ServerRequestInterface& other) = delete; 59 | ServerRequestInterface& operator=(const ServerRequestInterface& other) = 60 | delete; 61 | 62 | // The portion of the request URI after the host and port. 63 | // E.g. "/path/to/resource?param=value¶m=value#fragment". 64 | // Doesn't unescape the contents; returns "/" at least. 65 | virtual absl::string_view uri_path() const = 0; 66 | 67 | // HTTP request method. 68 | // Must be in Upper Case. 69 | virtual absl::string_view http_method() const = 0; 70 | 71 | // Input/output byte-buffer types are subject to change! 72 | // I/O buffer choices: 73 | // - absl::ByteStream would work but it is not yet open-sourced 74 | // - iovec doesn't add much value and may limit portability; but otherwise 75 | // the current API is compatible with iovec 76 | // - absl::Span is open-sourced, but string_view is simpler to use for writing 77 | // to an HTTP response. 78 | 79 | // Appends the data block of the specified size to the response body. 80 | // This request object takes the ownership of the data block. 81 | // 82 | // Note this is not a streaming write API. See PartialReply() below. 83 | virtual void WriteResponseBytes(const char* data, int64_t size) = 0; 84 | 85 | // Appends (by coping) the data of string_view to the end of 86 | // the response body. 87 | virtual void WriteResponseString(absl::string_view data) = 0; 88 | 89 | // Reads from the request body. 90 | // Returns the number bytes of data read, whose ownership will be transferred 91 | // to the caller. Returns nullptr when EOF is reached or when there 92 | // is no request body. 93 | // 94 | // The returned memory will be "free-ed" via the custom Deleter. Do not 95 | // release the memory manually as its allocator is subject to change. 96 | // 97 | // Note this is not a streaming read API in that the complete request body 98 | // should have already been received. 99 | virtual std::unique_ptr 100 | ReadRequestBytes(int64_t* size) = 0; 101 | 102 | // Returns the first value, including "", associated with a request 103 | // header name. The header name argument is case-insensitive. 104 | // Returns nullptr if the specified header doesn't exist. 105 | virtual absl::string_view GetRequestHeader( 106 | absl::string_view header) const = 0; 107 | 108 | // To be added: multi-value headers. 109 | 110 | // Returns all the request header names. 111 | // This is not an efficient way to access headers, mainly for debugging uses. 112 | virtual std::vector request_headers() const = 0; 113 | 114 | virtual void OverwriteResponseHeader(absl::string_view header, 115 | absl::string_view value) = 0; 116 | virtual void AppendResponseHeader(absl::string_view header, 117 | absl::string_view value) = 0; 118 | 119 | // The IO status of a request or response body. 120 | enum class BodyStatus { 121 | // The body hasn't been completely read or written. 122 | PENDING = 0, 123 | // The body has been completely read or written or when there is no body. 124 | COMPLETE = 1, 125 | // The transport has reported a failure and the request should be aborted. 126 | FAILED = 2, 127 | }; 128 | 129 | // This serves as the return value type for callbacks that may be 130 | // skipped for optimization reasons 131 | enum class CallbackStatus { 132 | NOT_SCHEDULED = 0, 133 | SCHEDULED = 1, 134 | }; 135 | 136 | // Sends headers and/or any buffered response body data to the client. 137 | // Assumes 200 if status is not specified. 138 | // If called for the first time, all the response headers will be sent 139 | // together including headers specified by the application and 140 | // headers generated by the server. 141 | // Trying to modify headers or specifying a status after the first 142 | // PartialReply() is called is considered a programming error and 143 | // the underlying behavior is undefined. 144 | virtual void PartialReplyWithStatus(HTTPStatusCode status) = 0; 145 | virtual void PartialReply() = 0; 146 | 147 | // Similar to PartialReply() but with an on_flush callback which will be 148 | // invoked when the response data has been completely flushed by the 149 | // transport. This allows the handler to apply transport-provided flow-control 150 | // in writing data to the peer. 151 | // 152 | // Returns SCHEDULED if the callback will be invoked asynchronously after 153 | // this method returns. Until the callback is invoked, the request object 154 | // should not be accessed by the handler. 155 | // 156 | // Returns NOT_SCHEDULED if data is already flushed when this method returns 157 | // or when the request should be aborted due to transport failures. 158 | // 159 | // The handler should check response_body_status() after this method returns 160 | // or from the callback to decide if the request should be aborted due to 161 | // transport failures. 162 | virtual CallbackStatus PartialReplyWithFlushCallback( 163 | std::function callback) = 0; 164 | virtual BodyStatus response_body_status() { return BodyStatus::PENDING; } 165 | 166 | // Request streaming is disabled by default 167 | virtual BodyStatus request_body_status() { return BodyStatus::COMPLETE; } 168 | 169 | // Completes the response and sends any buffered response body 170 | // to the client. Headers will be generated and sent first if PartialReply() 171 | // has never be called. 172 | // Assumes 200 if status is not specified. 173 | // Once Reply() is called, the request object will be owned and destructed 174 | // by the server runtime. 175 | virtual void ReplyWithStatus(HTTPStatusCode status) = 0; 176 | virtual void Reply() = 0; 177 | 178 | // Aborts the current request forcibly. 179 | // Once Abort() is called, the request object will be owned and destructed 180 | // by the server runtime. 181 | virtual void Abort() = 0; 182 | 183 | protected: 184 | ServerRequestInterface() = default; 185 | 186 | private: 187 | // Do not add any data members to this class. 188 | }; 189 | 190 | // Helper methods. 191 | 192 | inline void SetContentType(ServerRequestInterface* request, 193 | absl::string_view type) { 194 | request->OverwriteResponseHeader("Content-Type", type); 195 | } 196 | 197 | inline void SetContentTypeHTML(ServerRequestInterface* request) { 198 | SetContentType(request, "text/html"); 199 | } 200 | 201 | inline void SetContentTypeTEXT(ServerRequestInterface* request) { 202 | SetContentType(request, "text/plain"); 203 | } 204 | 205 | } // namespace net_http 206 | 207 | #endif // NET_HTTP_SERVER_PUBLIC_SERVER_REQUEST_INTERFACE_H_ 208 | -------------------------------------------------------------------------------- /net_http/server/testing/BUILD: -------------------------------------------------------------------------------- 1 | # Description: net_http/server/testing 2 | 3 | package(default_visibility = ["//visibility:private"]) 4 | 5 | licenses(["notice"]) 6 | 7 | cc_binary( 8 | name = "evhttp_echo_server", 9 | srcs = ["evhttp_echo_server.cc"], 10 | deps = [ 11 | "//net_http/server/public:http_server", 12 | "//net_http/server/public:http_server_api", 13 | "@com_google_absl//absl/memory", 14 | "@com_google_absl//absl/strings", 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /net_http/server/testing/evhttp_echo_server.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // A single-threaded server to print the request details as HTML 17 | // URI: /print 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "absl/memory/memory.h" 28 | #include "absl/strings/numbers.h" 29 | #include "absl/strings/str_cat.h" 30 | #include "absl/strings/string_view.h" 31 | 32 | #include "net_http/server/public/httpserver.h" 33 | #include "net_http/server/public/httpserver_interface.h" 34 | #include "net_http/server/public/server_request_interface.h" 35 | 36 | namespace { 37 | 38 | using absl::StrAppend; 39 | 40 | using net_http::EventExecutor; 41 | using net_http::HTTPServerInterface; 42 | using net_http::RequestHandlerOptions; 43 | using net_http::ServerOptions; 44 | using net_http::ServerRequestInterface; 45 | using net_http::SetContentTypeHTML; 46 | 47 | void EchoHandler(ServerRequestInterface* req) { 48 | std::string response; 49 | 50 | StrAppend(&response, 51 | "\n" 52 | "\n \n" 53 | " \n" 54 | " Request Echo HTTP Server\n" 55 | " \n" 56 | " \n" 57 | "

Print the HTTP request detail

\n" 58 | "
    \n"); 59 | 60 | StrAppend(&response, "HTTP Method: ", req->http_method(), "
    \n"); 61 | StrAppend(&response, "Request Uri: ", req->uri_path(), "
    \n"); 62 | 63 | StrAppend(&response, "

    ====

    \n"); 64 | for (auto header : req->request_headers()) { 65 | StrAppend(&response, header, ": ", req->GetRequestHeader(header), "
    \n"); 66 | } 67 | 68 | // read the request body 69 | int64_t num_bytes; 70 | auto request_chunk = req->ReadRequestBytes(&num_bytes); 71 | bool print_break = false; 72 | while (request_chunk != nullptr) { 73 | if (!print_break) { 74 | StrAppend(&response, "

    ====

    \n"); 75 | print_break = true; 76 | } 77 | StrAppend(&response, absl::string_view(request_chunk.get(), 78 | static_cast(num_bytes))); 79 | request_chunk = req->ReadRequestBytes(&num_bytes); 80 | } 81 | 82 | req->WriteResponseString(response); 83 | 84 | SetContentTypeHTML(req); 85 | req->Reply(); 86 | } 87 | 88 | // An executor that runs the current thread, testing only 89 | class MyExcecutor final : public EventExecutor { 90 | public: 91 | MyExcecutor() = default; 92 | 93 | void Schedule(std::function fn) override { fn(); } 94 | }; 95 | 96 | // Returns the server if success, or nullptr if there is any error. 97 | std::unique_ptr StartServer(int port) { 98 | auto options = absl::make_unique(); 99 | options->AddPort(port); 100 | options->SetExecutor(absl::make_unique()); 101 | 102 | auto server = CreateEvHTTPServer(std::move(options)); 103 | 104 | if (server == nullptr) { 105 | return nullptr; 106 | } 107 | 108 | RequestHandlerOptions handler_options; 109 | server->RegisterRequestHandler("/print", EchoHandler, handler_options); 110 | 111 | // Blocking here with the use of MyExecutor 112 | bool success = server->StartAcceptingRequests(); 113 | 114 | if (success) { 115 | return server; 116 | } 117 | 118 | return nullptr; 119 | } 120 | 121 | } // namespace 122 | 123 | int main(int argc, char** argv) { 124 | if (argc < 2) { 125 | std::cerr << "Usage: http-server " << std::endl; 126 | return 1; 127 | } 128 | 129 | int port = 8080; 130 | bool port_parsed = absl::SimpleAtoi(argv[1], &port); 131 | if (!port_parsed) { 132 | std::cerr << "Invalid port: " << argv[1] << std::endl; 133 | } 134 | 135 | auto server = StartServer(port); 136 | 137 | if (server != nullptr) { 138 | server->WaitForTermination(); 139 | return 0; 140 | } else { 141 | std::cerr << "Failed to start the server." << std::endl; 142 | return 1; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /net_http/socket/testing/BUILD: -------------------------------------------------------------------------------- 1 | # Description: net_http/socket testing utils 2 | 3 | package(default_visibility = ["//visibility:private"]) 4 | 5 | licenses(["notice"]) 6 | 7 | cc_binary( 8 | name = "ev_print_req_server", 9 | srcs = ["ev_print_req_server.cc"], 10 | deps = [ 11 | "@com_github_libevent_libevent//:libevent", 12 | "@com_google_absl//absl/strings", 13 | ], 14 | ) 15 | 16 | cc_binary( 17 | name = "ev_fetch_client", 18 | srcs = ["ev_fetch_client.cc"], 19 | deps = [ 20 | "@com_github_libevent_libevent//:libevent", 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /net_http/socket/testing/ev_fetch_client.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "event2/buffer.h" 23 | #include "event2/bufferevent.h" 24 | #include "event2/event.h" 25 | #include "event2/http.h" 26 | #include "event2/keyvalq_struct.h" 27 | #include "event2/util.h" 28 | 29 | namespace { 30 | 31 | struct event_base* ev_base; 32 | struct evhttp_uri* http_uri; 33 | struct evhttp_connection* evcon; 34 | 35 | void response_cb(struct evhttp_request* req, void* ctx) { 36 | char buffer[1024]; 37 | int nread; 38 | 39 | if (req == nullptr) { 40 | int errcode = EVUTIL_SOCKET_ERROR(); 41 | fprintf(stderr, "socket error = %s (%d)\n", 42 | evutil_socket_error_to_string(errcode), errcode); 43 | return; 44 | } 45 | 46 | fprintf(stderr, "Response line: %d %s\n", 47 | evhttp_request_get_response_code(req), 48 | evhttp_request_get_response_code_line(req)); 49 | 50 | struct evkeyvalq* headers = evhttp_request_get_input_headers(req); 51 | struct evkeyval* header; 52 | for (header = headers->tqh_first; header; header = header->next.tqe_next) { 53 | fprintf(stderr, "%s : %s\n", header->key, header->value); 54 | } 55 | 56 | while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req), buffer, 57 | sizeof(buffer))) > 0) { 58 | fwrite(buffer, static_cast(nread), 1, stderr); 59 | } 60 | } 61 | 62 | void help() { fputs("Usage: ev_fetch-client uri [body]\n", stderr); } 63 | 64 | void err(const char* msg) { fputs(msg, stderr); } 65 | 66 | void cleanup() { 67 | if (evcon != nullptr) { 68 | evhttp_connection_free(evcon); 69 | } 70 | if (http_uri != nullptr) { 71 | evhttp_uri_free(http_uri); 72 | } 73 | 74 | event_base_free(ev_base); 75 | } 76 | 77 | } // namespace 78 | 79 | int main(int argc, char** argv) { 80 | fprintf(stdout, "Start the http client ...\n"); 81 | 82 | if (argc < 2 || argc > 3) { 83 | help(); 84 | return 1; 85 | } 86 | 87 | const char* url = argv[1]; 88 | 89 | const char* body = nullptr; 90 | 91 | if (argc == 3) { 92 | body = argv[2]; 93 | } 94 | 95 | http_uri = evhttp_uri_parse(url); 96 | if (http_uri == nullptr) { 97 | err("malformed url"); 98 | return 1; 99 | } 100 | 101 | const char* scheme = evhttp_uri_get_scheme(http_uri); 102 | if (scheme == nullptr || strcasecmp(scheme, "http") != 0) { 103 | err("url must be http"); 104 | return 1; 105 | } 106 | 107 | const char* host = evhttp_uri_get_host(http_uri); 108 | if (host == nullptr) { 109 | err("url must have a host"); 110 | return 1; 111 | } 112 | 113 | int port = evhttp_uri_get_port(http_uri); 114 | if (port == -1) { 115 | port = (strcasecmp(scheme, "http") == 0) ? 80 : 443; 116 | } 117 | 118 | const char* path = evhttp_uri_get_path(http_uri); 119 | if (strlen(path) == 0) { 120 | path = "/"; 121 | } 122 | 123 | char uri[256]; 124 | const char* query = evhttp_uri_get_query(http_uri); 125 | if (query == nullptr) { 126 | snprintf(uri, sizeof(uri) - 1, "%s", path); 127 | } else { 128 | snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query); 129 | } 130 | uri[sizeof(uri) - 1] = '\0'; 131 | 132 | // Create event base 133 | ev_base = event_base_new(); 134 | if (ev_base == nullptr) { 135 | perror("event_base_new()"); 136 | return 1; 137 | } 138 | 139 | // struct bufferevent* bev = 140 | // bufferevent_socket_new(ev_base, -1, BEV_OPT_CLOSE_ON_FREE); 141 | // 142 | // if (bev == nullptr) { 143 | // fprintf(stderr, "bufferevent_socket_new() failed\n"); 144 | // return 1; 145 | // } 146 | 147 | // blocking call (DNS resolution) 148 | evcon = evhttp_connection_base_bufferevent_new( 149 | ev_base, nullptr, nullptr, host, static_cast(port)); 150 | if (evcon == nullptr) { 151 | fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n"); 152 | return 1; 153 | } 154 | 155 | int retries = 0; 156 | evhttp_connection_set_retries(evcon, retries); 157 | 158 | int timeout_in_secs = 5; 159 | evhttp_connection_set_timeout(evcon, timeout_in_secs); 160 | 161 | struct evhttp_request* req = evhttp_request_new(response_cb, evcon); 162 | if (req == nullptr) { 163 | fprintf(stderr, "evhttp_request_new() failed\n"); 164 | return 1; 165 | } 166 | 167 | struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req); 168 | evhttp_add_header(output_headers, "Host", host); 169 | evhttp_add_header(output_headers, "Connection", "close"); 170 | 171 | if (body) { 172 | struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req); 173 | size_t size = strlen(body); 174 | 175 | evbuffer_add(output_buffer, body, size); 176 | 177 | char length_header[8]; 178 | evutil_snprintf(length_header, sizeof(length_header) - 1, "%lu", size); 179 | evhttp_add_header(output_headers, "Content-Length", length_header); 180 | } 181 | 182 | int r = evhttp_make_request(evcon, req, 183 | body ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri); 184 | if (r != 0) { 185 | fprintf(stderr, "evhttp_make_request() failed\n"); 186 | return 1; 187 | } 188 | 189 | event_base_dispatch(ev_base); 190 | 191 | cleanup(); 192 | 193 | return 0; 194 | } 195 | -------------------------------------------------------------------------------- /net_http/socket/testing/ev_print_req_server.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "absl/strings/numbers.h" 25 | 26 | #include "event2/buffer.h" 27 | #include "event2/event.h" 28 | #include "event2/http.h" 29 | #include "event2/keyvalq_struct.h" 30 | #include "event2/util.h" 31 | 32 | namespace { 33 | 34 | char uri_root[512]; 35 | 36 | void request_cb(struct evhttp_request *req, void *arg) { 37 | const char *method; 38 | 39 | switch (evhttp_request_get_command(req)) { 40 | case EVHTTP_REQ_GET: 41 | method = "GET"; 42 | break; 43 | case EVHTTP_REQ_POST: 44 | method = "POST"; 45 | break; 46 | case EVHTTP_REQ_HEAD: 47 | method = "HEAD"; 48 | break; 49 | case EVHTTP_REQ_PUT: 50 | method = "PUT"; 51 | break; 52 | case EVHTTP_REQ_DELETE: 53 | method = "DELETE"; 54 | break; 55 | case EVHTTP_REQ_OPTIONS: 56 | method = "OPTIONS"; 57 | break; 58 | case EVHTTP_REQ_TRACE: 59 | method = "TRACE"; 60 | break; 61 | case EVHTTP_REQ_CONNECT: 62 | method = "CONNECT"; 63 | break; 64 | case EVHTTP_REQ_PATCH: 65 | method = "PATCH"; 66 | break; 67 | default: 68 | method = "unknown"; 69 | break; 70 | } 71 | 72 | struct evbuffer *response_body = evbuffer_new(); 73 | if (response_body == nullptr) { 74 | evhttp_send_error(req, HTTP_SERVUNAVAIL, nullptr); 75 | return; 76 | } 77 | 78 | evbuffer_add_printf(response_body, 79 | "\n" 80 | "\n \n" 81 | " \n" 82 | " Mini libevent httpserver\n" 83 | " \n" 84 | " \n" 85 | "

    Print the HTTP request detail

    \n" 86 | "
      \n"); 87 | 88 | evbuffer_add_printf(response_body, "HTTP Method: %s
      \n", method); 89 | 90 | const char *uri = evhttp_request_get_uri(req); // no malloc 91 | evbuffer_add_printf(response_body, "Request Uri: %s
      \n", uri); 92 | 93 | struct evhttp_uri *decoded_url = evhttp_uri_parse(uri); 94 | if (decoded_url == nullptr) { 95 | evhttp_send_error(req, HTTP_BADREQUEST, nullptr); 96 | return; 97 | } 98 | 99 | evbuffer_add_printf(response_body, "Decoded Uri:
      \n"); 100 | 101 | evbuffer_add_printf(response_body, "  scheme : %s
      \n", 102 | evhttp_uri_get_scheme(decoded_url)); 103 | evbuffer_add_printf(response_body, "  host : %s
      \n", 104 | evhttp_uri_get_host(decoded_url)); 105 | evbuffer_add_printf(response_body, "  port : %d
      \n", 106 | evhttp_uri_get_port(decoded_url)); 107 | evbuffer_add_printf(response_body, "  path : %s
      \n", 108 | evhttp_uri_get_path(decoded_url)); 109 | evbuffer_add_printf(response_body, "  query : %s
      \n", 110 | evhttp_uri_get_query(decoded_url)); 111 | 112 | const char *path = evhttp_uri_get_path(decoded_url); 113 | if (path == nullptr) { 114 | path = "/"; 115 | } 116 | 117 | evbuffer_add_printf(response_body, "Uri path: %s
      \n", path); 118 | 119 | char *decoded_path = evhttp_uridecode(path, 1, nullptr); 120 | if (decoded_path == nullptr) { 121 | evhttp_send_error(req, HTTP_BADREQUEST, nullptr); 122 | return; 123 | } 124 | 125 | evbuffer_add_printf(response_body, "Decoded path: %s
      \n", decoded_path); 126 | 127 | evbuffer_add_printf(response_body, "

      ====

      \n"); 128 | 129 | struct evkeyvalq *headers = evhttp_request_get_input_headers(req); 130 | 131 | struct evkeyval *header; 132 | for (header = headers->tqh_first; header; header = header->next.tqe_next) { 133 | evbuffer_add_printf(response_body, "%s : %s
      \n", header->key, 134 | header->value); 135 | } 136 | 137 | struct evbuffer *request_body = evhttp_request_get_input_buffer(req); 138 | if (request_body != nullptr) { 139 | evbuffer_add_printf(response_body, "

      ====

      \n"); 140 | int result = evbuffer_add_buffer_reference(response_body, request_body); 141 | if (result < 0) { 142 | evbuffer_add_printf(response_body, ">>> Failed to print the body
      \n"); 143 | } 144 | } 145 | 146 | evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", 147 | "text/html"); 148 | 149 | evhttp_send_reply(req, 200, "OK", response_body); 150 | 151 | evhttp_uri_free(decoded_url); 152 | free(decoded_path); 153 | evbuffer_free(response_body); 154 | } 155 | 156 | void help() { fprintf(stdout, "Usage: ev_print_req_server \n"); } 157 | 158 | } // namespace 159 | 160 | int main(int argc, char **argv) { 161 | fprintf(stdout, "Start the http server ...\n"); 162 | 163 | struct event_base *base; 164 | struct evhttp *http; 165 | struct evhttp_bound_socket *handle; 166 | 167 | ev_uint32_t port = 8080; 168 | 169 | if (argc < 2) { 170 | help(); 171 | return 1; 172 | } 173 | 174 | bool port_parsed = absl::SimpleAtoi(argv[1], &port); 175 | if (!port_parsed) { 176 | fprintf(stderr, "Invalid port: %s\n", argv[1]); 177 | } 178 | 179 | base = event_base_new(); 180 | if (!base) { 181 | fprintf(stderr, "Couldn't create an event_base: exiting\n"); 182 | return 1; 183 | } 184 | 185 | http = evhttp_new(base); 186 | if (!http) { 187 | fprintf(stderr, "couldn't create evhttp. Exiting.\n"); 188 | return 1; 189 | } 190 | 191 | // catch all 192 | evhttp_set_gencb(http, request_cb, NULL); 193 | 194 | // nullptr will bind to ipv4, which will fail to accept 195 | // requests from clients where getaddressinfo() defaults to AF_INET6 196 | handle = evhttp_bind_socket_with_handle(http, "::0", (ev_uint16_t)port); 197 | if (!handle) { 198 | fprintf(stderr, "couldn't bind to port %d. Exiting.\n", (int)port); 199 | return 1; 200 | } 201 | 202 | { 203 | /* Extract and display the address we're listening on. */ 204 | struct sockaddr_storage ss = {}; 205 | evutil_socket_t fd; 206 | ev_socklen_t socklen = sizeof(ss); 207 | char addrbuf[128]; 208 | void *inaddr; 209 | const char *addr; 210 | int got_port = -1; 211 | fd = evhttp_bound_socket_get_fd(handle); 212 | memset(&ss, 0, sizeof(ss)); 213 | if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) { 214 | perror("getsockname() failed"); 215 | return 1; 216 | } 217 | if (ss.ss_family == AF_INET) { 218 | got_port = ntohs(((struct sockaddr_in *)&ss)->sin_port); 219 | inaddr = &((struct sockaddr_in *)&ss)->sin_addr; 220 | } else if (ss.ss_family == AF_INET6) { 221 | got_port = ntohs(((struct sockaddr_in6 *)&ss)->sin6_port); 222 | inaddr = &((struct sockaddr_in6 *)&ss)->sin6_addr; 223 | } else { 224 | fprintf(stderr, "Weird address family %d\n", ss.ss_family); 225 | return 1; 226 | } 227 | addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf, sizeof(addrbuf)); 228 | if (addr) { 229 | printf("Listening on %s:%d\n", addr, got_port); 230 | evutil_snprintf(uri_root, sizeof(uri_root), "http://%s:%d", addr, 231 | got_port); 232 | } else { 233 | fprintf(stderr, "evutil_inet_ntop failed\n"); 234 | return 1; 235 | } 236 | } 237 | 238 | event_base_dispatch(base); 239 | 240 | return 0; 241 | } 242 | -------------------------------------------------------------------------------- /third_party/libevent/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """The build rule for building libevent.""" 16 | 17 | load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake") 18 | 19 | filegroup(name = "libevent_all", srcs = glob(["**"]), visibility = ["//visibility:public"]) 20 | 21 | cmake( 22 | name = "libevent", 23 | lib_source = ":libevent_all", 24 | out_lib_dir = "lib", 25 | out_static_libs = ["libevent.a","libevent_pthreads.a"], 26 | out_shared_libs = ["libevent.so","libevent_pthreads.so"], 27 | visibility = ["//visibility:public"], 28 | ) 29 | -------------------------------------------------------------------------------- /third_party/rapidjson/BUILD: -------------------------------------------------------------------------------- 1 | # RapidJSON (rapidjson.org) library. 2 | # from https://github.com/Tencent/rapidjson 3 | 4 | package( 5 | default_visibility = ["//visibility:public"], 6 | ) 7 | 8 | licenses(["notice"]) # BSD/MIT. 9 | 10 | cc_library( 11 | name = "rapidjson", 12 | hdrs = glob(["include/rapidjson/**/*.h"]), 13 | includes = ["include"], 14 | ) 15 | --------------------------------------------------------------------------------