├── .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 | [![HTTPParserC CI](https://github.com/Building42/HTTPParserC/actions/workflows/ci.yml/badge.svg)](https://github.com/Building42/HTTPParserC/actions/workflows/ci.yml) 5 | [![Version](https://img.shields.io/cocoapods/v/HTTPParserC.svg)](https://cocoapods.org/pods/HTTPParserC) 6 | [![License](https://img.shields.io/cocoapods/l/HTTPParserC.svg)](https://cocoapods.org/pods/HTTPParserC) 7 | [![Platform](https://img.shields.io/cocoapods/p/HTTPParserC.svg)](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 | --------------------------------------------------------------------------------