├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── HTTPParserC.podspec
├── HTTPParserC.xcodeproj
├── HTTPParserC_Info.plist
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── xcshareddata
│ └── xcschemes
│ └── HTTPParserC.xcscheme
├── LICENSE
├── Package.swift
├── README.md
├── Sources
├── HTTPParserC.h
├── api.c
├── http.c
├── llhttp.c
└── llhttp.h
└── update.sh
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: "HTTPParserC CI"
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | env:
12 | PROJECT: HTTPParserC.xcodeproj
13 | SCHEME: HTTPParserC
14 |
15 | jobs:
16 | iOS:
17 | name: Build iOS
18 | runs-on: macos-latest
19 | strategy:
20 | matrix:
21 | destination: ["iPhone 8", "iPhone 14 Pro", "iPad Air (5th generation)", "iPad Pro (9.7-inch)"]
22 | steps:
23 | - name: Checkout code
24 | uses: actions/checkout@v4
25 |
26 | - name: Select Xcode version
27 | uses: maxim-lobanov/setup-xcode@v1
28 | with:
29 | xcode-version: latest-stable
30 |
31 | - name: Build iOS
32 | run: |
33 | set -o pipefail
34 | xcodebuild clean build -project "$PROJECT" -scheme "$SCHEME" -destination "platform=iOS Simulator,name=${{ matrix.destination }}" -configuration Release ONLY_ACTIVE_ARCH=NO | xcpretty -c;
35 |
36 | tvOS:
37 | name: Build tvOS
38 | runs-on: macos-latest
39 | strategy:
40 | matrix:
41 | destination: ["Apple TV 4K (3rd generation)"]
42 | steps:
43 | - name: Checkout code
44 | uses: actions/checkout@v4
45 |
46 | - name: Select Xcode version
47 | uses: maxim-lobanov/setup-xcode@v1
48 | with:
49 | xcode-version: latest-stable
50 |
51 | - name: Build tvOS
52 | run: |
53 | set -o pipefail
54 | xcodebuild clean build -project "$PROJECT" -scheme "$SCHEME" -destination "platform=tvOS Simulator,name=${{ matrix.destination }}" -configuration Release ONLY_ACTIVE_ARCH=NO | xcpretty -c;
55 |
56 | macOS:
57 | name: Build macOS
58 | runs-on: macos-latest
59 | steps:
60 | - name: Checkout code
61 | uses: actions/checkout@v4
62 |
63 | - name: Select Xcode version
64 | uses: maxim-lobanov/setup-xcode@v1
65 | with:
66 | xcode-version: latest-stable
67 |
68 | - name: Build macOS
69 | run: |
70 | set -o pipefail
71 | xcodebuild clean build -project "$PROJECT" -scheme "$SCHEME" -destination "platform=macOS,arch=x86_64" -configuration Release ONLY_ACTIVE_ARCH=NO | xcpretty -c;
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | .DS_Store
3 | .Trashes
4 | *.swp
5 | *.lock
6 |
7 | # Xcode
8 | build/
9 | DerivedData/
10 | xcuserdata
11 | *.xccheckout
12 |
13 | *.mode1v3
14 | *.mode2v3
15 | *.moved-aside
16 | *.pbxuser
17 | *.perspectivev3
18 |
19 | # Swift Package Manager
20 | .build/
21 | .swiftpm/
22 | Package.resolved
23 |
24 | # Exceptions
25 | !default.pbxuser
26 | !default.mode1v3
27 | !default.mode2v3
28 | !default.perspectivev3
29 | !Podfile.lock
30 | !Pods/Manifest.lock
31 |
--------------------------------------------------------------------------------
/HTTPParserC.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'HTTPParserC'
3 | s.version = '9.2.0'
4 | s.license = 'MIT'
5 |
6 | s.summary = 'HTTP message parser written in C'
7 | s.description = <<-DESC
8 | HTTPParser is a HTTP message parser written in C, now based on llhttp. It parses both requests and responses.
9 | The parser is designed to be used in performance HTTP applications.
10 | It does not make any syscalls nor allocations, it does not buffer data, it can be interrupted at anytime.
11 | Depending on your architecture, it only requires about 40 bytes of data per message stream
12 | (in a web server that is per connection).
13 | DESC
14 |
15 | s.author = 'Building42'
16 | s.homepage = 'https://github.com/Building42/HTTPParserC'
17 | s.documentation_url = 'https://github.com/nodejs/llhttp'
18 |
19 | s.source = { :git => "https://github.com/Building42/HTTPParserC.git", :tag => s.version }
20 | s.source_files = 'Sources/*.{h,c}'
21 | s.preserve_paths = 'Sources/*.{h,c}'
22 |
23 | s.pod_target_xcconfig = { 'SWIFT_INCLUDE_PATHS' => '$(SRCROOT)/HTTPParserC/Sources/**' }
24 | s.requires_arc = false
25 |
26 | s.ios.deployment_target = '12.0'
27 | s.macos.deployment_target = '10.13'
28 | s.tvos.deployment_target = '12.0'
29 | s.visionos.deployment_target = '1.0'
30 | s.watchos.deployment_target = '4.0'
31 | end
32 |
--------------------------------------------------------------------------------
/HTTPParserC.xcodeproj/HTTPParserC_Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/HTTPParserC.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 822D1888241462E60000E4A8 /* HTTPParserC.h in Headers */ = {isa = PBXBuildFile; fileRef = 822D1885241462E30000E4A8 /* HTTPParserC.h */; settings = {ATTRIBUTES = (Public, ); }; };
11 | 824DF6682BB0D2A900764869 /* llhttp.c in Sources */ = {isa = PBXBuildFile; fileRef = 824DF6642BB0D2A900764869 /* llhttp.c */; };
12 | 824DF6692BB0D2A900764869 /* llhttp.h in Headers */ = {isa = PBXBuildFile; fileRef = 824DF6652BB0D2A900764869 /* llhttp.h */; settings = {ATTRIBUTES = (Public, ); }; };
13 | 824DF66C2BB0D2D200764869 /* api.c in Sources */ = {isa = PBXBuildFile; fileRef = 824DF66A2BB0D2D200764869 /* api.c */; };
14 | 824DF66D2BB0D2D200764869 /* http.c in Sources */ = {isa = PBXBuildFile; fileRef = 824DF66B2BB0D2D200764869 /* http.c */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXFileReference section */
18 | 822D1885241462E30000E4A8 /* HTTPParserC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTTPParserC.h; sourceTree = ""; };
19 | 824DF6642BB0D2A900764869 /* llhttp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = llhttp.c; sourceTree = ""; };
20 | 824DF6652BB0D2A900764869 /* llhttp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = llhttp.h; sourceTree = ""; };
21 | 824DF66A2BB0D2D200764869 /* api.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = api.c; sourceTree = ""; };
22 | 824DF66B2BB0D2D200764869 /* http.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = http.c; sourceTree = ""; };
23 | "HTTPParserC::HTTPParserC::Product" /* HTTPParserC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = HTTPParserC.framework; sourceTree = BUILT_PRODUCTS_DIR; };
24 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; };
25 | /* End PBXFileReference section */
26 |
27 | /* Begin PBXFrameworksBuildPhase section */
28 | OBJ_19 /* Frameworks */ = {
29 | isa = PBXFrameworksBuildPhase;
30 | buildActionMask = 0;
31 | files = (
32 | );
33 | runOnlyForDeploymentPostprocessing = 0;
34 | };
35 | /* End PBXFrameworksBuildPhase section */
36 |
37 | /* Begin PBXGroup section */
38 | 822D1884241462E30000E4A8 /* Sources */ = {
39 | isa = PBXGroup;
40 | children = (
41 | 822D1885241462E30000E4A8 /* HTTPParserC.h */,
42 | 824DF6652BB0D2A900764869 /* llhttp.h */,
43 | 824DF66A2BB0D2D200764869 /* api.c */,
44 | 824DF66B2BB0D2D200764869 /* http.c */,
45 | 824DF6642BB0D2A900764869 /* llhttp.c */,
46 | );
47 | path = Sources;
48 | sourceTree = "";
49 | };
50 | OBJ_11 /* Products */ = {
51 | isa = PBXGroup;
52 | children = (
53 | "HTTPParserC::HTTPParserC::Product" /* HTTPParserC.framework */,
54 | );
55 | name = Products;
56 | sourceTree = BUILT_PRODUCTS_DIR;
57 | };
58 | OBJ_5 = {
59 | isa = PBXGroup;
60 | children = (
61 | OBJ_6 /* Package.swift */,
62 | 822D1884241462E30000E4A8 /* Sources */,
63 | OBJ_11 /* Products */,
64 | );
65 | sourceTree = "";
66 | };
67 | /* End PBXGroup section */
68 |
69 | /* Begin PBXHeadersBuildPhase section */
70 | 8247321F23E633E300A9BAFA /* Headers */ = {
71 | isa = PBXHeadersBuildPhase;
72 | buildActionMask = 2147483647;
73 | files = (
74 | 824DF6692BB0D2A900764869 /* llhttp.h in Headers */,
75 | 822D1888241462E60000E4A8 /* HTTPParserC.h in Headers */,
76 | );
77 | runOnlyForDeploymentPostprocessing = 0;
78 | };
79 | /* End PBXHeadersBuildPhase section */
80 |
81 | /* Begin PBXNativeTarget section */
82 | "HTTPParserC::HTTPParserC" /* HTTPParserC */ = {
83 | isa = PBXNativeTarget;
84 | buildConfigurationList = OBJ_14 /* Build configuration list for PBXNativeTarget "HTTPParserC" */;
85 | buildPhases = (
86 | 8247321F23E633E300A9BAFA /* Headers */,
87 | OBJ_17 /* Sources */,
88 | OBJ_19 /* Frameworks */,
89 | );
90 | buildRules = (
91 | );
92 | dependencies = (
93 | );
94 | name = HTTPParserC;
95 | productName = HTTPParserC;
96 | productReference = "HTTPParserC::HTTPParserC::Product" /* HTTPParserC.framework */;
97 | productType = "com.apple.product-type.framework";
98 | };
99 | /* End PBXNativeTarget section */
100 |
101 | /* Begin PBXProject section */
102 | OBJ_1 /* Project object */ = {
103 | isa = PBXProject;
104 | attributes = {
105 | LastSwiftMigration = 9999;
106 | LastUpgradeCheck = 9999;
107 | };
108 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "HTTPParserC" */;
109 | compatibilityVersion = "Xcode 3.2";
110 | developmentRegion = en;
111 | hasScannedForEncodings = 0;
112 | knownRegions = (
113 | en,
114 | );
115 | mainGroup = OBJ_5;
116 | productRefGroup = OBJ_11 /* Products */;
117 | projectDirPath = "";
118 | projectRoot = "";
119 | targets = (
120 | "HTTPParserC::HTTPParserC" /* HTTPParserC */,
121 | );
122 | };
123 | /* End PBXProject section */
124 |
125 | /* Begin PBXSourcesBuildPhase section */
126 | OBJ_17 /* Sources */ = {
127 | isa = PBXSourcesBuildPhase;
128 | buildActionMask = 0;
129 | files = (
130 | 824DF66C2BB0D2D200764869 /* api.c in Sources */,
131 | 824DF6682BB0D2A900764869 /* llhttp.c in Sources */,
132 | 824DF66D2BB0D2D200764869 /* http.c in Sources */,
133 | );
134 | runOnlyForDeploymentPostprocessing = 0;
135 | };
136 | /* End PBXSourcesBuildPhase section */
137 |
138 | /* Begin XCBuildConfiguration section */
139 | OBJ_15 /* Debug */ = {
140 | isa = XCBuildConfiguration;
141 | buildSettings = {
142 | CURRENT_PROJECT_VERSION = 6;
143 | ENABLE_TESTABILITY = YES;
144 | FRAMEWORK_SEARCH_PATHS = (
145 | "$(inherited)",
146 | "$(PLATFORM_DIR)/Developer/Library/Frameworks",
147 | );
148 | HEADER_SEARCH_PATHS = (
149 | "$(inherited)",
150 | "$(SRCROOT)/Sources/HTTPParserC/include",
151 | );
152 | INFOPLIST_FILE = HTTPParserC.xcodeproj/HTTPParserC_Info.plist;
153 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
154 | MARKETING_VERSION = 9.2.0;
155 | OTHER_CFLAGS = "$(inherited)";
156 | OTHER_LDFLAGS = "$(inherited)";
157 | OTHER_SWIFT_FLAGS = "$(inherited)";
158 | PRODUCT_BUNDLE_IDENTIFIER = HTTPParserC;
159 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
160 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
161 | SKIP_INSTALL = YES;
162 | SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator xros xrsimulator";
163 | SUPPORTS_MACCATALYST = NO;
164 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
165 | TARGETED_DEVICE_FAMILY = "1,2,3,4,7";
166 | TARGET_NAME = HTTPParserC;
167 | };
168 | name = Debug;
169 | };
170 | OBJ_16 /* Release */ = {
171 | isa = XCBuildConfiguration;
172 | buildSettings = {
173 | CURRENT_PROJECT_VERSION = 6;
174 | ENABLE_TESTABILITY = YES;
175 | FRAMEWORK_SEARCH_PATHS = (
176 | "$(inherited)",
177 | "$(PLATFORM_DIR)/Developer/Library/Frameworks",
178 | );
179 | HEADER_SEARCH_PATHS = (
180 | "$(inherited)",
181 | "$(SRCROOT)/Sources/HTTPParserC/include",
182 | );
183 | INFOPLIST_FILE = HTTPParserC.xcodeproj/HTTPParserC_Info.plist;
184 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
185 | MARKETING_VERSION = 9.2.0;
186 | OTHER_CFLAGS = "$(inherited)";
187 | OTHER_LDFLAGS = "$(inherited)";
188 | OTHER_SWIFT_FLAGS = "$(inherited)";
189 | PRODUCT_BUNDLE_IDENTIFIER = HTTPParserC;
190 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
191 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
192 | SKIP_INSTALL = YES;
193 | SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator xros xrsimulator";
194 | SUPPORTS_MACCATALYST = NO;
195 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
196 | TARGETED_DEVICE_FAMILY = "1,2,3,4,7";
197 | TARGET_NAME = HTTPParserC;
198 | };
199 | name = Release;
200 | };
201 | OBJ_3 /* Debug */ = {
202 | isa = XCBuildConfiguration;
203 | buildSettings = {
204 | CLANG_ENABLE_MODULES = YES;
205 | CLANG_ENABLE_OBJC_ARC = YES;
206 | COMBINE_HIDPI_IMAGES = YES;
207 | COPY_PHASE_STRIP = NO;
208 | DEBUG_INFORMATION_FORMAT = dwarf;
209 | DEFINES_MODULE = YES;
210 | DYLIB_INSTALL_NAME_BASE = "@rpath";
211 | ENABLE_NS_ASSERTIONS = YES;
212 | GCC_OPTIMIZATION_LEVEL = 0;
213 | GCC_PREPROCESSOR_DEFINITIONS = (
214 | "$(inherited)",
215 | "SWIFT_PACKAGE=1",
216 | "DEBUG=1",
217 | );
218 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
219 | MACOSX_DEPLOYMENT_TARGET = 10.13;
220 | ONLY_ACTIVE_ARCH = YES;
221 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode";
222 | PRODUCT_NAME = "$(TARGET_NAME)";
223 | SDKROOT = macosx;
224 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
225 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE DEBUG";
226 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
227 | TVOS_DEPLOYMENT_TARGET = 12.0;
228 | USE_HEADERMAP = NO;
229 | WATCHOS_DEPLOYMENT_TARGET = 4.0;
230 | XROS_DEPLOYMENT_TARGET = 1.0;
231 | };
232 | name = Debug;
233 | };
234 | OBJ_4 /* Release */ = {
235 | isa = XCBuildConfiguration;
236 | buildSettings = {
237 | CLANG_ENABLE_MODULES = YES;
238 | CLANG_ENABLE_OBJC_ARC = YES;
239 | COMBINE_HIDPI_IMAGES = YES;
240 | COPY_PHASE_STRIP = YES;
241 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
242 | DEFINES_MODULE = YES;
243 | DYLIB_INSTALL_NAME_BASE = "@rpath";
244 | GCC_OPTIMIZATION_LEVEL = s;
245 | GCC_PREPROCESSOR_DEFINITIONS = (
246 | "$(inherited)",
247 | "SWIFT_PACKAGE=1",
248 | );
249 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
250 | MACOSX_DEPLOYMENT_TARGET = 10.13;
251 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode";
252 | PRODUCT_NAME = "$(TARGET_NAME)";
253 | SDKROOT = macosx;
254 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
255 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE";
256 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
257 | TVOS_DEPLOYMENT_TARGET = 12.0;
258 | USE_HEADERMAP = NO;
259 | WATCHOS_DEPLOYMENT_TARGET = 4.0;
260 | XROS_DEPLOYMENT_TARGET = 1.0;
261 | };
262 | name = Release;
263 | };
264 | /* End XCBuildConfiguration section */
265 |
266 | /* Begin XCConfigurationList section */
267 | OBJ_14 /* Build configuration list for PBXNativeTarget "HTTPParserC" */ = {
268 | isa = XCConfigurationList;
269 | buildConfigurations = (
270 | OBJ_15 /* Debug */,
271 | OBJ_16 /* Release */,
272 | );
273 | defaultConfigurationIsVisible = 0;
274 | defaultConfigurationName = Release;
275 | };
276 | OBJ_2 /* Build configuration list for PBXProject "HTTPParserC" */ = {
277 | isa = XCConfigurationList;
278 | buildConfigurations = (
279 | OBJ_3 /* Debug */,
280 | OBJ_4 /* Release */,
281 | );
282 | defaultConfigurationIsVisible = 0;
283 | defaultConfigurationName = Release;
284 | };
285 | /* End XCConfigurationList section */
286 | };
287 | rootObject = OBJ_1 /* Project object */;
288 | }
289 |
--------------------------------------------------------------------------------
/HTTPParserC.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/HTTPParserC.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/HTTPParserC.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
--------------------------------------------------------------------------------
/HTTPParserC.xcodeproj/xcshareddata/xcschemes/HTTPParserC.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This software is licensed under the MIT License.
2 |
3 | Copyright Fedor Indutny, 2018.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a
6 | copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to permit
10 | persons to whom the Software is furnished to do so, subject to the
11 | following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included
14 | in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
19 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
22 | USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | http_parser.c is based on src/http/ngx_http_parse.c
25 | from NGINX copyright Igor Sysoev.
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "HTTPParserC",
7 | platforms: [
8 | .iOS(.v9),
9 | .tvOS(.v9),
10 | .macOS(.v10_10),
11 | .watchOS(.v2)
12 | ],
13 | products: [
14 | .library(
15 | name: "HTTPParserC",
16 | targets: ["HTTPParserC"]
17 | )
18 | ],
19 | targets: [
20 | .target(
21 | name: "HTTPParserC",
22 | path: "Sources",
23 | publicHeadersPath: ""
24 | )
25 | ]
26 | )
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | HTTP Parser
2 | ===========
3 |
4 | [](https://github.com/Building42/HTTPParserC/actions/workflows/ci.yml)
5 | [](https://cocoapods.org/pods/HTTPParserC)
6 | [](https://cocoapods.org/pods/HTTPParserC)
7 | [](https://cocoapods.org/pods/HTTPParserC)
8 |
9 | HTTP message parser written in C based on [llhttp](https://github.com/nodejs/llhttp). It parses both requests and
10 | responses. The parser is designed to be used in performance HTTP
11 | applications. It does not make any syscalls nor allocations, it does not
12 | buffer data, it can be interrupted at anytime. Depending on your
13 | architecture, it only requires about 40 bytes of data per message
14 | stream (in a web server that is per connection).
15 |
16 | Features:
17 |
18 | * No dependencies
19 | * Handles persistent streams (keep-alive).
20 | * Decodes chunked encoding.
21 | * Upgrade support
22 | * Defends against buffer overflow attacks.
23 |
24 | The parser extracts the following information from HTTP messages:
25 |
26 | * Header fields and values
27 | * Content-Length
28 | * Request method
29 | * Response status code
30 | * Transfer-Encoding
31 | * HTTP version
32 | * Request URL
33 | * Message body
34 |
35 | ## Installation
36 |
37 | ### Swift Package Manager
38 |
39 | The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code.
40 |
41 | You can add Telegraph to your project by choosing the File - Swift Packages - Add Package Dependency option. Use the repository url as specified below and select the version you want to use.
42 |
43 | Or you can manually add a `Package.swift` file to your project with:
44 |
45 | ```swift
46 | dependencies: [
47 | .package(url: "https://github.com/Building42/HTTPParserC.git")
48 | ]
49 | ```
50 |
51 | ### CocoaPods
52 |
53 | CocoaPods is a dependency manager for Cocoa projects that makes dependencies a part of your workspace.
54 |
55 | ```ruby
56 | pod 'HTTPParserC'
57 | ```
58 |
59 | See [CocoaPods - Getting Started](https://guides.cocoapods.org/using/getting-started.html) for more information.
60 |
61 | ## Documentation
62 | Visit https://github.com/nodejs/llhttp for more information
63 |
--------------------------------------------------------------------------------
/Sources/HTTPParserC.h:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPParserC.h
3 | // HTTPParserC
4 | //
5 | // Created by Yvo van Beek on 5/17/17.
6 | //
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for HTTPParserC.
12 | FOUNDATION_EXPORT double HTTPParserC_VersionNumber;
13 |
14 | //! Project version string for HTTPParserC.
15 | FOUNDATION_EXPORT const unsigned char HTTPParserC_VersionString[];
16 |
17 | #import "llhttp.h"
18 |
--------------------------------------------------------------------------------
/Sources/api.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "llhttp.h"
6 |
7 | #define CALLBACK_MAYBE(PARSER, NAME) \
8 | do { \
9 | const llhttp_settings_t* settings; \
10 | settings = (const llhttp_settings_t*) (PARSER)->settings; \
11 | if (settings == NULL || settings->NAME == NULL) { \
12 | err = 0; \
13 | break; \
14 | } \
15 | err = settings->NAME((PARSER)); \
16 | } while (0)
17 |
18 | #define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
19 | do { \
20 | const llhttp_settings_t* settings; \
21 | settings = (const llhttp_settings_t*) (PARSER)->settings; \
22 | if (settings == NULL || settings->NAME == NULL) { \
23 | err = 0; \
24 | break; \
25 | } \
26 | err = settings->NAME((PARSER), (START), (LEN)); \
27 | if (err == -1) { \
28 | err = HPE_USER; \
29 | llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \
30 | } \
31 | } while (0)
32 |
33 | void llhttp_init(llhttp_t* parser, llhttp_type_t type,
34 | const llhttp_settings_t* settings) {
35 | llhttp__internal_init(parser);
36 |
37 | parser->type = type;
38 | parser->settings = (void*) settings;
39 | }
40 |
41 |
42 | #if defined(__wasm__)
43 |
44 | extern int wasm_on_message_begin(llhttp_t * p);
45 | extern int wasm_on_url(llhttp_t* p, const char* at, size_t length);
46 | extern int wasm_on_status(llhttp_t* p, const char* at, size_t length);
47 | extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);
48 | extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);
49 | extern int wasm_on_headers_complete(llhttp_t * p, int status_code,
50 | uint8_t upgrade, int should_keep_alive);
51 | extern int wasm_on_body(llhttp_t* p, const char* at, size_t length);
52 | extern int wasm_on_message_complete(llhttp_t * p);
53 |
54 | static int wasm_on_headers_complete_wrap(llhttp_t* p) {
55 | return wasm_on_headers_complete(p, p->status_code, p->upgrade,
56 | llhttp_should_keep_alive(p));
57 | }
58 |
59 | const llhttp_settings_t wasm_settings = {
60 | wasm_on_message_begin,
61 | wasm_on_url,
62 | wasm_on_status,
63 | NULL,
64 | NULL,
65 | wasm_on_header_field,
66 | wasm_on_header_value,
67 | NULL,
68 | NULL,
69 | wasm_on_headers_complete_wrap,
70 | wasm_on_body,
71 | wasm_on_message_complete,
72 | NULL,
73 | NULL,
74 | NULL,
75 | NULL,
76 | NULL,
77 | NULL,
78 | NULL,
79 | NULL,
80 | NULL,
81 | NULL,
82 | NULL,
83 | };
84 |
85 |
86 | llhttp_t* llhttp_alloc(llhttp_type_t type) {
87 | llhttp_t* parser = malloc(sizeof(llhttp_t));
88 | llhttp_init(parser, type, &wasm_settings);
89 | return parser;
90 | }
91 |
92 | void llhttp_free(llhttp_t* parser) {
93 | free(parser);
94 | }
95 |
96 | #endif // defined(__wasm__)
97 |
98 | /* Some getters required to get stuff from the parser */
99 |
100 | uint8_t llhttp_get_type(llhttp_t* parser) {
101 | return parser->type;
102 | }
103 |
104 | uint8_t llhttp_get_http_major(llhttp_t* parser) {
105 | return parser->http_major;
106 | }
107 |
108 | uint8_t llhttp_get_http_minor(llhttp_t* parser) {
109 | return parser->http_minor;
110 | }
111 |
112 | uint8_t llhttp_get_method(llhttp_t* parser) {
113 | return parser->method;
114 | }
115 |
116 | int llhttp_get_status_code(llhttp_t* parser) {
117 | return parser->status_code;
118 | }
119 |
120 | uint8_t llhttp_get_upgrade(llhttp_t* parser) {
121 | return parser->upgrade;
122 | }
123 |
124 |
125 | void llhttp_reset(llhttp_t* parser) {
126 | llhttp_type_t type = parser->type;
127 | const llhttp_settings_t* settings = parser->settings;
128 | void* data = parser->data;
129 | uint16_t lenient_flags = parser->lenient_flags;
130 |
131 | llhttp__internal_init(parser);
132 |
133 | parser->type = type;
134 | parser->settings = (void*) settings;
135 | parser->data = data;
136 | parser->lenient_flags = lenient_flags;
137 | }
138 |
139 |
140 | llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
141 | return llhttp__internal_execute(parser, data, data + len);
142 | }
143 |
144 |
145 | void llhttp_settings_init(llhttp_settings_t* settings) {
146 | memset(settings, 0, sizeof(*settings));
147 | }
148 |
149 |
150 | llhttp_errno_t llhttp_finish(llhttp_t* parser) {
151 | int err;
152 |
153 | /* We're in an error state. Don't bother doing anything. */
154 | if (parser->error != 0) {
155 | return 0;
156 | }
157 |
158 | switch (parser->finish) {
159 | case LLHTTP_FINISH_SAFE_WITH_CB:
160 | CALLBACK_MAYBE(parser, on_message_complete);
161 | if (err != HPE_OK) return err;
162 |
163 | /* FALLTHROUGH */
164 | case LLHTTP_FINISH_SAFE:
165 | return HPE_OK;
166 | case LLHTTP_FINISH_UNSAFE:
167 | parser->reason = "Invalid EOF state";
168 | return HPE_INVALID_EOF_STATE;
169 | default:
170 | abort();
171 | }
172 | }
173 |
174 |
175 | void llhttp_pause(llhttp_t* parser) {
176 | if (parser->error != HPE_OK) {
177 | return;
178 | }
179 |
180 | parser->error = HPE_PAUSED;
181 | parser->reason = "Paused";
182 | }
183 |
184 |
185 | void llhttp_resume(llhttp_t* parser) {
186 | if (parser->error != HPE_PAUSED) {
187 | return;
188 | }
189 |
190 | parser->error = 0;
191 | }
192 |
193 |
194 | void llhttp_resume_after_upgrade(llhttp_t* parser) {
195 | if (parser->error != HPE_PAUSED_UPGRADE) {
196 | return;
197 | }
198 |
199 | parser->error = 0;
200 | }
201 |
202 |
203 | llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
204 | return parser->error;
205 | }
206 |
207 |
208 | const char* llhttp_get_error_reason(const llhttp_t* parser) {
209 | return parser->reason;
210 | }
211 |
212 |
213 | void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
214 | parser->reason = reason;
215 | }
216 |
217 |
218 | const char* llhttp_get_error_pos(const llhttp_t* parser) {
219 | return parser->error_pos;
220 | }
221 |
222 |
223 | const char* llhttp_errno_name(llhttp_errno_t err) {
224 | #define LLHTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
225 | switch (err) {
226 | LLHTTP_ERRNO_MAP(LLHTTP_ERRNO_GEN)
227 | default: abort();
228 | }
229 | #undef LLHTTP_ERRNO_GEN
230 | }
231 |
232 |
233 | const char* llhttp_method_name(llhttp_method_t method) {
234 | #define LLHTTP_METHOD_GEN(NUM, NAME, STRING) case LLHTTP_##NAME: return #STRING;
235 | switch (method) {
236 | LLHTTP_ALL_METHOD_MAP(LLHTTP_METHOD_GEN)
237 | default: abort();
238 | }
239 | #undef LLHTTP_METHOD_GEN
240 | }
241 |
242 | const char* llhttp_status_name(llhttp_status_t status) {
243 | #define LLHTTP_STATUS_GEN(NUM, NAME, STRING) case LLHTTP_STATUS_##NAME: return #STRING;
244 | switch (status) {
245 | LLHTTP_STATUS_MAP(LLHTTP_STATUS_GEN)
246 | default: abort();
247 | }
248 | #undef LLHTTP_STATUS_GEN
249 | }
250 |
251 |
252 | void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {
253 | if (enabled) {
254 | parser->lenient_flags |= LENIENT_HEADERS;
255 | } else {
256 | parser->lenient_flags &= ~LENIENT_HEADERS;
257 | }
258 | }
259 |
260 |
261 | void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {
262 | if (enabled) {
263 | parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;
264 | } else {
265 | parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;
266 | }
267 | }
268 |
269 |
270 | void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {
271 | if (enabled) {
272 | parser->lenient_flags |= LENIENT_KEEP_ALIVE;
273 | } else {
274 | parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;
275 | }
276 | }
277 |
278 | void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) {
279 | if (enabled) {
280 | parser->lenient_flags |= LENIENT_TRANSFER_ENCODING;
281 | } else {
282 | parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING;
283 | }
284 | }
285 |
286 | void llhttp_set_lenient_version(llhttp_t* parser, int enabled) {
287 | if (enabled) {
288 | parser->lenient_flags |= LENIENT_VERSION;
289 | } else {
290 | parser->lenient_flags &= ~LENIENT_VERSION;
291 | }
292 | }
293 |
294 | void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) {
295 | if (enabled) {
296 | parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE;
297 | } else {
298 | parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE;
299 | }
300 | }
301 |
302 | void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) {
303 | if (enabled) {
304 | parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR;
305 | } else {
306 | parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR;
307 | }
308 | }
309 |
310 | void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) {
311 | if (enabled) {
312 | parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
313 | } else {
314 | parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
315 | }
316 | }
317 |
318 | void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) {
319 | if (enabled) {
320 | parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF;
321 | } else {
322 | parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF;
323 | }
324 | }
325 |
326 | void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) {
327 | if (enabled) {
328 | parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;
329 | } else {
330 | parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE;
331 | }
332 | }
333 |
334 | /* Callbacks */
335 |
336 |
337 | int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
338 | int err;
339 | CALLBACK_MAYBE(s, on_message_begin);
340 | return err;
341 | }
342 |
343 |
344 | int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
345 | int err;
346 | SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);
347 | return err;
348 | }
349 |
350 |
351 | int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {
352 | int err;
353 | CALLBACK_MAYBE(s, on_url_complete);
354 | return err;
355 | }
356 |
357 |
358 | int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
359 | int err;
360 | SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);
361 | return err;
362 | }
363 |
364 |
365 | int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {
366 | int err;
367 | CALLBACK_MAYBE(s, on_status_complete);
368 | return err;
369 | }
370 |
371 |
372 | int llhttp__on_method(llhttp_t* s, const char* p, const char* endp) {
373 | int err;
374 | SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p);
375 | return err;
376 | }
377 |
378 |
379 | int llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) {
380 | int err;
381 | CALLBACK_MAYBE(s, on_method_complete);
382 | return err;
383 | }
384 |
385 |
386 | int llhttp__on_version(llhttp_t* s, const char* p, const char* endp) {
387 | int err;
388 | SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p);
389 | return err;
390 | }
391 |
392 |
393 | int llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) {
394 | int err;
395 | CALLBACK_MAYBE(s, on_version_complete);
396 | return err;
397 | }
398 |
399 |
400 | int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
401 | int err;
402 | SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);
403 | return err;
404 | }
405 |
406 |
407 | int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {
408 | int err;
409 | CALLBACK_MAYBE(s, on_header_field_complete);
410 | return err;
411 | }
412 |
413 |
414 | int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
415 | int err;
416 | SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);
417 | return err;
418 | }
419 |
420 |
421 | int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {
422 | int err;
423 | CALLBACK_MAYBE(s, on_header_value_complete);
424 | return err;
425 | }
426 |
427 |
428 | int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
429 | int err;
430 | CALLBACK_MAYBE(s, on_headers_complete);
431 | return err;
432 | }
433 |
434 |
435 | int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
436 | int err;
437 | CALLBACK_MAYBE(s, on_message_complete);
438 | return err;
439 | }
440 |
441 |
442 | int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
443 | int err;
444 | SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);
445 | return err;
446 | }
447 |
448 |
449 | int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
450 | int err;
451 | CALLBACK_MAYBE(s, on_chunk_header);
452 | return err;
453 | }
454 |
455 |
456 | int llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) {
457 | int err;
458 | SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p);
459 | return err;
460 | }
461 |
462 |
463 | int llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) {
464 | int err;
465 | CALLBACK_MAYBE(s, on_chunk_extension_name_complete);
466 | return err;
467 | }
468 |
469 |
470 | int llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) {
471 | int err;
472 | SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p);
473 | return err;
474 | }
475 |
476 |
477 | int llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) {
478 | int err;
479 | CALLBACK_MAYBE(s, on_chunk_extension_value_complete);
480 | return err;
481 | }
482 |
483 |
484 | int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
485 | int err;
486 | CALLBACK_MAYBE(s, on_chunk_complete);
487 | return err;
488 | }
489 |
490 |
491 | int llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) {
492 | int err;
493 | CALLBACK_MAYBE(s, on_reset);
494 | return err;
495 | }
496 |
497 |
498 | /* Private */
499 |
500 |
501 | void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
502 | const char* msg) {
503 | if (p == endp) {
504 | fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
505 | s->flags, msg);
506 | } else {
507 | fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
508 | s->type, s->flags, *p, msg);
509 | }
510 | }
511 |
--------------------------------------------------------------------------------
/Sources/http.c:
--------------------------------------------------------------------------------
1 | #include
2 | #ifndef LLHTTP__TEST
3 | # include "llhttp.h"
4 | #else
5 | # define llhttp_t llparse_t
6 | #endif /* */
7 |
8 | int llhttp_message_needs_eof(const llhttp_t* parser);
9 | int llhttp_should_keep_alive(const llhttp_t* parser);
10 |
11 | int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
12 | const char* endp) {
13 | /* Set this here so that on_headers_complete() callbacks can see it */
14 | if ((parser->flags & F_UPGRADE) &&
15 | (parser->flags & F_CONNECTION_UPGRADE)) {
16 | /* For responses, "Upgrade: foo" and "Connection: upgrade" are
17 | * mandatory only when it is a 101 Switching Protocols response,
18 | * otherwise it is purely informational, to announce support.
19 | */
20 | parser->upgrade =
21 | (parser->type == LLHTTP_REQUEST || parser->status_code == 101);
22 | } else {
23 | parser->upgrade = (parser->method == LLHTTP_CONNECT);
24 | }
25 | return 0;
26 | }
27 |
28 |
29 | /* Return values:
30 | * 0 - No body, `restart`, message_complete
31 | * 1 - CONNECT request, `restart`, message_complete, and pause
32 | * 2 - chunk_size_start
33 | * 3 - body_identity
34 | * 4 - body_identity_eof
35 | * 5 - invalid transfer-encoding for request
36 | */
37 | int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
38 | const char* endp) {
39 | int hasBody;
40 |
41 | hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
42 | if (
43 | (parser->upgrade && (parser->method == LLHTTP_CONNECT ||
44 | (parser->flags & F_SKIPBODY) || !hasBody)) ||
45 | /* See RFC 2616 section 4.4 - 1xx e.g. Continue */
46 | (parser->type == LLHTTP_RESPONSE && parser->status_code == 101)
47 | ) {
48 | /* Exit, the rest of the message is in a different protocol. */
49 | return 1;
50 | }
51 |
52 | if (parser->type == LLHTTP_RESPONSE && parser->status_code == 100) {
53 | /* No body, restart as the message is complete */
54 | return 0;
55 | }
56 |
57 | /* See RFC 2616 section 4.4 */
58 | if (
59 | parser->flags & F_SKIPBODY || /* response to a HEAD request */
60 | (
61 | parser->type == LLHTTP_RESPONSE && (
62 | parser->status_code == 102 || /* Processing */
63 | parser->status_code == 103 || /* Early Hints */
64 | parser->status_code == 204 || /* No Content */
65 | parser->status_code == 304 /* Not Modified */
66 | )
67 | )
68 | ) {
69 | return 0;
70 | } else if (parser->flags & F_CHUNKED) {
71 | /* chunked encoding - ignore Content-Length header, prepare for a chunk */
72 | return 2;
73 | } else if (parser->flags & F_TRANSFER_ENCODING) {
74 | if (parser->type == LLHTTP_REQUEST &&
75 | (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 &&
76 | (parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) {
77 | /* RFC 7230 3.3.3 */
78 |
79 | /* If a Transfer-Encoding header field
80 | * is present in a request and the chunked transfer coding is not
81 | * the final encoding, the message body length cannot be determined
82 | * reliably; the server MUST respond with the 400 (Bad Request)
83 | * status code and then close the connection.
84 | */
85 | return 5;
86 | } else {
87 | /* RFC 7230 3.3.3 */
88 |
89 | /* If a Transfer-Encoding header field is present in a response and
90 | * the chunked transfer coding is not the final encoding, the
91 | * message body length is determined by reading the connection until
92 | * it is closed by the server.
93 | */
94 | return 4;
95 | }
96 | } else {
97 | if (!(parser->flags & F_CONTENT_LENGTH)) {
98 | if (!llhttp_message_needs_eof(parser)) {
99 | /* Assume content-length 0 - read the next */
100 | return 0;
101 | } else {
102 | /* Read body until EOF */
103 | return 4;
104 | }
105 | } else if (parser->content_length == 0) {
106 | /* Content-Length header given but zero: Content-Length: 0\r\n */
107 | return 0;
108 | } else {
109 | /* Content-Length header given and non-zero */
110 | return 3;
111 | }
112 | }
113 | }
114 |
115 |
116 | int llhttp__after_message_complete(llhttp_t* parser, const char* p,
117 | const char* endp) {
118 | int should_keep_alive;
119 |
120 | should_keep_alive = llhttp_should_keep_alive(parser);
121 | parser->finish = LLHTTP_FINISH_SAFE;
122 | parser->flags = 0;
123 |
124 | /* NOTE: this is ignored in loose parsing mode */
125 | return should_keep_alive;
126 | }
127 |
128 |
129 | int llhttp_message_needs_eof(const llhttp_t* parser) {
130 | if (parser->type == LLHTTP_REQUEST) {
131 | return 0;
132 | }
133 |
134 | /* See RFC 2616 section 4.4 */
135 | if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
136 | parser->status_code == 204 || /* No Content */
137 | parser->status_code == 304 || /* Not Modified */
138 | (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */
139 | return 0;
140 | }
141 |
142 | /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */
143 | if ((parser->flags & F_TRANSFER_ENCODING) &&
144 | (parser->flags & F_CHUNKED) == 0) {
145 | return 1;
146 | }
147 |
148 | if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
149 | return 0;
150 | }
151 |
152 | return 1;
153 | }
154 |
155 |
156 | int llhttp_should_keep_alive(const llhttp_t* parser) {
157 | if (parser->http_major > 0 && parser->http_minor > 0) {
158 | /* HTTP/1.1 */
159 | if (parser->flags & F_CONNECTION_CLOSE) {
160 | return 0;
161 | }
162 | } else {
163 | /* HTTP/1.0 or earlier */
164 | if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
165 | return 0;
166 | }
167 | }
168 |
169 | return !llhttp_message_needs_eof(parser);
170 | }
171 |
--------------------------------------------------------------------------------
/Sources/llhttp.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef INCLUDE_LLHTTP_H_
3 | #define INCLUDE_LLHTTP_H_
4 |
5 | #define LLHTTP_VERSION_MAJOR 9
6 | #define LLHTTP_VERSION_MINOR 2
7 | #define LLHTTP_VERSION_PATCH 0
8 |
9 | #ifndef INCLUDE_LLHTTP_ITSELF_H_
10 | #define INCLUDE_LLHTTP_ITSELF_H_
11 | #ifdef __cplusplus
12 | extern "C" {
13 | #endif
14 |
15 | #include
16 |
17 | typedef struct llhttp__internal_s llhttp__internal_t;
18 | struct llhttp__internal_s {
19 | int32_t _index;
20 | void* _span_pos0;
21 | void* _span_cb0;
22 | int32_t error;
23 | const char* reason;
24 | const char* error_pos;
25 | void* data;
26 | void* _current;
27 | uint64_t content_length;
28 | uint8_t type;
29 | uint8_t method;
30 | uint8_t http_major;
31 | uint8_t http_minor;
32 | uint8_t header_state;
33 | uint16_t lenient_flags;
34 | uint8_t upgrade;
35 | uint8_t finish;
36 | uint16_t flags;
37 | uint16_t status_code;
38 | uint8_t initial_message_completed;
39 | void* settings;
40 | };
41 |
42 | int llhttp__internal_init(llhttp__internal_t* s);
43 | int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
44 |
45 | #ifdef __cplusplus
46 | } /* extern "C" */
47 | #endif
48 | #endif /* INCLUDE_LLHTTP_ITSELF_H_ */
49 |
50 |
51 | #ifndef LLLLHTTP_C_HEADERS_
52 | #define LLLLHTTP_C_HEADERS_
53 | #ifdef __cplusplus
54 | extern "C" {
55 | #endif
56 |
57 | enum llhttp_errno {
58 | HPE_OK = 0,
59 | HPE_INTERNAL = 1,
60 | HPE_STRICT = 2,
61 | HPE_CR_EXPECTED = 25,
62 | HPE_LF_EXPECTED = 3,
63 | HPE_UNEXPECTED_CONTENT_LENGTH = 4,
64 | HPE_UNEXPECTED_SPACE = 30,
65 | HPE_CLOSED_CONNECTION = 5,
66 | HPE_INVALID_METHOD = 6,
67 | HPE_INVALID_URL = 7,
68 | HPE_INVALID_CONSTANT = 8,
69 | HPE_INVALID_VERSION = 9,
70 | HPE_INVALID_HEADER_TOKEN = 10,
71 | HPE_INVALID_CONTENT_LENGTH = 11,
72 | HPE_INVALID_CHUNK_SIZE = 12,
73 | HPE_INVALID_STATUS = 13,
74 | HPE_INVALID_EOF_STATE = 14,
75 | HPE_INVALID_TRANSFER_ENCODING = 15,
76 | HPE_CB_MESSAGE_BEGIN = 16,
77 | HPE_CB_HEADERS_COMPLETE = 17,
78 | HPE_CB_MESSAGE_COMPLETE = 18,
79 | HPE_CB_CHUNK_HEADER = 19,
80 | HPE_CB_CHUNK_COMPLETE = 20,
81 | HPE_PAUSED = 21,
82 | HPE_PAUSED_UPGRADE = 22,
83 | HPE_PAUSED_H2_UPGRADE = 23,
84 | HPE_USER = 24,
85 | HPE_CB_URL_COMPLETE = 26,
86 | HPE_CB_STATUS_COMPLETE = 27,
87 | HPE_CB_METHOD_COMPLETE = 32,
88 | HPE_CB_VERSION_COMPLETE = 33,
89 | HPE_CB_HEADER_FIELD_COMPLETE = 28,
90 | HPE_CB_HEADER_VALUE_COMPLETE = 29,
91 | HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34,
92 | HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35,
93 | HPE_CB_RESET = 31
94 | };
95 | typedef enum llhttp_errno llhttp_errno_t;
96 |
97 | enum llhttp_flags {
98 | F_CONNECTION_KEEP_ALIVE = 0x1,
99 | F_CONNECTION_CLOSE = 0x2,
100 | F_CONNECTION_UPGRADE = 0x4,
101 | F_CHUNKED = 0x8,
102 | F_UPGRADE = 0x10,
103 | F_CONTENT_LENGTH = 0x20,
104 | F_SKIPBODY = 0x40,
105 | F_TRAILING = 0x80,
106 | F_TRANSFER_ENCODING = 0x200
107 | };
108 | typedef enum llhttp_flags llhttp_flags_t;
109 |
110 | enum llhttp_lenient_flags {
111 | LENIENT_HEADERS = 0x1,
112 | LENIENT_CHUNKED_LENGTH = 0x2,
113 | LENIENT_KEEP_ALIVE = 0x4,
114 | LENIENT_TRANSFER_ENCODING = 0x8,
115 | LENIENT_VERSION = 0x10,
116 | LENIENT_DATA_AFTER_CLOSE = 0x20,
117 | LENIENT_OPTIONAL_LF_AFTER_CR = 0x40,
118 | LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80,
119 | LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100,
120 | LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200
121 | };
122 | typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;
123 |
124 | enum llhttp_type {
125 | LLHTTP_BOTH = 0,
126 | LLHTTP_REQUEST = 1,
127 | LLHTTP_RESPONSE = 2
128 | };
129 | typedef enum llhttp_type llhttp_type_t;
130 |
131 | enum llhttp_finish {
132 | LLHTTP_FINISH_SAFE = 0,
133 | LLHTTP_FINISH_SAFE_WITH_CB = 1,
134 | LLHTTP_FINISH_UNSAFE = 2
135 | };
136 | typedef enum llhttp_finish llhttp_finish_t;
137 |
138 | enum llhttp_method {
139 | LLHTTP_DELETE = 0,
140 | LLHTTP_GET = 1,
141 | LLHTTP_HEAD = 2,
142 | LLHTTP_POST = 3,
143 | LLHTTP_PUT = 4,
144 | LLHTTP_CONNECT = 5,
145 | LLHTTP_OPTIONS = 6,
146 | LLHTTP_TRACE = 7,
147 | LLHTTP_COPY = 8,
148 | LLHTTP_LOCK = 9,
149 | LLHTTP_MKCOL = 10,
150 | LLHTTP_MOVE = 11,
151 | LLHTTP_PROPFIND = 12,
152 | LLHTTP_PROPPATCH = 13,
153 | LLHTTP_SEARCH = 14,
154 | LLHTTP_UNLOCK = 15,
155 | LLHTTP_BIND = 16,
156 | LLHTTP_REBIND = 17,
157 | LLHTTP_UNBIND = 18,
158 | LLHTTP_ACL = 19,
159 | LLHTTP_REPORT = 20,
160 | LLHTTP_MKACTIVITY = 21,
161 | LLHTTP_CHECKOUT = 22,
162 | LLHTTP_MERGE = 23,
163 | LLHTTP_MSEARCH = 24,
164 | LLHTTP_NOTIFY = 25,
165 | LLHTTP_SUBSCRIBE = 26,
166 | LLHTTP_UNSUBSCRIBE = 27,
167 | LLHTTP_PATCH = 28,
168 | LLHTTP_PURGE = 29,
169 | LLHTTP_MKCALENDAR = 30,
170 | LLHTTP_LINK = 31,
171 | LLHTTP_UNLINK = 32,
172 | LLHTTP_SOURCE = 33,
173 | LLHTTP_PRI = 34,
174 | LLHTTP_DESCRIBE = 35,
175 | LLHTTP_ANNOUNCE = 36,
176 | LLHTTP_SETUP = 37,
177 | LLHTTP_PLAY = 38,
178 | LLHTTP_PAUSE = 39,
179 | LLHTTP_TEARDOWN = 40,
180 | LLHTTP_GET_PARAMETER = 41,
181 | LLHTTP_SET_PARAMETER = 42,
182 | LLHTTP_REDIRECT = 43,
183 | LLHTTP_RECORD = 44,
184 | LLHTTP_FLUSH = 45,
185 | LLHTTP_QUERY = 46
186 | };
187 | typedef enum llhttp_method llhttp_method_t;
188 |
189 | enum llhttp_status {
190 | LLHTTP_STATUS_CONTINUE = 100,
191 | LLHTTP_STATUS_SWITCHING_PROTOCOLS = 101,
192 | LLHTTP_STATUS_PROCESSING = 102,
193 | LLHTTP_STATUS_EARLY_HINTS = 103,
194 | LLHTTP_STATUS_RESPONSE_IS_STALE = 110,
195 | LLHTTP_STATUS_REVALIDATION_FAILED = 111,
196 | LLHTTP_STATUS_DISCONNECTED_OPERATION = 112,
197 | LLHTTP_STATUS_HEURISTIC_EXPIRATION = 113,
198 | LLHTTP_STATUS_MISCELLANEOUS_WARNING = 199,
199 | LLHTTP_STATUS_OK = 200,
200 | LLHTTP_STATUS_CREATED = 201,
201 | LLHTTP_STATUS_ACCEPTED = 202,
202 | LLHTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
203 | LLHTTP_STATUS_NO_CONTENT = 204,
204 | LLHTTP_STATUS_RESET_CONTENT = 205,
205 | LLHTTP_STATUS_PARTIAL_CONTENT = 206,
206 | LLHTTP_STATUS_MULTI_STATUS = 207,
207 | LLHTTP_STATUS_ALREADY_REPORTED = 208,
208 | LLHTTP_STATUS_TRANSFORMATION_APPLIED = 214,
209 | LLHTTP_STATUS_IM_USED = 226,
210 | LLHTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299,
211 | LLHTTP_STATUS_MULTIPLE_CHOICES = 300,
212 | LLHTTP_STATUS_MOVED_PERMANENTLY = 301,
213 | LLHTTP_STATUS_FOUND = 302,
214 | LLHTTP_STATUS_SEE_OTHER = 303,
215 | LLHTTP_STATUS_NOT_MODIFIED = 304,
216 | LLHTTP_STATUS_USE_PROXY = 305,
217 | LLHTTP_STATUS_SWITCH_PROXY = 306,
218 | LLHTTP_STATUS_TEMPORARY_REDIRECT = 307,
219 | LLHTTP_STATUS_PERMANENT_REDIRECT = 308,
220 | LLHTTP_STATUS_BAD_REQUEST = 400,
221 | LLHTTP_STATUS_UNAUTHORIZED = 401,
222 | LLHTTP_STATUS_PAYMENT_REQUIRED = 402,
223 | LLHTTP_STATUS_FORBIDDEN = 403,
224 | LLHTTP_STATUS_NOT_FOUND = 404,
225 | LLHTTP_STATUS_METHOD_NOT_ALLOWED = 405,
226 | LLHTTP_STATUS_NOT_ACCEPTABLE = 406,
227 | LLHTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
228 | LLHTTP_STATUS_REQUEST_TIMEOUT = 408,
229 | LLHTTP_STATUS_CONFLICT = 409,
230 | LLHTTP_STATUS_GONE = 410,
231 | LLHTTP_STATUS_LENGTH_REQUIRED = 411,
232 | LLHTTP_STATUS_PRECONDITION_FAILED = 412,
233 | LLHTTP_STATUS_PAYLOAD_TOO_LARGE = 413,
234 | LLHTTP_STATUS_URI_TOO_LONG = 414,
235 | LLHTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
236 | LLHTTP_STATUS_RANGE_NOT_SATISFIABLE = 416,
237 | LLHTTP_STATUS_EXPECTATION_FAILED = 417,
238 | LLHTTP_STATUS_IM_A_TEAPOT = 418,
239 | LLHTTP_STATUS_PAGE_EXPIRED = 419,
240 | LLHTTP_STATUS_ENHANCE_YOUR_CALM = 420,
241 | LLHTTP_STATUS_MISDIRECTED_REQUEST = 421,
242 | LLHTTP_STATUS_UNPROCESSABLE_ENTITY = 422,
243 | LLHTTP_STATUS_LOCKED = 423,
244 | LLHTTP_STATUS_FAILED_DEPENDENCY = 424,
245 | LLHTTP_STATUS_TOO_EARLY = 425,
246 | LLHTTP_STATUS_UPGRADE_REQUIRED = 426,
247 | LLHTTP_STATUS_PRECONDITION_REQUIRED = 428,
248 | LLHTTP_STATUS_TOO_MANY_REQUESTS = 429,
249 | LLHTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430,
250 | LLHTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
251 | LLHTTP_STATUS_LOGIN_TIMEOUT = 440,
252 | LLHTTP_STATUS_NO_RESPONSE = 444,
253 | LLHTTP_STATUS_RETRY_WITH = 449,
254 | LLHTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450,
255 | LLHTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
256 | LLHTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460,
257 | LLHTTP_STATUS_INVALID_X_FORWARDED_FOR = 463,
258 | LLHTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494,
259 | LLHTTP_STATUS_SSL_CERTIFICATE_ERROR = 495,
260 | LLHTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496,
261 | LLHTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497,
262 | LLHTTP_STATUS_INVALID_TOKEN = 498,
263 | LLHTTP_STATUS_CLIENT_CLOSED_REQUEST = 499,
264 | LLHTTP_STATUS_INTERNAL_SERVER_ERROR = 500,
265 | LLHTTP_STATUS_NOT_IMPLEMENTED = 501,
266 | LLHTTP_STATUS_BAD_GATEWAY = 502,
267 | LLHTTP_STATUS_SERVICE_UNAVAILABLE = 503,
268 | LLHTTP_STATUS_GATEWAY_TIMEOUT = 504,
269 | LLHTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
270 | LLHTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506,
271 | LLHTTP_STATUS_INSUFFICIENT_STORAGE = 507,
272 | LLHTTP_STATUS_LOOP_DETECTED = 508,
273 | LLHTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509,
274 | LLHTTP_STATUS_NOT_EXTENDED = 510,
275 | LLHTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511,
276 | LLHTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520,
277 | LLHTTP_STATUS_WEB_SERVER_IS_DOWN = 521,
278 | LLHTTP_STATUS_CONNECTION_TIMEOUT = 522,
279 | LLHTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523,
280 | LLHTTP_STATUS_TIMEOUT_OCCURED = 524,
281 | LLHTTP_STATUS_SSL_HANDSHAKE_FAILED = 525,
282 | LLHTTP_STATUS_INVALID_SSL_CERTIFICATE = 526,
283 | LLHTTP_STATUS_RAILGUN_ERROR = 527,
284 | LLHTTP_STATUS_SITE_IS_OVERLOADED = 529,
285 | LLHTTP_STATUS_SITE_IS_FROZEN = 530,
286 | LLHTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561,
287 | LLHTTP_STATUS_NETWORK_READ_TIMEOUT = 598,
288 | LLHTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599
289 | };
290 | typedef enum llhttp_status llhttp_status_t;
291 |
292 | #define LLHTTP_ERRNO_MAP(XX) \
293 | XX(0, OK, OK) \
294 | XX(1, INTERNAL, INTERNAL) \
295 | XX(2, STRICT, STRICT) \
296 | XX(25, CR_EXPECTED, CR_EXPECTED) \
297 | XX(3, LF_EXPECTED, LF_EXPECTED) \
298 | XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
299 | XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \
300 | XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
301 | XX(6, INVALID_METHOD, INVALID_METHOD) \
302 | XX(7, INVALID_URL, INVALID_URL) \
303 | XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
304 | XX(9, INVALID_VERSION, INVALID_VERSION) \
305 | XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
306 | XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
307 | XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
308 | XX(13, INVALID_STATUS, INVALID_STATUS) \
309 | XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
310 | XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
311 | XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
312 | XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
313 | XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
314 | XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
315 | XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
316 | XX(21, PAUSED, PAUSED) \
317 | XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
318 | XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \
319 | XX(24, USER, USER) \
320 | XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \
321 | XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \
322 | XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \
323 | XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \
324 | XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \
325 | XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \
326 | XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \
327 | XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \
328 | XX(31, CB_RESET, CB_RESET) \
329 |
330 |
331 | #define LLHTTP_METHOD_MAP(XX) \
332 | XX(0, DELETE, DELETE) \
333 | XX(1, GET, GET) \
334 | XX(2, HEAD, HEAD) \
335 | XX(3, POST, POST) \
336 | XX(4, PUT, PUT) \
337 | XX(5, CONNECT, CONNECT) \
338 | XX(6, OPTIONS, OPTIONS) \
339 | XX(7, TRACE, TRACE) \
340 | XX(8, COPY, COPY) \
341 | XX(9, LOCK, LOCK) \
342 | XX(10, MKCOL, MKCOL) \
343 | XX(11, MOVE, MOVE) \
344 | XX(12, PROPFIND, PROPFIND) \
345 | XX(13, PROPPATCH, PROPPATCH) \
346 | XX(14, SEARCH, SEARCH) \
347 | XX(15, UNLOCK, UNLOCK) \
348 | XX(16, BIND, BIND) \
349 | XX(17, REBIND, REBIND) \
350 | XX(18, UNBIND, UNBIND) \
351 | XX(19, ACL, ACL) \
352 | XX(20, REPORT, REPORT) \
353 | XX(21, MKACTIVITY, MKACTIVITY) \
354 | XX(22, CHECKOUT, CHECKOUT) \
355 | XX(23, MERGE, MERGE) \
356 | XX(24, MSEARCH, M-SEARCH) \
357 | XX(25, NOTIFY, NOTIFY) \
358 | XX(26, SUBSCRIBE, SUBSCRIBE) \
359 | XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
360 | XX(28, PATCH, PATCH) \
361 | XX(29, PURGE, PURGE) \
362 | XX(30, MKCALENDAR, MKCALENDAR) \
363 | XX(31, LINK, LINK) \
364 | XX(32, UNLINK, UNLINK) \
365 | XX(33, SOURCE, SOURCE) \
366 | XX(46, QUERY, QUERY) \
367 |
368 |
369 | #define RTSP_METHOD_MAP(XX) \
370 | XX(1, GET, GET) \
371 | XX(3, POST, POST) \
372 | XX(6, OPTIONS, OPTIONS) \
373 | XX(35, DESCRIBE, DESCRIBE) \
374 | XX(36, ANNOUNCE, ANNOUNCE) \
375 | XX(37, SETUP, SETUP) \
376 | XX(38, PLAY, PLAY) \
377 | XX(39, PAUSE, PAUSE) \
378 | XX(40, TEARDOWN, TEARDOWN) \
379 | XX(41, GET_PARAMETER, GET_PARAMETER) \
380 | XX(42, SET_PARAMETER, SET_PARAMETER) \
381 | XX(43, REDIRECT, REDIRECT) \
382 | XX(44, RECORD, RECORD) \
383 | XX(45, FLUSH, FLUSH) \
384 |
385 |
386 | #define LLHTTP_ALL_METHOD_MAP(XX) \
387 | XX(0, DELETE, DELETE) \
388 | XX(1, GET, GET) \
389 | XX(2, HEAD, HEAD) \
390 | XX(3, POST, POST) \
391 | XX(4, PUT, PUT) \
392 | XX(5, CONNECT, CONNECT) \
393 | XX(6, OPTIONS, OPTIONS) \
394 | XX(7, TRACE, TRACE) \
395 | XX(8, COPY, COPY) \
396 | XX(9, LOCK, LOCK) \
397 | XX(10, MKCOL, MKCOL) \
398 | XX(11, MOVE, MOVE) \
399 | XX(12, PROPFIND, PROPFIND) \
400 | XX(13, PROPPATCH, PROPPATCH) \
401 | XX(14, SEARCH, SEARCH) \
402 | XX(15, UNLOCK, UNLOCK) \
403 | XX(16, BIND, BIND) \
404 | XX(17, REBIND, REBIND) \
405 | XX(18, UNBIND, UNBIND) \
406 | XX(19, ACL, ACL) \
407 | XX(20, REPORT, REPORT) \
408 | XX(21, MKACTIVITY, MKACTIVITY) \
409 | XX(22, CHECKOUT, CHECKOUT) \
410 | XX(23, MERGE, MERGE) \
411 | XX(24, MSEARCH, M-SEARCH) \
412 | XX(25, NOTIFY, NOTIFY) \
413 | XX(26, SUBSCRIBE, SUBSCRIBE) \
414 | XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
415 | XX(28, PATCH, PATCH) \
416 | XX(29, PURGE, PURGE) \
417 | XX(30, MKCALENDAR, MKCALENDAR) \
418 | XX(31, LINK, LINK) \
419 | XX(32, UNLINK, UNLINK) \
420 | XX(33, SOURCE, SOURCE) \
421 | XX(34, PRI, PRI) \
422 | XX(35, DESCRIBE, DESCRIBE) \
423 | XX(36, ANNOUNCE, ANNOUNCE) \
424 | XX(37, SETUP, SETUP) \
425 | XX(38, PLAY, PLAY) \
426 | XX(39, PAUSE, PAUSE) \
427 | XX(40, TEARDOWN, TEARDOWN) \
428 | XX(41, GET_PARAMETER, GET_PARAMETER) \
429 | XX(42, SET_PARAMETER, SET_PARAMETER) \
430 | XX(43, REDIRECT, REDIRECT) \
431 | XX(44, RECORD, RECORD) \
432 | XX(45, FLUSH, FLUSH) \
433 | XX(46, QUERY, QUERY) \
434 |
435 |
436 | #define LLHTTP_STATUS_MAP(XX) \
437 | XX(100, CONTINUE, CONTINUE) \
438 | XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \
439 | XX(102, PROCESSING, PROCESSING) \
440 | XX(103, EARLY_HINTS, EARLY_HINTS) \
441 | XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \
442 | XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \
443 | XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \
444 | XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \
445 | XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \
446 | XX(200, OK, OK) \
447 | XX(201, CREATED, CREATED) \
448 | XX(202, ACCEPTED, ACCEPTED) \
449 | XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \
450 | XX(204, NO_CONTENT, NO_CONTENT) \
451 | XX(205, RESET_CONTENT, RESET_CONTENT) \
452 | XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \
453 | XX(207, MULTI_STATUS, MULTI_STATUS) \
454 | XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \
455 | XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \
456 | XX(226, IM_USED, IM_USED) \
457 | XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \
458 | XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \
459 | XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \
460 | XX(302, FOUND, FOUND) \
461 | XX(303, SEE_OTHER, SEE_OTHER) \
462 | XX(304, NOT_MODIFIED, NOT_MODIFIED) \
463 | XX(305, USE_PROXY, USE_PROXY) \
464 | XX(306, SWITCH_PROXY, SWITCH_PROXY) \
465 | XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \
466 | XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \
467 | XX(400, BAD_REQUEST, BAD_REQUEST) \
468 | XX(401, UNAUTHORIZED, UNAUTHORIZED) \
469 | XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \
470 | XX(403, FORBIDDEN, FORBIDDEN) \
471 | XX(404, NOT_FOUND, NOT_FOUND) \
472 | XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \
473 | XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \
474 | XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \
475 | XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \
476 | XX(409, CONFLICT, CONFLICT) \
477 | XX(410, GONE, GONE) \
478 | XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \
479 | XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \
480 | XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \
481 | XX(414, URI_TOO_LONG, URI_TOO_LONG) \
482 | XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \
483 | XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \
484 | XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \
485 | XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \
486 | XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \
487 | XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \
488 | XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \
489 | XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \
490 | XX(423, LOCKED, LOCKED) \
491 | XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \
492 | XX(425, TOO_EARLY, TOO_EARLY) \
493 | XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \
494 | XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \
495 | XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \
496 | XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \
497 | XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \
498 | XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \
499 | XX(444, NO_RESPONSE, NO_RESPONSE) \
500 | XX(449, RETRY_WITH, RETRY_WITH) \
501 | XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \
502 | XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \
503 | XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \
504 | XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \
505 | XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \
506 | XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \
507 | XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \
508 | XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \
509 | XX(498, INVALID_TOKEN, INVALID_TOKEN) \
510 | XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \
511 | XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \
512 | XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \
513 | XX(502, BAD_GATEWAY, BAD_GATEWAY) \
514 | XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \
515 | XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \
516 | XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \
517 | XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \
518 | XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \
519 | XX(508, LOOP_DETECTED, LOOP_DETECTED) \
520 | XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \
521 | XX(510, NOT_EXTENDED, NOT_EXTENDED) \
522 | XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \
523 | XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \
524 | XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \
525 | XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \
526 | XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \
527 | XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \
528 | XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \
529 | XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \
530 | XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \
531 | XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \
532 | XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \
533 | XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \
534 | XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \
535 | XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \
536 |
537 |
538 | #ifdef __cplusplus
539 | } /* extern "C" */
540 | #endif
541 | #endif /* LLLLHTTP_C_HEADERS_ */
542 |
543 |
544 | #ifndef INCLUDE_LLHTTP_API_H_
545 | #define INCLUDE_LLHTTP_API_H_
546 | #ifdef __cplusplus
547 | extern "C" {
548 | #endif
549 | #include
550 |
551 | #if defined(__wasm__)
552 | #define LLHTTP_EXPORT __attribute__((visibility("default")))
553 | #elif defined(_WIN32)
554 | #define LLHTTP_EXPORT __declspec(dllexport)
555 | #else
556 | #define LLHTTP_EXPORT
557 | #endif
558 |
559 | typedef llhttp__internal_t llhttp_t;
560 | typedef struct llhttp_settings_s llhttp_settings_t;
561 |
562 | typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
563 | typedef int (*llhttp_cb)(llhttp_t*);
564 |
565 | struct llhttp_settings_s {
566 | /* Possible return values 0, -1, `HPE_PAUSED` */
567 | llhttp_cb on_message_begin;
568 |
569 | /* Possible return values 0, -1, HPE_USER */
570 | llhttp_data_cb on_url;
571 | llhttp_data_cb on_status;
572 | llhttp_data_cb on_method;
573 | llhttp_data_cb on_version;
574 | llhttp_data_cb on_header_field;
575 | llhttp_data_cb on_header_value;
576 | llhttp_data_cb on_chunk_extension_name;
577 | llhttp_data_cb on_chunk_extension_value;
578 |
579 | /* Possible return values:
580 | * 0 - Proceed normally
581 | * 1 - Assume that request/response has no body, and proceed to parsing the
582 | * next message
583 | * 2 - Assume absence of body (as above) and make `llhttp_execute()` return
584 | * `HPE_PAUSED_UPGRADE`
585 | * -1 - Error
586 | * `HPE_PAUSED`
587 | */
588 | llhttp_cb on_headers_complete;
589 |
590 | /* Possible return values 0, -1, HPE_USER */
591 | llhttp_data_cb on_body;
592 |
593 | /* Possible return values 0, -1, `HPE_PAUSED` */
594 | llhttp_cb on_message_complete;
595 | llhttp_cb on_url_complete;
596 | llhttp_cb on_status_complete;
597 | llhttp_cb on_method_complete;
598 | llhttp_cb on_version_complete;
599 | llhttp_cb on_header_field_complete;
600 | llhttp_cb on_header_value_complete;
601 | llhttp_cb on_chunk_extension_name_complete;
602 | llhttp_cb on_chunk_extension_value_complete;
603 |
604 | /* When on_chunk_header is called, the current chunk length is stored
605 | * in parser->content_length.
606 | * Possible return values 0, -1, `HPE_PAUSED`
607 | */
608 | llhttp_cb on_chunk_header;
609 | llhttp_cb on_chunk_complete;
610 | llhttp_cb on_reset;
611 | };
612 |
613 | /* Initialize the parser with specific type and user settings.
614 | *
615 | * NOTE: lifetime of `settings` has to be at least the same as the lifetime of
616 | * the `parser` here. In practice, `settings` has to be either a static
617 | * variable or be allocated with `malloc`, `new`, etc.
618 | */
619 | LLHTTP_EXPORT
620 | void llhttp_init(llhttp_t* parser, llhttp_type_t type,
621 | const llhttp_settings_t* settings);
622 |
623 | LLHTTP_EXPORT
624 | llhttp_t* llhttp_alloc(llhttp_type_t type);
625 |
626 | LLHTTP_EXPORT
627 | void llhttp_free(llhttp_t* parser);
628 |
629 | LLHTTP_EXPORT
630 | uint8_t llhttp_get_type(llhttp_t* parser);
631 |
632 | LLHTTP_EXPORT
633 | uint8_t llhttp_get_http_major(llhttp_t* parser);
634 |
635 | LLHTTP_EXPORT
636 | uint8_t llhttp_get_http_minor(llhttp_t* parser);
637 |
638 | LLHTTP_EXPORT
639 | uint8_t llhttp_get_method(llhttp_t* parser);
640 |
641 | LLHTTP_EXPORT
642 | int llhttp_get_status_code(llhttp_t* parser);
643 |
644 | LLHTTP_EXPORT
645 | uint8_t llhttp_get_upgrade(llhttp_t* parser);
646 |
647 | /* Reset an already initialized parser back to the start state, preserving the
648 | * existing parser type, callback settings, user data, and lenient flags.
649 | */
650 | LLHTTP_EXPORT
651 | void llhttp_reset(llhttp_t* parser);
652 |
653 | /* Initialize the settings object */
654 | LLHTTP_EXPORT
655 | void llhttp_settings_init(llhttp_settings_t* settings);
656 |
657 | /* Parse full or partial request/response, invoking user callbacks along the
658 | * way.
659 | *
660 | * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
661 | * interrupts, and such errno is returned from `llhttp_execute()`. If
662 | * `HPE_PAUSED` was used as a errno, the execution can be resumed with
663 | * `llhttp_resume()` call.
664 | *
665 | * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
666 | * is returned after fully parsing the request/response. If the user wishes to
667 | * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
668 | *
669 | * NOTE: if this function ever returns a non-pause type error, it will continue
670 | * to return the same error upon each successive call up until `llhttp_init()`
671 | * is called.
672 | */
673 | LLHTTP_EXPORT
674 | llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
675 |
676 | /* This method should be called when the other side has no further bytes to
677 | * send (e.g. shutdown of readable side of the TCP connection.)
678 | *
679 | * Requests without `Content-Length` and other messages might require treating
680 | * all incoming bytes as the part of the body, up to the last byte of the
681 | * connection. This method will invoke `on_message_complete()` callback if the
682 | * request was terminated safely. Otherwise a error code would be returned.
683 | */
684 | LLHTTP_EXPORT
685 | llhttp_errno_t llhttp_finish(llhttp_t* parser);
686 |
687 | /* Returns `1` if the incoming message is parsed until the last byte, and has
688 | * to be completed by calling `llhttp_finish()` on EOF
689 | */
690 | LLHTTP_EXPORT
691 | int llhttp_message_needs_eof(const llhttp_t* parser);
692 |
693 | /* Returns `1` if there might be any other messages following the last that was
694 | * successfully parsed.
695 | */
696 | LLHTTP_EXPORT
697 | int llhttp_should_keep_alive(const llhttp_t* parser);
698 |
699 | /* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
700 | * appropriate error reason.
701 | *
702 | * Important: do not call this from user callbacks! User callbacks must return
703 | * `HPE_PAUSED` if pausing is required.
704 | */
705 | LLHTTP_EXPORT
706 | void llhttp_pause(llhttp_t* parser);
707 |
708 | /* Might be called to resume the execution after the pause in user's callback.
709 | * See `llhttp_execute()` above for details.
710 | *
711 | * Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
712 | */
713 | LLHTTP_EXPORT
714 | void llhttp_resume(llhttp_t* parser);
715 |
716 | /* Might be called to resume the execution after the pause in user's callback.
717 | * See `llhttp_execute()` above for details.
718 | *
719 | * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
720 | */
721 | LLHTTP_EXPORT
722 | void llhttp_resume_after_upgrade(llhttp_t* parser);
723 |
724 | /* Returns the latest return error */
725 | LLHTTP_EXPORT
726 | llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
727 |
728 | /* Returns the verbal explanation of the latest returned error.
729 | *
730 | * Note: User callback should set error reason when returning the error. See
731 | * `llhttp_set_error_reason()` for details.
732 | */
733 | LLHTTP_EXPORT
734 | const char* llhttp_get_error_reason(const llhttp_t* parser);
735 |
736 | /* Assign verbal description to the returned error. Must be called in user
737 | * callbacks right before returning the errno.
738 | *
739 | * Note: `HPE_USER` error code might be useful in user callbacks.
740 | */
741 | LLHTTP_EXPORT
742 | void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
743 |
744 | /* Returns the pointer to the last parsed byte before the returned error. The
745 | * pointer is relative to the `data` argument of `llhttp_execute()`.
746 | *
747 | * Note: this method might be useful for counting the number of parsed bytes.
748 | */
749 | LLHTTP_EXPORT
750 | const char* llhttp_get_error_pos(const llhttp_t* parser);
751 |
752 | /* Returns textual name of error code */
753 | LLHTTP_EXPORT
754 | const char* llhttp_errno_name(llhttp_errno_t err);
755 |
756 | /* Returns textual name of HTTP method */
757 | LLHTTP_EXPORT
758 | const char* llhttp_method_name(llhttp_method_t method);
759 |
760 | /* Returns textual name of HTTP status */
761 | LLHTTP_EXPORT
762 | const char* llhttp_status_name(llhttp_status_t status);
763 |
764 | /* Enables/disables lenient header value parsing (disabled by default).
765 | *
766 | * Lenient parsing disables header value token checks, extending llhttp's
767 | * protocol support to highly non-compliant clients/server. No
768 | * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
769 | * lenient parsing is "on".
770 | *
771 | * **Enabling this flag can pose a security issue since you will be exposed to
772 | * request smuggling attacks. USE WITH CAUTION!**
773 | */
774 | LLHTTP_EXPORT
775 | void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
776 |
777 |
778 | /* Enables/disables lenient handling of conflicting `Transfer-Encoding` and
779 | * `Content-Length` headers (disabled by default).
780 | *
781 | * Normally `llhttp` would error when `Transfer-Encoding` is present in
782 | * conjunction with `Content-Length`. This error is important to prevent HTTP
783 | * request smuggling, but may be less desirable for small number of cases
784 | * involving legacy servers.
785 | *
786 | * **Enabling this flag can pose a security issue since you will be exposed to
787 | * request smuggling attacks. USE WITH CAUTION!**
788 | */
789 | LLHTTP_EXPORT
790 | void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
791 |
792 |
793 | /* Enables/disables lenient handling of `Connection: close` and HTTP/1.0
794 | * requests responses.
795 | *
796 | * Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
797 | * the HTTP request/response after the request/response with `Connection: close`
798 | * and `Content-Length`. This is important to prevent cache poisoning attacks,
799 | * but might interact badly with outdated and insecure clients. With this flag
800 | * the extra request/response will be parsed normally.
801 | *
802 | * **Enabling this flag can pose a security issue since you will be exposed to
803 | * poisoning attacks. USE WITH CAUTION!**
804 | */
805 | LLHTTP_EXPORT
806 | void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
807 |
808 | /* Enables/disables lenient handling of `Transfer-Encoding` header.
809 | *
810 | * Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value
811 | * and another value after it (either in a single header or in multiple
812 | * headers whose value are internally joined using `, `).
813 | * This is mandated by the spec to reliably determine request body size and thus
814 | * avoid request smuggling.
815 | * With this flag the extra value will be parsed normally.
816 | *
817 | * **Enabling this flag can pose a security issue since you will be exposed to
818 | * request smuggling attacks. USE WITH CAUTION!**
819 | */
820 | LLHTTP_EXPORT
821 | void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled);
822 |
823 | /* Enables/disables lenient handling of HTTP version.
824 | *
825 | * Normally `llhttp` would error when the HTTP version in the request or status line
826 | * is not `0.9`, `1.0`, `1.1` or `2.0`.
827 | * With this flag the invalid value will be parsed normally.
828 | *
829 | * **Enabling this flag can pose a security issue since you will allow unsupported
830 | * HTTP versions. USE WITH CAUTION!**
831 | */
832 | LLHTTP_EXPORT
833 | void llhttp_set_lenient_version(llhttp_t* parser, int enabled);
834 |
835 | /* Enables/disables lenient handling of additional data received after a message ends
836 | * and keep-alive is disabled.
837 | *
838 | * Normally `llhttp` would error when additional unexpected data is received if the message
839 | * contains the `Connection` header with `close` value.
840 | * With this flag the extra data will discarded without throwing an error.
841 | *
842 | * **Enabling this flag can pose a security issue since you will be exposed to
843 | * poisoning attacks. USE WITH CAUTION!**
844 | */
845 | LLHTTP_EXPORT
846 | void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled);
847 |
848 | /* Enables/disables lenient handling of incomplete CRLF sequences.
849 | *
850 | * Normally `llhttp` would error when a CR is not followed by LF when terminating the
851 | * request line, the status line, the headers or a chunk header.
852 | * With this flag only a CR is required to terminate such sections.
853 | *
854 | * **Enabling this flag can pose a security issue since you will be exposed to
855 | * request smuggling attacks. USE WITH CAUTION!**
856 | */
857 | LLHTTP_EXPORT
858 | void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled);
859 |
860 | /*
861 | * Enables/disables lenient handling of line separators.
862 | *
863 | * Normally `llhttp` would error when a LF is not preceded by CR when terminating the
864 | * request line, the status line, the headers, a chunk header or a chunk data.
865 | * With this flag only a LF is required to terminate such sections.
866 | *
867 | * **Enabling this flag can pose a security issue since you will be exposed to
868 | * request smuggling attacks. USE WITH CAUTION!**
869 | */
870 | LLHTTP_EXPORT
871 | void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled);
872 |
873 | /* Enables/disables lenient handling of chunks not separated via CRLF.
874 | *
875 | * Normally `llhttp` would error when after a chunk data a CRLF is missing before
876 | * starting a new chunk.
877 | * With this flag the new chunk can start immediately after the previous one.
878 | *
879 | * **Enabling this flag can pose a security issue since you will be exposed to
880 | * request smuggling attacks. USE WITH CAUTION!**
881 | */
882 | LLHTTP_EXPORT
883 | void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled);
884 |
885 | /* Enables/disables lenient handling of spaces after chunk size.
886 | *
887 | * Normally `llhttp` would error when after a chunk size is followed by one or more
888 | * spaces are present instead of a CRLF or `;`.
889 | * With this flag this check is disabled.
890 | *
891 | * **Enabling this flag can pose a security issue since you will be exposed to
892 | * request smuggling attacks. USE WITH CAUTION!**
893 | */
894 | LLHTTP_EXPORT
895 | void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled);
896 |
897 | #ifdef __cplusplus
898 | } /* extern "C" */
899 | #endif
900 | #endif /* INCLUDE_LLHTTP_API_H_ */
901 |
902 |
903 | #endif /* INCLUDE_LLHTTP_H_ */
904 |
--------------------------------------------------------------------------------
/update.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # This script will download the latest version of LLHTTP and integrate
5 | # it into the project. You will still have to perform a few manual
6 | # steps like updating the version number to complete the integration.
7 | #
8 |
9 | set -eu
10 |
11 | # Determine the project folder
12 | PROJECT_FOLDER="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
13 | SOURCES_FOLDER="$PROJECT_FOLDER/Sources"
14 |
15 | # Create a temporary folder
16 | TEMP_CODE_FOLDER=$(mktemp -d /tmp/LLHTTP.XXXXXX)
17 | cd "$TEMP_CODE_FOLDER"
18 |
19 | # Clone and build the project
20 | git clone https://github.com/nodejs/llhttp.git
21 | cd llhttp
22 | npm ci
23 | make
24 |
25 | # Generate the release files
26 | VERSION=$(cat package.json | grep \"version\" | cut -d'"' -f 4)
27 | RELEASE="$VERSION" make release
28 |
29 | # Copy the build files
30 | cp $TEMP_CODE_FOLDER/llhttp/release/include/*.h "$SOURCES_FOLDER"
31 | cp $TEMP_CODE_FOLDER/llhttp/release/src/*.c "$SOURCES_FOLDER"
32 |
33 | # Rename HTTP definitions to prevent a name clash when using CocoaPods
34 | # NOTE: be careful of any incorrect string replacements (requires manual checks)
35 | find "$SOURCES_FOLDER" -type f -exec gsed -i -e '/^\s*XX/!s/\bHTTP_/LLHTTP_/g' {} \;
36 |
--------------------------------------------------------------------------------