├── .gitmodules ├── BUILD.gn ├── CMakeLists.txt ├── License ├── README.md ├── src ├── http_parser │ ├── http_byte_range.cc │ ├── http_byte_range.h │ ├── http_parser.c │ ├── http_parser.h │ ├── http_request_headers.cc │ ├── http_request_headers.hh │ ├── http_response_headers.cc │ ├── http_response_headers.hh │ ├── http_util.cc │ ├── http_util.hh │ └── http_version.h ├── net │ ├── base │ │ ├── escape.cc │ │ ├── escape.h │ │ ├── parse_number.cc │ │ └── parse_number.h │ └── cert │ │ ├── internal │ │ ├── cert_error_id.cc │ │ ├── cert_error_id.h │ │ ├── cert_error_params.cc │ │ ├── cert_error_params.h │ │ ├── cert_errors.cc │ │ ├── cert_errors.h │ │ ├── parse_certificate.cc │ │ └── parse_certificate.h │ │ ├── pem.cc │ │ ├── pem.h │ │ ├── x509_certificate.cc │ │ └── x509_certificate.h ├── quic_stack_api.h ├── tQuicAlarmFactory.cc ├── tQuicAlarmFactory.hh ├── tQuicClock.cc ├── tQuicClock.hh ├── tQuicConnectionHelper.cc ├── tQuicConnectionHelper.hh ├── tQuicCryptoServerStream.cc ├── tQuicCryptoServerStream.hh ├── tQuicDispatcher.cc ├── tQuicDispatcher.hh ├── tQuicPacketWriter.cc ├── tQuicPacketWriter.hh ├── tQuicProofSource.cc ├── tQuicProofSource.hh ├── tQuicServerSession.cc ├── tQuicServerSession.hh ├── tQuicServerStream.cc ├── tQuicServerStream.hh ├── tQuicStack.cc └── tQuicStack.hh └── utils └── quic_stack_api.hh /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/quiche"] 2 | path = third_party/quiche 3 | url = https://github.com/bilibili/quiche.git 4 | -------------------------------------------------------------------------------- /BUILD.gn: -------------------------------------------------------------------------------- 1 | ### nginx quic stack build spec 2 | source_set("ngx_quic_stack_src") { 3 | sources = [ 4 | "src/tQuicAlarmFactory.hh", 5 | "src/tQuicAlarmFactory.cc", 6 | "src/tQuicClock.hh", 7 | "src/tQuicClock.cc", 8 | "src/tQuicProofSource.hh", 9 | "src/tQuicProofSource.cc", 10 | "src/tQuicConnectionHelper.hh", 11 | "src/tQuicConnectionHelper.cc", 12 | "src/tQuicCryptoServerStream.hh", 13 | "src/tQuicCryptoServerStream.cc", 14 | "src/tQuicDispatcher.hh", 15 | "src/tQuicDispatcher.cc", 16 | "src/tQuicServerSession.hh", 17 | "src/tQuicServerSession.cc", 18 | "src/tQuicServerStream.hh", 19 | "src/tQuicServerStream.cc", 20 | ] 21 | deps = [ 22 | "//net", 23 | "//net:simple_quic_tools", 24 | "//net:epoll_quic_tools", 25 | "//base", 26 | "//base/third_party/dynamic_annotations", 27 | "//url", 28 | ] 29 | } 30 | 31 | if (is_linux) { 32 | shared_library("ngxquicstack") { 33 | sources = [ 34 | "src/quic_stack_api.h", 35 | "src/tQuicStack.hh", 36 | "src/tQuicStack.cc", 37 | ] 38 | 39 | deps = [ 40 | ":ngx_quic_stack_src", 41 | "//net", 42 | "//base", 43 | "//third_party/boringssl", 44 | ] 45 | 46 | ldflags = [ 47 | "-Wl,-Bsymbolic", 48 | ] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.2) 2 | 3 | PROJECT(ngxquicstack) 4 | 5 | SET(CMAKE_POSITION_INDEPENDENT_CODE ON) 6 | SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb -fPIC") 7 | #SET(CMAKE_BUILD_TYPE "Debug") 8 | #SET(BUILD_SHARED_LIBS OFF) 9 | SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a") 10 | 11 | SET(CMAKE_CXX_STANDARD 17) 12 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unused-result -fPIC") 13 | 14 | ADD_SUBDIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/third_party/quiche quiche) 15 | 16 | INCLUDE_DIRECTORIES( 17 | ${CMAKE_CURRENT_SOURCE_DIR} 18 | ${CMAKE_CURRENT_SOURCE_DIR}/src 19 | ${CMAKE_BINARY_DIR} 20 | ${CMAKE_BINARY_DIR}/quiche 21 | ${CMAKE_CURRENT_SOURCE_DIR}/third_party 22 | ${CMAKE_CURRENT_SOURCE_DIR}/third_party/quiche 23 | ${CMAKE_CURRENT_SOURCE_DIR}/third_party/quiche/gquiche 24 | ${CMAKE_CURRENT_SOURCE_DIR}/third_party/quiche/googleurl 25 | ${CMAKE_CURRENT_SOURCE_DIR}/third_party/quiche/third_party/boringssl/include 26 | ${CMAKE_CURRENT_SOURCE_DIR}/third_party/quiche/third_party/spdlog/include 27 | ) 28 | 29 | message("BINARY_SRC = ${CMAKE_CURRENT_BINARY_DIR}") 30 | 31 | SET(NGINX_QUIC_STACK_SRCS 32 | third_party/quiche/base/files/file_path.cc 33 | third_party/quiche/base/files/file_path_constants.cc 34 | third_party/quiche/base/files/file_util.cc 35 | third_party/quiche/base/files/file_util_posix.cc 36 | third_party/quiche/base/strings/stringprintf.cc 37 | src/net/base/escape.cc 38 | src/net/base/parse_number.cc 39 | src/http_parser/http_parser.c 40 | src/http_parser/http_request_headers.cc 41 | src/http_parser/http_response_headers.cc 42 | src/http_parser/http_util.cc 43 | src/http_parser/http_byte_range.cc 44 | src/tQuicStack.cc 45 | src/tQuicAlarmFactory.cc 46 | src/tQuicClock.cc 47 | src/tQuicProofSource.cc 48 | src/tQuicConnectionHelper.cc 49 | src/tQuicCryptoServerStream.cc 50 | src/tQuicDispatcher.cc 51 | src/tQuicServerSession.cc 52 | src/tQuicServerStream.cc 53 | ) 54 | 55 | # Build bvc quic server binaries. 56 | ADD_LIBRARY(ngxquicstack SHARED ${NGINX_QUIC_STACK_SRCS}) 57 | TARGET_LINK_LIBRARIES(ngxquicstack -static-libstdc++ 58 | quiche 59 | ) 60 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-quic-stack 2 | 3 | This project is developed by BVC (Bilibili Video Cloud team). It provides a suite of easy-to-use HTTP/3(QUIC) protocol api for any server application who plans to use QUIC as network protocol. We also provide another example project [nginx-quic-module](https://github.com/bilibili/nginx_quic_module) to illustra the usage of the quic stack in nginx server. 4 | 5 | ## Getting Started 6 | 7 | ### Prerequisite 8 | 9 | This project requires quiche(https://github.com/bilibili/quiche) to compile. 10 | ```bash 11 | git submodule update --init --recursive 12 | ``` 13 | 14 | ### Build 15 | 16 | ```bash 17 | mkdir build && cd build 18 | cmake .. && make 19 | cd - 20 | ``` 21 | You will have libngxquicstack.so generated in the build directory once the project was successfully compiled. 22 | 23 | ### Use 24 | 25 | Simply copy the library and api header file under utils directory to equip your server with QUIC protocol. 26 | -------------------------------------------------------------------------------- /src/http_parser/http_byte_range.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2009 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | 7 | #include "common/platform/api/quiche_logging.h" 8 | #include "http_parser/http_byte_range.h" 9 | #include "absl/strings/str_format.h" 10 | 11 | namespace { 12 | const int64_t kPositionNotSpecified = -1; 13 | } // namespace 14 | 15 | namespace bvc { 16 | HttpByteRange::HttpByteRange() 17 | : first_byte_position_(kPositionNotSpecified), 18 | last_byte_position_(kPositionNotSpecified), 19 | suffix_length_(kPositionNotSpecified), 20 | has_computed_bounds_(false) { 21 | } 22 | // static 23 | HttpByteRange HttpByteRange::Bounded(int64_t first_byte_position, 24 | int64_t last_byte_position) { 25 | HttpByteRange range; 26 | range.set_first_byte_position(first_byte_position); 27 | range.set_last_byte_position(last_byte_position); 28 | return range; 29 | } 30 | // static 31 | HttpByteRange HttpByteRange::RightUnbounded(int64_t first_byte_position) { 32 | HttpByteRange range; 33 | range.set_first_byte_position(first_byte_position); 34 | return range; 35 | } 36 | // static 37 | HttpByteRange HttpByteRange::Suffix(int64_t suffix_length) { 38 | HttpByteRange range; 39 | range.set_suffix_length(suffix_length); 40 | return range; 41 | } 42 | bool HttpByteRange::IsSuffixByteRange() const { 43 | return suffix_length_ != kPositionNotSpecified; 44 | } 45 | bool HttpByteRange::HasFirstBytePosition() const { 46 | return first_byte_position_ != kPositionNotSpecified; 47 | } 48 | bool HttpByteRange::HasLastBytePosition() const { 49 | return last_byte_position_ != kPositionNotSpecified; 50 | } 51 | bool HttpByteRange::IsValid() const { 52 | if (suffix_length_ > 0) 53 | return true; 54 | return (first_byte_position_ >= 0 && 55 | (last_byte_position_ == kPositionNotSpecified || 56 | last_byte_position_ >= first_byte_position_)); 57 | } 58 | std::string HttpByteRange::GetHeaderValue() const { 59 | QUICHE_DCHECK(IsValid()); 60 | if (IsSuffixByteRange()) 61 | return absl::StrFormat("bytes=-%d", suffix_length()); 62 | QUICHE_DCHECK(HasFirstBytePosition()); 63 | if (!HasLastBytePosition()) 64 | return absl::StrFormat("bytes=%d-", first_byte_position()); 65 | return absl::StrFormat("bytes=%d-%d", first_byte_position(), last_byte_position()); 66 | } 67 | bool HttpByteRange::ComputeBounds(int64_t size) { 68 | if (size < 0) 69 | return false; 70 | if (has_computed_bounds_) 71 | return false; 72 | has_computed_bounds_ = true; 73 | // Empty values. 74 | if (!HasFirstBytePosition() && 75 | !HasLastBytePosition() && 76 | !IsSuffixByteRange()) { 77 | first_byte_position_ = 0; 78 | last_byte_position_ = size - 1; 79 | return true; 80 | } 81 | if (!IsValid()) 82 | return false; 83 | if (IsSuffixByteRange()) { 84 | first_byte_position_ = size - std::min(size, suffix_length_); 85 | last_byte_position_ = size - 1; 86 | return true; 87 | } 88 | if (first_byte_position_ < size) { 89 | if (HasLastBytePosition()) 90 | last_byte_position_ = std::min(size - 1, last_byte_position_); 91 | else 92 | last_byte_position_ = size - 1; 93 | return true; 94 | } 95 | return false; 96 | } 97 | } // namespace bvc 98 | -------------------------------------------------------------------------------- /src/http_parser/http_byte_range.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BVC_HTTP_PARSER_HTTP_BYTE_RANGE_H_ 6 | #define BVC_HTTP_PARSER_HTTP_BYTE_RANGE_H_ 7 | #include 8 | #include 9 | 10 | namespace bvc { 11 | // A container class that represents a "range" specified for range request 12 | // specified by RFC 7233 Section 2.1. 13 | // https://tools.ietf.org/html/rfc7233#section-2.1 14 | class HttpByteRange { 15 | public: 16 | HttpByteRange(); 17 | // Convenience constructors. 18 | static HttpByteRange Bounded(int64_t first_byte_position, 19 | int64_t last_byte_position); 20 | static HttpByteRange RightUnbounded(int64_t first_byte_position); 21 | static HttpByteRange Suffix(int64_t suffix_length); 22 | 23 | // Since this class is POD, we use constructor, assignment operator 24 | // and destructor provided by compiler. 25 | int64_t first_byte_position() const { return first_byte_position_; } 26 | void set_first_byte_position(int64_t value) { first_byte_position_ = value; } 27 | int64_t last_byte_position() const { return last_byte_position_; } 28 | void set_last_byte_position(int64_t value) { last_byte_position_ = value; } 29 | int64_t suffix_length() const { return suffix_length_; } 30 | void set_suffix_length(int64_t value) { suffix_length_ = value; } 31 | 32 | // Returns true if this is a suffix byte range. 33 | bool IsSuffixByteRange() const; 34 | 35 | // Returns true if the first byte position is specified in this request. 36 | bool HasFirstBytePosition() const; 37 | 38 | // Returns true if the last byte position is specified in this request. 39 | bool HasLastBytePosition() const; 40 | 41 | // Returns true if this range is valid. 42 | bool IsValid() const; 43 | 44 | // Gets the header string, e.g. "bytes=0-100", "bytes=100-", "bytes=-100". 45 | // Assumes range is valid. 46 | std::string GetHeaderValue() const; 47 | 48 | // A method that when given the size in bytes of a file, adjust the internal 49 | // |first_byte_position_| and |last_byte_position_| values according to the 50 | // range specified by this object. If the range specified is invalid with 51 | // regard to the size or |size| is negative, returns false and there will be 52 | // no side effect. 53 | // Returns false if this method is called more than once and there will be 54 | // no side effect. 55 | bool ComputeBounds(int64_t size); 56 | 57 | private: 58 | int64_t first_byte_position_; 59 | int64_t last_byte_position_; 60 | int64_t suffix_length_; 61 | bool has_computed_bounds_; 62 | }; 63 | } // namespace bvc 64 | 65 | #endif // BVC_HTTP_PARSER_HTTP_BYTE_RANGE_H_ 66 | -------------------------------------------------------------------------------- /src/http_parser/http_request_headers.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | #include 5 | 6 | #include "base/strings/stringprintf.h" 7 | #include "quic/platform/api/quic_logging.h" 8 | #include "googleurl/base/strings/string_split.h" 9 | #include "googleurl/base/strings/string_util.h" 10 | #include "http_parser/http_util.hh" 11 | #include "http_parser/http_request_headers.hh" 12 | 13 | namespace bvc { 14 | 15 | const char HttpRequestHeaders::kConnectMethod[] = "CONNECT"; 16 | const char HttpRequestHeaders::kGetMethod[] = "GET"; 17 | const char HttpRequestHeaders::kHeadMethod[] = "HEAD"; 18 | const char HttpRequestHeaders::kOptionsMethod[] = "OPTIONS"; 19 | const char HttpRequestHeaders::kPostMethod[] = "POST"; 20 | const char HttpRequestHeaders::kTraceMethod[] = "TRACE"; 21 | const char HttpRequestHeaders::kTrackMethod[] = "TRACK"; 22 | const char HttpRequestHeaders::kAccept[] = "Accept"; 23 | const char HttpRequestHeaders::kAcceptCharset[] = "Accept-Charset"; 24 | const char HttpRequestHeaders::kAcceptEncoding[] = "Accept-Encoding"; 25 | const char HttpRequestHeaders::kAcceptLanguage[] = "Accept-Language"; 26 | const char HttpRequestHeaders::kAuthorization[] = "Authorization"; 27 | const char HttpRequestHeaders::kCacheControl[] = "Cache-Control"; 28 | const char HttpRequestHeaders::kConnection[] = "Connection"; 29 | const char HttpRequestHeaders::kContentLength[] = "Content-Length"; 30 | const char HttpRequestHeaders::kContentType[] = "Content-Type"; 31 | const char HttpRequestHeaders::kCookie[] = "Cookie"; 32 | const char HttpRequestHeaders::kHost[] = "Host"; 33 | const char HttpRequestHeaders::kIfMatch[] = "If-Match"; 34 | const char HttpRequestHeaders::kIfModifiedSince[] = "If-Modified-Since"; 35 | const char HttpRequestHeaders::kIfNoneMatch[] = "If-None-Match"; 36 | const char HttpRequestHeaders::kIfRange[] = "If-Range"; 37 | const char HttpRequestHeaders::kIfUnmodifiedSince[] = "If-Unmodified-Since"; 38 | const char HttpRequestHeaders::kOrigin[] = "Origin"; 39 | const char HttpRequestHeaders::kPragma[] = "Pragma"; 40 | const char HttpRequestHeaders::kProxyAuthorization[] = "Proxy-Authorization"; 41 | const char HttpRequestHeaders::kProxyConnection[] = "Proxy-Connection"; 42 | const char HttpRequestHeaders::kRange[] = "Range"; 43 | const char HttpRequestHeaders::kReferer[] = "Referer"; 44 | const char HttpRequestHeaders::kTransferEncoding[] = "Transfer-Encoding"; 45 | const char HttpRequestHeaders::kUserAgent[] = "User-Agent"; 46 | 47 | HttpRequestHeaders::HeaderKeyValuePair::HeaderKeyValuePair() = default; 48 | 49 | HttpRequestHeaders::HeaderKeyValuePair::HeaderKeyValuePair( 50 | const gurl_base::StringPiece& key, 51 | const gurl_base::StringPiece& value) 52 | : key(key.data(), key.size()), value(value.data(), value.size()) {} 53 | 54 | HttpRequestHeaders::Iterator::Iterator(const HttpRequestHeaders& headers) 55 | : started_(false), 56 | curr_(headers.headers_.begin()), 57 | end_(headers.headers_.end()) {} 58 | 59 | HttpRequestHeaders::Iterator::~Iterator() = default; 60 | 61 | bool HttpRequestHeaders::Iterator::GetNext() { 62 | if (!started_) { 63 | started_ = true; 64 | return curr_ != end_; 65 | } 66 | 67 | if (curr_ == end_) 68 | return false; 69 | 70 | ++curr_; 71 | return curr_ != end_; 72 | } 73 | 74 | HttpRequestHeaders::HttpRequestHeaders() = default; 75 | HttpRequestHeaders::HttpRequestHeaders(const HttpRequestHeaders& other) = 76 | default; 77 | HttpRequestHeaders::HttpRequestHeaders(HttpRequestHeaders&& other) = default; 78 | HttpRequestHeaders::~HttpRequestHeaders() = default; 79 | 80 | HttpRequestHeaders& HttpRequestHeaders::operator=( 81 | const HttpRequestHeaders& other) = default; 82 | HttpRequestHeaders& HttpRequestHeaders::operator=(HttpRequestHeaders&& other) = 83 | default; 84 | 85 | bool HttpRequestHeaders::GetHeader(const gurl_base::StringPiece& key, 86 | std::string* out) const { 87 | auto it = FindHeader(key); 88 | if (it == headers_.end()) 89 | return false; 90 | out->assign(it->value); 91 | return true; 92 | } 93 | 94 | void HttpRequestHeaders::Clear() { 95 | headers_.clear(); 96 | } 97 | 98 | void HttpRequestHeaders::SetHeader(const gurl_base::StringPiece& key, 99 | const gurl_base::StringPiece& value) { 100 | // Invalid header names or values could mean clients can attach 101 | // browser-internal headers. 102 | QUICHE_DCHECK(HttpUtil::IsValidHeaderName(key)) << key; 103 | QUICHE_DCHECK(HttpUtil::IsValidHeaderValue(value)) << key << ":" << value; 104 | SetHeaderInternal(key, value); 105 | } 106 | 107 | void HttpRequestHeaders::SetHeaderIfMissing(const gurl_base::StringPiece& key, 108 | const gurl_base::StringPiece& value) { 109 | // Invalid header names or values could mean clients can attach 110 | // browser-internal headers. 111 | QUICHE_DCHECK(HttpUtil::IsValidHeaderName(key)); 112 | QUICHE_DCHECK(HttpUtil::IsValidHeaderValue(value)); 113 | auto it = FindHeader(key); 114 | if (it == headers_.end()) 115 | headers_.push_back(HeaderKeyValuePair(key, value)); 116 | } 117 | 118 | void HttpRequestHeaders::RemoveHeader(const gurl_base::StringPiece& key) { 119 | auto it = FindHeader(key); 120 | if (it != headers_.end()) 121 | headers_.erase(it); 122 | } 123 | 124 | void HttpRequestHeaders::AddHeaderFromString( 125 | const gurl_base::StringPiece& header_line) { 126 | QUICHE_DCHECK_EQ(std::string::npos, header_line.find("\r\n")) 127 | << "\"" << header_line << "\" contains CRLF."; 128 | 129 | const std::string::size_type key_end_index = header_line.find(":"); 130 | if (key_end_index == std::string::npos) { 131 | QUIC_LOG(DFATAL) << "\"" << header_line << "\" is missing colon delimiter."; 132 | return; 133 | } 134 | 135 | if (key_end_index == 0) { 136 | QUIC_LOG(DFATAL) << "\"" << header_line << "\" is missing header key."; 137 | return; 138 | } 139 | 140 | const gurl_base::StringPiece header_key(header_line.data(), key_end_index); 141 | if (!HttpUtil::IsValidHeaderName(header_key)) { 142 | QUIC_LOG(DFATAL) << "\"" << header_line << "\" has invalid header key."; 143 | return; 144 | } 145 | 146 | const std::string::size_type value_index = key_end_index + 1; 147 | 148 | if (value_index < header_line.size()) { 149 | gurl_base::StringPiece header_value(header_line.data() + value_index, 150 | header_line.size() - value_index); 151 | header_value = HttpUtil::TrimLWS(header_value); 152 | if (!HttpUtil::IsValidHeaderValue(header_value)) { 153 | QUIC_LOG(DFATAL) << "\"" << header_line << "\" has invalid header value."; 154 | return; 155 | } 156 | SetHeader(header_key, header_value); 157 | } else if (value_index == header_line.size()) { 158 | SetHeader(header_key, ""); 159 | } else { 160 | QUIC_NOTREACHED(); 161 | } 162 | } 163 | 164 | void HttpRequestHeaders::AddHeadersFromString( 165 | const gurl_base::StringPiece& headers) { 166 | for (const gurl_base::StringPiece& header : gurl_base::SplitStringPieceUsingSubstr( 167 | headers, "\r\n", gurl_base::TRIM_WHITESPACE, gurl_base::SPLIT_WANT_NONEMPTY)) { 168 | AddHeaderFromString(header); 169 | } 170 | } 171 | 172 | void HttpRequestHeaders::MergeFrom(const HttpRequestHeaders& other) { 173 | for (auto it = other.headers_.begin(); it != other.headers_.end(); ++it) { 174 | SetHeader(it->key, it->value); 175 | } 176 | } 177 | 178 | std::string HttpRequestHeaders::ToString() const { 179 | std::string output; 180 | for (auto it = headers_.begin(); it != headers_.end(); ++it) { 181 | base::StringAppendF(&output, "%s: %s\r\n", it->key.c_str(), 182 | it->value.c_str()); 183 | } 184 | output.append("\r\n"); 185 | return output; 186 | } 187 | 188 | HttpRequestHeaders::HeaderVector::iterator HttpRequestHeaders::FindHeader( 189 | const gurl_base::StringPiece& key) { 190 | for (auto it = headers_.begin(); it != headers_.end(); ++it) { 191 | if (gurl_base::EqualsCaseInsensitiveASCII(key, it->key)) 192 | return it; 193 | } 194 | 195 | return headers_.end(); 196 | } 197 | 198 | HttpRequestHeaders::HeaderVector::const_iterator HttpRequestHeaders::FindHeader( 199 | const gurl_base::StringPiece& key) const { 200 | for (auto it = headers_.begin(); it != headers_.end(); ++it) { 201 | if (gurl_base::EqualsCaseInsensitiveASCII(key, it->key)) 202 | return it; 203 | } 204 | 205 | return headers_.end(); 206 | } 207 | 208 | void HttpRequestHeaders::SetHeaderInternal(const gurl_base::StringPiece& key, 209 | const gurl_base::StringPiece& value) { 210 | auto it = FindHeader(key); 211 | if (it != headers_.end()) 212 | it->value.assign(value.data(), value.size()); 213 | else 214 | headers_.push_back(HeaderKeyValuePair(key, value)); 215 | } 216 | 217 | } // namespace bvc 218 | -------------------------------------------------------------------------------- /src/http_parser/http_request_headers.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // HttpRequestHeaders manages the request headers. 5 | // It maintains these in a vector of header key/value pairs, thereby maintaining 6 | // the order of the headers. This means that any lookups are linear time 7 | // operations. 8 | 9 | #ifndef BVC_HTTP_PARSER_HTTP_REQUEST_HEADERS_HH_ 10 | #define BVC_HTTP_PARSER_HTTP_REQUEST_HEADERS_HH_ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "googleurl/base/macros.h" 17 | #include "googleurl/base/strings/string_piece.h" 18 | 19 | namespace bvc { 20 | 21 | class HttpRequestHeaders { 22 | public: 23 | struct HeaderKeyValuePair { 24 | HeaderKeyValuePair(); 25 | HeaderKeyValuePair(const gurl_base::StringPiece& key, 26 | const gurl_base::StringPiece& value); 27 | 28 | std::string key; 29 | std::string value; 30 | }; 31 | 32 | typedef std::vector HeaderVector; 33 | 34 | class Iterator { 35 | public: 36 | explicit Iterator(const HttpRequestHeaders& headers); 37 | ~Iterator(); 38 | 39 | // Advances the iterator to the next header, if any. Returns true if there 40 | // is a next header. Use name() and value() methods to access the resultant 41 | // header name and value. 42 | bool GetNext(); 43 | 44 | // These two accessors are only valid if GetNext() returned true. 45 | const std::string& name() const { return curr_->key; } 46 | const std::string& value() const { return curr_->value; } 47 | 48 | private: 49 | bool started_; 50 | HttpRequestHeaders::HeaderVector::const_iterator curr_; 51 | const HttpRequestHeaders::HeaderVector::const_iterator end_; 52 | 53 | DISALLOW_COPY_AND_ASSIGN(Iterator); 54 | }; 55 | 56 | static const char kConnectMethod[]; 57 | static const char kGetMethod[]; 58 | static const char kHeadMethod[]; 59 | static const char kOptionsMethod[]; 60 | static const char kPostMethod[]; 61 | static const char kTraceMethod[]; 62 | static const char kTrackMethod[]; 63 | 64 | static const char kAccept[]; 65 | static const char kAcceptCharset[]; 66 | static const char kAcceptEncoding[]; 67 | static const char kAcceptLanguage[]; 68 | static const char kAuthorization[]; 69 | static const char kCacheControl[]; 70 | static const char kConnection[]; 71 | static const char kContentType[]; 72 | static const char kCookie[]; 73 | static const char kContentLength[]; 74 | static const char kHost[]; 75 | static const char kIfMatch[]; 76 | static const char kIfModifiedSince[]; 77 | static const char kIfNoneMatch[]; 78 | static const char kIfRange[]; 79 | static const char kIfUnmodifiedSince[]; 80 | static const char kOrigin[]; 81 | static const char kPragma[]; 82 | static const char kProxyAuthorization[]; 83 | static const char kProxyConnection[]; 84 | static const char kRange[]; 85 | static const char kReferer[]; 86 | static const char kTransferEncoding[]; 87 | static const char kUserAgent[]; 88 | 89 | HttpRequestHeaders(); 90 | HttpRequestHeaders(const HttpRequestHeaders& other); 91 | HttpRequestHeaders(HttpRequestHeaders&& other); 92 | ~HttpRequestHeaders(); 93 | 94 | HttpRequestHeaders& operator=(const HttpRequestHeaders& other); 95 | HttpRequestHeaders& operator=(HttpRequestHeaders&& other); 96 | 97 | bool IsEmpty() const { return headers_.empty(); } 98 | 99 | bool HasHeader(const gurl_base::StringPiece& key) const { 100 | return FindHeader(key) != headers_.end(); 101 | } 102 | 103 | // Gets the first header that matches |key|. If found, returns true and 104 | // writes the value to |out|. 105 | bool GetHeader(const gurl_base::StringPiece& key, std::string* out) const; 106 | 107 | // Clears all the headers. 108 | void Clear(); 109 | 110 | // Sets the header value pair for |key| and |value|. If |key| already exists, 111 | // then the header value is modified, but the key is untouched, and the order 112 | // in the vector remains the same. When comparing |key|, case is ignored. 113 | // The caller must ensure that |key| passes HttpUtil::IsValidHeaderName() and 114 | // |value| passes HttpUtil::IsValidHeaderValue(). 115 | void SetHeader(const gurl_base::StringPiece& key, const gurl_base::StringPiece& value); 116 | 117 | // Does the same as above but without internal DCHECKs for validations. 118 | void SetHeaderWithoutCheckForTesting(const gurl_base::StringPiece& key, 119 | const gurl_base::StringPiece& value) { 120 | SetHeaderInternal(key, value); 121 | } 122 | 123 | // Sets the header value pair for |key| and |value|, if |key| does not exist. 124 | // If |key| already exists, the call is a no-op. 125 | // When comparing |key|, case is ignored. 126 | // 127 | // The caller must ensure that |key| passes HttpUtil::IsValidHeaderName() and 128 | // |value| passes HttpUtil::IsValidHeaderValue(). 129 | void SetHeaderIfMissing(const gurl_base::StringPiece& key, 130 | const gurl_base::StringPiece& value); 131 | 132 | // Removes the first header that matches (case insensitive) |key|. 133 | void RemoveHeader(const gurl_base::StringPiece& key); 134 | 135 | // Parses the header from a string and calls SetHeader() with it. This string 136 | // should not contain any CRLF. As per RFC7230 Section 3.2, the format is: 137 | // 138 | // header-field = field-name ":" OWS field-value OWS 139 | // 140 | // field-name = token 141 | // field-value = *( field-content / obs-fold ) 142 | // field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] 143 | // field-vchar = VCHAR / obs-text 144 | // 145 | // obs-fold = CRLF 1*( SP / HTAB ) 146 | // ; obsolete line folding 147 | // ; see Section 3.2.4 148 | // 149 | // AddHeaderFromString() will trim any LWS surrounding the 150 | // field-content. 151 | void AddHeaderFromString(const gurl_base::StringPiece& header_line); 152 | 153 | // Same thing as AddHeaderFromString() except that |headers| is a "\r\n" 154 | // delimited string of header lines. It will split up the string by "\r\n" 155 | // and call AddHeaderFromString() on each. 156 | void AddHeadersFromString(const gurl_base::StringPiece& headers); 157 | 158 | // Calls SetHeader() on each header from |other|, maintaining order. 159 | void MergeFrom(const HttpRequestHeaders& other); 160 | 161 | // Copies from |other| to |this|. 162 | void CopyFrom(const HttpRequestHeaders& other) { *this = other; } 163 | 164 | void Swap(HttpRequestHeaders* other) { headers_.swap(other->headers_); } 165 | 166 | // Serializes HttpRequestHeaders to a string representation. Joins all the 167 | // header keys and values with ": ", and inserts "\r\n" between each header 168 | // line, and adds the trailing "\r\n". 169 | std::string ToString() const; 170 | 171 | const HeaderVector& GetHeaderVector() const { return headers_; } 172 | 173 | private: 174 | HeaderVector::iterator FindHeader(const gurl_base::StringPiece& key); 175 | HeaderVector::const_iterator FindHeader(const gurl_base::StringPiece& key) const; 176 | 177 | void SetHeaderInternal(const gurl_base::StringPiece& key, 178 | const gurl_base::StringPiece& value); 179 | 180 | HeaderVector headers_; 181 | 182 | // Allow the copy construction and operator= to facilitate copying in 183 | // HttpRequestHeaders. 184 | // TODO(willchan): Investigate to see if we can remove the need to copy 185 | // HttpRequestHeaders. 186 | // DISALLOW_COPY_AND_ASSIGN(HttpRequestHeaders); 187 | }; 188 | 189 | } // namespace bvc 190 | 191 | #endif // BVC_HTTP_PARSER_HTTP_REQUEST_HEADERS_HH_ 192 | -------------------------------------------------------------------------------- /src/http_parser/http_response_headers.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BVC_HTTP_PARSER_HTTP_RESPONSE_HEADERS_H_ 6 | #define BVC_HTTP_PARSER_HTTP_RESPONSE_HEADERS_H_ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "googleurl/base/macros.h" 14 | #include "googleurl/base/strings/string_piece.h" 15 | #include "quic/core/quic_time.h" 16 | #include "http_parser/http_version.h" 17 | 18 | namespace bvc { 19 | class HttpByteRange; 20 | enum ValidationType { 21 | VALIDATION_NONE, // The resource is fresh. 22 | VALIDATION_ASYNCHRONOUS, // The resource requires async revalidation. 23 | VALIDATION_SYNCHRONOUS // The resource requires sync revalidation. 24 | }; 25 | // HttpResponseHeaders: parses and holds HTTP response headers. 26 | class HttpResponseHeaders { 27 | public: 28 | // Persist options. 29 | typedef int PersistOptions; 30 | static const PersistOptions PERSIST_RAW = -1; // Raw, unparsed headers. 31 | static const PersistOptions PERSIST_ALL = 0; // Parsed headers. 32 | static const PersistOptions PERSIST_SANS_COOKIES = 1 << 0; 33 | static const PersistOptions PERSIST_SANS_CHALLENGES = 1 << 1; 34 | static const PersistOptions PERSIST_SANS_HOP_BY_HOP = 1 << 2; 35 | static const PersistOptions PERSIST_SANS_NON_CACHEABLE = 1 << 3; 36 | static const PersistOptions PERSIST_SANS_RANGES = 1 << 4; 37 | static const PersistOptions PERSIST_SANS_SECURITY_STATE = 1 << 5; 38 | 39 | struct FreshnessLifetimes { 40 | FreshnessLifetimes() : freshness(quic::QuicTime::Delta::Zero()), staleness(quic::QuicTime::Delta::Zero()) {} 41 | // How long the resource will be fresh for. 42 | //base::TimeDelta freshness; 43 | quic::QuicTime::Delta freshness; 44 | // How long after becoming not fresh that the resource will be stale but 45 | // usable (if async revalidation is enabled). 46 | //base::TimeDelta staleness; 47 | quic::QuicTime::Delta staleness; 48 | }; 49 | 50 | static const char kContentRange[]; 51 | HttpResponseHeaders() = delete; 52 | // Parses the given raw_headers. raw_headers should be formatted thus: 53 | // includes the http status response line, each line is \0-terminated, and 54 | // it's terminated by an empty line (ie, 2 \0s in a row). 55 | // (Note that line continuations should have already been joined; 56 | // see HttpUtil::AssembleRawHeaders) 57 | // 58 | // HttpResponseHeaders does not perform any encoding changes on the input. 59 | // 60 | explicit HttpResponseHeaders(const std::string& raw_headers); 61 | // Initializes from the representation stored in the given pickle. The data 62 | // for this object is found relative to the given pickle_iter, which should 63 | // be passed to the pickle's various Read* methods. 64 | //explicit HttpResponseHeaders(base::PickleIterator* pickle_iter); 65 | ~HttpResponseHeaders(); 66 | 67 | // Takes headers as an ASCII string and tries to parse them as HTTP response 68 | // headers. returns nullptr on failure. Unlike the HttpResponseHeaders 69 | // constructor that takes a std::string, HttpUtil::AssembleRawHeaders should 70 | // not be called on |headers| before calling this method. 71 | static std::shared_ptr TryToCreate( 72 | gurl_base::StringPiece headers); 73 | 74 | // Appends a representation of this object to the given pickle. 75 | // The options argument can be a combination of PersistOptions. 76 | //void Persist(base::Pickle* pickle, PersistOptions options); 77 | 78 | // Performs header merging as described in 13.5.3 of RFC 2616. 79 | void Update(const HttpResponseHeaders& new_headers); 80 | 81 | // Removes all instances of a particular header. 82 | void RemoveHeader(gurl_base::StringPiece name); 83 | 84 | // Removes all instances of particular headers. 85 | void RemoveHeaders(const std::unordered_set& header_names); 86 | 87 | // Removes a particular header line. The header name is compared 88 | // case-insensitively. 89 | void RemoveHeaderLine(const std::string& name, const std::string& value); 90 | 91 | // Adds the specified response header. If a header with the same name is 92 | // already stored, the two headers are not merged together by this method; the 93 | // one provided is simply put at the end of the list. 94 | void AddHeader(gurl_base::StringPiece name, gurl_base::StringPiece value); 95 | 96 | // Sets the specified response header, removing any matching old one if 97 | // present. The new header is added to the end of the header list, rather than 98 | // replacing the old one. This is the same as calling RemoveHeader() followed 99 | // be SetHeader(). 100 | void SetHeader(gurl_base::StringPiece name, gurl_base::StringPiece value); 101 | 102 | // Adds a cookie header. |cookie_string| should be the header value without 103 | // the header name (Set-Cookie). 104 | void AddCookie(const std::string& cookie_string); 105 | 106 | // Replaces the current status line with the provided one (|new_status| should 107 | // not have any EOL). 108 | void ReplaceStatusLine(const std::string& new_status); 109 | 110 | // Updates headers (Content-Length and Content-Range) in the |headers| to 111 | // include the right content length and range for |byte_range|. This also 112 | // updates HTTP status line if |replace_status_line| is true. 113 | // |byte_range| must have a valid, bounded range (i.e. coming from a valid 114 | // response or should be usable for a response). 115 | void UpdateWithNewRange(const HttpByteRange& byte_range, 116 | int64_t resource_size, 117 | bool replace_status_line); 118 | 119 | // Fetch the "normalized" value of a single header, where all values for the 120 | // header name are separated by commas. See the GetNormalizedHeaders for 121 | // format details. Returns false if this header wasn't found. 122 | // 123 | // NOTE: Do not make any assumptions about the encoding of this output 124 | // string. It may be non-ASCII, and the encoding used by the server is not 125 | // necessarily known to us. Do not assume that this output is UTF-8! 126 | bool GetNormalizedHeader(gurl_base::StringPiece name, std::string* value) const; 127 | 128 | // Returns the normalized status line. 129 | std::string GetStatusLine() const; 130 | 131 | // Get the HTTP version of the normalized status line. 132 | HttpVersion GetHttpVersion() const { 133 | return http_version_; 134 | } 135 | 136 | // Get the HTTP status text of the normalized status line. 137 | std::string GetStatusText() const; 138 | 139 | // Enumerate the "lines" of the response headers. This skips over the status 140 | // line. Use GetStatusLine if you are interested in that. Note that this 141 | // method returns the un-coalesced response header lines, so if a response 142 | // header appears on multiple lines, then it will appear multiple times in 143 | // this enumeration (in the order the header lines were received from the 144 | // server). Also, a given header might have an empty value. Initialize a 145 | // 'size_t' variable to 0 and pass it by address to EnumerateHeaderLines. 146 | // Call EnumerateHeaderLines repeatedly until it returns false. The 147 | // out-params 'name' and 'value' are set upon success. 148 | bool EnumerateHeaderLines(size_t* iter, 149 | std::string* name, 150 | std::string* value) const; 151 | 152 | // Enumerate the values of the specified header. If you are only interested 153 | // in the first header, then you can pass nullptr for the 'iter' parameter. 154 | // Otherwise, to iterate across all values for the specified header, 155 | // initialize a 'size_t' variable to 0 and pass it by address to 156 | // EnumerateHeader. Note that a header might have an empty value. Call 157 | // EnumerateHeader repeatedly until it returns false. 158 | // 159 | // Unless a header is explicitly marked as non-coalescing (see 160 | // HttpUtil::IsNonCoalescingHeader), headers that contain 161 | // comma-separated lists are treated "as if" they had been sent as 162 | // distinct headers. That is, a header of "Foo: a, b, c" would 163 | // enumerate into distinct values of "a", "b", and "c". This is also 164 | // true for headers that occur multiple times in a response; unless 165 | // they are marked non-coalescing, "Foo: a, b" followed by "Foo: c" 166 | // will enumerate to "a", "b", "c". Commas inside quoted strings are ignored, 167 | // for example a header of 'Foo: "a, b", "c"' would enumerate as '"a, b"', 168 | // '"c"'. 169 | // 170 | // This can cause issues for headers that might have commas in fields that 171 | // aren't quoted strings, for example a header of "Foo: , " would 172 | // enumerate as '', '', rather than as '', ''. 173 | // 174 | // To handle cases such as this, use GetNormalizedHeader to return the full 175 | // concatenated header, and then parse manually. 176 | bool EnumerateHeader(size_t* iter, 177 | gurl_base::StringPiece name, 178 | std::string* value) const; 179 | 180 | // Returns true if the response contains the specified header-value pair. 181 | // Both name and value are compared case insensitively. 182 | bool HasHeaderValue(gurl_base::StringPiece name, gurl_base::StringPiece value) const; 183 | 184 | // Returns true if the response contains the specified header. 185 | // The name is compared case insensitively. 186 | bool HasHeader(gurl_base::StringPiece name) const; 187 | 188 | // Get the mime type and charset values in lower case form from the headers. 189 | // Empty strings are returned if the values are not present. 190 | void GetMimeTypeAndCharset(std::string* mime_type, 191 | std::string* charset) const; 192 | 193 | // Get the mime type in lower case from the headers. If there's no mime 194 | // type, returns false. 195 | bool GetMimeType(std::string* mime_type) const; 196 | 197 | // Get the charset in lower case from the headers. If there's no charset, 198 | // returns false. 199 | bool GetCharset(std::string* charset) const; 200 | 201 | // Returns true if this response corresponds to a redirect. The target 202 | // location of the redirect is optionally returned if location is non-null. 203 | bool IsRedirect(std::string* location) const; 204 | 205 | // Returns true if the HTTP response code passed in corresponds to a 206 | // redirect. 207 | static bool IsRedirectResponseCode(int response_code); 208 | 209 | // Returns VALIDATION_NONE if the response can be reused without 210 | // validation. VALIDATION_ASYNCHRONOUS means the response can be re-used, but 211 | // asynchronous revalidation must be performed. VALIDATION_SYNCHRONOUS means 212 | // that the result cannot be reused without revalidation. 213 | // The result is relative to the current_time parameter, which is 214 | // a parameter to support unit testing. The request_time parameter indicates 215 | // the time at which the request was made that resulted in this response, 216 | // which was received at response_time. 217 | ValidationType RequiresValidation(const quic::QuicTime& request_time, 218 | const quic::QuicTime& response_time, 219 | const quic::QuicTime& current_time) const; 220 | // Calculates the amount of time the server claims the response is fresh from 221 | // the time the response was generated. See section 13.2.4 of RFC 2616. See 222 | // RequiresValidation for a description of the response_time parameter. See 223 | // the definition of FreshnessLifetimes above for the meaning of the return 224 | // value. See RFC 5861 section 3 for the definition of 225 | // stale-while-revalidate. 226 | FreshnessLifetimes GetFreshnessLifetimes( 227 | const quic::QuicTime& response_time) const; 228 | 229 | // Returns the age of the response. See section 13.2.3 of RFC 2616. 230 | // See RequiresValidation for a description of this method's parameters. 231 | quic::QuicTime::Delta GetCurrentAge(const quic::QuicTime& request_time, 232 | const quic::QuicTime& response_time, 233 | const quic::QuicTime& current_time) const; 234 | 235 | // The following methods extract values from the response headers. If a 236 | // value is not present, or is invalid, then false is returned. Otherwise, 237 | // true is returned and the out param is assigned to the corresponding value. 238 | bool GetMaxAgeValue(quic::QuicTime::Delta* value) const; 239 | bool GetAgeValue(quic::QuicTime::Delta* value) const; 240 | bool GetDateValue(quic::QuicTime* value) const; 241 | bool GetLastModifiedValue(quic::QuicTime* value) const; 242 | bool GetExpiresValue(quic::QuicTime* value) const; 243 | bool GetStaleWhileRevalidateValue(quic::QuicTime::Delta* value) const; 244 | 245 | // Extracts the time value of a particular header. This method looks for the 246 | // first matching header value and parses its value as a HTTP-date. 247 | bool GetTimeValuedHeader(const std::string& name, quic::QuicTime* result) const; 248 | 249 | // Determines if this response indicates a keep-alive connection. 250 | bool IsKeepAlive() const; 251 | 252 | // Extracts the value of the Content-Length header or returns -1 if there is 253 | // no such header in the response. 254 | int64_t GetContentLength() const; 255 | 256 | // Extracts the value of the specified header or returns -1 if there is no 257 | // such header in the response. 258 | int64_t GetInt64HeaderValue(const std::string& header) const; 259 | 260 | // Extracts the values in a Content-Range header and returns true if all three 261 | // values are present and valid for a 206 response; otherwise returns false. 262 | // The following values will be outputted: 263 | // |*first_byte_position| = inclusive position of the first byte of the range 264 | // |*last_byte_position| = inclusive position of the last byte of the range 265 | // |*instance_length| = size in bytes of the object requested 266 | // If this method returns false, then all of the outputs will be -1. 267 | bool GetContentRangeFor206(int64_t* first_byte_position, 268 | int64_t* last_byte_position, 269 | int64_t* instance_length) const; 270 | 271 | // Returns true if the response is chunk-encoded. 272 | bool IsChunkEncoded() const; 273 | 274 | // Creates a Value for use with the NetLog containing the response headers. 275 | //base::Value NetLogParams(NetLogCaptureMode capture_mode) const; 276 | 277 | // Returns the HTTP response code. This is 0 if the response code text seems 278 | // to exist but could not be parsed. Otherwise, it defaults to 200 if the 279 | // response code is not found in the raw headers. 280 | int response_code() const { return response_code_; } 281 | 282 | // Returns the raw header string. 283 | const std::string& raw_headers() const { return raw_headers_; } 284 | 285 | // Returns true if |name| is a cookie related header name. This is consistent 286 | // with |PERSIST_SANS_COOKIES|. 287 | static bool IsCookieResponseHeader(gurl_base::StringPiece name); 288 | 289 | 290 | private: 291 | //friend class base::RefCountedThreadSafe; 292 | using HeaderSet = std::unordered_set; 293 | 294 | // The members of this structure point into raw_headers_. 295 | struct ParsedHeader; 296 | typedef std::vector HeaderList; 297 | // ~HttpResponseHeaders(); 298 | 299 | // Initializes from the given raw headers. 300 | void Parse(const std::string& raw_input); 301 | 302 | // Helper function for ParseStatusLine. 303 | // Tries to extract the "HTTP/X.Y" from a status line formatted like: 304 | // HTTP/1.1 200 OK 305 | // with line_begin and end pointing at the begin and end of this line. If the 306 | // status line is malformed, returns HttpVersion(0,0). 307 | static HttpVersion ParseVersion(std::string::const_iterator line_begin, 308 | std::string::const_iterator line_end); 309 | 310 | // Tries to extract the status line from a header block, given the first 311 | // line of said header block. If the status line is malformed, we'll 312 | // construct a valid one. Example input: 313 | // HTTP/1.1 200 OK 314 | // with line_begin and end pointing at the begin and end of this line. 315 | // Output will be a normalized version of this. 316 | void ParseStatusLine(std::string::const_iterator line_begin, 317 | std::string::const_iterator line_end, 318 | bool has_headers); 319 | 320 | // Find the header in our list (case-insensitive) starting with |parsed_| at 321 | // index |from|. Returns string::npos if not found. 322 | size_t FindHeader(size_t from, gurl_base::StringPiece name) const; 323 | 324 | // Search the Cache-Control header for a directive matching |directive|. If 325 | // present, treat its value as a time offset in seconds, write it to |result|, 326 | // and return true. 327 | bool GetCacheControlDirective(gurl_base::StringPiece directive, 328 | quic::QuicTime::Delta* result) const; 329 | 330 | // Add a header->value pair to our list. If we already have header in our 331 | // list, append the value to it. 332 | void AddHeader(std::string::const_iterator name_begin, 333 | std::string::const_iterator name_end, 334 | std::string::const_iterator value_begin, 335 | std::string::const_iterator value_end); 336 | 337 | // Add to parsed_ given the fields of a ParsedHeader object. 338 | void AddToParsed(std::string::const_iterator name_begin, 339 | std::string::const_iterator name_end, 340 | std::string::const_iterator value_begin, 341 | std::string::const_iterator value_end); 342 | // Replaces the current headers with the merged version of |raw_headers| and 343 | // the current headers without the headers in |headers_to_remove|. Note that 344 | // |headers_to_remove| are removed from the current headers (before the 345 | // merge), not after the merge. 346 | void MergeWithHeaders(const std::string& raw_headers, 347 | const HeaderSet& headers_to_remove); 348 | 349 | // Adds the values from any 'cache-control: no-cache="foo,bar"' headers. 350 | void AddNonCacheableHeaders(HeaderSet* header_names) const; 351 | 352 | // Adds the set of header names that contain cookie values. 353 | static void AddSensitiveHeaders(HeaderSet* header_names); 354 | 355 | // Adds the set of rfc2616 hop-by-hop response headers. 356 | static void AddHopByHopHeaders(HeaderSet* header_names); 357 | 358 | // Adds the set of challenge response headers. 359 | static void AddChallengeHeaders(HeaderSet* header_names); 360 | 361 | // Adds the set of cookie response headers. 362 | static void AddCookieHeaders(HeaderSet* header_names); 363 | 364 | // Adds the set of content range response headers. 365 | static void AddHopContentRangeHeaders(HeaderSet* header_names); 366 | 367 | // Adds the set of transport security state headers. 368 | static void AddSecurityStateHeaders(HeaderSet* header_names); 369 | 370 | // We keep a list of ParsedHeader objects. These tell us where to locate the 371 | // header-value pairs within raw_headers_. 372 | HeaderList parsed_; 373 | 374 | // The raw_headers_ consists of the normalized status line (terminated with a 375 | // null byte) and then followed by the raw null-terminated headers from the 376 | // input that was passed to our constructor. We preserve the input [*] to 377 | // maintain as much ancillary fidelity as possible (since it is sometimes 378 | // hard to tell what may matter down-stream to a consumer of XMLHttpRequest). 379 | // [*] The status line may be modified. 380 | std::string raw_headers_; 381 | 382 | // This is the parsed HTTP response code. 383 | int response_code_; 384 | 385 | // The normalized http version (consistent with what GetStatusLine() returns). 386 | HttpVersion http_version_; 387 | DISALLOW_COPY_AND_ASSIGN(HttpResponseHeaders); 388 | }; 389 | } // namespace bvc 390 | #endif // BVC_HTTP_PARSER_HTTP_RESPONSE_HEADERS_H_ 391 | -------------------------------------------------------------------------------- /src/http_parser/http_version.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BVC_HTTP_PARSER_HTTP_VERSION_H_ 6 | #define BVC_HTTP_PARSER_HTTP_VERSION_H_ 7 | #pragma once 8 | 9 | namespace bvc { 10 | 11 | // Wrapper for an HTTP (major,minor) version pair. 12 | class HttpVersion { 13 | public: 14 | // Default constructor (major=0, minor=0). 15 | HttpVersion() : value_(0) { } 16 | 17 | // Build from unsigned major/minor pair. 18 | HttpVersion(uint16_t major, uint16_t minor) : value_(major << 16 | minor) { } 19 | 20 | // Major version number. 21 | uint16_t major_value() const { 22 | return value_ >> 16; 23 | } 24 | 25 | // Minor version number. 26 | uint16_t minor_value() const { 27 | return value_ & 0xffff; 28 | } 29 | 30 | // Overloaded operators: 31 | 32 | bool operator==(const HttpVersion& v) const { 33 | return value_ == v.value_; 34 | } 35 | bool operator!=(const HttpVersion& v) const { 36 | return value_ != v.value_; 37 | } 38 | bool operator>(const HttpVersion& v) const { 39 | return value_ > v.value_; 40 | } 41 | bool operator>=(const HttpVersion& v) const { 42 | return value_ >= v.value_; 43 | } 44 | bool operator<(const HttpVersion& v) const { 45 | return value_ < v.value_; 46 | } 47 | bool operator<=(const HttpVersion& v) const { 48 | return value_ <= v.value_; 49 | } 50 | 51 | private: 52 | uint32_t value_; // Packed as : 53 | }; 54 | 55 | } // namespace bvc 56 | 57 | #endif // BVC_HTTP_PARSER_HTTP_VERSION_H_ 58 | -------------------------------------------------------------------------------- /src/net/base/escape.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | #include "net/base/escape.h" 5 | #include "platform/quiche_platform_impl/quiche_logging_impl.h" 6 | #include "common/platform/api/quiche_logging.h" 7 | #include "googleurl/base/stl_util.h" 8 | #include "googleurl/base/strings/string_util.h" 9 | #include "googleurl/base/strings/utf_string_conversion_utils.h" 10 | #include "googleurl/base/strings/utf_string_conversions.h" 11 | #include "googleurl/base/third_party/icu/icu_utf.h" 12 | 13 | namespace bvc { 14 | namespace { 15 | const char kHexString[] = "0123456789ABCDEF"; 16 | inline char IntToHex(int i) { 17 | QUICHE_DCHECK_GE(i, 0) << i << " not a hex value"; 18 | QUICHE_DCHECK_LE(i, 15) << i << " not a hex value"; 19 | return kHexString[i]; 20 | } 21 | 22 | // A fast bit-vector map for ascii characters. 23 | // 24 | // Internally stores 256 bits in an array of 8 ints. 25 | // Does quick bit-flicking to lookup needed characters. 26 | struct Charmap { 27 | bool Contains(unsigned char c) const { 28 | return ((map[c >> 5] & (1 << (c & 31))) != 0); 29 | } 30 | uint32_t map[8]; 31 | }; 32 | 33 | // Given text to escape and a Charmap defining which values to escape, 34 | // return an escaped string. If use_plus is true, spaces are converted 35 | // to +, otherwise, if spaces are in the charmap, they are converted to 36 | // %20. And if keep_escaped is true, %XX will be kept as it is, otherwise, if 37 | // '%' is in the charmap, it is converted to %25. 38 | std::string Escape(gurl_base::StringPiece text, 39 | const Charmap& charmap, 40 | bool use_plus, 41 | bool keep_escaped = false) { 42 | std::string escaped; 43 | escaped.reserve(text.length() * 3); 44 | for (unsigned int i = 0; i < text.length(); ++i) { 45 | unsigned char c = static_cast(text[i]); 46 | if (use_plus && ' ' == c) { 47 | escaped.push_back('+'); 48 | } else if (keep_escaped && '%' == c && i + 2 < text.length() && 49 | gurl_base::IsHexDigit(text[i + 1]) && gurl_base::IsHexDigit(text[i + 2])) { 50 | escaped.push_back('%'); 51 | } else if (charmap.Contains(c)) { 52 | escaped.push_back('%'); 53 | escaped.push_back(IntToHex(c >> 4)); 54 | escaped.push_back(IntToHex(c & 0xf)); 55 | } else { 56 | escaped.push_back(c); 57 | } 58 | } 59 | return escaped; 60 | } 61 | 62 | // Convert a character |c| to a form that will not be mistaken as HTML. 63 | template 64 | void AppendEscapedCharForHTMLImpl(typename str::value_type c, str* output) { 65 | static constexpr struct { 66 | char key; 67 | gurl_base::StringPiece replacement; 68 | } kCharsToEscape[] = { 69 | {'<', "<"}, {'>', ">"}, {'&', "&"}, 70 | {'"', """}, {'\'', "'"}, 71 | }; 72 | for (const auto& char_to_escape : kCharsToEscape) { 73 | if (c == char_to_escape.key) { 74 | output->append(std::begin(char_to_escape.replacement), 75 | std::end(char_to_escape.replacement)); 76 | return; 77 | } 78 | } 79 | output->push_back(c); 80 | } 81 | 82 | // Convert |input| string to a form that will not be interpreted as HTML. 83 | template 84 | str EscapeForHTMLImpl(gurl_base::BasicStringPiece input) { 85 | str result; 86 | result.reserve(input.size()); // Optimize for no escaping. 87 | for (auto c : input) { 88 | AppendEscapedCharForHTMLImpl(c, &result); 89 | } 90 | return result; 91 | } 92 | 93 | // Everything except alphanumerics and -._~ 94 | // See RFC 3986 for the list of unreserved characters. 95 | static const Charmap kUnreservedCharmap = { 96 | {0xffffffffL, 0xfc009fffL, 0x78000001L, 0xb8000001L, 0xffffffffL, 97 | 0xffffffffL, 0xffffffffL, 0xffffffffL}}; 98 | 99 | // Everything except alphanumerics and !'()*-._~ 100 | // See RFC 2396 for the list of reserved characters. 101 | static const Charmap kQueryCharmap = {{ 102 | 0xffffffffL, 0xfc00987dL, 0x78000001L, 0xb8000001L, 103 | 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL 104 | }}; 105 | 106 | // non-printable, non-7bit, and (including space) "#%:<>?[\]^`{|} 107 | static const Charmap kPathCharmap = {{ 108 | 0xffffffffL, 0xd400002dL, 0x78000000L, 0xb8000001L, 109 | 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL 110 | }}; 111 | 112 | #if defined(OS_APPLE) 113 | // non-printable, non-7bit, and (including space) "#%<>[\]^`{|} 114 | static const Charmap kNSURLCharmap = {{ 115 | 0xffffffffL, 0x5000002dL, 0x78000000L, 0xb8000001L, 116 | 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL 117 | }}; 118 | #endif // defined(OS_APPLE) 119 | 120 | // non-printable, non-7bit, and (including space) ?>=<;+'&%$#"![\]^`{|} 121 | static const Charmap kUrlEscape = {{ 122 | 0xffffffffL, 0xf80008fdL, 0x78000001L, 0xb8000001L, 123 | 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL 124 | }}; 125 | 126 | // non-7bit, as well as %. 127 | static const Charmap kNonASCIICharmapAndPercent = { 128 | {0x00000000L, 0x00000020L, 0x00000000L, 0x00000000L, 0xffffffffL, 129 | 0xffffffffL, 0xffffffffL, 0xffffffffL}}; 130 | 131 | // non-7bit 132 | static const Charmap kNonASCIICharmap = {{0x00000000L, 0x00000000L, 0x00000000L, 133 | 0x00000000L, 0xffffffffL, 0xffffffffL, 134 | 0xffffffffL, 0xffffffffL}}; 135 | 136 | // Everything except alphanumerics, the reserved characters(;/?:@&=+$,) and 137 | // !'()*-._~#[] 138 | static const Charmap kExternalHandlerCharmap = {{ 139 | 0xffffffffL, 0x50000025L, 0x50000000L, 0xb8000001L, 140 | 0xffffffffL, 0xffffffffL, 0xffffffffL, 0xffffffffL 141 | }}; 142 | } // namespace 143 | 144 | std::string EscapeAllExceptUnreserved(gurl_base::StringPiece text) { 145 | return Escape(text, kUnreservedCharmap, false); 146 | } 147 | std::string EscapeQueryParamValue(gurl_base::StringPiece text, bool use_plus) { 148 | return Escape(text, kQueryCharmap, use_plus); 149 | } 150 | std::string EscapePath(gurl_base::StringPiece path) { 151 | return Escape(path, kPathCharmap, false); 152 | } 153 | 154 | #if defined(OS_APPLE) 155 | std::string EscapeNSURLPrecursor(gurl_base::StringPiece precursor) { 156 | return Escape(precursor, kNSURLCharmap, false, true); 157 | } 158 | #endif // defined(OS_APPLE) 159 | 160 | std::string EscapeUrlEncodedData(gurl_base::StringPiece path, bool use_plus) { 161 | return Escape(path, kUrlEscape, use_plus); 162 | } 163 | std::string EscapeNonASCIIAndPercent(gurl_base::StringPiece input) { 164 | return Escape(input, kNonASCIICharmapAndPercent, false); 165 | } 166 | std::string EscapeNonASCII(gurl_base::StringPiece input) { 167 | return Escape(input, kNonASCIICharmap, false); 168 | } 169 | std::string EscapeExternalHandlerValue(gurl_base::StringPiece text) { 170 | return Escape(text, kExternalHandlerCharmap, false, true); 171 | } 172 | void AppendEscapedCharForHTML(char c, std::string* output) { 173 | AppendEscapedCharForHTMLImpl(c, output); 174 | } 175 | std::string EscapeForHTML(gurl_base::StringPiece input) { 176 | return EscapeForHTMLImpl(input); 177 | } 178 | gurl_base::string16 EscapeForHTML(gurl_base::StringPiece16 input) { 179 | return EscapeForHTMLImpl(input); 180 | } 181 | 182 | // TODO(crbug/1100760): Move functions from net/base/escape to 183 | // base/strings/escape. 184 | std::string UnescapeURLComponent(gurl_base::StringPiece escaped_text, 185 | UnescapeRule::Type rules) { 186 | return gurl_base::UnescapeURLComponent(escaped_text, rules); 187 | } 188 | gurl_base::string16 UnescapeAndDecodeUTF8URLComponentWithAdjustments( 189 | gurl_base::StringPiece text, 190 | UnescapeRule::Type rules, 191 | gurl_base::OffsetAdjuster::Adjustments* adjustments) { 192 | return gurl_base::UnescapeAndDecodeUTF8URLComponentWithAdjustments(text, rules, 193 | adjustments); 194 | } 195 | std::string UnescapeBinaryURLComponent(gurl_base::StringPiece escaped_text, 196 | UnescapeRule::Type rules) { 197 | return gurl_base::UnescapeBinaryURLComponent(escaped_text, rules); 198 | } 199 | bool UnescapeBinaryURLComponentSafe(gurl_base::StringPiece escaped_text, 200 | bool fail_on_path_separators, 201 | std::string* unescaped_text) { 202 | return gurl_base::UnescapeBinaryURLComponentSafe( 203 | escaped_text, fail_on_path_separators, unescaped_text); 204 | } 205 | 206 | gurl_base::string16 UnescapeForHTML(gurl_base::StringPiece16 input) { 207 | static const struct { 208 | const char* ampersand_code; 209 | const char replacement; 210 | } kEscapeToChars[] = { 211 | {"<", '<'}, {">", '>'}, {"&", '&'}, 212 | {""", '"'}, {"'", '\''}, 213 | }; 214 | constexpr size_t kEscapeToCharsCount = gurl_base::size(kEscapeToChars); 215 | if (input.find(gurl_base::ASCIIToUTF16("&")) == std::string::npos) 216 | return gurl_base::string16(input); 217 | gurl_base::string16 ampersand_chars[kEscapeToCharsCount]; 218 | gurl_base::string16 text(input); 219 | for (gurl_base::string16::iterator iter = text.begin(); 220 | iter != text.end(); ++iter) { 221 | if (*iter == '&') { 222 | // Potential ampersand encode char. 223 | size_t index = iter - text.begin(); 224 | for (size_t i = 0; i < gurl_base::size(kEscapeToChars); i++) { 225 | if (ampersand_chars[i].empty()) { 226 | ampersand_chars[i] = 227 | gurl_base::ASCIIToUTF16(kEscapeToChars[i].ampersand_code); 228 | } 229 | if (text.find(ampersand_chars[i], index) == index) { 230 | text.replace(iter, iter + ampersand_chars[i].length(), 231 | 1, kEscapeToChars[i].replacement); 232 | break; 233 | } 234 | } 235 | } 236 | } 237 | return text; 238 | } 239 | 240 | bool ContainsEncodedBytes(gurl_base::StringPiece escaped_text, 241 | const std::set& bytes) { 242 | return gurl_base::ContainsEncodedBytes(escaped_text, bytes); 243 | } 244 | } // namespace bvc 245 | -------------------------------------------------------------------------------- /src/net/base/escape.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | #ifndef BVC_NET_BASE_ESCAPE_H_ 5 | #define BVC_NET_BASE_ESCAPE_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "googleurl/base/strings/escape.h" 11 | #include "googleurl/base/strings/string16.h" 12 | #include "googleurl/base/strings/string_piece.h" 13 | #include "googleurl/base/strings/utf_offset_string_conversions.h" 14 | 15 | namespace bvc { 16 | // Escaping -------------------------------------------------------------------- 17 | // Escapes all characters except unreserved characters. Unreserved characters, 18 | // as defined in RFC 3986, include alphanumerics and -._~ 19 | std::string EscapeAllExceptUnreserved(gurl_base::StringPiece text); 20 | 21 | // Escapes characters in text suitable for use as a query parameter value. 22 | // We %XX everything except alphanumerics and -_.!~*'() 23 | // Spaces change to "+" unless you pass usePlus=false. 24 | // This is basically the same as encodeURIComponent in javascript. 25 | std::string EscapeQueryParamValue(gurl_base::StringPiece text, 26 | bool use_plus); 27 | 28 | // Escapes a partial or complete file/pathname. This includes: 29 | // non-printable, non-7bit, and (including space) "#%:<>?[\]^`{|} 30 | std::string EscapePath(gurl_base::StringPiece path); 31 | 32 | #if defined(OS_APPLE) 33 | // Escapes characters as per expectations of NSURL. This includes: 34 | // non-printable, non-7bit, and (including space) "#%<>[\]^`{|} 35 | std::string EscapeNSURLPrecursor(gurl_base::StringPiece precursor); 36 | #endif // defined(OS_APPLE) 37 | 38 | // Escapes application/x-www-form-urlencoded content. This includes: 39 | // non-printable, non-7bit, and (including space) ?>=<;+'&%$#"![\]^`{|} 40 | // Space is escaped as + (if use_plus is true) and other special characters 41 | // as %XX (hex). 42 | std::string EscapeUrlEncodedData(gurl_base::StringPiece path, 43 | bool use_plus); 44 | // Escapes all non-ASCII input, as well as escaping % to %25. 45 | std::string EscapeNonASCIIAndPercent(gurl_base::StringPiece input); 46 | 47 | // Escapes all non-ASCII input. Note this function leaves % unescaped, which 48 | // means the unescaping the resulting string will not give back the original 49 | // input. 50 | std::string EscapeNonASCII(gurl_base::StringPiece input); 51 | 52 | // Escapes characters in text suitable for use as an external protocol handler 53 | // command. 54 | // We %XX everything except alphanumerics and -_.!~*'() and the restricted 55 | // characters (;/?:@&=+$,#[]) and a valid percent escape sequence (%XX). 56 | std::string EscapeExternalHandlerValue(gurl_base::StringPiece text); 57 | 58 | // Appends the given character to the output string, escaping the character if 59 | // the character would be interpreted as an HTML delimiter. 60 | void AppendEscapedCharForHTML(char c, std::string* output); 61 | 62 | // Escapes chars that might cause this text to be interpreted as HTML tags. 63 | std::string EscapeForHTML(gurl_base::StringPiece text); 64 | 65 | gurl_base::string16 EscapeForHTML(gurl_base::StringPiece16 text); 66 | 67 | // Unescaping ------------------------------------------------------------------ 68 | // TODO(crbug/1100760): Migrate callers to call functions in 69 | // base/strings/escape. 70 | using UnescapeRule = gurl_base::UnescapeRule; 71 | 72 | // Unescapes |escaped_text| and returns the result. 73 | // Unescaping consists of looking for the exact pattern "%XX", where each X is 74 | // a hex digit, and converting to the character with the numerical value of 75 | // those digits. Thus "i%20=%203%3b" unescapes to "i = 3;", if the 76 | // "UnescapeRule::SPACES" used. 77 | // 78 | // This method does not ensure that the output is a valid string using any 79 | // character encoding. However, it does leave escaped certain byte sequences 80 | // that would be dangerous to display to the user, because if interpreted as 81 | // UTF-8, they could be used to mislead the user. Callers that want to 82 | // unconditionally unescape everything for uses other than displaying data to 83 | // the user should use UnescapeBinaryURLComponent(). 84 | std::string UnescapeURLComponent(gurl_base::StringPiece escaped_text, 85 | UnescapeRule::Type rules); 86 | 87 | // Unescapes the given substring as a URL, and then tries to interpret the 88 | // result as being encoded as UTF-8. If the result is convertible into UTF-8, it 89 | // will be returned as converted. If it is not, the original escaped string will 90 | // be converted into a gurl_base::string16 and returned. |adjustments| provides 91 | // information on how the original string was adjusted to get the string 92 | // returned. 93 | gurl_base::string16 UnescapeAndDecodeUTF8URLComponentWithAdjustments( 94 | gurl_base::StringPiece text, 95 | UnescapeRule::Type rules, 96 | gurl_base::OffsetAdjuster::Adjustments* adjustments); 97 | 98 | // Unescapes a component of a URL for use as binary data. Unlike 99 | // UnescapeURLComponent, leaves nothing unescaped, including nulls, invalid 100 | // characters, characters that are unsafe to display, etc. This should *not* 101 | // be used when displaying the decoded data to the user. 102 | // 103 | // Only the NORMAL and REPLACE_PLUS_WITH_SPACE rules are allowed. 104 | std::string UnescapeBinaryURLComponent( 105 | gurl_base::StringPiece escaped_text, 106 | UnescapeRule::Type rules = UnescapeRule::NORMAL); 107 | 108 | // Variant of UnescapeBinaryURLComponent(). Writes output to |unescaped_text|. 109 | // Returns true on success, returns false and clears |unescaped_text| on 110 | // failure. Fails on characters escaped that are unsafe to unescape in some 111 | // contexts, which are defined as characters "\0" through "\x1F" (Which includes 112 | // CRLF but not space), and optionally path separators. Path separators include 113 | // both forward and backward slashes on all platforms. Does not fail if any of 114 | // those characters appear unescaped in the input string. 115 | bool UnescapeBinaryURLComponentSafe(gurl_base::StringPiece escaped_text, 116 | bool fail_on_path_separators, 117 | std::string* unescaped_text); 118 | 119 | // Unescapes the following ampersand character codes from |text|: 120 | // < > & " ' 121 | gurl_base::string16 UnescapeForHTML(gurl_base::StringPiece16 text); 122 | } // namespace bvc 123 | 124 | #endif // BVC_NET_BASE_ESCAPE_H_ 125 | -------------------------------------------------------------------------------- /src/net/base/parse_number.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "platform/quiche_platform_impl/quiche_text_utils_impl.h" 6 | #include "net/base/parse_number.h" 7 | #include "googleurl/base/strings/string_util.h" 8 | 9 | namespace bvc { 10 | namespace { 11 | // The string to number conversion functions in //base include the type in the 12 | // name (like StringToInt64()). The following wrapper methods create a 13 | // consistent interface to StringToXXX() that calls the appropriate //base 14 | // version. This simplifies writing generic code with a template. 15 | bool StringToNumber(const quiche::QuicheStringPiece& input, int32_t* output) { 16 | // This assumes ints are 32-bits (will fail compile if that ever changes). 17 | return quiche::QuicheTextUtilsImpl::StringToInt(input, output); 18 | } 19 | 20 | bool StringToNumber(const quiche::QuicheStringPiece& input, uint32_t* output) { 21 | // This assumes ints are 32-bits (will fail compile if that ever changes). 22 | return quiche::QuicheTextUtilsImpl::StringToUint32(input, output); 23 | } 24 | 25 | bool StringToNumber(const quiche::QuicheStringPiece& input, int64_t* output) { 26 | //return quiche::QuicheTextUtils::StringToInt64(input, output); 27 | return absl::SimpleAtoi(input, output); 28 | } 29 | 30 | bool StringToNumber(const quiche::QuicheStringPiece& input, uint64_t* output) { 31 | return quiche::QuicheTextUtilsImpl::StringToUint64(input, output); 32 | } 33 | 34 | bool SetError(ParseIntError error, ParseIntError* optional_error) { 35 | if (optional_error) 36 | *optional_error = error; 37 | return false; 38 | } 39 | 40 | template 41 | bool ParseIntHelper(const quiche::QuicheStringPiece& input, 42 | ParseIntFormat format, 43 | T* output, 44 | ParseIntError* optional_error) { 45 | // Check that the input matches the format before calling StringToNumber(). 46 | // Numbers must start with either a digit or a negative sign. 47 | if (input.empty()) 48 | return SetError(ParseIntError::FAILED_PARSE, optional_error); 49 | bool starts_with_negative = input[0] == '-'; 50 | bool starts_with_digit = gurl_base::IsAsciiDigit(input[0]); 51 | if (!starts_with_digit) { 52 | if (format == ParseIntFormat::NON_NEGATIVE || !starts_with_negative) 53 | return SetError(ParseIntError::FAILED_PARSE, optional_error); 54 | } 55 | // Dispatch to the appropriate flavor of base::StringToXXX() by calling one of 56 | // the type-specific overloads. 57 | T result; 58 | if (StringToNumber(input, &result)) { 59 | *output = result; 60 | return true; 61 | } 62 | // Optimization: If the error is not going to be inspected, don't bother 63 | // calculating it. 64 | if (!optional_error) 65 | return false; 66 | // Set an error that distinguishes between parsing/underflow/overflow errors. 67 | // 68 | // Note that the output set by base::StringToXXX() on failure cannot be used 69 | // as it has ambiguity with parse errors. 70 | // Strip any leading negative sign off the number. 71 | quiche::QuicheStringPiece numeric_portion = 72 | starts_with_negative ? input.substr(1) : input; 73 | // Test if |numeric_portion| is a valid non-negative integer. 74 | if (!numeric_portion.empty() && 75 | numeric_portion.find_first_not_of("0123456789") == std::string::npos) { 76 | // If it was, the failure must have been due to underflow/overflow. 77 | return SetError(starts_with_negative ? ParseIntError::FAILED_UNDERFLOW 78 | : ParseIntError::FAILED_OVERFLOW, 79 | optional_error); 80 | } 81 | // Otherwise it was a mundane parsing error. 82 | return SetError(ParseIntError::FAILED_PARSE, optional_error); 83 | } 84 | } // namespace 85 | 86 | bool ParseInt32(const quiche::QuicheStringPiece& input, 87 | ParseIntFormat format, 88 | int32_t* output, 89 | ParseIntError* optional_error) { 90 | return ParseIntHelper(input, format, output, optional_error); 91 | } 92 | 93 | bool ParseInt64(const quiche::QuicheStringPiece& input, 94 | ParseIntFormat format, 95 | int64_t* output, 96 | ParseIntError* optional_error) { 97 | return ParseIntHelper(input, format, output, optional_error); 98 | } 99 | 100 | bool ParseUint32(const quiche::QuicheStringPiece& input, 101 | uint32_t* output, 102 | ParseIntError* optional_error) { 103 | return ParseIntHelper(input, ParseIntFormat::NON_NEGATIVE, output, 104 | optional_error); 105 | } 106 | 107 | bool ParseUint64(const quiche::QuicheStringPiece& input, 108 | uint64_t* output, 109 | ParseIntError* optional_error) { 110 | return ParseIntHelper(input, ParseIntFormat::NON_NEGATIVE, output, 111 | optional_error); 112 | } 113 | } // namespace bvc 114 | -------------------------------------------------------------------------------- /src/net/base/parse_number.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BVC_NET_BASE_PARSE_NUMBER_H_ 6 | #define BVC_NET_BASE_PARSE_NUMBER_H_ 7 | 8 | #include "googleurl/base/compiler_specific.h" 9 | #include "googleurl/base/strings/string_piece.h" 10 | #include "common/platform/api/quiche_text_utils.h" 11 | 12 | // This file contains utility functions for parsing numbers, in the context of 13 | // network protocols. 14 | // 15 | // Q: Doesn't //base already provide these in string_number_conversions.h, with 16 | // functions like base::StringToInt()? 17 | // 18 | // A: Yes, and those functions are used under the hood by these implementations. 19 | // 20 | // However using the base::StringTo*() has historically led to subtle bugs 21 | // in the context of parsing network protocols: 22 | // 23 | // * Permitting a leading '+' 24 | // * Incorrectly classifying overflow/underflow from a parsing failure 25 | // * Allowing negative numbers for non-negative fields 26 | // 27 | // This API tries to avoid these problems by picking sensible defaults for 28 | // //net code. For more details see crbug.com/596523. 29 | 30 | namespace bvc { 31 | // Format to use when parsing integers. 32 | enum class ParseIntFormat { 33 | // Accepts non-negative base 10 integers of the form: 34 | // 35 | // 1*DIGIT 36 | // 37 | // This construction is used in a variety of IETF standards, such as RFC 7230 38 | // (HTTP). 39 | // 40 | // When attempting to parse a negative number using this format, the failure 41 | // will be FAILED_PARSE since it violated the expected format (and not 42 | // FAILED_UNDERFLOW). 43 | // 44 | // Also note that inputs need not be in minimal encoding: "0003" is valid and 45 | // equivalent to "3". 46 | NON_NEGATIVE, 47 | // Accept optionally negative base 10 integers of the form: 48 | // 49 | // ["-"] 1*DIGIT 50 | // 51 | // In other words, this accepts the same things as NON_NEGATIVE, and 52 | // additionally recognizes those numbers prefixed with a '-'. 53 | // 54 | // Note that by this defintion "-0" IS a valid input. 55 | OPTIONALLY_NEGATIVE 56 | }; 57 | 58 | // The specific reason why a ParseInt*() function failed. 59 | enum class ParseIntError { 60 | // The parsed number couldn't fit into the provided output type because it was 61 | // too high. 62 | FAILED_OVERFLOW, 63 | // The parsed number couldn't fit into the provided output type because it was 64 | // too low. 65 | FAILED_UNDERFLOW, 66 | // The number failed to be parsed because it wasn't a valid decimal number (as 67 | // determined by the policy). 68 | FAILED_PARSE, 69 | }; 70 | 71 | // The ParseInt*() functions parse a string representing a number. 72 | // 73 | // The format of the strings that are accepted is controlled by the |format| 74 | // parameter. This allows rejecting negative numbers. 75 | // 76 | // These functions return true on success, and fill |*output| with the result. 77 | // 78 | // On failure, it is guaranteed that |*output| was not modified. If 79 | // |optional_error| was non-null, then it is filled with the reason for the 80 | // failure. 81 | bool ParseInt32(const quiche::QuicheStringPiece& input, 82 | ParseIntFormat format, 83 | int32_t* output, 84 | ParseIntError* optional_error = nullptr) 85 | WARN_UNUSED_RESULT; 86 | bool ParseInt64(const quiche::QuicheStringPiece& input, 87 | ParseIntFormat format, 88 | int64_t* output, 89 | ParseIntError* optional_error = nullptr) 90 | WARN_UNUSED_RESULT; 91 | // The ParseUint*() functions parse a string representing a number. 92 | // 93 | // These are equivalent to calling ParseInt*() with a format string of 94 | // ParseIntFormat::NON_NEGATIVE and unsigned output types. 95 | bool ParseUint32(const quiche::QuicheStringPiece& input, 96 | uint32_t* output, 97 | ParseIntError* optional_error = nullptr) 98 | WARN_UNUSED_RESULT; 99 | bool ParseUint64(const quiche::QuicheStringPiece& input, 100 | uint64_t* output, 101 | ParseIntError* optional_error = nullptr) 102 | WARN_UNUSED_RESULT; 103 | } // namespace bvc 104 | 105 | #endif // BVC_NET_BASE_PARSE_NUMBER_H_ 106 | -------------------------------------------------------------------------------- /src/net/cert/internal/cert_error_id.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "net/cert/internal/cert_error_id.h" 6 | 7 | namespace bvc { 8 | 9 | const char* CertErrorIdToDebugString(CertErrorId id) { 10 | // The CertErrorId is simply a pointer for a C-string literal. 11 | return reinterpret_cast(id); 12 | } 13 | 14 | } // namespace bvc 15 | -------------------------------------------------------------------------------- /src/net/cert/internal/cert_error_id.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BVC_CERT_INTERNAL_CERT_ERROR_ID_H_ 6 | #define BVC_CERT_INTERNAL_CERT_ERROR_ID_H_ 7 | 8 | namespace bvc { 9 | 10 | // Each "class" of certificate error/warning has its own unique ID. This is 11 | // essentially like an error code, however the value is not stable. Under the 12 | // hood these IDs are pointers and use the process's address space to ensure 13 | // uniqueness. 14 | // 15 | // Equality of CertErrorId can be done using the == operator. 16 | // 17 | // To define new error IDs use the macro DEFINE_CERT_ERROR_ID(). 18 | using CertErrorId = const void*; 19 | 20 | // DEFINE_CERT_ERROR_ID() creates a CertErrorId given a non-null C-string 21 | // literal. The string should be a textual name for the error which will appear 22 | // when pretty-printing errors for debugging. It should be ASCII. 23 | // 24 | // TODO(crbug.com/634443): Implement this -- add magic to ensure that storage 25 | // of identical strings isn't pool. 26 | #define DEFINE_CERT_ERROR_ID(name, c_str_literal) \ 27 | const CertErrorId name = c_str_literal 28 | 29 | // Returns a debug string for a CertErrorId. In practice this returns the 30 | // string literal given to DEFINE_CERT_ERROR_ID(), which is human-readable. 31 | const char* CertErrorIdToDebugString(CertErrorId id); 32 | 33 | } // namespace bvc 34 | 35 | #endif // BVC_CERT_INTERNAL_CERT_ERROR_ID_H_ 36 | -------------------------------------------------------------------------------- /src/net/cert/internal/cert_error_params.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "net/cert/internal/cert_error_params.h" 6 | 7 | #include 8 | 9 | #include "quic/platform/api/quic_logging.h" 10 | #include "googleurl/base/strings/string_number_conversions.h" 11 | #include "net/der/input.h" 12 | 13 | namespace bvc { 14 | 15 | namespace { 16 | 17 | // Parameters subclass for describing (and pretty-printing) 1 or 2 DER 18 | // blobs. It makes a copy of the der::Inputs. 19 | class CertErrorParams2Der : public CertErrorParams { 20 | public: 21 | CertErrorParams2Der(const char* name1, 22 | const der::Input& der1, 23 | const char* name2, 24 | const der::Input& der2) 25 | : name1_(name1), 26 | der1_(der1.AsString()), 27 | name2_(name2), 28 | der2_(der2.AsString()) {} 29 | 30 | std::string ToDebugString() const override { 31 | std::string result; 32 | AppendDer(name1_, der1_, &result); 33 | if (name2_) { 34 | result += "\n"; 35 | AppendDer(name2_, der2_, &result); 36 | } 37 | return result; 38 | } 39 | 40 | private: 41 | static void AppendDer(const char* name, 42 | const std::string& der, 43 | std::string* out) { 44 | *out += name; 45 | *out += ": " + gurl_base::HexEncode(der.data(), der.size()); 46 | } 47 | 48 | const char* name1_; 49 | std::string der1_; 50 | 51 | const char* name2_; 52 | std::string der2_; 53 | 54 | DISALLOW_COPY_AND_ASSIGN(CertErrorParams2Der); 55 | }; 56 | 57 | // Parameters subclass for describing (and pretty-printing) a single size_t. 58 | class CertErrorParams1SizeT : public CertErrorParams { 59 | public: 60 | CertErrorParams1SizeT(const char* name, size_t value) 61 | : name_(name), value_(value) {} 62 | 63 | std::string ToDebugString() const override { 64 | return name_ + std::string(": ") + gurl_base::NumberToString(value_); 65 | } 66 | 67 | private: 68 | const char* name_; 69 | size_t value_; 70 | 71 | DISALLOW_COPY_AND_ASSIGN(CertErrorParams1SizeT); 72 | }; 73 | 74 | // Parameters subclass for describing (and pretty-printing) two size_t 75 | // values. 76 | class CertErrorParams2SizeT : public CertErrorParams { 77 | public: 78 | CertErrorParams2SizeT(const char* name1, 79 | size_t value1, 80 | const char* name2, 81 | size_t value2) 82 | : name1_(name1), value1_(value1), name2_(name2), value2_(value2) {} 83 | 84 | std::string ToDebugString() const override { 85 | return name1_ + std::string(": ") + gurl_base::NumberToString(value1_) + "\n" + 86 | name2_ + std::string(": ") + gurl_base::NumberToString(value2_); 87 | } 88 | 89 | private: 90 | const char* name1_; 91 | size_t value1_; 92 | const char* name2_; 93 | size_t value2_; 94 | 95 | DISALLOW_COPY_AND_ASSIGN(CertErrorParams2SizeT); 96 | }; 97 | 98 | } // namespace 99 | 100 | CertErrorParams::CertErrorParams() = default; 101 | CertErrorParams::~CertErrorParams() = default; 102 | 103 | std::unique_ptr CreateCertErrorParams1Der( 104 | const char* name, 105 | const der::Input& der) { 106 | DCHECK(name); 107 | return std::make_unique(name, der, nullptr, 108 | der::Input()); 109 | } 110 | 111 | std::unique_ptr CreateCertErrorParams2Der( 112 | const char* name1, 113 | const der::Input& der1, 114 | const char* name2, 115 | const der::Input& der2) { 116 | DCHECK(name1); 117 | DCHECK(name2); 118 | return std::make_unique(name1, der1, name2, der2); 119 | } 120 | 121 | std::unique_ptr CreateCertErrorParams1SizeT(const char* name, 122 | size_t value) { 123 | DCHECK(name); 124 | return std::make_unique(name, value); 125 | } 126 | 127 | NET_EXPORT std::unique_ptr CreateCertErrorParams2SizeT( 128 | const char* name1, 129 | size_t value1, 130 | const char* name2, 131 | size_t value2) { 132 | DCHECK(name1); 133 | DCHECK(name2); 134 | return std::make_unique(name1, value1, name2, value2); 135 | } 136 | } // namespace bvc 137 | -------------------------------------------------------------------------------- /src/net/cert/internal/cert_error_params.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BVC_NET_CERT_INTERNAL_CERT_ERROR_PARAMS_H_ 6 | #define BVC_NET_CERT_INTERNAL_CERT_ERROR_PARAMS_H_ 7 | 8 | #include 9 | #include 10 | 11 | #include "googleurl/base/macros.h" 12 | 13 | namespace bvc { 14 | 15 | namespace der { 16 | class Input; 17 | } 18 | 19 | // CertErrorParams is a base class for describing extra parameters attached to 20 | // a CertErrorNode. 21 | // 22 | // An example use for parameters is to identify the OID for an unconsumed 23 | // critical extension. This parameter could then be pretty printed when 24 | // diagnosing the error. 25 | class CertErrorParams { 26 | public: 27 | CertErrorParams(); 28 | virtual ~CertErrorParams(); 29 | 30 | // Creates a representation of this parameter as a string, which may be 31 | // used for pretty printing the error. 32 | virtual std::string ToDebugString() const = 0; 33 | 34 | private: 35 | DISALLOW_COPY_AND_ASSIGN(CertErrorParams); 36 | }; 37 | 38 | // Creates a parameter object that holds a copy of |der|, and names it |name| 39 | // in debug string outputs. 40 | std::unique_ptr CreateCertErrorParams1Der( 41 | const char* name, 42 | const der::Input& der); 43 | 44 | // Same as CreateCertErrorParams1Der() but has a second DER blob. 45 | std::unique_ptr CreateCertErrorParams2Der( 46 | const char* name1, 47 | const der::Input& der1, 48 | const char* name2, 49 | const der::Input& der2); 50 | 51 | // Creates a parameter object that holds a single size_t value. |name| is used 52 | // when pretty-printing the parameters. 53 | std::unique_ptr CreateCertErrorParams1SizeT( 54 | const char* name, 55 | size_t value); 56 | 57 | // Same as CreateCertErrorParams1SizeT() but has a second size_t. 58 | std::unique_ptr CreateCertErrorParams2SizeT( 59 | const char* name1, 60 | size_t value1, 61 | const char* name2, 62 | size_t value2); 63 | 64 | } // namespace bvc 65 | 66 | #endif // BVC_NET_CERT_INTERNAL_CERT_ERROR_PARAMS_H_ 67 | -------------------------------------------------------------------------------- /src/net/cert/internal/cert_errors.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "net/cert/internal/cert_errors.h" 6 | 7 | #include "googleurl/base/strings/strcat.h" 8 | #include "googleurl/base/strings/string_split.h" 9 | #include "googleurl/base/strings/stringprintf.h" 10 | #include "net/cert/internal/cert_error_params.h" 11 | #include "net/cert/internal/parse_name.h" 12 | #include "net/cert/internal/parsed_certificate.h" 13 | 14 | namespace bvc { 15 | 16 | namespace { 17 | 18 | void AppendLinesWithIndentation(const std::string& text, 19 | const std::string& indentation, 20 | std::string* out) { 21 | std::vector lines = gurl_base::SplitStringPieceUsingSubstr( 22 | text, "\n", gurl_base::KEEP_WHITESPACE, gurl_base::SPLIT_WANT_ALL); 23 | 24 | for (const auto& line : lines) { 25 | gurl_base::StrAppend(out, {indentation, line, "\n"}); 26 | } 27 | } 28 | 29 | } // namespace 30 | 31 | CertError::CertError() = default; 32 | 33 | CertError::CertError(Severity severity, 34 | CertErrorId id, 35 | std::unique_ptr params) 36 | : severity(severity), id(id), params(std::move(params)) {} 37 | 38 | CertError::CertError(CertError&& other) = default; 39 | 40 | CertError& CertError::operator=(CertError&&) = default; 41 | 42 | CertError::~CertError() = default; 43 | 44 | std::string CertError::ToDebugString() const { 45 | std::string result; 46 | switch (severity) { 47 | case SEVERITY_WARNING: 48 | result += "WARNING: "; 49 | break; 50 | case SEVERITY_HIGH: 51 | result += "ERROR: "; 52 | break; 53 | } 54 | result += CertErrorIdToDebugString(id); 55 | result += +"\n"; 56 | 57 | if (params) 58 | AppendLinesWithIndentation(params->ToDebugString(), " ", &result); 59 | 60 | return result; 61 | } 62 | 63 | CertErrors::CertErrors() = default; 64 | CertErrors::CertErrors(CertErrors&& other) = default; 65 | CertErrors& CertErrors::operator=(CertErrors&&) = default; 66 | CertErrors::~CertErrors() = default; 67 | 68 | void CertErrors::Add(CertError::Severity severity, 69 | CertErrorId id, 70 | std::unique_ptr params) { 71 | nodes_.push_back(CertError(severity, id, std::move(params))); 72 | } 73 | 74 | void CertErrors::AddError(CertErrorId id, 75 | std::unique_ptr params) { 76 | Add(CertError::SEVERITY_HIGH, id, std::move(params)); 77 | } 78 | 79 | void CertErrors::AddError(CertErrorId id) { 80 | AddError(id, nullptr); 81 | } 82 | 83 | void CertErrors::AddWarning(CertErrorId id, 84 | std::unique_ptr params) { 85 | Add(CertError::SEVERITY_WARNING, id, std::move(params)); 86 | } 87 | 88 | void CertErrors::AddWarning(CertErrorId id) { 89 | AddWarning(id, nullptr); 90 | } 91 | 92 | std::string CertErrors::ToDebugString() const { 93 | std::string result; 94 | for (const CertError& node : nodes_) 95 | result += node.ToDebugString(); 96 | 97 | return result; 98 | } 99 | 100 | bool CertErrors::ContainsError(CertErrorId id) const { 101 | for (const CertError& node : nodes_) { 102 | if (node.id == id) 103 | return true; 104 | } 105 | return false; 106 | } 107 | 108 | bool CertErrors::ContainsAnyErrorWithSeverity( 109 | CertError::Severity severity) const { 110 | for (const CertError& node : nodes_) { 111 | if (node.severity == severity) 112 | return true; 113 | } 114 | return false; 115 | } 116 | 117 | } // namespace bvc -------------------------------------------------------------------------------- /src/net/cert/internal/cert_errors.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // ---------------------------- 6 | // Overview of error design 7 | // ---------------------------- 8 | // 9 | // Certificate path building/validation/parsing may emit a sequence of errors 10 | // and warnings. 11 | // 12 | // Each individual error/warning entry (CertError) is comprised of: 13 | // 14 | // * A unique identifier. 15 | // 16 | // This serves similarly to an error code, and is used to query if a 17 | // particular error/warning occurred. 18 | // 19 | // * [optional] A parameters object. 20 | // 21 | // Nodes may attach a heap-allocated subclass of CertErrorParams to carry 22 | // extra information that is used when reporting the error. For instance 23 | // a parsing error may describe where in the DER the failure happened, or 24 | // what the unexpected value was. 25 | // 26 | // A collection of errors is represented by the CertErrors object. This may be 27 | // used to group errors that have a common context, such as all the 28 | // errors/warnings that apply to a specific certificate. 29 | // 30 | // Lastly, CertPathErrors composes multiple CertErrors -- one for each 31 | // certificate in the verified chain. 32 | // 33 | // ---------------------------- 34 | // Defining new errors 35 | // ---------------------------- 36 | // 37 | // The error IDs are extensible and do not need to be centrally defined. 38 | // 39 | // To define a new error use the macro DEFINE_CERT_ERROR_ID() in a .cc file. 40 | // If consumers are to be able to query for this error then the symbol should 41 | // also be exposed in a header file. 42 | // 43 | // Error IDs are in truth string literals, whose pointer value will be unique 44 | // per process. 45 | 46 | #ifndef BVC_NET_CERT_INTERNAL_CERT_ERRORS_H_ 47 | #define BVC_NET_CERT_INTERNAL_CERT_ERRORS_H_ 48 | 49 | #include 50 | #include 51 | 52 | #include "net/cert/internal/cert_error_id.h" 53 | 54 | namespace bvc { 55 | 56 | class CertErrorParams; 57 | 58 | // CertError represents either an error or a warning. 59 | struct CertError { 60 | enum Severity { 61 | SEVERITY_HIGH, 62 | SEVERITY_WARNING, 63 | }; 64 | 65 | CertError(); 66 | CertError(Severity severity, 67 | CertErrorId id, 68 | std::unique_ptr params); 69 | CertError(CertError&& other); 70 | CertError& operator=(CertError&&); 71 | ~CertError(); 72 | 73 | // Pretty-prints the error and its parameters. 74 | std::string ToDebugString() const; 75 | 76 | Severity severity; 77 | CertErrorId id; 78 | std::unique_ptr params; 79 | }; 80 | 81 | // CertErrors is a collection of CertError, along with convenience methods to 82 | // add and inspect errors. 83 | class NET_EXPORT CertErrors { 84 | public: 85 | CertErrors(); 86 | CertErrors(CertErrors&& other); 87 | CertErrors& operator=(CertErrors&&); 88 | ~CertErrors(); 89 | 90 | // Adds an error/warning. |params| may be null. 91 | void Add(CertError::Severity severity, 92 | CertErrorId id, 93 | std::unique_ptr params); 94 | 95 | // Adds a high severity error. 96 | void AddError(CertErrorId id, std::unique_ptr params); 97 | void AddError(CertErrorId id); 98 | 99 | // Adds a low severity error. 100 | void AddWarning(CertErrorId id, std::unique_ptr params); 101 | void AddWarning(CertErrorId id); 102 | 103 | // Dumps a textual representation of the errors for debugging purposes. 104 | std::string ToDebugString() const; 105 | 106 | // Returns true if the error |id| was added to this CertErrors (of any 107 | // severity). 108 | bool ContainsError(CertErrorId id) const; 109 | 110 | // Returns true if this contains any errors of the given severity level. 111 | bool ContainsAnyErrorWithSeverity(CertError::Severity severity) const; 112 | 113 | private: 114 | std::vector nodes_; 115 | }; 116 | 117 | } // namespace bvc 118 | 119 | #endif // BVC_NET_CERT_INTERNAL_CERT_ERRORS_H_ -------------------------------------------------------------------------------- /src/net/cert/internal/parse_certificate.cc: -------------------------------------------------------------------------------- 1 | #include "net/cert/internal/parse_certificate.h" 2 | 3 | #include "net/cert/internal/cert_errors.h" 4 | 5 | namespace bvc { 6 | namespace { 7 | // Returns true if |input| is a SEQUENCE and nothing else. 8 | WARN_UNUSED_RESULT bool IsSequenceTLV(const der::Input& input) { 9 | der::Parser parser(input); 10 | der::Parser unused_sequence_parser; 11 | if (!parser.ReadSequence(&unused_sequence_parser)) 12 | return false; 13 | // Should by a single SEQUENCE by definition of the function. 14 | return !parser.HasMore(); 15 | } 16 | 17 | // Reads a SEQUENCE from |parser| and writes the full tag-length-value into 18 | // |out|. On failure |parser| may or may not have been advanced. 19 | WARN_UNUSED_RESULT bool ReadSequenceTLV(der::Parser* parser, der::Input* out) { 20 | return parser->ReadRawTLV(out) && IsSequenceTLV(*out); 21 | } 22 | } // namespace 23 | 24 | bool ParseCertificate(const der::Input& certificate_tlv, 25 | der::Input* out_tbs_certificate_tlv, 26 | der::Input* out_signature_algorithm_tlv, 27 | der::BitString* out_signature_value, 28 | CertErrors* out_errors) { 29 | // |out_errors| is optional. But ensure it is non-null for the remainder of 30 | // this function. 31 | if (!out_errors) { 32 | CertErrors unused_errors; 33 | return ParseCertificate(certificate_tlv, out_tbs_certificate_tlv, 34 | out_signature_algorithm_tlv, out_signature_value, 35 | &unused_errors); 36 | } 37 | 38 | der::Parser parser(certificate_tlv); 39 | 40 | // Certificate ::= SEQUENCE { 41 | der::Parser certificate_parser; 42 | if (!parser.ReadSequence(&certificate_parser)) { 43 | out_errors->AddError(kCertificateNotSequence); 44 | return false; 45 | } 46 | 47 | // tbsCertificate TBSCertificate, 48 | if (!ReadSequenceTLV(&certificate_parser, out_tbs_certificate_tlv)) { 49 | out_errors->AddError(kTbsCertificateNotSequence); 50 | return false; 51 | } 52 | 53 | // signatureAlgorithm AlgorithmIdentifier, 54 | if (!ReadSequenceTLV(&certificate_parser, out_signature_algorithm_tlv)) { 55 | out_errors->AddError(kSignatureAlgorithmNotSequence); 56 | return false; 57 | } 58 | 59 | // signatureValue BIT STRING } 60 | if (!certificate_parser.ReadBitString(out_signature_value)) { 61 | out_errors->AddError(kSignatureValueNotBitString); 62 | return false; 63 | } 64 | 65 | // There isn't an extension point at the end of Certificate. 66 | if (certificate_parser.HasMore()) { 67 | out_errors->AddError(kUnconsumedDataInsideCertificateSequence); 68 | return false; 69 | } 70 | 71 | // By definition the input was a single Certificate, so there shouldn't be 72 | // unconsumed data. 73 | if (parser.HasMore()) { 74 | out_errors->AddError(kUnconsumedDataAfterCertificateSequence); 75 | return false; 76 | } 77 | 78 | return true; 79 | } 80 | } // namespace bvc 81 | -------------------------------------------------------------------------------- /src/net/cert/internal/parse_certificate.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef BVC_NET_CERT_INTERNAL_PARSE_CERTIFICATE_H_ 3 | #define BVC_NET_CERT_INTERNAL_PARSE_CERTIFICATE_H_ 4 | 5 | #include "googleurl/base/compiler_specific.h" 6 | 7 | namespace bvc { 8 | 9 | class CertErrors; 10 | 11 | // Parses a DER-encoded "Certificate" as specified by RFC 5280. Returns true on 12 | // success and sets the results in the |out_*| parameters. On both the failure 13 | // and success case, if |out_errors| was non-null it may contain extra error 14 | // information. 15 | // 16 | // Note that on success the out parameters alias data from the input 17 | // |certificate_tlv|. Hence the output values are only valid as long as 18 | // |certificate_tlv| remains valid. 19 | // 20 | // On failure the out parameters have an undefined state, except for 21 | // out_errors. Some of them may have been updated during parsing, whereas 22 | // others may not have been changed. 23 | // 24 | // The out parameters represent each field of the Certificate SEQUENCE: 25 | // Certificate ::= SEQUENCE { 26 | // 27 | // The |out_tbs_certificate_tlv| parameter corresponds with "tbsCertificate" 28 | // from RFC 5280: 29 | // tbsCertificate TBSCertificate, 30 | // 31 | // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No 32 | // guarantees are made regarding the value of this SEQUENCE. 33 | // This can be further parsed using ParseTbsCertificate(). 34 | // 35 | // The |out_signature_algorithm_tlv| parameter corresponds with 36 | // "signatureAlgorithm" from RFC 5280: 37 | // signatureAlgorithm AlgorithmIdentifier, 38 | // 39 | // This contains the full (unverified) Tag-Length-Value for a SEQUENCE. No 40 | // guarantees are made regarding the value of this SEQUENCE. 41 | // This can be further parsed using SignatureValue::Create(). 42 | // 43 | // The |out_signature_value| parameter corresponds with "signatureValue" from 44 | // RFC 5280: 45 | // signatureValue BIT STRING } 46 | // 47 | // Parsing guarantees that this is a valid BIT STRING. 48 | bool ParseCertificate(const der::Input& certificate_tlv, 49 | der::Input* out_tbs_certificate_tlv, 50 | der::Input* out_signature_algorithm_tlv, 51 | der::BitString* out_signature_value, 52 | CertErrors* out_errors) WARN_UNUSED_RESULT; 53 | 54 | } // namespace bvc 55 | 56 | #endif //BVC_NET_CERT_INTERNAL_PARSE_CERTIFICATE_H_ -------------------------------------------------------------------------------- /src/net/cert/pem.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "net/cert/pem.h" 6 | #include "base/base64.h" 7 | #include "common/platform/api/quiche_text_utils.h" 8 | #include "common/platform/api/quiche_optional.h" 9 | #include "googleurl/base/strings/string_util.h" 10 | #include "googleurl/base/strings/stringprintf.h" 11 | 12 | namespace { 13 | 14 | const char kPEMSearchBlock[] = "-----BEGIN "; 15 | const char kPEMBeginBlock[] = "-----BEGIN %s-----"; 16 | const char kPEMEndBlock[] = "-----END %s-----"; 17 | 18 | } // namespace 19 | 20 | namespace bvc { 21 | 22 | using gurl_base::StringPiece; 23 | 24 | struct PEMTokenizer::PEMType { 25 | std::string type; 26 | std::string header; 27 | std::string footer; 28 | }; 29 | 30 | PEMTokenizer::PEMTokenizer( 31 | const StringPiece& str, 32 | const std::vector& allowed_block_types) { 33 | Init(str, allowed_block_types); 34 | } 35 | 36 | PEMTokenizer::~PEMTokenizer() = default; 37 | 38 | bool PEMTokenizer::GetNext() { 39 | while (pos_ != StringPiece::npos) { 40 | // Scan for the beginning of the next PEM encoded block. 41 | pos_ = str_.find(kPEMSearchBlock, pos_); 42 | if (pos_ == StringPiece::npos) 43 | return false; // No more PEM blocks 44 | 45 | std::vector::const_iterator it; 46 | // Check to see if it is of an acceptable block type. 47 | for (it = block_types_.begin(); it != block_types_.end(); ++it) { 48 | if (!base::StartsWith(str_.substr(pos_), it->header)) 49 | continue; 50 | 51 | // Look for a footer matching the header. If none is found, then all 52 | // data following this point is invalid and should not be parsed. 53 | StringPiece::size_type footer_pos = str_.find(it->footer, pos_); 54 | if (footer_pos == StringPiece::npos) { 55 | pos_ = StringPiece::npos; 56 | return false; 57 | } 58 | 59 | // Chop off the header and footer and parse the data in between. 60 | StringPiece::size_type data_begin = pos_ + it->header.size(); 61 | pos_ = footer_pos + it->footer.size(); 62 | block_type_ = it->type; 63 | 64 | StringPiece encoded = str_.substr(data_begin, footer_pos - data_begin); 65 | QuicheOptional data = quiche::QuicheTextUtils::Base64Decode( 66 | gurl_base::CollapseWhitespaceASCII(encoded.as_string(), true)); 67 | if (!data.has_value()) { 68 | break; 69 | } 70 | return true; 71 | } 72 | 73 | // If the block did not match any acceptable type, move past it and 74 | // continue the search. Otherwise, |pos_| has been updated to the most 75 | // appropriate search position to continue searching from and should not 76 | // be adjusted. 77 | if (it == block_types_.end()) 78 | pos_ += sizeof(kPEMSearchBlock); 79 | } 80 | 81 | return false; 82 | } 83 | 84 | void PEMTokenizer::Init(const StringPiece& str, 85 | const std::vector& allowed_block_types) { 86 | str_ = str; 87 | pos_ = 0; 88 | 89 | // Construct PEM header/footer strings for all the accepted types, to 90 | // reduce parsing later. 91 | for (auto it = allowed_block_types.begin(); it != allowed_block_types.end(); 92 | ++it) { 93 | PEMType allowed_type; 94 | allowed_type.type = *it; 95 | allowed_type.header = gurl_base::StringPrintf(kPEMBeginBlock, it->c_str()); 96 | allowed_type.footer = gurl_base::StringPrintf(kPEMEndBlock, it->c_str()); 97 | block_types_.push_back(allowed_type); 98 | } 99 | } 100 | 101 | std::string PEMEncode(StringPiece data, const std::string& type) { 102 | std::string b64_encoded; 103 | quiche::QuicheTextUtils::Base64Encode(data.data(), data.size(), &b64_encoded); 104 | 105 | // Divide the Base-64 encoded data into 64-character chunks, as per 106 | // 4.3.2.4 of RFC 1421. 107 | static const size_t kChunkSize = 64; 108 | size_t chunks = (b64_encoded.size() + (kChunkSize - 1)) / kChunkSize; 109 | 110 | std::string pem_encoded; 111 | pem_encoded.reserve( 112 | // header & footer 113 | 17 + 15 + type.size() * 2 + 114 | // encoded data 115 | b64_encoded.size() + 116 | // newline characters for line wrapping in encoded data 117 | chunks); 118 | 119 | pem_encoded = "-----BEGIN "; 120 | pem_encoded.append(type); 121 | pem_encoded.append("-----\n"); 122 | 123 | for (size_t i = 0, chunk_offset = 0; i < chunks; 124 | ++i, chunk_offset += kChunkSize) { 125 | pem_encoded.append(b64_encoded, chunk_offset, kChunkSize); 126 | pem_encoded.append("\n"); 127 | } 128 | 129 | pem_encoded.append("-----END "); 130 | pem_encoded.append(type); 131 | pem_encoded.append("-----\n"); 132 | return pem_encoded; 133 | } 134 | 135 | } // namespace bvc 136 | -------------------------------------------------------------------------------- /src/net/cert/pem.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef BVC_NET_CERT_PEM_H_ 6 | #define BVC_NET_CERT_PEM_H_ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "googleurl/base/macros.h" 14 | #include "googleurl/base/strings/string_piece.h" 15 | 16 | namespace bvc { 17 | 18 | // PEMTokenizer is a utility class for the parsing of data encapsulated 19 | // using RFC 1421, Privacy Enhancement for Internet Electronic Mail. It 20 | // does not implement the full specification, most notably it does not 21 | // support the Encapsulated Header Portion described in Section 4.4. 22 | class PEMTokenizer { 23 | public: 24 | // Create a new PEMTokenizer that iterates through |str| searching for 25 | // instances of PEM encoded blocks that are of the |allowed_block_types|. 26 | // |str| must remain valid for the duration of the PEMTokenizer. 27 | PEMTokenizer(const gurl_base::StringPiece& str, 28 | const std::vector& allowed_block_types); 29 | ~PEMTokenizer(); 30 | 31 | // Attempts to decode the next PEM block in the string. Returns false if no 32 | // PEM blocks can be decoded. The decoded PEM block will be available via 33 | // data(). 34 | bool GetNext(); 35 | 36 | // Returns the PEM block type (eg: CERTIFICATE) of the last successfully 37 | // decoded PEM block. 38 | // GetNext() must have returned true before calling this method. 39 | const std::string& block_type() const { return block_type_; } 40 | 41 | // Returns the raw, Base64-decoded data of the last successfully decoded 42 | // PEM block. 43 | // GetNext() must have returned true before calling this method. 44 | const std::string& data() const { return data_; } 45 | 46 | private: 47 | void Init(const gurl_base::StringPiece& str, 48 | const std::vector& allowed_block_types); 49 | 50 | // A simple cache of the allowed PEM header and footer for a given PEM 51 | // block type, so that it is only computed once. 52 | struct PEMType; 53 | 54 | // The string to search, which must remain valid for as long as this class 55 | // is around. 56 | gurl_base::StringPiece str_; 57 | 58 | // The current position within |str_| that searching should begin from, 59 | // or StringPiece::npos if iteration is complete 60 | base::StringPiece::size_type pos_; 61 | 62 | // The type of data that was encoded, as indicated in the PEM 63 | // Pre-Encapsulation Boundary (eg: CERTIFICATE, PKCS7, or 64 | // PRIVACY-ENHANCED MESSAGE). 65 | std::string block_type_; 66 | 67 | // The types of PEM blocks that are allowed. PEM blocks that are not of 68 | // one of these types will be skipped. 69 | std::vector block_types_; 70 | 71 | // The raw (Base64-decoded) data of the last successfully decoded block. 72 | std::string data_; 73 | 74 | DISALLOW_COPY_AND_ASSIGN(PEMTokenizer); 75 | }; 76 | 77 | // Encodes |data| in the encapsulated message format described in RFC 1421, 78 | // with |type| as the PEM block type (eg: CERTIFICATE). 79 | std::string PEMEncode(gurl_base::StringPiece data, 80 | const std::string& type); 81 | 82 | } // namespace bvc 83 | 84 | #endif // BVC_NET_CERT_PEM_H_ 85 | -------------------------------------------------------------------------------- /src/net/cert/x509_certificate.cc: -------------------------------------------------------------------------------- 1 | #include "net/cert/x509_certificate.h" 2 | #include "net/cert/pem.h" 3 | #include "net/der/input.h" 4 | 5 | namespace bvc { 6 | namespace { 7 | // Indicates the order to use when trying to decode binary data, which is 8 | // based on (speculation) as to what will be most common -> least common 9 | const X509Certificate::Format kFormatDecodePriority[] = { 10 | X509Certificate::FORMAT_SINGLE_CERTIFICATE, 11 | X509Certificate::FORMAT_PKCS7 12 | }; 13 | 14 | // The PEM block header used for DER certificates 15 | const char kCertificateHeader[] = "CERTIFICATE"; 16 | // The PEM block header used for PKCS#7 data 17 | const char kPKCS7Header[] = "PKCS7"; 18 | } // namespace 19 | 20 | // static 21 | CertificateList X509Certificate::CreateCertificateListFromBytes( 22 | const char* data, 23 | size_t length, 24 | int format) { 25 | std::vector> certificates; 26 | 27 | // Check to see if it is in a PEM-encoded form. This check is performed 28 | // first, as both OS X and NSS will both try to convert if they detect 29 | // PEM encoding, except they don't do it consistently between the two. 30 | gurl_base::StringPiece data_string(data, length); 31 | std::vector pem_headers; 32 | 33 | // To maintain compatibility with NSS/Firefox, CERTIFICATE is a universally 34 | // valid PEM block header for any format. 35 | pem_headers.push_back(kCertificateHeader); 36 | if (format & FORMAT_PKCS7) 37 | pem_headers.push_back(kPKCS7Header); 38 | 39 | PEMTokenizer pem_tokenizer(data_string, pem_headers); 40 | while (pem_tokenizer.GetNext()) { 41 | std::string decoded(pem_tokenizer.data()); 42 | 43 | bssl::UniquePtr handle; 44 | if (format & FORMAT_PEM_CERT_SEQUENCE) 45 | handle = CreateCertBufferFromBytes(decoded.c_str(), decoded.size()); 46 | if (handle) { 47 | // Parsed a DER encoded certificate. All PEM blocks that follow must 48 | // also be DER encoded certificates wrapped inside of PEM blocks. 49 | format = FORMAT_PEM_CERT_SEQUENCE; 50 | certificates.push_back(std::move(handle)); 51 | continue; 52 | } 53 | 54 | // If the first block failed to parse as a DER certificate, and 55 | // formats other than PEM are acceptable, check to see if the decoded 56 | // data is one of the accepted formats. 57 | if (format & ~FORMAT_PEM_CERT_SEQUENCE) { 58 | for (size_t i = 0; 59 | certificates.empty() && i < base::size(kFormatDecodePriority); ++i) { 60 | if (format & kFormatDecodePriority[i]) { 61 | certificates = CreateCertBuffersFromBytes( 62 | decoded.c_str(), decoded.size(), kFormatDecodePriority[i]); 63 | } 64 | } 65 | } 66 | 67 | // Stop parsing after the first block for any format but a sequence of 68 | // PEM-encoded DER certificates. The case of FORMAT_PEM_CERT_SEQUENCE 69 | // is handled above, and continues processing until a certificate fails 70 | // to parse. 71 | break; 72 | } 73 | 74 | // Try each of the formats, in order of parse preference, to see if |data| 75 | // contains the binary representation of a Format, if it failed to parse 76 | // as a PEM certificate/chain. 77 | for (size_t i = 0; 78 | certificates.empty() && i < base::size(kFormatDecodePriority); ++i) { 79 | if (format & kFormatDecodePriority[i]) 80 | certificates = 81 | CreateCertBuffersFromBytes(data, length, kFormatDecodePriority[i]); 82 | } 83 | 84 | CertificateList results; 85 | // No certificates parsed. 86 | if (certificates.empty()) 87 | return results; 88 | 89 | for (auto& it : certificates) { 90 | scoped_refptr cert = CreateFromBuffer(std::move(it), {}); 91 | if (cert) 92 | results.push_back(std::move(cert)); 93 | } 94 | 95 | return results; 96 | } 97 | 98 | // static 99 | bssl::UniquePtr X509Certificate::CreateCertBufferFromBytes( 100 | const char* data, 101 | size_t length) { 102 | der::Input tbs_certificate_tlv; 103 | der::Input signature_algorithm_tlv; 104 | der::BitString signature_value; 105 | // Do a bare minimum of DER parsing here to make sure the input is not 106 | // completely crazy. (This is required for at least 107 | // CreateCertificateListFromBytes with FORMAT_AUTO, if not more.) 108 | if (!ParseCertificate( 109 | der::Input(reinterpret_cast(data), length), 110 | &tbs_certificate_tlv, &signature_algorithm_tlv, &signature_value, 111 | nullptr)) { 112 | return nullptr; 113 | } 114 | 115 | return x509_util::CreateCryptoBuffer(reinterpret_cast(data), 116 | length); 117 | } 118 | } // namespace bvc 119 | -------------------------------------------------------------------------------- /src/net/cert/x509_certificate.h: -------------------------------------------------------------------------------- 1 | #ifndef BVC_NET_CERT_X509_CERTIFICATE_H_ 2 | #define BVC_NET_CERT_X509_CERTIFICATE_H_ 3 | 4 | #include "boringssl/include/openssl/base.h" 5 | 6 | namespace bvc { 7 | 8 | class X509Certificate { 9 | public: 10 | 11 | // Parses all of the certificates possible from |data|. |format| is a 12 | // bit-wise OR of Format, indicating the possible formats the 13 | // certificates may have been serialized as. If an error occurs, an empty 14 | // collection will be returned. 15 | static CertificateList CreateCertificateListFromBytes(const char* data, 16 | size_t length, 17 | int format); 18 | /* 19 | // Construct an X509Certificate from a CRYPTO_BUFFER containing the 20 | // DER-encoded representation. 21 | X509Certificate(bssl::UniquePtr cert_buffer, 22 | std::vector> intermediates); 23 | X509Certificate(bssl::UniquePtr cert_buffer, 24 | std::vector> intermediates, 25 | UnsafeCreateOptions options); 26 | 27 | ~X509Certificate(); 28 | */ 29 | // Creates a CRYPTO_BUFFER from the DER-encoded representation. Unlike 30 | // creating a CRYPTO_BUFFER directly, this function does some minimal 31 | // checking to reject obviously invalid inputs. 32 | // Returns NULL on failure. 33 | static bssl::UniquePtr CreateCertBufferFromBytes( 34 | const char* data, 35 | size_t length); 36 | 37 | }; 38 | 39 | } // namespace bvc 40 | #endif //BVC_NET_CERT_X509_CERTIFICATE_H_ -------------------------------------------------------------------------------- /src/quic_stack_api.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack APIs header file. 3 | 4 | #ifndef _QUIC_STACK_API_H_ 5 | #define _QUIC_STACK_API_H_ 6 | 7 | #if defined(WIN32) 8 | #define EXPORT_API __declspec(dllexport) 9 | #else 10 | #define EXPORT_API __attribute__((visibility("default"))) 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define QUIC_STACK_OK 0 20 | #define QUIC_STACK_MEM -1 21 | #define QUIC_STACK_SERVER -2 22 | #define QUIC_STACK_PARAMETER -3 23 | #define QUIC_STACK_STREAM_CLOSED -4 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | typedef void* tQuicStackHandler; 30 | typedef void* tQuicSessionHandler; 31 | typedef void* tQuicStreamHandler; 32 | typedef void* tQuicStackContext; 33 | 34 | typedef struct { 35 | char connection_data[18]; 36 | uint8_t connection_len; 37 | uint32_t stream_id; 38 | struct sockaddr *self_sockaddr; 39 | socklen_t self_socklen; 40 | struct sockaddr *peer_sockaddr; 41 | socklen_t peer_socklen; 42 | } tQuicRequestID; 43 | 44 | struct tQuicServerCtx { 45 | uint32_t module_idx; 46 | int (*on_request_header_impl)( 47 | const tQuicRequestID *id, 48 | const char *data, 49 | size_t len, 50 | void **ctx, 51 | tQuicServerCtx *server_ctx); 52 | int (*on_request_body_impl)( 53 | const tQuicRequestID *id, 54 | void *ctx, 55 | tQuicServerCtx *server_ctx); 56 | int (*on_request_close_impl)( 57 | const tQuicRequestID *id, 58 | void *ctx, 59 | tQuicServerCtx *server_ctx); 60 | void *server_conf; 61 | }; 62 | 63 | typedef struct { 64 | /* OnRequestHeader 65 | Called when quic stack received request headers and parsed them. 66 | id: unique id for quic request 67 | data: header raw string data 68 | len: data length 69 | ctx: callback context 70 | */ 71 | int (*OnRequestHeader)( 72 | const tQuicRequestID *id, 73 | const char *data, 74 | size_t len, 75 | void **ctx, 76 | tQuicServerCtx *server_ctx); 77 | 78 | /* OnRequestBody 79 | Called when quic stack received request body. 80 | id: unique id for quic request 81 | ctx: callback context 82 | */ 83 | int (*OnRequestBody)( 84 | const tQuicRequestID *id, 85 | void *ctx, 86 | tQuicServerCtx *server_ctx); 87 | 88 | /* OnRequestClose 89 | Called when quic stack close request. 90 | id: unique id for quic request 91 | ctx: callback context 92 | */ 93 | int (*OnRequestClose)( 94 | const tQuicRequestID *id, 95 | void *ctx, 96 | tQuicServerCtx *server_ctx); 97 | 98 | } tQuicRequestCallback; 99 | 100 | typedef struct { 101 | int64_t (*ApproximateTimeNowInUsec)(); 102 | int64_t (*TimeNowInUsec)(); 103 | } tQuicClockTimeGenerator; 104 | 105 | typedef struct tQuicOnCanWriteCallback { 106 | void (*OnCanWriteCallback)(void* ctx); 107 | void *OnCanWriteContext; 108 | } tQuicOnCanWriteCallback; 109 | 110 | typedef struct tQuicStackCertificate { 111 | char *certificate; 112 | int certificate_len; 113 | char *certificate_key; 114 | int certificate_key_len; 115 | char *hostname; 116 | int hostname_len; 117 | tQuicServerCtx *server_ctx; // used for SNI 118 | } tQuicStackCertificate; 119 | 120 | typedef struct tQuicStackConfig { 121 | int max_streams_per_connection; // 100 by default 122 | 123 | int64_t initial_idle_timeout_in_sec; // 10 by default 124 | int64_t default_idle_timeout_in_sec; // 60 by default 125 | int64_t max_idle_timeout_in_sec; // 60*10 by default 126 | int64_t max_time_before_crypto_handshake_in_sec; // 15 by default 127 | 128 | tQuicStackContext stack_ctx; 129 | 130 | tQuicRequestCallback req_cb; 131 | 132 | tQuicClockTimeGenerator clock_gen; 133 | 134 | int *active_connection_nums; 135 | size_t *established_connection_nums; 136 | size_t *zero_rtt_connection_nums; 137 | } tQuicStackConfig; 138 | 139 | 140 | EXPORT_API 141 | tQuicStackHandler quic_stack_create(const tQuicStackConfig* opt_ptr); 142 | 143 | EXPORT_API 144 | void quic_stack_add_certificate(tQuicStackHandler handler, const tQuicStackCertificate* cert_ptr); 145 | 146 | EXPORT_API 147 | void quic_stack_init_writer(tQuicStackHandler handler, int sockfd, tQuicOnCanWriteCallback write_blocked_cb); 148 | 149 | EXPORT_API 150 | void quic_stack_process_chlos(tQuicStackHandler handler, size_t max_connection_to_create); 151 | 152 | EXPORT_API 153 | void quic_stack_process_packet( 154 | tQuicStackHandler handler, 155 | const struct sockaddr* self_saddr, socklen_t self_len, 156 | const struct sockaddr* peer_saddr, socklen_t peer_len, 157 | char *buffer, size_t len); 158 | 159 | EXPORT_API 160 | void quic_stack_on_can_write(tQuicStackHandler handler); 161 | 162 | EXPORT_API 163 | int quic_stack_has_chlos_buffered(tQuicStackHandler handler); 164 | 165 | EXPORT_API 166 | int quic_stack_has_pending_writes(tQuicStackHandler handler); 167 | 168 | EXPORT_API 169 | int quic_stack_read_request_body( 170 | tQuicStackHandler handler, 171 | const tQuicRequestID* id, 172 | char* data, 173 | size_t len); 174 | 175 | EXPORT_API 176 | int quic_stack_write_response_header( 177 | tQuicStackHandler handler, 178 | const tQuicRequestID* id, 179 | const char* data, 180 | size_t len, 181 | const char* trailers, 182 | size_t trailers_len, 183 | int last); 184 | 185 | EXPORT_API 186 | int quic_stack_write_response_body( 187 | tQuicStackHandler handler, 188 | const tQuicRequestID* id, 189 | const char* data, 190 | size_t len, 191 | const char* trailers, 192 | size_t trailers_len, 193 | size_t limit, 194 | int last); 195 | 196 | EXPORT_API 197 | void quic_stack_close_stream( 198 | tQuicStackHandler handler, 199 | const tQuicRequestID* id); 200 | 201 | EXPORT_API 202 | int64_t quic_stack_next_alarm_time(tQuicStackHandler handler); 203 | 204 | EXPORT_API 205 | void quic_stack_on_alarm_timeout( 206 | tQuicStackHandler handler, 207 | int64_t deadline_ms); 208 | 209 | EXPORT_API 210 | int quic_stack_supported_versions( 211 | tQuicStackHandler handler, 212 | char* buf, 213 | size_t len, 214 | uint64_t port, 215 | uint64_t max_age); 216 | 217 | EXPORT_API 218 | void quic_stack_add_on_can_write_callback_once( 219 | tQuicStackHandler handler, 220 | const tQuicRequestID* id, 221 | tQuicOnCanWriteCallback cb); 222 | 223 | 224 | #ifdef __cplusplus 225 | } 226 | #endif 227 | 228 | 229 | #endif /* _QUIC_STACK_API_H_ */ 230 | -------------------------------------------------------------------------------- /src/tQuicAlarmFactory.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "quic/core/quic_arena_scoped_ptr.h" 3 | 4 | #include "src/tQuicAlarmFactory.hh" 5 | 6 | using namespace quic; 7 | 8 | 9 | namespace nginx { 10 | 11 | tQuicAlarmEventQueue::tQuicAlarmEventQueue() {} 12 | 13 | tQuicAlarmEventQueue::~tQuicAlarmEventQueue() { 14 | CleanupTimeToAlarmCBMap(); 15 | } 16 | 17 | void tQuicAlarmEventQueue::RegisterAlarm( 18 | int64_t timeout_time_in_us, AlarmCB* ac) 19 | { 20 | QUICHE_DCHECK(ac != nullptr) << "Invalid AlarmCB"; 21 | if (all_alarms_.find(ac) != all_alarms_.end()) { 22 | QUICHE_DCHECK(false) << "Alarm already exists"; 23 | return; 24 | } 25 | 26 | auto alarm_iter = alarm_map_.insert(std::make_pair(timeout_time_in_us, ac)); 27 | all_alarms_.insert(ac); 28 | ac->OnRegistration(alarm_iter, this); 29 | } 30 | 31 | void tQuicAlarmEventQueue::UnregisterAlarm( 32 | const AlarmRegToken& iterator_token) 33 | { 34 | AlarmCB* cb = iterator_token->second; 35 | alarm_map_.erase(iterator_token); 36 | all_alarms_.erase(cb); 37 | cb->OnUnregistration(); 38 | } 39 | 40 | tQuicAlarmEventQueue::AlarmRegToken tQuicAlarmEventQueue::ReregisterAlarm( 41 | tQuicAlarmEventQueue::AlarmRegToken iterator_token, 42 | int64_t timeout_time_in_us) 43 | { 44 | AlarmCB* cb = iterator_token->second; 45 | alarm_map_.erase(iterator_token); 46 | return alarm_map_.emplace(timeout_time_in_us, cb); 47 | } 48 | 49 | int64_t tQuicAlarmEventQueue::NextAlarmTimeInUs() 50 | { 51 | if (alarm_map_.empty()) { 52 | return 0; 53 | } 54 | return alarm_map_.begin()->first; 55 | } 56 | 57 | void tQuicAlarmEventQueue::CallTimeoutAlarms(int64_t now_in_us) 58 | { 59 | if (now_in_us <= 0) { 60 | return; 61 | } 62 | 63 | TimeToAlarmCBMap::iterator erase_it; 64 | for (auto i = alarm_map_.begin(); i != alarm_map_.end();) { 65 | if (i->first > now_in_us) { 66 | break; 67 | } 68 | AlarmCB* cb = i->second; 69 | // Execute the OnAlarm() only if we did not register 70 | // it in this loop itself. 71 | const bool added_in_this_round = 72 | alarms_reregistered_and_should_be_skipped_.find(cb) != 73 | alarms_reregistered_and_should_be_skipped_.end(); 74 | if (added_in_this_round) { 75 | ++i; 76 | continue; 77 | } 78 | all_alarms_.erase(cb); 79 | const int64_t new_timeout_time_in_us = cb->OnAlarm(); 80 | 81 | erase_it = i; 82 | ++i; 83 | alarm_map_.erase(erase_it); 84 | 85 | if (new_timeout_time_in_us > 0) { 86 | // We add to hash_set only if the new timeout is <= now_in_us. 87 | // if timeout is > now_in_us then we have no fear that this alarm 88 | // can be reexecuted in this loop, and hence we do not need to 89 | // worry about a recursive loop. 90 | if (new_timeout_time_in_us <= now_in_us) { 91 | alarms_reregistered_and_should_be_skipped_.insert(cb); 92 | } 93 | RegisterAlarm(new_timeout_time_in_us, cb); 94 | } 95 | } 96 | alarms_reregistered_and_should_be_skipped_.clear(); 97 | } 98 | 99 | void tQuicAlarmEventQueue::CleanupTimeToAlarmCBMap() 100 | { 101 | TimeToAlarmCBMap::iterator erase_it; 102 | for (auto i = alarm_map_.begin(); i != alarm_map_.end();) { 103 | i->second->OnShutdown(this); 104 | erase_it = i; 105 | ++i; 106 | alarm_map_.erase(erase_it); 107 | } 108 | } 109 | 110 | tQuicAlarmEvent::tQuicAlarmEvent() 111 | : registered_(false), 112 | eq_(nullptr) 113 | {} 114 | 115 | tQuicAlarmEvent::~tQuicAlarmEvent() 116 | { 117 | UnregisterIfRegistered(); 118 | } 119 | 120 | int64_t tQuicAlarmEvent::OnAlarm() 121 | { 122 | registered_ = false; 123 | return 0; 124 | } 125 | 126 | void tQuicAlarmEvent::OnRegistration( 127 | const tQuicAlarmEventQueue::AlarmRegToken& token, 128 | tQuicAlarmEventQueue* eq) 129 | { 130 | QUICHE_DCHECK_EQ(false, registered_); 131 | 132 | registered_ = true; 133 | token_ = token; 134 | eq_ = eq; 135 | 136 | } 137 | 138 | void tQuicAlarmEvent::OnUnregistration() 139 | { 140 | registered_ = false; 141 | } 142 | 143 | void tQuicAlarmEvent::OnShutdown(tQuicAlarmEventQueue* /*eq*/) 144 | { 145 | registered_ = false; 146 | eq_ = nullptr; 147 | } 148 | 149 | void tQuicAlarmEvent::UnregisterIfRegistered() 150 | { 151 | if (!registered_) { 152 | return; 153 | } 154 | 155 | eq_->UnregisterAlarm(token_); 156 | } 157 | 158 | void tQuicAlarmEvent::ReregisterAlarm(int64_t timeout_time_in_us) 159 | { 160 | QUICHE_DCHECK(registered_); 161 | token_ = eq_->ReregisterAlarm(token_, timeout_time_in_us); 162 | } 163 | 164 | tQuicAlarm::QuicAlarmImpl::QuicAlarmImpl(tQuicAlarm* alarm) 165 | : alarm_(alarm) 166 | {} 167 | 168 | int64_t tQuicAlarm::QuicAlarmImpl::OnAlarm() 169 | { 170 | tQuicAlarmEvent::OnAlarm(); 171 | alarm_->Fire(); 172 | // Fire will take care of registering the alarm, if needed. 173 | return 0; 174 | } 175 | 176 | tQuicAlarm::tQuicAlarm( 177 | tQuicAlarmEventQueue* eq, 178 | quic::QuicArenaScopedPtr delegate) 179 | : QuicAlarm(std::move(delegate)), 180 | eq_(eq), 181 | alarm_impl_(this) 182 | { 183 | } 184 | 185 | void tQuicAlarm::SetImpl() 186 | { 187 | QUICHE_DCHECK(deadline().IsInitialized()); 188 | eq_->RegisterAlarm( 189 | (deadline() - QuicTime::Zero()).ToMicroseconds(), &alarm_impl_); 190 | } 191 | 192 | void tQuicAlarm::CancelImpl() 193 | { 194 | QUICHE_DCHECK(!deadline().IsInitialized()); 195 | alarm_impl_.UnregisterIfRegistered(); 196 | } 197 | 198 | void tQuicAlarm::UpdateImpl() 199 | { 200 | QUICHE_DCHECK(deadline().IsInitialized()); 201 | int64_t epoll_deadline = (deadline() - QuicTime::Zero()).ToMicroseconds(); 202 | if (alarm_impl_.registered()) { 203 | alarm_impl_.ReregisterAlarm(epoll_deadline); 204 | } else { 205 | eq_->RegisterAlarm(epoll_deadline, &alarm_impl_); 206 | } 207 | } 208 | 209 | tQuicAlarmFactory::tQuicAlarmFactory() 210 | : alarm_evq_(new tQuicAlarmEventQueue) 211 | { 212 | } 213 | 214 | tQuicAlarmFactory::~tQuicAlarmFactory() = default; 215 | 216 | QuicAlarm* tQuicAlarmFactory::CreateAlarm(QuicAlarm::Delegate* delegate) { 217 | return new tQuicAlarm(alarm_evq_.get(), QuicArenaScopedPtr(delegate)); 218 | } 219 | 220 | QuicArenaScopedPtr tQuicAlarmFactory::CreateAlarm( 221 | QuicArenaScopedPtr delegate, 222 | QuicConnectionArena* arena) { 223 | if (arena != nullptr) { 224 | return arena->New(alarm_evq_.get(), std::move(delegate)); 225 | } 226 | return QuicArenaScopedPtr( 227 | new tQuicAlarm(alarm_evq_.get(), std::move(delegate))); 228 | } 229 | 230 | tQuicAlarmEventQueue* tQuicAlarmFactory::quic_alarm_event_queue() 231 | { 232 | return alarm_evq_.get(); 233 | } 234 | 235 | } // namespace nginx 236 | -------------------------------------------------------------------------------- /src/tQuicAlarmFactory.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack alarm factory class. 3 | 4 | #ifndef _NGINX_T_QUIC_ALARM_FACTORY_H_ 5 | #define _NGINX_T_QUIC_ALARM_FACTORY_H_ 6 | 7 | #include 8 | #include "quic/core/quic_alarm.h" 9 | #include "quic/core/quic_alarm_factory.h" 10 | #include "quic/core/quic_one_block_arena.h" 11 | #include "src/quic_stack_api.h" 12 | 13 | namespace nginx { 14 | 15 | class tQuicAlarmEvent; 16 | 17 | class tQuicAlarmEventQueue { 18 | public: 19 | typedef tQuicAlarmEvent AlarmCB; 20 | typedef std::multimap TimeToAlarmCBMap; 21 | typedef TimeToAlarmCBMap::iterator AlarmRegToken; 22 | 23 | public: 24 | tQuicAlarmEventQueue(); 25 | virtual ~tQuicAlarmEventQueue(); 26 | 27 | void RegisterAlarm(int64_t timeout_time_in_us, AlarmCB* ac); 28 | 29 | void UnregisterAlarm(const AlarmRegToken& iterator_token); 30 | 31 | AlarmRegToken ReregisterAlarm( 32 | AlarmRegToken iterator_token, 33 | int64_t timeout_time_in_us); 34 | 35 | int64_t NextAlarmTimeInUs(); 36 | void CallTimeoutAlarms(int64_t now_in_us); 37 | 38 | protected: 39 | void CleanupTimeToAlarmCBMap(); 40 | 41 | struct AlarmCBHash { 42 | size_t operator()(AlarmCB* const& p) const { 43 | return reinterpret_cast(p); 44 | } 45 | }; 46 | 47 | using AlarmCBMap = std::unordered_set; 48 | 49 | AlarmCBMap all_alarms_; 50 | AlarmCBMap alarms_reregistered_and_should_be_skipped_; 51 | TimeToAlarmCBMap alarm_map_; 52 | }; 53 | 54 | class tQuicAlarmEvent { 55 | public: 56 | tQuicAlarmEvent(); 57 | virtual ~tQuicAlarmEvent(); 58 | 59 | virtual int64_t OnAlarm(); 60 | 61 | void OnRegistration( 62 | const tQuicAlarmEventQueue::AlarmRegToken& token, 63 | tQuicAlarmEventQueue* eq); 64 | 65 | void OnUnregistration(); 66 | 67 | void OnShutdown(tQuicAlarmEventQueue* eq); 68 | 69 | void UnregisterIfRegistered(); 70 | 71 | void ReregisterAlarm(int64_t timeout_time_in_us); 72 | 73 | bool registered() const { return registered_; } 74 | 75 | const tQuicAlarmEventQueue* event_queue() const { return eq_; } 76 | 77 | private: 78 | bool registered_; 79 | tQuicAlarmEventQueue::AlarmRegToken token_; 80 | tQuicAlarmEventQueue* eq_; 81 | }; 82 | 83 | class tQuicAlarm : public quic::QuicAlarm { 84 | public: 85 | tQuicAlarm( 86 | tQuicAlarmEventQueue* eq, 87 | quic::QuicArenaScopedPtr delegate); 88 | 89 | protected: 90 | void SetImpl() override; 91 | void CancelImpl() override; 92 | void UpdateImpl() override; 93 | 94 | private: 95 | class QuicAlarmImpl : public tQuicAlarmEvent { 96 | public: 97 | explicit QuicAlarmImpl(tQuicAlarm* alarm); 98 | 99 | int64_t OnAlarm() override; 100 | private: 101 | tQuicAlarm* alarm_; 102 | }; 103 | 104 | tQuicAlarmEventQueue* eq_; 105 | QuicAlarmImpl alarm_impl_; 106 | }; 107 | 108 | class tQuicAlarmFactory : public quic::QuicAlarmFactory { 109 | public: 110 | tQuicAlarmFactory(); 111 | tQuicAlarmFactory(const tQuicAlarmFactory&) = delete; 112 | tQuicAlarmFactory& operator=(const tQuicAlarmFactory&) = delete; 113 | ~tQuicAlarmFactory() override; 114 | 115 | // QuicAlarmFactory interface. 116 | quic::QuicAlarm* CreateAlarm(quic::QuicAlarm::Delegate* delegate) override; 117 | quic::QuicArenaScopedPtr CreateAlarm( 118 | quic::QuicArenaScopedPtr delegate, 119 | quic::QuicConnectionArena* arena) override; 120 | 121 | tQuicAlarmEventQueue* quic_alarm_event_queue(); 122 | 123 | private: 124 | std::unique_ptr alarm_evq_; 125 | }; 126 | 127 | } // namespace nginx 128 | 129 | #endif // _NGINX_T_QUIC_ALARM_FACTORY_H_ 130 | -------------------------------------------------------------------------------- /src/tQuicClock.cc: -------------------------------------------------------------------------------- 1 | #include "epoll_server/simple_epoll_server.h" 2 | #include "quic/platform/api/quic_flag_utils.h" 3 | #include "quic/platform/api/quic_flags.h" 4 | 5 | #include "src/tQuicClock.hh" 6 | 7 | using namespace quic; 8 | 9 | namespace nginx { 10 | 11 | tQuicClock::tQuicClock(tQuicClockTimeGenerator time_gen) 12 | : time_gen_(time_gen), largest_time_(QuicTime::Zero()) {} 13 | 14 | tQuicClock::~tQuicClock() {} 15 | 16 | QuicTime tQuicClock::ApproximateNow() const { 17 | return CreateTimeFromMicroseconds(time_gen_.ApproximateTimeNowInUsec()); 18 | } 19 | 20 | QuicTime tQuicClock::Now() const { 21 | QuicTime now = CreateTimeFromMicroseconds(time_gen_.TimeNowInUsec()); 22 | 23 | if (now <= largest_time_) { 24 | // Time not increasing, return |largest_time_|. 25 | return largest_time_; 26 | } 27 | 28 | largest_time_ = now; 29 | return largest_time_; 30 | } 31 | 32 | QuicWallTime tQuicClock::WallNow() const { 33 | return QuicWallTime::FromUNIXMicroseconds( 34 | time_gen_.ApproximateTimeNowInUsec()); 35 | } 36 | 37 | QuicTime tQuicClock::ConvertWallTimeToQuicTime( 38 | const QuicWallTime& walltime) const { 39 | return QuicTime::Zero() + 40 | QuicTime::Delta::FromMicroseconds(walltime.ToUNIXMicroseconds()); 41 | } 42 | 43 | } // namespace nginx 44 | -------------------------------------------------------------------------------- /src/tQuicClock.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack clock class. 3 | 4 | #ifndef _NGINX_T_QUIC_CLOCK_H_ 5 | #define _NGINX_T_QUIC_CLOCK_H_ 6 | 7 | #include "base/compiler_specific.h" 8 | #include "base/macros.h" 9 | #include "quic/core/quic_time.h" 10 | #include "quic/core/quic_clock.h" 11 | #include "src/quic_stack_api.h" 12 | 13 | namespace nginx { 14 | 15 | class tQuicClock : public quic::QuicClock { 16 | public: 17 | explicit tQuicClock(tQuicClockTimeGenerator time_gen); 18 | ~tQuicClock() override; 19 | 20 | // Returns the approximate current time as a QuicTime object. 21 | quic::QuicTime ApproximateNow() const override; 22 | 23 | // Returns the current time as a QuicTime object. 24 | // Note: this uses significant resources, please use only if needed. 25 | quic::QuicTime Now() const override; 26 | 27 | // Returns the current time as a QuicWallTime object. 28 | // Note: this uses significant resources, please use only if needed. 29 | quic::QuicWallTime WallNow() const override; 30 | 31 | // Override to do less work in this implementation. 32 | quic::QuicTime ConvertWallTimeToQuicTime( 33 | const quic::QuicWallTime& walltime) const override; 34 | 35 | protected: 36 | tQuicClockTimeGenerator time_gen_; 37 | // Largest time returned from Now() so far. 38 | mutable quic::QuicTime largest_time_; 39 | 40 | private: 41 | DISALLOW_COPY_AND_ASSIGN(tQuicClock); 42 | }; 43 | 44 | } // namespace nginx 45 | 46 | #endif // _NGINX_T_QUIC_CLOCK_H_ 47 | -------------------------------------------------------------------------------- /src/tQuicConnectionHelper.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "quic/core/crypto/quic_random.h" 5 | #include "quic/platform/api/quic_socket_address.h" 6 | 7 | #include "src/tQuicConnectionHelper.hh" 8 | 9 | using namespace quic; 10 | 11 | namespace nginx { 12 | 13 | tQuicConnectionHelper::tQuicConnectionHelper( 14 | tQuicClock* clock, 15 | QuicAllocator type) 16 | : clock_(clock), 17 | random_generator_(QuicRandom::GetInstance()), 18 | allocator_type_(type) {} 19 | 20 | tQuicConnectionHelper::~tQuicConnectionHelper() = default; 21 | 22 | const QuicClock* tQuicConnectionHelper::GetClock() const { 23 | return clock_; 24 | } 25 | 26 | QuicRandom* tQuicConnectionHelper::GetRandomGenerator() { 27 | return random_generator_; 28 | } 29 | 30 | QuicBufferAllocator* tQuicConnectionHelper::GetStreamSendBufferAllocator() { 31 | if (allocator_type_ == QuicAllocator::BUFFER_POOL) { 32 | return &stream_buffer_allocator_; 33 | } else { 34 | QUICHE_DCHECK(allocator_type_ == QuicAllocator::SIMPLE); 35 | return &simple_buffer_allocator_; 36 | } 37 | } 38 | 39 | } // namespace nginx 40 | -------------------------------------------------------------------------------- /src/tQuicConnectionHelper.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack connection helper class. 3 | 4 | #ifndef _NGINX_T_QUIC_CONNECTION_HELPER_H_ 5 | #define _NGINX_T_QUIC_CONNECTION_HELPER_H_ 6 | 7 | #include 8 | #include 9 | 10 | #include "quic/core/quic_connection.h" 11 | #include "quic/core/quic_default_packet_writer.h" 12 | #include "quic/core/quic_packet_writer.h" 13 | #include "quic/core/quic_packets.h" 14 | #include "quic/core/quic_simple_buffer_allocator.h" 15 | #include "quic/core/quic_time.h" 16 | #include "quic/platform/api/quic_epoll.h" 17 | #include "quic/platform/api/quic_stream_buffer_allocator.h" 18 | #include "src/tQuicClock.hh" 19 | 20 | namespace quic { 21 | class QuicRandom; 22 | } 23 | 24 | namespace nginx { 25 | 26 | enum class QuicAllocator { SIMPLE, BUFFER_POOL }; 27 | 28 | class tQuicConnectionHelper : public quic::QuicConnectionHelperInterface { 29 | public: 30 | tQuicConnectionHelper(tQuicClock* clock, QuicAllocator allocator); 31 | tQuicConnectionHelper(const tQuicConnectionHelper&) = delete; 32 | tQuicConnectionHelper& operator=(const tQuicConnectionHelper&) = delete; 33 | ~tQuicConnectionHelper() override; 34 | 35 | // QuicConnectionHelperInterface 36 | const quic::QuicClock* GetClock() const override; 37 | quic::QuicRandom* GetRandomGenerator() override; 38 | quic::QuicBufferAllocator* GetStreamSendBufferAllocator() override; 39 | 40 | private: 41 | const tQuicClock* clock_; 42 | quic::QuicRandom* random_generator_; 43 | // Set up allocators. They take up minimal memory before use. 44 | // Allocator for stream send buffers. 45 | // TODO use nginx pool allocator 46 | quic::QuicStreamBufferAllocator stream_buffer_allocator_; 47 | quic::SimpleBufferAllocator simple_buffer_allocator_; 48 | QuicAllocator allocator_type_; 49 | }; 50 | 51 | } // namespace nginx 52 | 53 | #endif // _NGINX_T_QUIC_CONNECTION_HELPER_H_ 54 | -------------------------------------------------------------------------------- /src/tQuicCryptoServerStream.cc: -------------------------------------------------------------------------------- 1 | #include "quic/core/quic_utils.h" 2 | #include "src/tQuicCryptoServerStream.hh" 3 | 4 | using namespace quic; 5 | 6 | namespace nginx { 7 | 8 | tQuicCryptoServerStream::~tQuicCryptoServerStream() {} 9 | 10 | bool tQuicCryptoServerStream::CanAcceptClientHello( 11 | const CryptoHandshakeMessage& /*message*/, 12 | const QuicSocketAddress& /*client_address*/, 13 | const QuicSocketAddress& /*peer_address*/, 14 | const QuicSocketAddress& /*self_address*/, 15 | std::string* /*error_details*/) const { 16 | return true; 17 | } 18 | 19 | } // namespace nginx 20 | -------------------------------------------------------------------------------- /src/tQuicCryptoServerStream.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack server crypto stream class. 3 | 4 | #ifndef _NGINX_T_QUIC_CRYPTO_SERVER_STREAM_H_ 5 | #define _NGINX_T_QUIC_CRYPTO_SERVER_STREAM_H_ 6 | 7 | #include "quic/core/crypto/quic_random.h" 8 | #include "quic/core/quic_crypto_server_stream.h" 9 | 10 | namespace nginx { 11 | 12 | // Simple helper for server crypto streams which generates a new random 13 | // connection ID for rejects. 14 | class tQuicCryptoServerStream 15 | : public quic::QuicCryptoServerStream::Helper { 16 | public: 17 | ~tQuicCryptoServerStream() override; 18 | 19 | bool CanAcceptClientHello(const quic::CryptoHandshakeMessage& message, 20 | const quic::QuicSocketAddress& client_address, 21 | const quic::QuicSocketAddress& peer_address, 22 | const quic::QuicSocketAddress& self_address, 23 | std::string* error_details) const override; 24 | }; 25 | 26 | } // namespace nginx 27 | 28 | #endif // _NGINX_T_QUIC_CRYPTO_SERVER_STREAM_H_ 29 | -------------------------------------------------------------------------------- /src/tQuicDispatcher.cc: -------------------------------------------------------------------------------- 1 | #include "src/tQuicDispatcher.hh" 2 | #include "src/tQuicServerSession.hh" 3 | 4 | using namespace quic; 5 | 6 | namespace nginx { 7 | 8 | tQuicDispatcher::tQuicDispatcher( 9 | const QuicConfig* config, 10 | const QuicCryptoServerConfig* crypto_config, 11 | QuicVersionManager* version_manager, 12 | std::unique_ptr helper, 13 | std::unique_ptr session_helper, 14 | std::unique_ptr alarm_factory, 15 | uint8_t expected_server_connection_id_length, 16 | tQuicStackContext stack_ctx, 17 | tQuicRequestCallback cb, 18 | tQuicServerIdentifyManager* qsi_ptr) 19 | : QuicDispatcher(config, 20 | crypto_config, 21 | version_manager, 22 | std::move(helper), 23 | std::move(session_helper), 24 | std::move(alarm_factory), 25 | expected_server_connection_id_length), 26 | stack_ctx_(stack_ctx), 27 | callback_(cb), 28 | qsi_mgr_(qsi_ptr) { 29 | write_blocked_cb_.OnCanWriteCallback = nullptr; 30 | write_blocked_cb_.OnCanWriteContext = nullptr; 31 | } 32 | 33 | tQuicDispatcher::~tQuicDispatcher() {} 34 | 35 | void tQuicDispatcher::SetWriteBlockedCallback(tQuicOnCanWriteCallback write_blocked_cb) { 36 | write_blocked_cb_ = write_blocked_cb; 37 | } 38 | 39 | void tQuicDispatcher::OnWriteBlocked(quic::QuicBlockedWriterInterface* blocked_writer) { 40 | quic::QuicDispatcher::OnWriteBlocked(blocked_writer); 41 | 42 | if (write_blocked_cb_.OnCanWriteCallback) { 43 | write_blocked_cb_.OnCanWriteCallback(write_blocked_cb_.OnCanWriteContext); 44 | } 45 | } 46 | 47 | int tQuicDispatcher::GetRstErrorCount( 48 | QuicRstStreamErrorCode error_code) const { 49 | auto it = rst_error_map_.find(error_code); 50 | if (it == rst_error_map_.end()) { 51 | return 0; 52 | } 53 | return it->second; 54 | } 55 | 56 | void tQuicDispatcher::OnRstStreamReceived( 57 | const QuicRstStreamFrame& frame) { 58 | auto it = rst_error_map_.find(frame.error_code); 59 | if (it == rst_error_map_.end()) { 60 | rst_error_map_.insert(std::make_pair(frame.error_code, 1)); 61 | } else { 62 | it->second++; 63 | } 64 | } 65 | 66 | std::unique_ptr tQuicDispatcher::CreateQuicSession( 67 | QuicConnectionId connection_id, 68 | const QuicSocketAddress& self_address, 69 | const QuicSocketAddress& peer_address, 70 | absl::string_view /*alpn*/, 71 | const ParsedQuicVersion& version, 72 | const ParsedClientHello& /*parsed_chlo*/) { 73 | // The QuicServerSessionBase takes ownership of |connection| below. 74 | QuicConnection* connection = new QuicConnection( 75 | connection_id, self_address, peer_address, helper(), alarm_factory(), writer(), 76 | /* owns_writer= */ false, Perspective::IS_SERVER, 77 | ParsedQuicVersionVector{version}); 78 | 79 | std::unique_ptr session = std::make_unique ( 80 | config(), GetSupportedVersions(), connection, this, session_helper(), 81 | crypto_config(), compressed_certs_cache(), stack_ctx_, callback_, qsi_mgr_); 82 | session->Initialize(); 83 | return session; 84 | } 85 | 86 | } // namespace nginx 87 | -------------------------------------------------------------------------------- /src/tQuicDispatcher.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack packet dispatcher class. 3 | 4 | #ifndef _NGINX_T_QUIC_DISPATCH_H_ 5 | #define _NGINX_T_QUIC_DISPATCH_H_ 6 | 7 | #include "quic/core/http/quic_server_session_base.h" 8 | #include "quic/core/quic_crypto_server_stream.h" 9 | #include "quic/core/quic_dispatcher.h" 10 | #include "quic/core/quic_types.h" 11 | #include "src/quic_stack_api.h" 12 | #include "src/tQuicServerStream.hh" 13 | 14 | namespace nginx { 15 | 16 | class tQuicDispatcher : public quic::QuicDispatcher { 17 | public: 18 | tQuicDispatcher( 19 | const quic::QuicConfig* config, 20 | const quic::QuicCryptoServerConfig* crypto_config, 21 | quic::QuicVersionManager* version_manager, 22 | std::unique_ptr helper, 23 | std::unique_ptr session_helper, 24 | std::unique_ptr alarm_factory, 25 | uint8_t expected_server_connection_id_length, 26 | tQuicStackContext stack_ctx, 27 | tQuicRequestCallback cb, 28 | tQuicServerIdentifyManager* qsi_ptr); 29 | ~tQuicDispatcher() override; 30 | 31 | int GetRstErrorCount(quic::QuicRstStreamErrorCode rst_error_code) const; 32 | 33 | void OnRstStreamReceived(const quic::QuicRstStreamFrame& frame) override; 34 | 35 | void SetWriteBlockedCallback(tQuicOnCanWriteCallback write_blocked_cb); 36 | 37 | void OnWriteBlocked(quic::QuicBlockedWriterInterface* blocked_writer) override; 38 | 39 | protected: 40 | std::unique_ptr CreateQuicSession( 41 | quic::QuicConnectionId server_connection_id, 42 | const quic::QuicSocketAddress& self_address, 43 | const quic::QuicSocketAddress& peer_address, 44 | absl::string_view alpn, 45 | const quic::ParsedQuicVersion& version, 46 | const quic::ParsedClientHello& parsed_chlo) override; 47 | 48 | private: 49 | // The map of the reset error code with its counter. 50 | std::map rst_error_map_; 51 | tQuicStackContext stack_ctx_; 52 | tQuicRequestCallback callback_; 53 | tQuicServerIdentifyManager* qsi_mgr_; 54 | tQuicOnCanWriteCallback write_blocked_cb_; 55 | }; 56 | 57 | } // namespace nginx 58 | 59 | #endif // _NGINX_T_QUIC_DISPATCH_H_ 60 | -------------------------------------------------------------------------------- /src/tQuicPacketWriter.cc: -------------------------------------------------------------------------------- 1 | #include "net/quic/platform/impl/quic_socket_utils.h" 2 | #include "src/tQuicPacketWriter.hh" 3 | 4 | using namespace quic; 5 | 6 | namespace nginx { 7 | 8 | tQuicPacketWriter::tQuicPacketWriter(int fd) 9 | : fd_(fd), write_blocked_(false) {} 10 | 11 | tQuicPacketWriter::~tQuicPacketWriter() = default; 12 | 13 | WriteResult tQuicPacketWriter::WritePacket( 14 | const char* buffer, 15 | size_t buf_len, 16 | const QuicIpAddress& self_address, 17 | const QuicSocketAddress& peer_address, 18 | PerPacketOptions* options) { 19 | DCHECK(!write_blocked_); 20 | DCHECK(nullptr == options) 21 | << "tQuicPacketWriter does not accept any options."; 22 | WriteResult result = QuicSocketUtils::WritePacket(fd_, buffer, buf_len, 23 | self_address, peer_address); 24 | if (IsWriteBlockedStatus(result.status)) { 25 | write_blocked_ = true; 26 | } 27 | return result; 28 | } 29 | 30 | bool tQuicPacketWriter::IsWriteBlocked() const { 31 | return write_blocked_; 32 | } 33 | 34 | void tQuicPacketWriter::SetWritable() { 35 | write_blocked_ = false; 36 | } 37 | 38 | QuicByteCount tQuicPacketWriter::GetMaxPacketSize( 39 | const QuicSocketAddress& /*peer_address*/) const { 40 | return kMaxOutgoingPacketSize; 41 | } 42 | 43 | bool tQuicPacketWriter::SupportsReleaseTime() const { 44 | return false; 45 | } 46 | 47 | bool tQuicPacketWriter::IsBatchMode() const { 48 | return false; 49 | } 50 | 51 | char* tQuicPacketWriter::GetNextWriteLocation( 52 | const QuicIpAddress& /*self_address*/, 53 | const QuicSocketAddress& /*peer_address*/) { 54 | return nullptr; 55 | } 56 | 57 | WriteResult tQuicPacketWriter::Flush() { 58 | return WriteResult(WRITE_STATUS_OK, 0); 59 | } 60 | 61 | void tQuicPacketWriter::set_write_blocked(bool is_blocked) { 62 | write_blocked_ = is_blocked; 63 | } 64 | 65 | } // namespace nginx 66 | -------------------------------------------------------------------------------- /src/tQuicPacketWriter.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack packet writer class. 3 | 4 | #ifndef _NGINX_T_QUIC_PACKET_WRITER_H_ 5 | #define _NGINX_T_QUIC_PACKET_WRITER_H_ 6 | 7 | #include 8 | 9 | #include "quic/core/quic_packet_writer.h" 10 | #include "quic/platform/api/quic_export.h" 11 | #include "quic/platform/api/quic_socket_address.h" 12 | 13 | namespace quic { 14 | struct WriteResult; 15 | } 16 | 17 | namespace nginx { 18 | 19 | class tQuicPacketWriter : public quic::QuicPacketWriter { 20 | public: 21 | explicit tQuicPacketWriter(int fd); 22 | tQuicPacketWriter(const tQuicPacketWriter&) = delete; 23 | tQuicPacketWriter& operator=(const tQuicPacketWriter&) = delete; 24 | ~tQuicPacketWriter() override; 25 | 26 | // QuicPacketWriter 27 | quic::WriteResult WritePacket(const char* buffer, 28 | size_t buf_len, 29 | const quic::QuicIpAddress& self_address, 30 | const quic::QuicSocketAddress& peer_address, 31 | quic::PerPacketOptions* options) override; 32 | bool IsWriteBlocked() const override; 33 | void SetWritable() override; 34 | quic::QuicByteCount GetMaxPacketSize( 35 | const quic::QuicSocketAddress& peer_address) const override; 36 | bool SupportsReleaseTime() const override; 37 | bool IsBatchMode() const override; 38 | char* GetNextWriteLocation(const quic::QuicIpAddress& self_address, 39 | const quic::QuicSocketAddress& peer_address) override; 40 | quic::WriteResult Flush() override; 41 | 42 | void set_fd(int fd) { fd_ = fd; } 43 | 44 | protected: 45 | void set_write_blocked(bool is_blocked); 46 | int fd() { return fd_; } 47 | 48 | private: 49 | int fd_; 50 | bool write_blocked_; 51 | }; 52 | 53 | } // namespace nginx 54 | 55 | #endif // _NGINX_T_QUIC_PACKET_WRITER_H_ 56 | -------------------------------------------------------------------------------- /src/tQuicProofSource.cc: -------------------------------------------------------------------------------- 1 | #include "src/tQuicProofSource.hh" 2 | 3 | #include "base/strings/pattern.h" 4 | #include "base/strings/string_number_conversions.h" 5 | #include "base/files/file_util.h" 6 | #include "base/files/file_path.h" 7 | #include "quic/core/quic_clock.h" 8 | #include "quic/core/quic_data_writer.h" 9 | #include "quic/core/crypto/crypto_protocol.h" 10 | #include "quic/core/crypto/certificate_view.h" 11 | #include "quic/core/crypto/proof_source_x509.h" 12 | #include "quic/platform/api/quic_bug_tracker.h" 13 | #include "quic/tools/simple_ticket_crypter.h" 14 | #include "openssl/digest.h" 15 | #include "openssl/evp.h" 16 | #include "openssl/rsa.h" 17 | #include "openssl/base.h" 18 | #include "openssl/crypto.h" 19 | 20 | using std::string; 21 | using namespace quic; 22 | 23 | namespace nginx { 24 | 25 | tQuicProofSource::tQuicProofSource(tQuicClock* clock) { 26 | quic::QuicClock* quic_clock = static_cast(clock); 27 | SetTicketCrypter(std::make_unique(quic_clock)); 28 | } 29 | 30 | tQuicProofSource::~tQuicProofSource() {} 31 | 32 | bool tQuicProofSource::AddCertificateChainFromPath( 33 | const base::FilePath& cert_path, 34 | const base::FilePath& key_path) { 35 | CRYPTO_library_init(); 36 | 37 | std::string cert_data; 38 | if (!base::ReadFileToString(cert_path, &cert_data)) { 39 | QUIC_BUG(-1) << "Unable to read certificates."; 40 | return false; 41 | } 42 | 43 | std::stringstream cert_stream(cert_data); 44 | std::vector certs = 45 | quic::CertificateView::LoadPemFromStream(&cert_stream); 46 | 47 | quic::QuicReferenceCountedPointer chain(new quic::ProofSource::Chain(certs)); 48 | 49 | std::string key_data; 50 | if (!base::ReadFileToString(key_path, &key_data)) { 51 | QUIC_BUG(-1) << "Unable to read key."; 52 | return false; 53 | } 54 | 55 | std::stringstream key_stream(key_data); 56 | std::unique_ptr private_key = quic::CertificatePrivateKey::LoadPemFromStream(&key_stream); 57 | if (private_key == nullptr) { 58 | QUIC_BUG(-1) << "default key is null."; 59 | } 60 | 61 | return AddCertificateChain(chain, std::move(*private_key)); 62 | } 63 | 64 | absl::InlinedVector 65 | tQuicProofSource::SupportedTlsSignatureAlgorithms() const { 66 | // Let ComputeTlsSignature() report an error if a bad signature algorithm is 67 | // requested. 68 | return {}; 69 | } 70 | 71 | void tQuicProofSource::GetProof( 72 | const quic::QuicSocketAddress& /*server_address*/, 73 | const quic::QuicSocketAddress& /*client_address*/, 74 | const std::string& hostname, 75 | const std::string& server_config, 76 | quic::QuicTransportVersion /*transport_version*/, 77 | quiche::QuicheStringPiece chlo_hash, 78 | std::unique_ptr callback) { 79 | quic::QuicCryptoProof proof; 80 | 81 | size_t payload_size = sizeof(kProofSignatureLabel) + sizeof(uint32_t) + 82 | chlo_hash.size() + server_config.size(); 83 | auto payload = std::make_unique(payload_size); 84 | QuicDataWriter payload_writer(payload_size, payload.get(), 85 | quiche::Endianness::HOST_BYTE_ORDER); 86 | bool success = payload_writer.WriteBytes(kProofSignatureLabel, 87 | sizeof(kProofSignatureLabel)) && 88 | payload_writer.WriteUInt32(chlo_hash.size()) && 89 | payload_writer.WriteStringPiece(chlo_hash) && 90 | payload_writer.WriteStringPiece(server_config); 91 | if (!success) { 92 | callback->Run(/*ok=*/false, nullptr, proof, nullptr); 93 | return; 94 | } 95 | 96 | Certificate* certificate = GetCertificate(hostname); 97 | proof.signature = certificate->key.Sign( 98 | quiche::QuicheStringPiece(payload.get(), payload_size), 99 | SSL_SIGN_RSA_PSS_RSAE_SHA256); 100 | callback->Run(/*ok=*/!proof.signature.empty(), certificate->chain, proof, 101 | nullptr); 102 | } 103 | 104 | QuicReferenceCountedPointer tQuicProofSource::GetCertChain( 105 | const quic::QuicSocketAddress& /*server_address*/, 106 | const quic::QuicSocketAddress& /*client_address*/, 107 | const std::string& hostname, 108 | bool* /*cert_matched_sni*/) { 109 | return GetCertificate(hostname)->chain; 110 | } 111 | 112 | void tQuicProofSource::ComputeTlsSignature( 113 | const quic::QuicSocketAddress& /*server_address*/, 114 | const quic::QuicSocketAddress& /*client_address*/, 115 | const std::string& hostname, 116 | uint16_t signature_algorithm, 117 | quiche::QuicheStringPiece in, 118 | std::unique_ptr callback) { 119 | std::string signature = 120 | GetCertificate(hostname)->key.Sign(in, signature_algorithm); 121 | callback->Run(/*ok=*/!signature.empty(), signature, nullptr); 122 | } 123 | 124 | bool tQuicProofSource::AddCertificateChain( 125 | QuicReferenceCountedPointer chain, 126 | CertificatePrivateKey key) { 127 | if (chain->certs.empty()) { 128 | QUIC_BUG(-1) << "Empty certificate chain supplied."; 129 | return false; 130 | } 131 | 132 | std::unique_ptr leaf = 133 | CertificateView::ParseSingleCertificate(chain->certs[0]); 134 | if (leaf == nullptr) { 135 | QUIC_BUG(-1) << "Unable to parse X.509 leaf certificate in the supplied chain."; 136 | return false; 137 | } 138 | if (!key.MatchesPublicKey(*leaf)) { 139 | QUIC_BUG(-1) << "Private key does not match the leaf certificate."; 140 | return false; 141 | } 142 | 143 | certificates_.push_front(Certificate{ 144 | chain, 145 | std::move(key), 146 | }); 147 | Certificate* certificate = &certificates_.front(); 148 | 149 | for (quiche::QuicheStringPiece host : leaf->subject_alt_name_domains()) { 150 | certificate_map_[std::string(host)] = certificate; 151 | } 152 | return true; 153 | } 154 | 155 | tQuicProofSource::Certificate* tQuicProofSource::GetCertificate( 156 | const std::string& hostname) { 157 | auto it = certificate_map_.find(hostname); 158 | if (it != certificate_map_.end()) { 159 | return it->second; 160 | } 161 | auto dot_pos = hostname.find('.'); 162 | if (dot_pos != std::string::npos) { 163 | std::string wildcard = absl::StrCat("*", hostname.substr(dot_pos)); 164 | it = certificate_map_.find(wildcard); 165 | if (it != certificate_map_.end()) { 166 | return it->second; 167 | } 168 | } 169 | return &certificates_.front(); 170 | } 171 | 172 | void tQuicProofSource::SetTicketCrypter( 173 | std::unique_ptr ticket_crypter) { 174 | ticket_crypter_ = std::move(ticket_crypter); 175 | } 176 | 177 | quic::ProofSource::TicketCrypter* tQuicProofSource::GetTicketCrypter() { 178 | return ticket_crypter_.get(); 179 | } 180 | 181 | } // namespace nginx -------------------------------------------------------------------------------- /src/tQuicProofSource.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack proof source class. 3 | 4 | #ifndef _NGINX_T_QUIC_PROOF_SOURCE_H_ 5 | #define _NGINX_T_QUIC_PROOF_SOURCE_H_ 6 | 7 | #include 8 | #include 9 | 10 | #include "src/tQuicClock.hh" 11 | #include "googleurl/base/compiler_specific.h" 12 | #include "googleurl/base/macros.h" 13 | #include "base/files/file_util.h" 14 | #include "quic/core/crypto/certificate_view.h" 15 | #include "quic/core/crypto/proof_source.h" 16 | #include "platform/quiche_platform_impl/quiche_text_utils_impl.h" 17 | #include "platform/quic_platform_impl/quic_containers_impl.h" 18 | namespace nginx { 19 | 20 | class tQuicProofSource : public quic::ProofSource { 21 | public: 22 | tQuicProofSource(tQuicClock* clock); 23 | ~tQuicProofSource() override; 24 | 25 | // Initializes this object based on the certificate chain in |cert_path|, 26 | // and the PKCS#8 RSA private key in |key_path|. Signed certificate 27 | // timestamp may be loaded from |sct_path| if it is non-empty. 28 | bool AddCertificateChainFromPath(const base::FilePath& cert_path, 29 | const base::FilePath& key_path); 30 | 31 | ProofSource::TicketCrypter* GetTicketCrypter() override; 32 | 33 | // ProofSource implementation. 34 | void GetProof(const quic::QuicSocketAddress& server_address, 35 | const quic::QuicSocketAddress& client_address, 36 | const std::string& hostname, 37 | const std::string& server_config, 38 | quic::QuicTransportVersion transport_version, 39 | quiche::QuicheStringPiece chlo_hash, 40 | std::unique_ptr callback) override; 41 | quic::QuicReferenceCountedPointer GetCertChain( 42 | const quic::QuicSocketAddress& server_address, 43 | const quic::QuicSocketAddress& client_address, 44 | const std::string& hostname, 45 | bool* cert_matched_sni) override; 46 | void ComputeTlsSignature( 47 | const quic::QuicSocketAddress& server_address, 48 | const quic::QuicSocketAddress& client_address, 49 | const std::string& hostname, 50 | uint16_t signature_algorithm, 51 | quiche::QuicheStringPiece in, 52 | std::unique_ptr callback) override; 53 | 54 | // Adds a certificate chain to the verifier. Returns false if the chain is 55 | // not valid. Newer certificates will override older certificates with the 56 | // same SubjectAltName value. 57 | ABSL_MUST_USE_RESULT bool AddCertificateChain( 58 | quic::QuicReferenceCountedPointer chain, 59 | quic::CertificatePrivateKey key); 60 | 61 | private: 62 | struct Certificate { 63 | quic::QuicReferenceCountedPointer chain; 64 | quic::CertificatePrivateKey key; 65 | }; 66 | 67 | // Looks up certficiate for hostname, returns the default if no certificate is 68 | // found. 69 | Certificate* GetCertificate(const std::string& hostname); 70 | 71 | absl::InlinedVector SupportedTlsSignatureAlgorithms() const override; 72 | 73 | std::forward_list certificates_; 74 | absl::node_hash_map> certificate_map_; 75 | void SetTicketCrypter(std::unique_ptr ticket_crypter); 76 | 77 | std::unique_ptr ticket_crypter_; 78 | 79 | DISALLOW_COPY_AND_ASSIGN(tQuicProofSource); 80 | }; 81 | 82 | } // namespace nginx 83 | 84 | #endif // _NGINX_T_QUIC_PROOF_SOURCE_H_ 85 | -------------------------------------------------------------------------------- /src/tQuicServerSession.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "quic/core/quic_connection.h" 4 | #include "quic/core/quic_utils.h" 5 | #include "quic/core/quic_session.h" 6 | #include "quic/platform/api/quic_flags.h" 7 | #include "quic/platform/api/quic_logging.h" 8 | #include "src/tQuicServerSession.hh" 9 | #include "src/tQuicServerStream.hh" 10 | 11 | using namespace quic; 12 | 13 | namespace nginx { 14 | 15 | tQuicServerSession::tQuicServerSession( 16 | const QuicConfig& config, 17 | const ParsedQuicVersionVector& supported_versions, 18 | QuicConnection* connection, 19 | QuicSession::Visitor* visitor, 20 | QuicCryptoServerStream::Helper* helper, 21 | const QuicCryptoServerConfig* crypto_config, 22 | QuicCompressedCertsCache* compressed_certs_cache, 23 | tQuicStackContext stack_ctx, 24 | tQuicRequestCallback cb, 25 | tQuicServerIdentifyManager* qsi_ptr) 26 | : QuicServerSessionBase(config, 27 | supported_versions, 28 | connection, 29 | visitor, 30 | helper, 31 | crypto_config, 32 | compressed_certs_cache), 33 | stack_ctx_(stack_ctx), 34 | callback_(cb), 35 | qsi_mgr_(qsi_ptr) { 36 | } 37 | 38 | tQuicServerSession::~tQuicServerSession() { 39 | delete connection(); 40 | } 41 | 42 | void tQuicServerSession::SetDefaultEncryptionLevel(EncryptionLevel level) { 43 | QuicSession::SetDefaultEncryptionLevel(level); 44 | } 45 | 46 | void tQuicServerSession::OnTlsHandshakeComplete() 47 | { 48 | QuicSession::OnTlsHandshakeComplete(); 49 | } 50 | 51 | tQuicServerStream* tQuicServerSession::GetStream(const QuicStreamId stream_id) 52 | { 53 | QuicStream* stream = QuicSession::GetActiveStream(stream_id); 54 | if (stream == nullptr) { 55 | return nullptr; 56 | } 57 | return static_cast(stream); 58 | } 59 | 60 | std::unique_ptr 61 | tQuicServerSession::CreateQuicCryptoServerStream( 62 | const QuicCryptoServerConfig* crypto_config, 63 | QuicCompressedCertsCache* compressed_certs_cache) { 64 | return CreateCryptoServerStream(crypto_config, compressed_certs_cache, this, 65 | stream_helper()); 66 | } 67 | 68 | QuicSpdyStream* tQuicServerSession::CreateIncomingStream(QuicStreamId id) { 69 | if (!ShouldCreateIncomingStream(id)) { 70 | return nullptr; 71 | } 72 | 73 | QuicSpdyStream* stream = new tQuicServerStream( 74 | id, this, BIDIRECTIONAL, stack_ctx_, callback_, qsi_mgr_); 75 | ActivateStream(absl::WrapUnique(stream)); 76 | return stream; 77 | } 78 | 79 | QuicSpdyStream* tQuicServerSession::CreateIncomingStream( 80 | PendingStream* pending) { 81 | QuicSpdyStream* stream = new tQuicServerStream( 82 | pending, this, stack_ctx_, callback_, qsi_mgr_); 83 | ActivateStream(absl::WrapUnique(stream)); 84 | return stream; 85 | } 86 | 87 | tQuicServerStream* 88 | tQuicServerSession::CreateOutgoingBidirectionalStream() { 89 | QUICHE_DCHECK(false); 90 | return nullptr; 91 | } 92 | 93 | tQuicServerStream* 94 | tQuicServerSession::CreateOutgoingUnidirectionalStream() { 95 | if (!ShouldCreateOutgoingUnidirectionalStream()) { 96 | return nullptr; 97 | } 98 | 99 | tQuicServerStream* stream = new tQuicServerStream( 100 | GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL, stack_ctx_, callback_, qsi_mgr_); 101 | ActivateStream(absl::WrapUnique(stream)); 102 | return stream; 103 | } 104 | 105 | } // namespace nginx 106 | -------------------------------------------------------------------------------- /src/tQuicServerSession.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack server session class. 3 | 4 | #ifndef _NGINX_T_QUIC_SERVER_SESSION_H_ 5 | #define _NGINX_T_QUIC_SERVER_SESSION_H_ 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "quic/core/http/quic_server_session_base.h" 17 | #include "quic/core/http/quic_spdy_session.h" 18 | #include "quic/core/quic_crypto_server_stream.h" 19 | #include "quic/core/quic_packets.h" 20 | #include "quic/platform/api/quic_containers.h" 21 | #include "src/tQuicServerStream.hh" 22 | #include "src/quic_stack_api.h" 23 | 24 | namespace nginx { 25 | 26 | class tQuicServerSession : public quic::QuicServerSessionBase { 27 | public: 28 | // Takes ownership of |connection|. 29 | tQuicServerSession(const quic::QuicConfig& config, 30 | const quic::ParsedQuicVersionVector& supported_versions, 31 | quic::QuicConnection* connection, 32 | quic::QuicSession::Visitor* visitor, 33 | quic::QuicCryptoServerStream::Helper* helper, 34 | const quic::QuicCryptoServerConfig* crypto_config, 35 | quic::QuicCompressedCertsCache* compressed_certs_cache, 36 | tQuicStackContext stack_ctx, 37 | tQuicRequestCallback cb, 38 | tQuicServerIdentifyManager* qsi_ptr); 39 | tQuicServerSession(const tQuicServerSession&) = delete; 40 | tQuicServerSession& operator=(const tQuicServerSession&) = delete; 41 | 42 | ~tQuicServerSession() override; 43 | 44 | tQuicServerStream* GetStream(const quic::QuicStreamId stream_id); 45 | 46 | //only for GQUIC 47 | void SetDefaultEncryptionLevel(quic::EncryptionLevel level) override; 48 | //only for IQUIC 49 | void OnTlsHandshakeComplete() override; 50 | 51 | protected: 52 | // QuicSession methods: 53 | quic::QuicSpdyStream* CreateIncomingStream(quic::QuicStreamId id) override; 54 | quic::QuicSpdyStream* CreateIncomingStream(quic::PendingStream* pending) override; 55 | tQuicServerStream* CreateOutgoingBidirectionalStream() override; 56 | tQuicServerStream* CreateOutgoingUnidirectionalStream() override; 57 | 58 | // QuicServerSessionBaseMethod: 59 | virtual std::unique_ptr 60 | CreateQuicCryptoServerStream( 61 | const quic::QuicCryptoServerConfig* crypto_config, 62 | quic::QuicCompressedCertsCache* compressed_certs_cache) override; 63 | 64 | private: 65 | tQuicStackContext stack_ctx_; 66 | tQuicRequestCallback callback_; 67 | tQuicServerIdentifyManager* qsi_mgr_; 68 | }; 69 | 70 | } // namespace nginx 71 | 72 | #endif // _NGINX_T_QUIC_SERVER_SESSION_H_ 73 | -------------------------------------------------------------------------------- /src/tQuicServerStream.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "quic/core/http/quic_spdy_stream.h" 5 | #include "quic/core/http/spdy_utils.h" 6 | #include "quic/core/quic_utils.h" 7 | #include "quic/platform/api/quic_bug_tracker.h" 8 | #include "quic/platform/api/quic_flags.h" 9 | #include "quic/platform/api/quic_logging.h" 10 | #include "quic/platform/api/quic_map_util.h" 11 | #include "quic/core/http/quic_spdy_session.h" 12 | #include "googleurl/base/strings/pattern.h" 13 | #include "googleurl/base/strings/string_split.h" 14 | #include "googleurl/base/strings/string_util.h" 15 | #include "http_parser/http_request_headers.hh" 16 | #include "http_parser/http_response_headers.hh" 17 | #include "src/tQuicServerStream.hh" 18 | 19 | using namespace bvc; 20 | using namespace quic; 21 | using spdy::SpdyHeaderBlock; 22 | 23 | namespace nginx { 24 | 25 | tQuicServerIdentify::tQuicServerIdentify() {} 26 | tQuicServerIdentify::tQuicServerIdentify(const tQuicServerIdentify& qsi) { 27 | name = qsi.name; 28 | cert_path = qsi.cert_path; 29 | key_path = qsi.key_path; 30 | ctx = qsi.ctx; 31 | } 32 | tQuicServerIdentifyManager::tQuicServerIdentifyManager() {} 33 | tQuicServerIdentifyManager::~tQuicServerIdentifyManager() {} 34 | 35 | tQuicServerIdentify* tQuicServerIdentifyManager::GetServerIdentifyByName( 36 | const std::string& name) 37 | { 38 | for (auto& i : servers_) { 39 | if (gurl_base::MatchPattern(name, i.name)) { 40 | return &i; 41 | } 42 | } 43 | 44 | return nullptr; 45 | } 46 | 47 | bool tQuicServerIdentifyManager::AddServerIdentify(const tQuicServerIdentify& qsi) 48 | { 49 | tQuicServerIdentify* p = GetServerIdentifyByName(qsi.name); 50 | if (p) { 51 | return false; 52 | } 53 | servers_.push_back(qsi); 54 | return true; 55 | } 56 | 57 | 58 | const std::set tQuicServerStream::kHopHeaders = { 59 | "alt-svc", 60 | "connection", 61 | "proxy-connection", // non-standard but still sent by libcurl and rejected 62 | // by e.g. google 63 | "keep-alive", "proxy-authenticate", "proxy-authorization", 64 | "te", // canonicalized version of "TE" 65 | "trailer", // not Trailers per URL above; 66 | // http://www.rfc-editor.org/errata_search.php?eid=4522 67 | "transfer-encoding", "upgrade", 68 | "vary", 69 | }; 70 | 71 | 72 | QueuedWriteIOBuffer::QueuedWriteIOBuffer() 73 | : total_size_(0), 74 | max_buffer_size_(kDefaultMaxBufferSize) { 75 | } 76 | 77 | QueuedWriteIOBuffer::~QueuedWriteIOBuffer() { 78 | data_ = nullptr; // pending_data_ owns data_. 79 | } 80 | 81 | bool QueuedWriteIOBuffer::IsEmpty() const { 82 | return pending_data_.empty(); 83 | } 84 | 85 | bool QueuedWriteIOBuffer::Append(const std::string& data) { 86 | if (data.empty()) 87 | return true; 88 | 89 | if (total_size_ + static_cast(data.size()) > max_buffer_size_) { 90 | //LOG(ERROR) << "Too large write data is pending: size=" 91 | // << total_size_ + data.size() 92 | // << ", max_buffer_size=" << max_buffer_size_; 93 | return false; 94 | } 95 | 96 | pending_data_.push_back(std::make_unique(data)); 97 | total_size_ += data.size(); 98 | 99 | // If new data is the first pending data, updates data_. 100 | if (pending_data_.size() == 1) 101 | data_ = const_cast(pending_data_.front()->data()); 102 | return true; 103 | } 104 | 105 | void QueuedWriteIOBuffer::DidConsume(int size) { 106 | QUICHE_DCHECK_GE(total_size_, size); 107 | QUICHE_DCHECK_GE(GetSizeToWrite(), size); 108 | if (size == 0) 109 | return; 110 | 111 | if (size < GetSizeToWrite()) { 112 | data_ += size; 113 | } else { // size == GetSizeToWrite(). Updates data_ to next pending data. 114 | pending_data_.pop_front(); 115 | data_ = 116 | IsEmpty() ? nullptr : const_cast(pending_data_.front()->data()); 117 | } 118 | total_size_ -= size; 119 | } 120 | 121 | int QueuedWriteIOBuffer::GetSizeToWrite() const { 122 | if (IsEmpty()) { 123 | QUICHE_DCHECK_EQ(0, total_size_); 124 | return 0; 125 | } 126 | 127 | QUICHE_DCHECK_GE(data_, pending_data_.front()->data()); 128 | int consumed = static_cast(data_ - pending_data_.front()->data()); 129 | QUICHE_DCHECK_GT(static_cast(pending_data_.front()->size()), consumed); 130 | return pending_data_.front()->size() - consumed; 131 | } 132 | 133 | tQuicServerStream::tQuicServerStream( 134 | QuicStreamId id, 135 | QuicSpdySession* session, 136 | StreamType type, 137 | tQuicStackContext stack_ctx, 138 | tQuicRequestCallback cb, 139 | tQuicServerIdentifyManager* qsi_ptr) 140 | : QuicSpdyServerStreamBase(id, session, type), 141 | content_length_(-1), 142 | header_sent_(false), 143 | callback_ctx_(stack_ctx), // first time, callback ctx is stack context 144 | callback_(cb), 145 | qsi_mgr_(qsi_ptr), 146 | is_new_ok_(true), 147 | body_(new QueuedWriteIOBuffer()){ 148 | can_write_cb_.OnCanWriteCallback = nullptr; 149 | can_write_cb_.OnCanWriteContext = nullptr; 150 | SetRequestID(); 151 | } 152 | 153 | tQuicServerStream::tQuicServerStream( 154 | PendingStream* pending, 155 | QuicSpdySession* session, 156 | tQuicStackContext stack_ctx, 157 | tQuicRequestCallback cb, 158 | tQuicServerIdentifyManager* qsi_ptr) 159 | : QuicSpdyServerStreamBase(pending, session), 160 | content_length_(-1), 161 | header_sent_(false), 162 | callback_ctx_(stack_ctx), 163 | callback_(cb), 164 | qsi_mgr_(qsi_ptr), 165 | is_new_ok_(true), 166 | body_(new QueuedWriteIOBuffer()){ 167 | can_write_cb_.OnCanWriteCallback = nullptr; 168 | can_write_cb_.OnCanWriteContext = nullptr; 169 | SetRequestID(); 170 | } 171 | 172 | tQuicServerStream::~tQuicServerStream() { 173 | } 174 | 175 | void tQuicServerStream::OnRequestHeader() 176 | { 177 | if (callback_.OnRequestHeader && !request_host_.empty()) { 178 | 179 | qsi_ = qsi_mgr_->GetServerIdentifyByName(request_host_); 180 | if (qsi_ == nullptr) { 181 | SendErrorResponseInternal(403, k403ResponseBody); 182 | is_new_ok_ = false; 183 | return; 184 | } 185 | int rc = callback_.OnRequestHeader( 186 | &request_id_, 187 | raw_header_str_.c_str(), 188 | raw_header_str_.length(), 189 | &callback_ctx_, 190 | &qsi_->ctx); 191 | if (rc != QUIC_STACK_OK) { 192 | SendErrorResponse(0); 193 | is_new_ok_ = false; 194 | return; 195 | } 196 | } 197 | 198 | } 199 | 200 | void tQuicServerStream::OnRequestBody() 201 | { 202 | if (is_new_ok_ && qsi_ && callback_.OnRequestBody) { 203 | int rc = callback_.OnRequestBody( 204 | &request_id_, 205 | callback_ctx_, 206 | &qsi_->ctx); 207 | if (rc < 0) { 208 | SendErrorResponse(0); 209 | is_new_ok_ = false; 210 | } 211 | } 212 | } 213 | 214 | void tQuicServerStream::OnInitialHeadersComplete( 215 | bool fin, 216 | size_t frame_len, 217 | const QuicHeaderList& header_list) { 218 | QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list); 219 | if (!CopyAndValidateHeaders(header_list, content_length_, raw_header_str_)) { 220 | SendErrorResponse(0); 221 | } 222 | 223 | if (fin) { 224 | OnRequestHeader(); 225 | raw_header_str_.clear(); 226 | header_sent_ = true; 227 | } 228 | 229 | ConsumeHeaderList(); 230 | } 231 | 232 | void tQuicServerStream::OnTrailingHeadersComplete( 233 | bool /*fin*/, 234 | size_t /*frame_len*/, 235 | const QuicHeaderList& /*header_list*/) { 236 | QUIC_BUG(-1) << "Server does not support receiving Trailers."; 237 | SendErrorResponse(0); 238 | } 239 | 240 | void tQuicServerStream::OnBodyAvailable() { 241 | 242 | while (HasBytesToRead()) { 243 | struct iovec iov; 244 | if (GetReadableRegions(&iov, 1) == 0) { 245 | // No more data to read. 246 | break; 247 | } 248 | 249 | bool rc = body_->Append(std::string(static_cast(iov.iov_base), iov.iov_len)); 250 | if (!rc) { 251 | SendErrorResponse(0); 252 | return; 253 | } 254 | 255 | if (content_length_ >= 0 && 256 | static_cast(body_->total_size()) > content_length_) { 257 | SendErrorResponse(0); 258 | return; 259 | } 260 | 261 | MarkConsumed(iov.iov_len); 262 | } 263 | 264 | if (!sequencer()->IsClosed()) { 265 | sequencer()->SetUnblocked(); 266 | return; 267 | } 268 | 269 | // If the sequencer is closed, then all the body, including the fin, has been 270 | // consumed. 271 | OnFinRead(); 272 | if (write_side_closed() || fin_buffered()) { 273 | return; 274 | } 275 | 276 | if (content_length_ < 0) { 277 | content_length_ = body_->total_size(); 278 | HttpRequestHeaders kContentLength; 279 | kContentLength.SetHeader("content-length", std::to_string(content_length_)); 280 | raw_header_str_ = raw_header_str_.substr(0, raw_header_str_.length()-2); 281 | raw_header_str_ += kContentLength.ToString(); 282 | } 283 | 284 | if (!header_sent_) { 285 | OnRequestHeader(); 286 | header_sent_ = true; 287 | } 288 | OnRequestBody(); 289 | } 290 | 291 | void tQuicServerStream::OnClose() 292 | { 293 | if (is_new_ok_ && qsi_ && callback_.OnRequestClose) { 294 | callback_.OnRequestClose(&request_id_, callback_ctx_, &qsi_->ctx); 295 | } 296 | is_new_ok_ = false; 297 | } 298 | 299 | void tQuicServerStream::OnCanWriteNewData() 300 | { 301 | tQuicOnCanWriteCallback once_cb = can_write_cb_; 302 | can_write_cb_.OnCanWriteCallback = nullptr; 303 | can_write_cb_.OnCanWriteContext = nullptr; 304 | if (once_cb.OnCanWriteCallback) { 305 | once_cb.OnCanWriteCallback(once_cb.OnCanWriteContext); 306 | } 307 | } 308 | 309 | void tQuicServerStream::AddOnCanWriteCallback(tQuicOnCanWriteCallback cb) 310 | { 311 | can_write_cb_ = cb; 312 | } 313 | 314 | int tQuicServerStream::ReadRequestBody(char* data, size_t len) 315 | { 316 | size_t n; 317 | int read_bytes = 0; 318 | 319 | while(!body_->IsEmpty() && len > 0) { 320 | struct iovec iov; 321 | iov.iov_base = body_->data(); 322 | iov.iov_len = body_->GetSizeToWrite(); 323 | n = (len <= iov.iov_len ? len : iov.iov_len); 324 | 325 | memcpy(data+read_bytes, iov.iov_base, n); 326 | 327 | body_->DidConsume(n); 328 | len -= n; 329 | read_bytes += n; 330 | } 331 | 332 | if (read_bytes > 0) { 333 | return read_bytes; 334 | } 335 | 336 | if (body_->IsEmpty()) { 337 | return QUIC_STACK_STREAM_CLOSED; 338 | } 339 | 340 | // TODO find a better way to notify nginx finialzie request. 341 | return QUIC_STACK_SERVER; 342 | } 343 | 344 | void tQuicServerStream::SetTrailers( 345 | const std::string& str, 346 | spdy::SpdyHeaderBlock& headers) 347 | { 348 | if (str.empty()) { 349 | return; 350 | } 351 | 352 | std::vector trailers_v = SplitString(str, "\n"); 353 | 354 | for (const auto& trailers_e : trailers_v) { 355 | std::size_t pos = trailers_e.find(":"); 356 | if (pos != std::string::npos) { 357 | std::string hk = gurl_base::ToLowerASCII(trailers_e.substr(0, pos)); 358 | 359 | std::string hv; 360 | gurl_base::TrimString(trailers_e.substr(pos + 1), " ", &hv); 361 | if (!hk.empty() && !hv.empty()) { 362 | headers[hk] = hv; 363 | } 364 | } 365 | } 366 | } 367 | 368 | void tQuicServerStream::FlushResponse() 369 | { 370 | response_headers_["content-length"] = std::to_string(response_body_.size()); 371 | SendHeadersAndBodyAndTrailers( 372 | std::move(response_headers_), 373 | response_body_, 374 | SpdyHeaderBlock()); 375 | } 376 | 377 | bool tQuicServerStream::WriteResponseHeader( 378 | const char* data, size_t len, const char* trailers, size_t trailers_len, int fin) 379 | { 380 | gurl_base::StringPiece header_str(data, len); 381 | if (header_str.empty()) { 382 | return false; 383 | } 384 | 385 | std::shared_ptr resp_header = HttpResponseHeaders::TryToCreate(header_str); 386 | if (resp_header == nullptr || !resp_header->response_code()) { 387 | return false; 388 | } 389 | 390 | response_headers_[":status"] = std::to_string(resp_header->response_code()); 391 | size_t itr = 0; 392 | std::string name, value; 393 | while(resp_header->EnumerateHeaderLines(&itr, &name, &value)) { 394 | const auto name_lower = gurl_base::ToLowerASCII(name); 395 | if (QuicContainsKey(kHopHeaders, name_lower)) { 396 | continue; 397 | } 398 | response_headers_.AppendValueOrAddHeader(name_lower, value); 399 | } 400 | 401 | SetTrailers(std::string(trailers, trailers_len), response_trailers_); 402 | 403 | if (fin) { 404 | FlushResponse(); 405 | } 406 | 407 | return true; 408 | } 409 | 410 | int tQuicServerStream::WriteResponseBody( 411 | const char* data, size_t len, const char* trailers, size_t trailers_len, size_t limit, bool fin) 412 | { 413 | size_t to_write_size = len; 414 | if (limit > 0) { 415 | size_t buffered_size = BufferedDataBytes(); 416 | to_write_size = buffered_size >= limit ? 0 : limit - buffered_size; 417 | if (to_write_size > len) { 418 | to_write_size = len; 419 | } 420 | } 421 | 422 | gurl_base::StringPiece body_str(data, to_write_size); 423 | if (!body_str.empty()) { 424 | response_body_.append(body_str.data(), body_str.size()); 425 | } 426 | 427 | SetTrailers(std::string(trailers, trailers_len), response_trailers_); 428 | 429 | if (fin) { 430 | FlushResponse(); 431 | } 432 | 433 | return body_str.size(); 434 | } 435 | 436 | void tQuicServerStream::SendErrorResponse(int resp_code) { 437 | SendErrorResponseInternal(resp_code, kErrorResponseBody); 438 | } 439 | 440 | void tQuicServerStream::SendErrorResponseInternal(int resp_code, const char* resp_body) { 441 | SpdyHeaderBlock headers; 442 | if (resp_code <= 0) { 443 | headers[":status"] = "500"; 444 | } else { 445 | headers[":status"] = quiche::QuicheTextUtilsImpl::Uint64ToString(resp_code); 446 | } 447 | headers["content-length"] = 448 | quiche::QuicheTextUtilsImpl::Uint64ToString(strlen(resp_body)); 449 | SendHeadersAndBody(std::move(headers), resp_body); 450 | } 451 | 452 | void tQuicServerStream::SendHeadersAndBody( 453 | SpdyHeaderBlock response_headers, 454 | quiche::QuicheStringPiece body) { 455 | SendHeadersAndBodyAndTrailers(std::move(response_headers), body, 456 | SpdyHeaderBlock()); 457 | } 458 | 459 | void tQuicServerStream::SendHeadersAndBodyAndTrailers( 460 | SpdyHeaderBlock response_headers, 461 | quiche::QuicheStringPiece body, 462 | SpdyHeaderBlock response_trailers) { 463 | // Send the headers, with a FIN if there's nothing else to send. 464 | bool send_fin = (body.empty() && response_trailers.empty()); 465 | WriteHeaders(std::move(response_headers), send_fin, nullptr); 466 | if (send_fin) { 467 | // Nothing else to send. 468 | return; 469 | } 470 | 471 | // Send the body, with a FIN if there's no trailers to send. 472 | send_fin = response_trailers.empty(); 473 | if (!body.empty() || send_fin) { 474 | WriteOrBufferBody(body, send_fin); 475 | } 476 | 477 | if (send_fin) { 478 | // Nothing else to send. 479 | return; 480 | } 481 | 482 | // Send the trailers. A FIN is always sent with trailers. 483 | WriteTrailers(std::move(response_trailers), nullptr); 484 | } 485 | 486 | void tQuicServerStream::CopySocketAddress( 487 | sockaddr** sa, socklen_t& len, sockaddr_storage& ss) 488 | { 489 | switch (ss.ss_family) { 490 | case AF_INET6: 491 | len = sizeof(sockaddr_in6); 492 | *sa = reinterpret_cast(&ss); 493 | break; 494 | case AF_INET: 495 | len = sizeof(sockaddr_in); 496 | *sa = reinterpret_cast(&ss); 497 | break; 498 | default: 499 | len = sizeof(sockaddr_in); 500 | (*sa)->sa_family = AF_UNSPEC; 501 | break; 502 | } 503 | } 504 | 505 | void tQuicServerStream::SetRequestID() 506 | { 507 | QuicConnectionId cid = spdy_session()->connection_id(); 508 | memcpy(request_id_.connection_data, cid.data(), cid.length()); 509 | request_id_.connection_len = cid.length(); 510 | request_id_.stream_id = id(); 511 | 512 | self_generic_address_ = spdy_session()->self_address().generic_address(); 513 | peer_generic_address_ = spdy_session()->peer_address().Normalized().generic_address(); 514 | 515 | // self address 516 | CopySocketAddress( 517 | &request_id_.self_sockaddr, 518 | request_id_.self_socklen, 519 | self_generic_address_); 520 | 521 | // peer address 522 | CopySocketAddress( 523 | &request_id_.peer_sockaddr, 524 | request_id_.peer_socklen, 525 | peer_generic_address_); 526 | } 527 | 528 | bool tQuicServerStream::CopyAndValidateHeaders( 529 | const QuicHeaderList& header_list, 530 | int64_t& content_length, 531 | std::string& header_str) 532 | { 533 | header_str.clear(); 534 | 535 | // first of all, fetch request line 536 | std::string method; 537 | std::string path; 538 | std::string cookies; 539 | HttpRequestHeaders headers; 540 | for (const auto& p : header_list) { 541 | std::string name = p.first; 542 | if (name.empty()) { 543 | //QUIC_DLOG(ERROR) << "Header name must not be empty."; 544 | return false; 545 | } 546 | 547 | if (std::find_if(name.begin(), name.end(), ::isupper) != name.end()) { 548 | //QUIC_DLOG(ERROR) << "Malformed header: Header name " << name 549 | // << " contains upper-case characters."; 550 | name = gurl_base::ToLowerASCII(name); 551 | } 552 | 553 | if (name == "content-length") { 554 | uint64_t val; 555 | if (!quiche::QuicheTextUtilsImpl::StringToUint64(p.second, &val)) { 556 | return false; 557 | } 558 | if (content_length < 0) { 559 | content_length = val; 560 | } 561 | continue; 562 | } 563 | 564 | if (name == "cookie" && !p.second.empty()) { 565 | cookies += p.second; 566 | cookies += ";"; 567 | continue; 568 | } 569 | 570 | if (name[0] == ':') { 571 | if (name[1] == 'm') { // method 572 | method = p.second; 573 | } else if (name[1] == 'p') { // path 574 | path = p.second; 575 | } else if (name[1] == 'a') { // authority 576 | headers.SetHeader("Host", p.second); 577 | request_host_ = p.second; 578 | } 579 | continue; 580 | } 581 | 582 | headers.SetHeader(name, p.second); 583 | } 584 | 585 | if (!cookies.empty()) { 586 | headers.SetHeader("cookie", cookies); 587 | } 588 | 589 | if (content_length >= 0) { 590 | headers.SetHeader("content-length", std::to_string(content_length)); 591 | } 592 | headers.SetHeader("transport-protocol", std::string("quic")); 593 | header_str = method + std::string(" ") + 594 | path + std::string(" HTTP/1.1\r\n") + 595 | headers.ToString(); 596 | 597 | return true; 598 | } 599 | 600 | const char* const tQuicServerStream::kErrorResponseBody = "bad"; 601 | 602 | const char* const tQuicServerStream::k403ResponseBody = 603 | "\r\n" 604 | "403 Forbidden\r\n" 605 | "\r\n" 606 | "

403 Forbidden

\r\n"; 607 | 608 | std::vector 609 | tQuicServerStream::SplitString(const std::string& str, const std::string& delim) { 610 | std::vector output; 611 | std::string::size_type pos1, pos2; 612 | pos1 = 0; 613 | pos2 = str.find(delim); 614 | 615 | while(std::string::npos != pos2) { 616 | output.push_back(str.substr(pos1, pos2 - pos1)); 617 | pos1 = pos2 + delim.size(); 618 | pos2 = str.find(delim, pos1); 619 | } 620 | 621 | if (pos1 != str.length()) { 622 | output.push_back(str.substr(pos1)); 623 | } 624 | 625 | return output; 626 | } 627 | 628 | } // namespace nginx 629 | -------------------------------------------------------------------------------- /src/tQuicServerStream.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack server stream class. 3 | 4 | #ifndef _NGINX_T_QUIC_SERVER_STREAM_H_ 5 | #define _NGINX_T_QUIC_SERVER_STREAM_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "quic/core/quic_circular_deque.h" 11 | #include "base/macros.h" 12 | #include "quic/platform/api/quic_reference_counted.h" 13 | 14 | #include "net/base/io_buffer.h" 15 | #include "quic/core/http/quic_spdy_server_stream_base.h" 16 | #include "quic/core/quic_packets.h" 17 | #include "platform/quiche_platform_impl/quiche_text_utils_impl.h" 18 | #include "spdy/core/spdy_framer.h" 19 | #include "quic_stack_api.h" 20 | 21 | 22 | namespace nginx { 23 | 24 | struct tQuicServerIdentify { 25 | std::string name; 26 | std::string cert_path; 27 | std::string key_path; 28 | tQuicServerCtx ctx; 29 | tQuicServerIdentify(); 30 | tQuicServerIdentify(const tQuicServerIdentify& qsi); 31 | }; 32 | 33 | class tQuicServerIdentifyManager { 34 | public: 35 | tQuicServerIdentifyManager(); 36 | virtual ~tQuicServerIdentifyManager(); 37 | 38 | tQuicServerIdentify* GetServerIdentifyByName(const std::string& name); 39 | bool AddServerIdentify(const tQuicServerIdentify& qsi); 40 | 41 | private: 42 | std::vector servers_; 43 | }; 44 | 45 | // IOBuffer of pending data to write which has a queue of pending data. Each 46 | // pending data is stored in std::string. data() is the data of first 47 | // std::string stored. 48 | class QueuedWriteIOBuffer : public net::IOBuffer { 49 | public: 50 | static const int kDefaultMaxBufferSize = 32 * 1024 * 1024; // 32 Mbytes. 51 | 52 | QueuedWriteIOBuffer(); 53 | 54 | // Whether or not pending data exists. 55 | bool IsEmpty() const; 56 | 57 | // Appends new pending data and returns true if total size doesn't exceed 58 | // the limit, |total_size_limit_|. It would change data() if new data is 59 | // the first pending data. 60 | bool Append(const std::string& data); 61 | 62 | // Consumes data and changes data() accordingly. It cannot be more than 63 | // GetSizeToWrite(). 64 | void DidConsume(int size); 65 | 66 | // Gets size of data to write this time. It is NOT total data size. 67 | int GetSizeToWrite() const; 68 | 69 | // Total size of all pending data. 70 | int total_size() const { return total_size_; } 71 | 72 | // Limit of how much data can be pending. 73 | int max_buffer_size() const { return max_buffer_size_; } 74 | void set_max_buffer_size(int max_buffer_size) { 75 | max_buffer_size_ = max_buffer_size; 76 | } 77 | 78 | private: 79 | ~QueuedWriteIOBuffer() override; 80 | 81 | // This needs to indirect since we need pointer stability for the payload 82 | // chunks, as they may be handed out via net::IOBuffer::data(). 83 | quic::QuicCircularDeque> pending_data_; 84 | int total_size_; 85 | int max_buffer_size_; 86 | 87 | DISALLOW_COPY_AND_ASSIGN(QueuedWriteIOBuffer); 88 | }; 89 | 90 | // All this does right now is aggregate data, and on fin, send an HTTP 91 | // response. 92 | class tQuicServerStream : public quic::QuicSpdyServerStreamBase { 93 | public: 94 | tQuicServerStream(quic::QuicStreamId id, 95 | quic::QuicSpdySession* session, 96 | quic::StreamType type, 97 | tQuicStackContext stack_ctx, 98 | tQuicRequestCallback cb, 99 | tQuicServerIdentifyManager* qsi_ptr); 100 | tQuicServerStream(quic::PendingStream* pending, 101 | quic::QuicSpdySession* session, 102 | tQuicStackContext stack_ctx, 103 | tQuicRequestCallback cb, 104 | tQuicServerIdentifyManager* qsi_ptr); 105 | tQuicServerStream(const tQuicServerStream&) = delete; 106 | tQuicServerStream& operator=(const tQuicServerStream&) = delete; 107 | ~tQuicServerStream() override; 108 | 109 | // QuicSpdyStream 110 | void OnInitialHeadersComplete(bool fin, 111 | size_t frame_len, 112 | const quic::QuicHeaderList& header_list) override; 113 | void OnTrailingHeadersComplete(bool fin, 114 | size_t frame_len, 115 | const quic::QuicHeaderList& header_list) override; 116 | 117 | // QuicStream implementation called by the sequencer when there is 118 | // data (or a FIN) to be read. 119 | void OnBodyAvailable() override; 120 | 121 | void OnClose() override; 122 | 123 | // The response body of error responses. 124 | static const char* const kErrorResponseBody; 125 | static const char* const k403ResponseBody; 126 | 127 | int ReadRequestBody(char* data, size_t len); 128 | 129 | void FlushResponse(); 130 | 131 | bool WriteResponseHeader(const char* data, size_t len, const char* trailers, size_t trailers_len, int fin); 132 | 133 | int WriteResponseBody(const char* data, size_t len, const char* trailers, size_t trailers_len, size_t limit, bool fin); 134 | 135 | void OnCanWriteNewData() override; // override from quic_stream 136 | void AddOnCanWriteCallback(tQuicOnCanWriteCallback cb); 137 | 138 | std::vector SplitString(const std::string& str, const std::string& delim); 139 | 140 | protected: 141 | 142 | static const std::set kHopHeaders; 143 | static const std::set kTrailersHeaders; 144 | 145 | void SetRequestID(); 146 | 147 | void CopySocketAddress(sockaddr** sa, socklen_t& len, sockaddr_storage& ss); 148 | 149 | void SendErrorResponse(int resp_code); 150 | void SendErrorResponseInternal(int resp_code, const char* resp_body); 151 | 152 | void OnRequestHeader(); 153 | void OnRequestBody(); 154 | 155 | void SetTrailers(const std::string& str, spdy::SpdyHeaderBlock& header); 156 | 157 | void SendHeadersAndBody(spdy::SpdyHeaderBlock response_headers, 158 | quiche::QuicheStringPiece body); 159 | void SendHeadersAndBodyAndTrailers(spdy::SpdyHeaderBlock response_headers, 160 | quiche::QuicheStringPiece body, 161 | spdy::SpdyHeaderBlock response_trailers); 162 | 163 | spdy::SpdyHeaderBlock* request_headers() { return &request_headers_; } 164 | 165 | bool CopyAndValidateHeaders(const quic::QuicHeaderList& header_list, 166 | int64_t& content_length, 167 | std::string& header_str); 168 | 169 | // The parsed headers received from the client. 170 | spdy::SpdyHeaderBlock request_headers_; 171 | int64_t content_length_; 172 | bool header_sent_; 173 | 174 | std::string request_host_; 175 | std::string raw_header_str_; 176 | tQuicRequestID request_id_; 177 | void *callback_ctx_; // not owned 178 | tQuicRequestCallback callback_; 179 | 180 | tQuicServerIdentifyManager* qsi_mgr_; 181 | tQuicServerIdentify* qsi_; 182 | bool is_new_ok_; 183 | tQuicOnCanWriteCallback can_write_cb_; 184 | 185 | spdy::SpdyHeaderBlock response_headers_; 186 | std::string response_body_; 187 | spdy::SpdyHeaderBlock response_trailers_; 188 | 189 | sockaddr_storage self_generic_address_; 190 | sockaddr_storage peer_generic_address_; 191 | private: 192 | const quic::QuicReferenceCountedPointer body_; 193 | }; 194 | 195 | } // namespace nginx 196 | 197 | #endif // _NGINX_T_QUIC_SERVER_STREAM_H_ 198 | -------------------------------------------------------------------------------- /src/tQuicStack.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack class. 3 | 4 | #ifndef _NGINX_T_QUIC_STACK_H_ 5 | #define _NGINX_T_QUIC_STACK_H_ 6 | 7 | #include 8 | #include 9 | #include "quic/core/crypto/proof_source.h" 10 | #include "quic/platform/api/quic_socket_address.h" 11 | #include "quic/core/crypto/quic_crypto_server_config.h" 12 | #include "quic/core/crypto/quic_random.h" 13 | #include "quic/core/proto/crypto_server_config_proto.h" 14 | #include "src/tQuicDispatcher.hh" 15 | #include "src/tQuicServerStream.hh" 16 | #include "src/tQuicAlarmFactory.hh" 17 | #include "src/quic_stack_api.h" 18 | #include "src/tQuicClock.hh" 19 | 20 | namespace nginx { 21 | 22 | class tQuicStack { 23 | public: 24 | tQuicStack(tQuicStackContext stack_ctx, 25 | tQuicRequestCallback cb, 26 | tQuicClockTimeGenerator clock_gen, 27 | uint32_t max_streams_per_connection, 28 | uint64_t initial_idle_timeout_in_sec, 29 | uint64_t default_idle_timeout_in_sec, 30 | uint64_t max_idle_timeout_in_sec, 31 | uint64_t max_time_before_crypto_handshake_in_sec, 32 | uint8_t expected_connection_id_length); 33 | 34 | ~tQuicStack(); 35 | 36 | void AddCertificate(const tQuicServerIdentify& qsi); 37 | 38 | void InitializeWithWriter(int fd, tQuicOnCanWriteCallback cb); 39 | 40 | void ProcessBufferedChlos(size_t max_connections_to_create); 41 | 42 | void ProcessPacket(const quic::QuicSocketAddress& self_addr, 43 | const quic::QuicSocketAddress& peer_addr, 44 | char* buffer, size_t length); 45 | 46 | void OnCanWrite(); 47 | 48 | bool HasChlosBuffered(); 49 | 50 | bool HasPendingWrites(); 51 | 52 | int ReadRequestBody( 53 | const tQuicRequestID& id, 54 | char* data, 55 | size_t len); 56 | 57 | bool WriteResponseHeader( 58 | const tQuicRequestID& id, 59 | const char* data, 60 | size_t len, 61 | const char* trailers, 62 | size_t trailers_len, 63 | int fin); 64 | 65 | int WriteResponseBody( 66 | const tQuicRequestID& id, 67 | const char* data, 68 | size_t len, 69 | const char* trailers, 70 | size_t trailers_len, 71 | size_t limit, 72 | bool fin); 73 | 74 | void CloseStream(const tQuicRequestID& id); 75 | 76 | void AddOnCanWriteCallback( 77 | const tQuicRequestID& id, 78 | tQuicOnCanWriteCallback cb); 79 | 80 | int64_t NextAlarmTime(); 81 | void OnAlarmTimeout(int64_t deadline_ms); 82 | 83 | tQuicServerIdentify* GetServerIdentifyByName(const std::string& name); 84 | bool AddServerIdentify(const tQuicServerIdentify& qsi); 85 | 86 | private: 87 | // Initialize the internal state of the stack. 88 | void Initialize(); 89 | void InitializeConfigOptions(); 90 | // Generates a QuicServerConfigProtobuf protobuf suitable for 91 | // QuicServAddConfig and SetConfigs in QuicCryptoServerConfig. 92 | quic::QuicServerConfigProtobuf GenerateConfigProtobuf( 93 | quic::QuicRandom* rand, 94 | const quic::QuicClock* clock, 95 | const quic::QuicCryptoServerConfig::ConfigOptions& options); 96 | 97 | tQuicServerSession* GetSession(const tQuicRequestID& id); 98 | tQuicServerStream* GetStream(const tQuicRequestID& id); 99 | 100 | 101 | private: 102 | std::unique_ptr dispatcher_; 103 | tQuicStackContext stack_ctx_; 104 | tQuicRequestCallback callback_; 105 | tQuicClock clock_; 106 | 107 | tQuicServerIdentifyManager qsi_mgr_; 108 | 109 | // config_ contains non-crypto parameters that are negotiated in the crypto 110 | // handshake. 111 | quic::QuicConfig config_; 112 | // crypto_config_ contains crypto parameters for the handshake. 113 | quic::QuicCryptoServerConfig crypto_config_; 114 | // crypto_config_options_ contains crypto parameters for the handshake. 115 | quic::QuicCryptoServerConfig::ConfigOptions crypto_config_options_; 116 | 117 | // Used to generate current supported versions. 118 | quic::QuicVersionManager version_manager_; 119 | 120 | // The timeout before the handshake succeeds. 121 | uint32_t max_streams_per_connection_; 122 | 123 | // Maximum idle time before the crypto handshake is completed. 124 | uint64_t initial_idle_timeout_in_sec_; 125 | // The default idle timeout. 126 | uint64_t default_idle_timeout_in_sec_; 127 | // The maximum idle timeout than can be negotiated. 128 | uint64_t max_idle_timeout_in_sec_; 129 | // Maximum time the session can be alive before crypto handshake is finished (should not be less than initial_idle_timeout_in_sec_). 130 | uint64_t max_time_before_crypto_handshake_in_sec_; 131 | 132 | // Connection ID length expected to be read on incoming IETF short headers. 133 | uint8_t expected_connection_id_length_; 134 | 135 | // Stack alarm events 136 | tQuicAlarmEventQueue* quic_alarm_evq_; 137 | 138 | }; 139 | 140 | } // namespace nginx 141 | 142 | #endif /* _NGINX_T_QUIC_STACK_H_ */ 143 | -------------------------------------------------------------------------------- /utils/quic_stack_api.hh: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Bilibili Video Cloud Team. All rights reserved. 2 | // Description: QUIC Stack APIs header file. 3 | 4 | #ifndef _QUIC_STACK_API_H_ 5 | #define _QUIC_STACK_API_H_ 6 | 7 | #if defined(WIN32) 8 | #define EXPORT_API __declspec(dllexport) 9 | #else 10 | #define EXPORT_API __attribute__((visibility("default"))) 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define QUIC_STACK_OK 0 20 | #define QUIC_STACK_MEM -1 21 | #define QUIC_STACK_SERVER -2 22 | #define QUIC_STACK_PARAMETER -3 23 | #define QUIC_STACK_STREAM_CLOSED -4 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | typedef void* tQuicStackHandler; 30 | typedef void* tQuicSessionHandler; 31 | typedef void* tQuicStreamHandler; 32 | typedef void* tQuicStackContext; 33 | 34 | typedef struct { 35 | char connection_data[18]; 36 | uint8_t connection_len; 37 | uint32_t stream_id; 38 | struct sockaddr *self_sockaddr; 39 | socklen_t self_socklen; 40 | struct sockaddr *peer_sockaddr; 41 | socklen_t peer_socklen; 42 | } tQuicRequestID; 43 | 44 | struct tQuicServerCtx { 45 | uint32_t module_idx; 46 | int (*on_request_header_impl)( 47 | const tQuicRequestID *id, 48 | const char *data, 49 | size_t len, 50 | void **ctx, 51 | tQuicServerCtx *server_ctx); 52 | int (*on_request_body_impl)( 53 | const tQuicRequestID *id, 54 | void *ctx, 55 | tQuicServerCtx *server_ctx); 56 | int (*on_request_close_impl)( 57 | const tQuicRequestID *id, 58 | void *ctx, 59 | tQuicServerCtx *server_ctx); 60 | void *server_conf; 61 | }; 62 | 63 | typedef struct { 64 | /* OnRequestHeader 65 | Called when quic stack received request headers and parsed them. 66 | id: unique id for quic request 67 | data: header raw string data 68 | len: data length 69 | ctx: callback context 70 | */ 71 | int (*OnRequestHeader)( 72 | const tQuicRequestID *id, 73 | const char *data, 74 | size_t len, 75 | void **ctx, 76 | tQuicServerCtx *server_ctx); 77 | 78 | /* OnRequestBody 79 | Called when quic stack received request body. 80 | id: unique id for quic request 81 | ctx: callback context 82 | */ 83 | int (*OnRequestBody)( 84 | const tQuicRequestID *id, 85 | void *ctx, 86 | tQuicServerCtx *server_ctx); 87 | 88 | /* OnRequestClose 89 | Called when quic stack close request. 90 | id: unique id for quic request 91 | ctx: callback context 92 | */ 93 | int (*OnRequestClose)( 94 | const tQuicRequestID *id, 95 | void *ctx, 96 | tQuicServerCtx *server_ctx); 97 | 98 | } tQuicRequestCallback; 99 | 100 | typedef struct { 101 | int64_t (*ApproximateTimeNowInUsec)(); 102 | int64_t (*TimeNowInUsec)(); 103 | } tQuicClockTimeGenerator; 104 | 105 | typedef struct tQuicOnCanWriteCallback { 106 | void (*OnCanWriteCallback)(void* ctx); 107 | void *OnCanWriteContext; 108 | } tQuicOnCanWriteCallback; 109 | 110 | typedef struct tQuicStackCertificate { 111 | char *certificate; 112 | int certificate_len; 113 | char *certificate_key; 114 | int certificate_key_len; 115 | char *hostname; 116 | int hostname_len; 117 | tQuicServerCtx *server_ctx; // used for SNI 118 | } tQuicStackCertificate; 119 | 120 | typedef struct tQuicStackConfig { 121 | int max_streams_per_connection; // 100 by default 122 | 123 | int64_t initial_idle_timeout_in_sec; // 10 by default 124 | int64_t default_idle_timeout_in_sec; // 60 by default 125 | int64_t max_idle_timeout_in_sec; // 60*10 by default 126 | int64_t max_time_before_crypto_handshake_in_sec; // 15 by default 127 | 128 | tQuicStackContext stack_ctx; 129 | 130 | tQuicRequestCallback req_cb; 131 | 132 | tQuicClockTimeGenerator clock_gen; 133 | 134 | int *active_connection_nums; 135 | size_t *established_connection_nums; 136 | size_t *zero_rtt_connection_nums; 137 | } tQuicStackConfig; 138 | 139 | 140 | EXPORT_API 141 | tQuicStackHandler quic_stack_create(const tQuicStackConfig* opt_ptr); 142 | 143 | EXPORT_API 144 | void quic_stack_add_certificate(tQuicStackHandler handler, const tQuicStackCertificate* cert_ptr); 145 | 146 | EXPORT_API 147 | void quic_stack_init_writer(tQuicStackHandler handler, int sockfd, tQuicOnCanWriteCallback write_blocked_cb); 148 | 149 | EXPORT_API 150 | void quic_stack_process_chlos(tQuicStackHandler handler, size_t max_connection_to_create); 151 | 152 | EXPORT_API 153 | void quic_stack_process_packet( 154 | tQuicStackHandler handler, 155 | const struct sockaddr* self_saddr, socklen_t self_len, 156 | const struct sockaddr* peer_saddr, socklen_t peer_len, 157 | char *buffer, size_t len); 158 | 159 | EXPORT_API 160 | void quic_stack_on_can_write(tQuicStackHandler handler); 161 | 162 | EXPORT_API 163 | int quic_stack_has_chlos_buffered(tQuicStackHandler handler); 164 | 165 | EXPORT_API 166 | int quic_stack_has_pending_writes(tQuicStackHandler handler); 167 | 168 | EXPORT_API 169 | int quic_stack_read_request_body( 170 | tQuicStackHandler handler, 171 | const tQuicRequestID* id, 172 | char* data, 173 | size_t len); 174 | 175 | EXPORT_API 176 | int quic_stack_write_response_header( 177 | tQuicStackHandler handler, 178 | const tQuicRequestID* id, 179 | const char* data, 180 | size_t len, 181 | const char* trailers, 182 | size_t trailers_len, 183 | int last); 184 | 185 | EXPORT_API 186 | int quic_stack_write_response_body( 187 | tQuicStackHandler handler, 188 | const tQuicRequestID* id, 189 | const char* data, 190 | size_t len, 191 | const char* trailers, 192 | size_t trailers_len, 193 | size_t limit, 194 | int last); 195 | 196 | EXPORT_API 197 | void quic_stack_close_stream( 198 | tQuicStackHandler handler, 199 | const tQuicRequestID* id); 200 | 201 | EXPORT_API 202 | int64_t quic_stack_next_alarm_time(tQuicStackHandler handler); 203 | 204 | EXPORT_API 205 | void quic_stack_on_alarm_timeout( 206 | tQuicStackHandler handler, 207 | int64_t deadline_ms); 208 | 209 | EXPORT_API 210 | int quic_stack_supported_versions( 211 | tQuicStackHandler handler, 212 | char* buf, 213 | size_t len); 214 | 215 | EXPORT_API 216 | void quic_stack_add_on_can_write_callback_once( 217 | tQuicStackHandler handler, 218 | const tQuicRequestID* id, 219 | tQuicOnCanWriteCallback cb); 220 | 221 | 222 | #ifdef __cplusplus 223 | } 224 | #endif 225 | 226 | 227 | #endif /* _QUIC_STACK_API_H_ */ 228 | --------------------------------------------------------------------------------