Update to version 3.8.0
This commit is contained in:
		
							
								
								
									
										246
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,246 @@ | ||||
| # Clang format version: 18.1.3 | ||||
| --- | ||||
| BasedOnStyle: LLVM | ||||
| AccessModifierOffset: -2 | ||||
| AlignAfterOpenBracket: BlockIndent | ||||
| AlignArrayOfStructures: None | ||||
| AlignConsecutiveAssignments: | ||||
|   Enabled: false | ||||
|   AcrossEmptyLines: false | ||||
|   AcrossComments: false | ||||
|   AlignCompound: false | ||||
|   AlignFunctionPointers: false | ||||
|   PadOperators: true | ||||
| AlignConsecutiveBitFields: | ||||
|   Enabled: true | ||||
|   AcrossEmptyLines: false | ||||
|   AcrossComments: false | ||||
|   AlignCompound: false | ||||
|   AlignFunctionPointers: false | ||||
|   PadOperators: false | ||||
| AlignConsecutiveDeclarations: | ||||
|   Enabled: false | ||||
|   AcrossEmptyLines: false | ||||
|   AcrossComments: false | ||||
|   AlignCompound: false | ||||
|   AlignFunctionPointers: false | ||||
|   PadOperators: false | ||||
| AlignConsecutiveMacros: | ||||
|   Enabled: true | ||||
|   AcrossEmptyLines: false | ||||
|   AcrossComments: false | ||||
|   AlignCompound: false | ||||
|   AlignFunctionPointers: false | ||||
|   PadOperators: false | ||||
| AlignConsecutiveShortCaseStatements: | ||||
|   Enabled: true | ||||
|   AcrossEmptyLines: false | ||||
|   AcrossComments: false | ||||
|   AlignCaseColons: false | ||||
| AlignEscapedNewlines: Left | ||||
| AlignOperands: Align | ||||
| AlignTrailingComments: | ||||
|   Kind: Always | ||||
|   OverEmptyLines: 0 | ||||
| AllowAllArgumentsOnNextLine: true | ||||
| AllowAllParametersOfDeclarationOnNextLine: true | ||||
| AllowBreakBeforeNoexceptSpecifier: Never | ||||
| AllowShortBlocksOnASingleLine: Empty | ||||
| AllowShortCaseLabelsOnASingleLine: true | ||||
| AllowShortCompoundRequirementOnASingleLine: true | ||||
| AllowShortEnumsOnASingleLine: false | ||||
| AllowShortFunctionsOnASingleLine: Empty | ||||
| AllowShortIfStatementsOnASingleLine: Never | ||||
| AllowShortLambdasOnASingleLine: Empty | ||||
| AllowShortLoopsOnASingleLine: true | ||||
| AlwaysBreakAfterDefinitionReturnType: None | ||||
| AlwaysBreakAfterReturnType: None | ||||
| AlwaysBreakBeforeMultilineStrings: false | ||||
| AlwaysBreakTemplateDeclarations: MultiLine | ||||
| AttributeMacros: | ||||
|   - __capability | ||||
| BinPackArguments: true | ||||
| BinPackParameters: true | ||||
| BitFieldColonSpacing: Both | ||||
| BraceWrapping: | ||||
|   AfterCaseLabel: true | ||||
|   AfterClass: false | ||||
|   AfterControlStatement: Never | ||||
|   AfterEnum: false | ||||
|   AfterFunction: false | ||||
|   AfterNamespace: false | ||||
|   AfterObjCDeclaration: false | ||||
|   AfterStruct: false | ||||
|   AfterUnion: false | ||||
|   AfterExternBlock: false | ||||
|   BeforeCatch: false | ||||
|   BeforeElse: false | ||||
|   BeforeLambdaBody: false | ||||
|   BeforeWhile: false | ||||
|   IndentBraces: false | ||||
|   SplitEmptyFunction: false | ||||
|   SplitEmptyRecord: true | ||||
|   SplitEmptyNamespace: true | ||||
| BreakAdjacentStringLiterals: true | ||||
| BreakAfterAttributes: Always | ||||
| BreakAfterJavaFieldAnnotations: false | ||||
| BreakArrays: false | ||||
| BreakBeforeBinaryOperators: NonAssignment | ||||
| BreakBeforeBraces: Custom | ||||
| BreakBeforeConceptDeclarations: Always | ||||
| BreakBeforeInlineASMColon: OnlyMultiline | ||||
| BreakBeforeTernaryOperators: true | ||||
| BreakConstructorInitializers: BeforeColon | ||||
| BreakInheritanceList: BeforeColon | ||||
| BreakStringLiterals: true | ||||
| ColumnLimit: 160 | ||||
| CommentPragmas: "" | ||||
| CompactNamespaces: false | ||||
| ConstructorInitializerIndentWidth: 2 | ||||
| ContinuationIndentWidth: 2 | ||||
| Cpp11BracedListStyle: true | ||||
| DerivePointerAlignment: false | ||||
| DisableFormat: false | ||||
| EmptyLineAfterAccessModifier: Never | ||||
| EmptyLineBeforeAccessModifier: LogicalBlock | ||||
| ExperimentalAutoDetectBinPacking: false | ||||
| FixNamespaceComments: true | ||||
| ForEachMacros: | ||||
|   - foreach | ||||
|   - Q_FOREACH | ||||
|   - BOOST_FOREACH | ||||
| IfMacros: | ||||
|   - KJ_IF_MAYBE | ||||
| IncludeBlocks: Preserve | ||||
| IncludeCategories: | ||||
|   - Regex: ^"(llvm|llvm-c|clang|clang-c)/ | ||||
|     Priority: 2 | ||||
|     SortPriority: 0 | ||||
|     CaseSensitive: false | ||||
|   - Regex: ^(<|"(gtest|gmock|isl|json)/) | ||||
|     Priority: 3 | ||||
|     SortPriority: 0 | ||||
|     CaseSensitive: false | ||||
|   - Regex: .* | ||||
|     Priority: 1 | ||||
|     SortPriority: 0 | ||||
|     CaseSensitive: false | ||||
| IncludeIsMainRegex: "" | ||||
| IncludeIsMainSourceRegex: "" | ||||
| IndentAccessModifiers: false | ||||
| IndentCaseBlocks: false | ||||
| IndentCaseLabels: true | ||||
| IndentExternBlock: NoIndent | ||||
| IndentGotoLabels: false | ||||
| IndentPPDirectives: None | ||||
| IndentRequiresClause: false | ||||
| IndentWidth: 2 | ||||
| IndentWrappedFunctionNames: true | ||||
| InsertBraces: true | ||||
| InsertNewlineAtEOF: true | ||||
| InsertTrailingCommas: None | ||||
| IntegerLiteralSeparator: | ||||
|   Binary: 0 | ||||
|   BinaryMinDigits: 0 | ||||
|   Decimal: 0 | ||||
|   DecimalMinDigits: 0 | ||||
|   Hex: 0 | ||||
|   HexMinDigits: 0 | ||||
| JavaScriptQuotes: Leave | ||||
| JavaScriptWrapImports: true | ||||
| KeepEmptyLinesAtEOF: false | ||||
| KeepEmptyLinesAtTheStartOfBlocks: true | ||||
| LambdaBodyIndentation: Signature | ||||
| Language: Cpp | ||||
| LineEnding: LF | ||||
| MacroBlockBegin: "" | ||||
| MacroBlockEnd: "" | ||||
| MaxEmptyLinesToKeep: 1 | ||||
| NamespaceIndentation: None | ||||
| ObjCBinPackProtocolList: Auto | ||||
| ObjCBlockIndentWidth: 2 | ||||
| ObjCBreakBeforeNestedBlockParam: true | ||||
| ObjCSpaceAfterProperty: false | ||||
| ObjCSpaceBeforeProtocolList: true | ||||
| PPIndentWidth: -1 | ||||
| PackConstructorInitializers: BinPack | ||||
| PenaltyBreakAssignment: 2 | ||||
| PenaltyBreakBeforeFirstCallParameter: 19 | ||||
| PenaltyBreakComment: 300 | ||||
| PenaltyBreakFirstLessLess: 120 | ||||
| PenaltyBreakOpenParenthesis: 0 | ||||
| PenaltyBreakScopeResolution: 500 | ||||
| PenaltyBreakString: 1000 | ||||
| PenaltyBreakTemplateDeclaration: 10 | ||||
| PenaltyExcessCharacter: 1000000 | ||||
| PenaltyIndentedWhitespace: 0 | ||||
| PenaltyReturnTypeOnItsOwnLine: 60 | ||||
| PointerAlignment: Right | ||||
| QualifierAlignment: Leave | ||||
| ReferenceAlignment: Pointer | ||||
| ReflowComments: false | ||||
| RemoveBracesLLVM: false | ||||
| RemoveParentheses: Leave | ||||
| RemoveSemicolon: false | ||||
| RequiresClausePosition: OwnLine | ||||
| RequiresExpressionIndentation: OuterScope | ||||
| SeparateDefinitionBlocks: Leave | ||||
| ShortNamespaceLines: 1 | ||||
| SkipMacroDefinitionBody: false | ||||
| SortIncludes: Never | ||||
| SortJavaStaticImport: Before | ||||
| SortUsingDeclarations: LexicographicNumeric | ||||
| SpaceAfterCStyleCast: false | ||||
| SpaceAfterLogicalNot: false | ||||
| SpaceAfterTemplateKeyword: false | ||||
| SpaceAroundPointerQualifiers: Default | ||||
| SpaceBeforeAssignmentOperators: true | ||||
| SpaceBeforeCaseColon: false | ||||
| SpaceBeforeCpp11BracedList: false | ||||
| SpaceBeforeCtorInitializerColon: true | ||||
| SpaceBeforeInheritanceColon: true | ||||
| SpaceBeforeJsonColon: false | ||||
| SpaceBeforeParens: ControlStatements | ||||
| SpaceBeforeParensOptions: | ||||
|   AfterControlStatements: true | ||||
|   AfterForeachMacros: true | ||||
|   AfterFunctionDeclarationName: false | ||||
|   AfterFunctionDefinitionName: false | ||||
|   AfterIfMacros: true | ||||
|   AfterOverloadedOperator: true | ||||
|   AfterPlacementOperator: true | ||||
|   AfterRequiresInClause: false | ||||
|   AfterRequiresInExpression: false | ||||
|   BeforeNonEmptyParentheses: false | ||||
| SpaceBeforeRangeBasedForLoopColon: true | ||||
| SpaceBeforeSquareBrackets: false | ||||
| SpaceInEmptyBlock: false | ||||
| SpacesBeforeTrailingComments: 2 | ||||
| SpacesInAngles: Never | ||||
| SpacesInContainerLiterals: false | ||||
| SpacesInLineCommentPrefix: | ||||
|   Minimum: 1 | ||||
|   Maximum: -1 | ||||
| SpacesInParens: Never | ||||
| SpacesInParensOptions: | ||||
|   InConditionalStatements: false | ||||
|   InCStyleCasts: false | ||||
|   InEmptyParentheses: false | ||||
|   Other: false | ||||
| SpacesInSquareBrackets: false | ||||
| Standard: Auto | ||||
| StatementAttributeLikeMacros: | ||||
|   - Q_EMIT | ||||
| StatementMacros: | ||||
|   - Q_UNUSED | ||||
|   - QT_REQUIRE_VERSION | ||||
| TabWidth: 2 | ||||
| UseTab: Never | ||||
| VerilogBreakBetweenInstancePorts: true | ||||
| WhitespaceSensitiveMacros: | ||||
|   - BOOST_PP_STRINGIZE | ||||
|   - CF_SWIFT_NAME | ||||
|   - NS_SWIFT_NAME | ||||
|   - PP_STRINGIZE | ||||
|   - STRINGIZE | ||||
| BracedInitializerIndentWidth: 2 | ||||
							
								
								
									
										8
									
								
								.codespellrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.codespellrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| [codespell] | ||||
| # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/spell-check/.codespellrc | ||||
| # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: | ||||
| ignore-words-list = ba,licence,varius | ||||
| skip = ./.git,./.licenses,__pycache__,.clang-format,.codespellrc,.editorconfig,.flake8,.prettierignore,.yamllint.yml,.gitignore | ||||
| builtin = clear,informal,en-GB_to_en-US | ||||
| check-filenames = | ||||
| check-hidden = | ||||
							
								
								
									
										60
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/general/.editorconfig | ||||
| # See: https://editorconfig.org/ | ||||
| # The formatting style defined in this file is the official standardized style to be used in all Arduino Tooling | ||||
| # projects and should not be modified. | ||||
| # Note: indent style for each file type is defined even when it matches the universal config in order to make it clear | ||||
| # that this type has an official style. | ||||
|  | ||||
| [*] | ||||
| charset = utf-8 | ||||
| end_of_line = lf | ||||
| indent_size = 2 | ||||
| indent_style = space | ||||
| insert_final_newline = true | ||||
| trim_trailing_whitespace = true | ||||
|  | ||||
| [*.{adoc,asc,asciidoc}] | ||||
| indent_size = 2 | ||||
| indent_style = space | ||||
|  | ||||
| [*.{bash,sh}] | ||||
| indent_size = 4 | ||||
| indent_style = space | ||||
|  | ||||
| [*.{c,cc,cp,cpp,cxx,h,hh,hpp,hxx,ii,inl,ino,ixx,pde,tpl,tpp,txx}] | ||||
| indent_size = 2 | ||||
| indent_style = space | ||||
|  | ||||
| [*.{go,mod}] | ||||
| indent_style = tab | ||||
|  | ||||
| [*.java] | ||||
| indent_size = 2 | ||||
| indent_style = space | ||||
|  | ||||
| [*.{js,jsx,json,jsonc,json5,ts,tsx}] | ||||
| indent_size = 2 | ||||
| indent_style = space | ||||
|  | ||||
| [*.{md,mdx,mkdn,mdown,markdown}] | ||||
| indent_size = unset | ||||
| indent_style = space | ||||
|  | ||||
| [*.proto] | ||||
| indent_size = 2 | ||||
| indent_style = space | ||||
|  | ||||
| [*.py] | ||||
| indent_size = 4 | ||||
| indent_style = space | ||||
|  | ||||
| [*.svg] | ||||
| indent_size = 2 | ||||
| indent_style = space | ||||
|  | ||||
| [*.{yaml,yml}] | ||||
| indent_size = 2 | ||||
| indent_style = space | ||||
|  | ||||
| [{.gitconfig,.gitmodules}] | ||||
| indent_style = tab | ||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| .DS_Store | ||||
| .lh | ||||
| /.pio | ||||
| /.vscode | ||||
| /logs | ||||
							
								
								
									
										2
									
								
								.gitpod.Dockerfile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitpod.Dockerfile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| FROM gitpod/workspace-python-3.11 | ||||
| USER gitpod | ||||
							
								
								
									
										9
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| tasks: | ||||
|   - command: pip install --upgrade pip && pip install -U platformio && platformio run | ||||
|  | ||||
| image: | ||||
|   file: .gitpod.Dockerfile | ||||
|  | ||||
| vscode: | ||||
|   extensions: | ||||
|     - shardulm94.trailing-spaces | ||||
							
								
								
									
										42
									
								
								.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| exclude: | | ||||
|   (?x)( | ||||
|       ^\.github\/| | ||||
|       LICENSE$ | ||||
|   ) | ||||
|  | ||||
| default_language_version: | ||||
|   # force all unspecified python hooks to run python3 | ||||
|   python: python3 | ||||
|  | ||||
| repos: | ||||
|   - repo: https://github.com/pre-commit/pre-commit-hooks | ||||
|     rev: "v5.0.0" | ||||
|     hooks: | ||||
|       # Generic checks | ||||
|       - id: check-case-conflict | ||||
|       - id: check-symlinks | ||||
|       - id: debug-statements | ||||
|       - id: destroyed-symlinks | ||||
|       - id: detect-private-key | ||||
|       - id: end-of-file-fixer | ||||
|         exclude: ^.*\.(bin|BIN)$ | ||||
|       - id: mixed-line-ending | ||||
|         args: [--fix=lf] | ||||
|       - id: trailing-whitespace | ||||
|         args: [--markdown-linebreak-ext=md] | ||||
|         exclude: ^platformio\.ini$ | ||||
|  | ||||
|   - repo: https://github.com/codespell-project/codespell | ||||
|     rev: "v2.3.0" | ||||
|     hooks: | ||||
|       # Spell checking | ||||
|       - id: codespell | ||||
|         exclude: ^.*\.(svd|SVD)$ | ||||
|  | ||||
|   - repo: https://github.com/pre-commit/mirrors-clang-format | ||||
|     rev: "v18.1.3" | ||||
|     hooks: | ||||
|       # C/C++ formatting | ||||
|       - id: clang-format | ||||
|         types_or: [c, c++] | ||||
|         exclude: ^.*\/build_opt\.h$ | ||||
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
								
							| @@ -72,6 +72,19 @@ lib_deps = | ||||
|   ESP32Async/ESPAsyncWebServer | ||||
| ``` | ||||
|  | ||||
| ### LibreTiny (BK7231N/T, RTL8710B, etc.) | ||||
|  | ||||
| Version 1.9.1 or newer is required. | ||||
|  | ||||
| ```ini | ||||
| [env:stable] | ||||
| platform = libretiny @ ^1.9.1 | ||||
| lib_ldf_mode = chain | ||||
| lib_deps = | ||||
|   ESP32Async/AsyncTCP | ||||
|   ESP32Async/ESPAsyncWebServer | ||||
| ``` | ||||
|  | ||||
| ### Unofficial dependencies | ||||
|  | ||||
| **AsyncTCPSock** | ||||
|   | ||||
| @@ -1,48 +0,0 @@ | ||||
| Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod | ||||
| rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper | ||||
| arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit | ||||
| accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. | ||||
| Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo | ||||
| dapibus elit, id varius sem dui id lacus.</p> | ||||
| <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod | ||||
| rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper | ||||
| arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit | ||||
| accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. | ||||
| Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo | ||||
| dapibus elit, id varius sem dui id lacus.</p> | ||||
| <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod | ||||
| rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper | ||||
| arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit | ||||
| accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. | ||||
| Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo | ||||
| dapibus elit, id varius sem dui id lacus.</p> | ||||
| <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod | ||||
| rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper | ||||
| arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit | ||||
| accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. | ||||
| Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo | ||||
| dapibus elit, id varius sem dui id lacus.</p> | ||||
| <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod | ||||
| rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper | ||||
| arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit | ||||
| accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. | ||||
| Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo | ||||
| dapibus elit, id varius sem dui id lacus.</p> | ||||
| <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod | ||||
| rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper | ||||
| arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit | ||||
| accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. | ||||
| Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo | ||||
| dapibus elit, id varius sem dui id lacus.</p> | ||||
| <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod | ||||
| rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper | ||||
| arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit | ||||
| accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. | ||||
| Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo | ||||
| dapibus elit, id varius sem dui id lacus.</p> | ||||
| <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod | ||||
| rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper | ||||
| arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit | ||||
| accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. | ||||
| Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo | ||||
| dapibus elit, id varius sem dui id lacus. | ||||
| @@ -2,7 +2,7 @@ | ||||
| // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov | ||||
|  | ||||
| #include <DNSServer.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -20,7 +20,7 @@ static AsyncWebServer server(80); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										210
									
								
								examples/AsyncTunnel/AsyncTunnel.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								examples/AsyncTunnel/AsyncTunnel.ino
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | ||||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||||
| // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov | ||||
|  | ||||
| // | ||||
| // Shows how to trigger an async client request from a browser request and send the client response back to the browser through websocket | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| #include <ESP8266WiFi.h> | ||||
| #include <ESPAsyncTCP.h> | ||||
| #elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) | ||||
| #include <RPAsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #endif | ||||
|  | ||||
| #include <ESPAsyncWebServer.h> | ||||
|  | ||||
| #define WIFI_SSID     "IoT" | ||||
| #define WIFI_PASSWORD "" | ||||
|  | ||||
| static AsyncWebServer server(80); | ||||
| static AsyncWebSocketMessageHandler wsHandler; | ||||
| static AsyncWebSocket ws("/ws", wsHandler.eventHandler()); | ||||
|  | ||||
| static const char *htmlContent PROGMEM = R"( | ||||
|   <!DOCTYPE html> | ||||
|   <html> | ||||
|   <head> | ||||
|     <title>WebSocket Tunnel Example</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <h1>WebSocket Tunnel Example</h1> | ||||
|     <div><input type="text" id="url" value="http://www.google.com" /></div> | ||||
|     <div><button onclick='fetch()'>Fetch</button></div> | ||||
|     <div><pre id="response"></pre></div> | ||||
|     <script> | ||||
|       var ws = new WebSocket('/ws'); | ||||
|       ws.binaryType = "arraybuffer"; | ||||
|       ws.onopen = function() { | ||||
|         console.log("WebSocket connected"); | ||||
|       }; | ||||
|       ws.onmessage = function(event) { | ||||
|         let uint8array = new Uint8Array(event.data); | ||||
|         let string = new TextDecoder().decode(uint8array); | ||||
|         console.log("WebSocket message: " + string); | ||||
|         document.getElementById("response").innerText += string; | ||||
|       }; | ||||
|       ws.onclose = function() { | ||||
|         console.log("WebSocket closed"); | ||||
|       }; | ||||
|       ws.onerror = function(error) { | ||||
|         console.log("WebSocket error: " + error); | ||||
|       }; | ||||
|       function fetch() { | ||||
|         document.getElementById("response").innerText = ""; | ||||
|         var url = document.getElementById("url").value; | ||||
|         ws.send(url); | ||||
|         console.log("WebSocket sent: " + url); | ||||
|       } | ||||
|     </script> | ||||
|   </body> | ||||
|   </html> | ||||
|     )"; | ||||
| static const size_t htmlContentLength = strlen_P(htmlContent); | ||||
|  | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.begin(WIFI_SSID, WIFI_PASSWORD); | ||||
|   while (WiFi.status() != WL_CONNECTED) { | ||||
|     delay(500); | ||||
|   } | ||||
|   Serial.println("Connected to WiFi!"); | ||||
|   Serial.println(WiFi.localIP()); | ||||
| #endif | ||||
|  | ||||
|   server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { | ||||
|     request->send(200, "text/html", (const uint8_t *)htmlContent, htmlContentLength); | ||||
|   }); | ||||
|  | ||||
|   wsHandler.onMessage([](AsyncWebSocket *server, AsyncWebSocketClient *wsClient, const uint8_t *data, size_t len) { | ||||
|     String url; | ||||
|     String host; | ||||
|     String port; | ||||
|     String path; | ||||
|  | ||||
|     url.concat((const char *)data, len); | ||||
|  | ||||
|     if (!url.startsWith("http://")) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (!url.endsWith("/")) { | ||||
|       url += "/"; | ||||
|     } | ||||
|  | ||||
|     // Parse the URL to extract the host and port | ||||
|     int start = url.indexOf("://") + 3; | ||||
|     int end = url.indexOf("/", start); | ||||
|     if (end == -1) { | ||||
|       end = url.length(); | ||||
|     } | ||||
|     String hostPort = url.substring(start, end); | ||||
|     int colonIndex = hostPort.indexOf(":"); | ||||
|     if (colonIndex != -1) { | ||||
|       host = hostPort.substring(0, colonIndex); | ||||
|       port = hostPort.substring(colonIndex + 1); | ||||
|     } else { | ||||
|       host = hostPort; | ||||
|       port = "80";  // Default HTTP port | ||||
|     } | ||||
|     path = url.substring(end); | ||||
|  | ||||
|     Serial.printf("Host: %s\n", host.c_str()); | ||||
|     Serial.printf("Port: %s\n", port.c_str()); | ||||
|     Serial.printf("Path: %s\n", path.c_str()); | ||||
|  | ||||
|     // Ensure client does not get deleted while the websocket holds a reference to it | ||||
|     std::shared_ptr<AsyncClient> *safeAsyncClient = new std::shared_ptr<AsyncClient>(std::make_shared<AsyncClient>()); | ||||
|     AsyncClient *asyncClient = safeAsyncClient->get(); | ||||
|  | ||||
|     asyncClient->onDisconnect([safeAsyncClient](void *arg, AsyncClient *client) { | ||||
|       Serial.printf("Tunnel disconnected!\n"); | ||||
|       delete safeAsyncClient; | ||||
|     }); | ||||
|  | ||||
|     // register a callback when an error occurs | ||||
|     // note: onDisconnect also called on error | ||||
|     asyncClient->onError([](void *arg, AsyncClient *client, int8_t error) { | ||||
|       Serial.printf("Tunnel error: %s\n", client->errorToString(error)); | ||||
|     }); | ||||
|  | ||||
|     // register a callback when data arrives, to accumulate it | ||||
|     asyncClient->onPacket( | ||||
|       [safeAsyncClient](void *arg, AsyncClient *, struct pbuf *pb) { | ||||
|         std::shared_ptr<AsyncClient> safeAsyncClientRef = *safeAsyncClient;  // add a reference | ||||
|         AsyncWebSocketClient *wsClient = (AsyncWebSocketClient *)arg; | ||||
|         Serial.printf("Tunnel received %u bytes\n", pb->len); | ||||
|         AsyncWebSocketSharedBuffer wsBuffer = | ||||
|           AsyncWebSocketSharedBuffer(new std::vector<uint8_t>((uint8_t *)pb->payload, (uint8_t *)pb->payload + pb->len), [=](std::vector<uint8_t> *bufptr) { | ||||
|             delete bufptr; | ||||
|             Serial.printf("ACK %u bytes\n", pb->len); | ||||
|             safeAsyncClientRef->ackPacket(pb); | ||||
|           }); | ||||
|         Serial.printf("Tunnel sending %u bytes\n", wsBuffer->size()); | ||||
|         Serial.printf("%.*s\n", (int)wsBuffer->size(), wsBuffer->data()); | ||||
|         wsClient->binary(std::move(wsBuffer)); | ||||
|       }, | ||||
|       wsClient | ||||
|     ); | ||||
|  | ||||
|     asyncClient->onConnect([=](void *arg, AsyncClient *client) { | ||||
|       Serial.printf("Tunnel connected!\n"); | ||||
|  | ||||
|       client->write("GET "); | ||||
|       client->write(path.c_str()); | ||||
|       client->write(" HTTP/1.1\r\n"); | ||||
|       client->write("Host: "); | ||||
|       client->write(host.c_str()); | ||||
|       client->write(":"); | ||||
|       client->write(port.c_str()); | ||||
|       client->write("\r\n"); | ||||
|       client->write("User-Agent: ESP32\r\n"); | ||||
|       client->write("Accept: */*\r\n"); | ||||
|       client->write("Connection: close\r\n"); | ||||
|       client->write("\r\n"); | ||||
|     }); | ||||
|  | ||||
|     Serial.printf("Fetching: http://%s:%s%s\n", host.c_str(), port.c_str(), path.c_str()); | ||||
|  | ||||
|     if (!asyncClient->connect(host.c_str(), port.toInt())) { | ||||
|       Serial.printf("Failed to open tunnel!\n"); | ||||
|       delete safeAsyncClient; | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   wsHandler.onConnect([](AsyncWebSocket *server, AsyncWebSocketClient *client) { | ||||
|     Serial.printf("Client %" PRIu32 " connected\n", client->id()); | ||||
|     client->binary("WebSocket connected!"); | ||||
|   }); | ||||
|  | ||||
|   wsHandler.onDisconnect([](AsyncWebSocket *server, uint32_t clientId) { | ||||
|     Serial.printf("Client %" PRIu32 " disconnected\n", clientId); | ||||
|   }); | ||||
|  | ||||
|   server.addHandler(&ws); | ||||
|   server.begin(); | ||||
|   Serial.println("Server started!"); | ||||
| } | ||||
|  | ||||
| static uint32_t lastHeap = 0; | ||||
|  | ||||
| void loop() { | ||||
|   ws.cleanupClients(2); | ||||
|  | ||||
| #ifdef ESP32 | ||||
|   uint32_t now = millis(); | ||||
|   if (now - lastHeap >= 2000) { | ||||
|     Serial.printf("Uptime: %3lu s, Free heap: %" PRIu32 "\n", millis() / 1000, ESP.getFreeHeap()); | ||||
|     lastHeap = now; | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   delay(500); | ||||
| } | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -52,7 +52,7 @@ static AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest *request) { | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -25,7 +25,7 @@ static AsyncCorsMiddleware cors; | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov | ||||
|  | ||||
| #include <DNSServer.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -28,7 +28,7 @@ public: | ||||
|     response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>"); | ||||
|     response->print("<p>This is our captive portal front page.</p>"); | ||||
|     response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str()); | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|     response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str()); | ||||
| #endif | ||||
|     response->print("</body></html>"); | ||||
| @@ -41,7 +41,7 @@ void setup() { | ||||
|   Serial.println(); | ||||
|   Serial.println("Configuring access point..."); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   if (!WiFi.softAP("esp-captive")) { | ||||
|     Serial.println("Soft AP creation failed."); | ||||
|     while (1); | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -86,7 +86,7 @@ static const size_t htmlContentLength = strlen_P(htmlContent); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -86,7 +86,7 @@ static const size_t htmlContentLength = strlen_P(htmlContent); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -96,7 +96,7 @@ static int key = -1; | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
| @@ -131,9 +131,17 @@ void setup() { | ||||
|     "/api", HTTP_POST, | ||||
|     [](AsyncWebServerRequest *request) { | ||||
|       // request parsing has finished | ||||
|       String *data = (String *)request->_tempObject; | ||||
|  | ||||
|       if (!data) { | ||||
|         request->send(400); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       // no data ? | ||||
|       if (!((String *)request->_tempObject)->length()) { | ||||
|       if (!data->length()) { | ||||
|         delete data; | ||||
|         request->_tempObject = nullptr; | ||||
|         request->send(400); | ||||
|         return; | ||||
|       } | ||||
| @@ -141,11 +149,16 @@ void setup() { | ||||
|       JsonDocument doc; | ||||
|  | ||||
|       // deserialize and check for errors | ||||
|       if (deserializeJson(doc, *(String *)request->_tempObject)) { | ||||
|       if (deserializeJson(doc, *data)) { | ||||
|         delete data; | ||||
|         request->_tempObject = nullptr; | ||||
|         request->send(400); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       delete data; | ||||
|       request->_tempObject = nullptr; | ||||
|  | ||||
|       // start UART com: UART will send the data to the Serial console and wait for the key press | ||||
|       triggerUART = doc["input"].as<const char *>(); | ||||
|       key = -1; | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -24,7 +24,7 @@ static AsyncWebServer server(80); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <DNSServer.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -32,7 +32,7 @@ public: | ||||
|     response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>"); | ||||
|     response->print("<p>This is out captive portal front page.</p>"); | ||||
|     response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str()); | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|     response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str()); | ||||
| #endif | ||||
|     response->print("</body></html>"); | ||||
| @@ -51,17 +51,17 @@ void setup() { | ||||
|       "/", HTTP_GET, | ||||
|       [](AsyncWebServerRequest *request) { | ||||
|         Serial.println("Captive portal request..."); | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|         Serial.println("WiFi.localIP(): " + WiFi.localIP().toString()); | ||||
| #endif | ||||
|         Serial.println("request->client()->localIP(): " + request->client()->localIP().toString()); | ||||
| #if ESP_IDF_VERSION_MAJOR >= 5 | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|         Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type())); | ||||
| #endif | ||||
|         Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type())); | ||||
| #endif | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|         Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER"); | ||||
|         Serial.println(WiFi.localIP() == request->client()->localIP()); | ||||
|         Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString()); | ||||
| @@ -77,17 +77,17 @@ void setup() { | ||||
|       "/", HTTP_GET, | ||||
|       [](AsyncWebServerRequest *request) { | ||||
|         Serial.println("Website request..."); | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|         Serial.println("WiFi.localIP(): " + WiFi.localIP().toString()); | ||||
| #endif | ||||
|         Serial.println("request->client()->localIP(): " + request->client()->localIP().toString()); | ||||
| #if ESP_IDF_VERSION_MAJOR >= 5 | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|         Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type())); | ||||
| #endif | ||||
|         Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type())); | ||||
| #endif | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|         Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER"); | ||||
|         Serial.println(WiFi.localIP() == request->client()->localIP()); | ||||
|         Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString()); | ||||
| @@ -113,7 +113,7 @@ void setup() { | ||||
|   // dnsServer.stop(); | ||||
|   // WiFi.softAPdisconnect(); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.persistent(false); | ||||
|   WiFi.begin("IoT"); | ||||
|   while (WiFi.status() != WL_CONNECTED) { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -86,7 +86,7 @@ static const size_t htmlContentLength = strlen_P(htmlContent); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -33,7 +33,7 @@ AsyncHeaderFreeMiddleware headerFree; | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
| @@ -79,6 +79,29 @@ void setup() { | ||||
|     ) | ||||
|     .addMiddleware(&headerFree); | ||||
|  | ||||
|   // curl -v http://192.168.4.1/ | ||||
|   server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { | ||||
|     AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "Hello, world!"); | ||||
|     response->addHeader(AsyncWebHeader::parse("X-Test-1: value1")); | ||||
|     response->addHeader(AsyncWebHeader::parse("X-Test-2:value2")); | ||||
|     response->addHeader(AsyncWebHeader::parse("X-Test-3:")); | ||||
|     response->addHeader(AsyncWebHeader::parse("X-Test-4: ")); | ||||
|     response->addHeader(AsyncWebHeader::parse("")); | ||||
|     response->addHeader(AsyncWebHeader::parse(":")); | ||||
|     request->send(response); | ||||
|     /** | ||||
| < HTTP/1.1 200 OK | ||||
| < connection: close | ||||
| < X-Test-1: value1 | ||||
| < X-Test-2: value2 | ||||
| < X-Test-3: | ||||
| < X-Test-4: | ||||
| < accept-ranges: none | ||||
| < content-length: 13 | ||||
| < content-type: text/plain | ||||
|      */ | ||||
|   }); | ||||
|  | ||||
|   server.begin(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -24,7 +24,7 @@ static AsyncWebServer server(80); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -34,7 +34,7 @@ static AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler("/ | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
| @@ -63,14 +63,27 @@ void setup() { | ||||
|     JsonObject root = doc.to<JsonObject>(); | ||||
|     root["foo"] = "bar"; | ||||
|     serializeJson(root, *response); | ||||
|     Serial.println(); | ||||
|     request->send(response); | ||||
|   }); | ||||
|  | ||||
|   // curl -v -X POST -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2 | ||||
|   // curl -v -X PUT -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2 | ||||
|   // | ||||
|   // edge cases: | ||||
|   // | ||||
|   // curl -v -X POST -H "Content-Type: application/json" -d "1234" -H "Content-Length: 5" http://192.168.4.1/json2 => rx timeout | ||||
|   // curl -v -X POST -H "Content-Type: application/json" -d "1234" -H "Content-Length: 2" http://192.168.4.1/json2 => 12 | ||||
|   // curl -v -X POST -H "Content-Type: application/json" -d "1234" -H "Content-Length: 4" http://192.168.4.1/json2 => 1234 | ||||
|   // curl -v -X POST -H "Content-Type: application/json" -d "1234" -H "Content-Length: 10" http://192.168.4.1/json2 => rx timeout | ||||
|   // curl -v -X POST -H "Content-Type: application/json" -d "12345678" -H "Content-Length: 8" http://192.168.4.1/json2 => 12345678 | ||||
|   // curl -v -X POST -H "Content-Type: application/json" -d "123456789" -H "Content-Length: 8" http://192.168.4.1/json2 => 12345678 | ||||
|   // curl -v -X POST -H "Content-Type: application/json" -d "123456789" -H "Content-Length: 9" http://192.168.4.1/json2 => 413: Content length exceeds maximum allowed | ||||
|   handler->setMaxContentLength(8); | ||||
|   handler->setMethod(HTTP_POST | HTTP_PUT); | ||||
|   handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) { | ||||
|     serializeJson(json, Serial); | ||||
|     Serial.println(); | ||||
|     AsyncJsonResponse *response = new AsyncJsonResponse(); | ||||
|     JsonObject root = response->getRoot().to<JsonObject>(); | ||||
|     root["hello"] = json.as<JsonObject>()["name"]; | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -25,7 +25,7 @@ static AsyncLoggingMiddleware requestLogger; | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -34,7 +34,7 @@ static AsyncCallbackMessagePackWebHandler *handler = new AsyncCallbackMessagePac | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -34,7 +34,7 @@ public: | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -74,7 +74,7 @@ static const size_t htmlContentLength = strlen_P(htmlContent); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -34,7 +34,7 @@ static AsyncWebServer server(80); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -91,7 +91,7 @@ static volatile size_t requests = 0; | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -25,7 +25,7 @@ static AsyncRateLimitMiddleware rateLimit; | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -24,7 +24,7 @@ static AsyncWebServer server(80); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -34,7 +34,7 @@ static AsyncWebServerRequestPtr gpioRequest; | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -94,7 +94,7 @@ static bool processLongRunningOperation(LongRunningOperation *op) { | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -24,7 +24,7 @@ static AsyncWebServer server(80); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -24,7 +24,7 @@ static AsyncWebServer server(80); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -58,7 +58,7 @@ static AsyncEventSource events("/events"); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										141
									
								
								examples/ServerSentEvents_PR156/ServerSentEvents_PR156.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								examples/ServerSentEvents_PR156/ServerSentEvents_PR156.ino
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||||
| // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov | ||||
|  | ||||
| // | ||||
| // SSE example | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| #include <ESP8266WiFi.h> | ||||
| #include <ESPAsyncTCP.h> | ||||
| #elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) | ||||
| #include <RPAsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #endif | ||||
|  | ||||
| #include <ESPAsyncWebServer.h> | ||||
|  | ||||
| static const char *htmlContent PROGMEM = R"( | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|   <title>Server-Sent Events</title> | ||||
|   <script> | ||||
|     if (!!window.EventSource) { | ||||
|       var source = new EventSource('/events'); | ||||
|       source.onopen = function(e) { | ||||
|         console.log("Events Connected"); | ||||
|       }; | ||||
|       source.onerror = function(e) { | ||||
|         if (e.target.readyState != EventSource.OPEN) { | ||||
|           console.log("Events Disconnected"); | ||||
|         } | ||||
|         // Uncomment below to prevent the client from proactively establishing a new connection. | ||||
|         // source.close(); | ||||
|       }; | ||||
|       source.onmessage = function(e) { | ||||
|         console.log("Message: " + e.data); | ||||
|       }; | ||||
|       source.addEventListener('heartbeat', function(e) { | ||||
|         console.log("Heartbeat", e.data); | ||||
|       }, false); | ||||
|     } | ||||
|   </script> | ||||
| </head> | ||||
| <body> | ||||
|   <h1>Open your browser console!</h1> | ||||
| </body> | ||||
| </html> | ||||
| )"; | ||||
|  | ||||
| static const size_t htmlContentLength = strlen_P(htmlContent); | ||||
|  | ||||
| static AsyncWebServer server(80); | ||||
| static AsyncEventSource events("/events"); | ||||
|  | ||||
| static volatile size_t connectionCount = 0; | ||||
| static volatile uint32_t timestampConnected = 0; | ||||
| static constexpr uint32_t timeoutClose = 15000; | ||||
|  | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|  | ||||
|   // curl -v http://192.168.4.1/ | ||||
|   server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { | ||||
|     // need to cast to uint8_t* | ||||
|     // if you do not, the const char* will be copied in a temporary String buffer | ||||
|     request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength); | ||||
|   }); | ||||
|  | ||||
|   events.onConnect([](AsyncEventSourceClient *client) { | ||||
|     /** | ||||
|      * @brief: Purpose for a test case: count() function | ||||
|      * Task watchdog shall be triggered due to a self-deadlock by mutex handling of the AsyncEventSource. | ||||
|      * | ||||
|      * E (61642) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time: | ||||
|      * E (61642) task_wdt:  - async_tcp (CPU 0/1) | ||||
|      * | ||||
|      * Resolve: using recursive_mutex insteads of mutex. | ||||
|     */ | ||||
|     connectionCount = events.count(); | ||||
|  | ||||
|     timestampConnected = millis(); | ||||
|     Serial.printf("SSE Client connected! ID: %" PRIu32 "\n", client->lastId()); | ||||
|     client->send("hello!", NULL, millis(), 1000); | ||||
|     Serial.printf("Number of connected clients: %u\n", connectionCount); | ||||
|   }); | ||||
|  | ||||
|   events.onDisconnect([](AsyncEventSourceClient *client) { | ||||
|     connectionCount = events.count(); | ||||
|     Serial.printf("SSE Client disconnected! ID: %" PRIu32 "\n", client->lastId()); | ||||
|     Serial.printf("Number of connected clients: %u\n", connectionCount); | ||||
|   }); | ||||
|  | ||||
|   server.addHandler(&events); | ||||
|  | ||||
|   server.begin(); | ||||
| } | ||||
|  | ||||
| static constexpr uint32_t deltaSSE = 3000; | ||||
| static uint32_t lastSSE = 0; | ||||
| static uint32_t lastHeap = 0; | ||||
|  | ||||
| void loop() { | ||||
|   uint32_t now = millis(); | ||||
|   if (connectionCount > 0) { | ||||
|     if (now - lastSSE >= deltaSSE) { | ||||
|       events.send(String("ping-") + now, "heartbeat", now); | ||||
|       lastSSE = millis(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief: Purpose for a test case: close() function | ||||
|      * Task watchdog shall be triggered due to a self-deadlock by mutex handling of the AsyncEventSource. | ||||
|      * | ||||
|      * E (61642) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time: | ||||
|      * E (61642) task_wdt:  - async_tcp (CPU 0/1) | ||||
|      * | ||||
|      * Resolve: using recursive_mutex insteads of mutex. | ||||
|     */ | ||||
|     if (now - timestampConnected >= timeoutClose) { | ||||
|       Serial.printf("SSE Clients close\n"); | ||||
|       events.close(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| #ifdef ESP32 | ||||
|   if (now - lastHeap >= 2000) { | ||||
|     Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap()); | ||||
|     lastHeap = now; | ||||
|   } | ||||
| #endif | ||||
| } | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -25,7 +25,7 @@ static AsyncWebServer server2(80); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -27,7 +27,7 @@ static AsyncLoggingMiddleware logging; | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -89,7 +89,7 @@ static size_t charactersIndex = 0; | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -84,10 +84,34 @@ static const char *htmlContent PROGMEM = R"( | ||||
|  | ||||
| static const size_t htmlContentLength = strlen_P(htmlContent); | ||||
|  | ||||
| // sample_html_gz.h | ||||
| static const uint8_t index2_html_gz[] = { | ||||
|   0x1f, 0x8b, 0x08, 0x08, 0x13, 0x45, 0x92, 0x68, 0x00, 0x03, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x32, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x00, 0xed, 0x98, 0xcb, | ||||
|   0x6e, 0xdc, 0x30, 0x0c, 0x45, 0xf7, 0xf3, 0x15, 0xcc, 0xde, 0xb0, 0x91, 0xbd, 0xe1, 0x4d, 0x1f, 0x48, 0x81, 0xbc, 0x8a, 0x26, 0x2d, 0xba, 0xe4, 0x48, | ||||
|   0x8c, 0x87, 0x81, 0x1e, 0x0e, 0x25, 0x19, 0xc8, 0xdf, 0x97, 0xb2, 0x67, 0x82, 0x6c, 0xfa, 0x07, 0x32, 0x0c, 0x58, 0xa6, 0xa8, 0xcb, 0x4b, 0xe9, 0xac, | ||||
|   0x34, 0x5e, 0x7d, 0x7d, 0xf8, 0xf2, 0xf4, 0xf7, 0xf1, 0x1b, 0x9c, 0xb2, 0x77, 0xd3, 0x61, 0xbc, 0x7c, 0x08, 0xed, 0x74, 0x00, 0x7d, 0xc6, 0xcc, 0xd9, | ||||
|   0xd1, 0xf4, 0x0b, 0xfd, 0xe2, 0x08, 0x6e, 0x9e, 0xee, 0x6e, 0xc7, 0x61, 0x0f, 0x1d, 0xc6, 0x61, 0x4f, 0x1b, 0x8f, 0xd1, 0xbe, 0x9f, 0xb3, 0x4f, 0xd7, | ||||
|   0xd3, 0x0d, 0x39, 0x17, 0x3b, 0xf8, 0x13, 0xc5, 0xd9, 0x2b, 0xcd, 0xb9, 0x3e, 0x4f, 0x2d, 0xd3, 0x6d, 0x14, 0xf2, 0xc0, 0x4b, 0x2a, 0x1e, 0x6c, 0x74, | ||||
|   0x51, 0x20, 0x71, 0x06, 0xf4, 0x94, 0x3b, 0x30, 0x31, 0x24, 0x32, 0x99, 0x72, 0x11, 0x40, 0xcb, 0x0b, 0x27, 0xc3, 0x61, 0x06, 0x72, 0x9c, 0x7b, 0x78, | ||||
|   0x94, 0xc8, 0x01, 0xa8, 0x70, 0xf2, 0xd1, 0x76, 0xb0, 0x14, 0x29, 0x09, 0xf0, 0x12, 0xd8, 0xe4, 0xe5, 0x14, 0x83, 0x29, 0xa9, 0x83, 0x22, 0x01, 0xcf, | ||||
|   0x35, 0x4c, 0x91, 0xa4, 0x89, 0x1e, 0x53, 0xc2, 0x4e, 0xb3, 0xc1, 0xb2, 0xc9, 0x1a, 0xcf, 0xea, 0x50, 0xe3, 0xaf, 0x25, 0xe5, 0x08, 0x68, 0xf6, 0x41, | ||||
|   0x0f, 0x3f, 0x55, 0xee, 0xad, 0x10, 0x14, 0xe7, 0xd0, 0x9b, 0x28, 0x0b, 0xc9, 0x26, 0x8d, 0x62, 0x0a, 0x04, 0x32, 0x90, 0xa3, 0xe8, 0xfb, 0x79, 0xbe, | ||||
|   0x83, 0x95, 0x1c, 0xbc, 0x90, 0x78, 0x0a, 0x55, 0x79, 0x97, 0xfc, 0xf8, 0xef, 0xe1, 0x37, 0xaf, 0xe8, 0xb5, 0x56, 0x22, 0x5b, 0x53, 0xb5, 0xdd, 0x92, | ||||
|   0xb7, 0xa6, 0x76, 0x65, 0x63, 0x8a, 0x4f, 0x18, 0x6a, 0xf7, 0x73, 0xad, 0xbc, 0x4f, 0x07, 0xd6, 0x95, 0xcf, 0xb9, 0x3a, 0xde, 0x05, 0x75, 0xe0, 0x50, | ||||
|   0xbb, 0x83, 0x15, 0x85, 0xf5, 0x33, 0x0b, 0xae, 0x6c, 0xb1, 0x26, 0xe3, 0xb9, 0x9b, 0x1e, 0xee, 0xab, 0x2f, 0x78, 0x41, 0xc3, 0x8e, 0x13, 0xf7, 0x5b, | ||||
|   0x81, 0x1f, 0x21, 0xd3, 0x4c, 0xba, 0xa3, 0xc5, 0x54, 0xe7, 0x9f, 0x37, 0xb9, 0xb8, 0x2c, 0x6c, 0x98, 0x74, 0xe5, 0xf7, 0x92, 0x0c, 0xa9, 0xeb, 0x32, | ||||
|   0x33, 0xea, 0x51, 0x78, 0xfe, 0x38, 0x17, 0x38, 0xf2, 0x91, 0x82, 0xd5, 0xce, 0x56, 0x5e, 0x49, 0x44, 0xb7, 0x31, 0x8a, 0x61, 0x70, 0x14, 0x37, 0x7d, | ||||
|   0x8b, 0x0b, 0x1f, 0xd5, 0x50, 0xed, 0xa8, 0x03, 0xb6, 0x17, 0x83, 0x49, 0xcf, 0xd9, 0x16, 0xae, 0x91, 0xcd, 0x78, 0x3f, 0x0e, 0x4b, 0xc3, 0xa0, 0x61, | ||||
|   0xd0, 0x30, 0x68, 0x18, 0x34, 0x0c, 0x1a, 0x06, 0x0d, 0x83, 0x86, 0x41, 0xc3, 0xa0, 0x61, 0xd0, 0x30, 0x68, 0x18, 0x34, 0x0c, 0x1a, 0x06, 0xff, 0xc1, | ||||
|   0x60, 0x1c, 0xf6, 0xab, 0x85, 0x71, 0xd8, 0xee, 0x25, 0xfe, 0x01, 0xa0, 0xec, 0x78, 0xfe, 0xae, 0x10, 0x00, 0x00 | ||||
| }; | ||||
|  | ||||
| static const size_t index2_html_gz_len = sizeof(index2_html_gz); | ||||
|  | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
| @@ -105,6 +129,13 @@ void setup() { | ||||
|     f.close(); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     File f = LittleFS.open("/index2.html.gz", "w"); | ||||
|     assert(f); | ||||
|     f.write(index2_html_gz, index2_html_gz_len); | ||||
|     f.close(); | ||||
|   } | ||||
|  | ||||
|   LittleFS.mkdir("/files"); | ||||
|  | ||||
|   { | ||||
| @@ -129,6 +160,9 @@ void setup() { | ||||
|   // curl -v http://192.168.4.1/index.html | ||||
|   server.serveStatic("/index.html", LittleFS, "/index.html"); | ||||
|  | ||||
|   // curl -v http://192.168.4.1/index2.html | gunzip -c | ||||
|   server.serveStatic("/index2.html", LittleFS, "/index2.html"); | ||||
|  | ||||
|   // Example to serve a directory content | ||||
|   // curl -v http://192.168.4.1/base/ => serves a.txt | ||||
|   // curl -v http://192.168.4.1/base/a.txt => serves a.txt | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -36,7 +36,7 @@ static const size_t htmlContentLength = strlen_P(htmlContent); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -31,7 +31,7 @@ void setup() { | ||||
|     LittleFS.begin(); | ||||
|   } | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -25,7 +25,7 @@ static AsyncWebSocket ws("/ws"); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
| @@ -102,8 +102,10 @@ void loop() { | ||||
|   } | ||||
|  | ||||
|   if (now - lastHeap >= 2000) { | ||||
|     // cleanup disconnected clients or too many clients | ||||
|     ws.cleanupClients(); | ||||
|     Serial.printf("Connected clients: %u / %u total\n", ws.count(), ws.getClients().size()); | ||||
|  | ||||
|     // this can be called to also set a soft limit on the number of connected clients | ||||
|     ws.cleanupClients(2);  // no more than 2 clients | ||||
|  | ||||
| #ifdef ESP32 | ||||
|     Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap()); | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| // | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| @@ -71,7 +71,7 @@ static const size_t htmlContentLength = strlen_P(htmlContent); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -25,11 +25,14 @@ files: | ||||
|     - "platformio.ini" | ||||
|     - "pre-commit.requirements.txt" | ||||
| dependencies: | ||||
|   espressif/arduino-esp32: | ||||
|     version: "^3.1.1" | ||||
|     require: public | ||||
|   esp32async/asynctcp: | ||||
|     version: "^3.3.8" | ||||
|     version: "^3.4.7" | ||||
|     require: public | ||||
|   bblanchon/arduinojson: | ||||
|     version: "^7.3.1" | ||||
|     version: "^7.4.2" | ||||
|     require: public | ||||
| examples: | ||||
|   - path: ./idf_component_examples/catchall | ||||
|   | ||||
| @@ -78,7 +78,7 @@ static const size_t htmlContentLength = strlen_P(htmlContent); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -50,7 +50,7 @@ static AsyncEventSource events("/events"); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -17,7 +17,7 @@ static AsyncWebSocket ws("/ws"); | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   WiFi.mode(WIFI_AP); | ||||
|   WiFi.softAP("esp-captive"); | ||||
| #endif | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "ESPAsyncWebServer", | ||||
|   "version": "3.7.5", | ||||
|   "version": "3.8.0", | ||||
|   "description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.", | ||||
|   "keywords": "http,async,websocket,webserver", | ||||
|   "homepage": "https://github.com/ESP32Async/ESPAsyncWebServer", | ||||
| @@ -18,14 +18,18 @@ | ||||
|   "platforms": [ | ||||
|     "espressif32", | ||||
|     "espressif8266", | ||||
|     "raspberrypi" | ||||
|     "raspberrypi", | ||||
|     "libretiny" | ||||
|   ], | ||||
|   "dependencies": [ | ||||
|     { | ||||
|       "owner": "ESP32Async", | ||||
|       "name": "AsyncTCP", | ||||
|       "version": "^3.3.8", | ||||
|       "platforms": "espressif32" | ||||
|       "version": "^3.4.7", | ||||
|       "platforms": [ | ||||
|         "espressif32", | ||||
|         "libretiny" | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "owner": "ESP32Async", | ||||
| @@ -1,6 +1,6 @@ | ||||
| name=ESP Async WebServer | ||||
| includes=ESPAsyncWebServer.h | ||||
| version=3.7.5 | ||||
| version=3.8.0 | ||||
| author=ESP32Async | ||||
| maintainer=ESP32Async | ||||
| sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040 | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| [env] | ||||
| framework = arduino | ||||
| platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20/platform-espressif32.zip | ||||
| platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30-2/platform-espressif32.zip | ||||
| build_flags = | ||||
|   -Og | ||||
|   -Wall -Wextra | ||||
| @@ -17,7 +17,7 @@ monitor_filters = esp32_exception_decoder, log2file | ||||
| lib_compat_mode = strict | ||||
| lib_ldf_mode = chain | ||||
| lib_deps = | ||||
|   ESP32Async/AsyncTCP @ 3.3.8 | ||||
|   ESP32Async/AsyncTCP @ 3.4.7 | ||||
|   ESP32Async/ESpAsyncWebServer @ 3.7.0 | ||||
|  | ||||
| custom_sdkconfig = CONFIG_LWIP_MAX_ACTIVE_TCP=32 | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| default_envs = arduino-2, arduino-3, esp8266, raspberrypi | ||||
| lib_dir = . | ||||
| ; src_dir = examples/AsyncResponseStream | ||||
| ; src_dir = examples/AsyncTunnel | ||||
| ; src_dir = examples/Auth | ||||
| ; src_dir = examples/CaptivePortal | ||||
| ; src_dir = examples/CatchAllHandler | ||||
| @@ -37,7 +38,7 @@ src_dir = examples/PerfTests | ||||
|  | ||||
| [env] | ||||
| framework = arduino | ||||
| platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20/platform-espressif32.zip | ||||
| platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30-2/platform-espressif32.zip | ||||
| board = esp32dev | ||||
| build_flags = | ||||
|   -Og | ||||
| @@ -58,24 +59,26 @@ monitor_filters = esp32_exception_decoder, log2file | ||||
| lib_compat_mode = strict | ||||
| lib_ldf_mode = chain | ||||
| lib_deps = | ||||
|   bblanchon/ArduinoJson @ 7.3.1 | ||||
|   ESP32Async/AsyncTCP @ 3.3.8 | ||||
|   bblanchon/ArduinoJson @ 7.4.2 | ||||
|   ESP32Async/AsyncTCP @ 3.4.7 | ||||
| board_build.partitions = partitions-4MB.csv | ||||
| board_build.filesystem = littlefs | ||||
|  | ||||
| [env:arduino-2] | ||||
| platform = espressif32@6.10.0 | ||||
| platform = espressif32@6.12.0 | ||||
|  | ||||
| [env:arduino-3] | ||||
| ; board = esp32-p4 | ||||
| ; board = esp32-h2-devkitm-1 | ||||
|  | ||||
| [env:arduino-3-latest] | ||||
| [env:arduino-rc] | ||||
| platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20-rc2/platform-espressif32.zip | ||||
|  | ||||
| [env:arduino-3-no-json] | ||||
| lib_deps = | ||||
|   ESP32Async/AsyncTCP @ 3.3.8 | ||||
|   ESP32Async/AsyncTCP @ 3.4.7 | ||||
|  | ||||
| [env:arduino-3-latest-asynctcp] | ||||
| [env:arduino-rc-asynctcp] | ||||
| lib_deps = | ||||
|   https://github.com/ESP32Async/AsyncTCP | ||||
|  | ||||
| @@ -93,7 +96,7 @@ platform = espressif8266 | ||||
| ; board = huzzah | ||||
| board = d1_mini | ||||
| lib_deps = | ||||
|   bblanchon/ArduinoJson @ 7.3.1 | ||||
|   bblanchon/ArduinoJson @ 7.4.2 | ||||
|   ESP32Async/ESPAsyncTCP @ 2.0.0 | ||||
|  | ||||
| [env:raspberrypi] | ||||
| @@ -108,25 +111,36 @@ lib_ignore = | ||||
| build_flags = ${env.build_flags} | ||||
|   -Wno-missing-field-initializers | ||||
|  | ||||
| [env:libretiny] | ||||
| platform = libretiny @ ^1.9.1 | ||||
| board = generic-bk7231n-qfn32-tuya | ||||
| ; board = generic-rtl8710bn-2mb-788k | ||||
| lib_compat_mode = off | ||||
| lib_deps = | ||||
|   ESP32Async/AsyncTCP @ 3.4.3 | ||||
| ; use FreeRTOS v9.0.0 for RTL8710BN | ||||
| ; (BK7231 already uses it) | ||||
| custom_versions.freertos = 9.0.0 | ||||
|  | ||||
| ;  CI | ||||
|  | ||||
| [env:ci-arduino-2] | ||||
| platform = espressif32@6.10.0 | ||||
| platform = espressif32@6.12.0 | ||||
| board = ${sysenv.PIO_BOARD} | ||||
|  | ||||
| [env:ci-arduino-3] | ||||
| board = ${sysenv.PIO_BOARD} | ||||
|  | ||||
| [env:ci-arduino-3-latest] | ||||
| [env:ci-arduino-rc] | ||||
| platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20-rc2/platform-espressif32.zip | ||||
| board = ${sysenv.PIO_BOARD} | ||||
|  | ||||
| [env:ci-arduino-3-no-json] | ||||
| board = ${sysenv.PIO_BOARD} | ||||
| lib_deps = | ||||
|   ESP32Async/AsyncTCP @ 3.3.8 | ||||
|   ESP32Async/AsyncTCP @ 3.4.7 | ||||
|  | ||||
| [env:ci-arduino-3-latest-asynctcp] | ||||
| [env:ci-arduino-rc-asynctcp] | ||||
| lib_deps = | ||||
|   https://github.com/ESP32Async/AsyncTCP | ||||
|  | ||||
| @@ -139,7 +153,7 @@ build_flags = ${env.build_flags} | ||||
| platform = espressif8266 | ||||
| board = ${sysenv.PIO_BOARD} | ||||
| lib_deps = | ||||
|   bblanchon/ArduinoJson @ 7.3.1 | ||||
|   bblanchon/ArduinoJson @ 7.4.2 | ||||
|   ESP32Async/ESPAsyncTCP @ 2.0.0 | ||||
|  | ||||
| [env:ci-raspberrypi] | ||||
| @@ -153,3 +167,13 @@ lib_ignore = | ||||
|   lwIP_ESPHost | ||||
| build_flags = ${env.build_flags} | ||||
|   -Wno-missing-field-initializers | ||||
|  | ||||
| [env:ci-libretiny] | ||||
| platform = libretiny @ ^1.9.1 | ||||
| board = ${sysenv.PIO_BOARD} | ||||
| lib_compat_mode = off | ||||
| lib_deps = | ||||
| ; add DNS server library for LibreTiny | ||||
|   DNSServer | ||||
|   ESP32Async/AsyncTCP @ 3.4.3 | ||||
| custom_versions.freertos = 9.0.0 | ||||
|   | ||||
| @@ -193,7 +193,7 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A | ||||
|  | ||||
| AsyncEventSourceClient::~AsyncEventSourceClient() { | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_lockmq); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_lockmq); | ||||
| #endif | ||||
|   _messageQueue.clear(); | ||||
|   close(); | ||||
| @@ -211,7 +211,7 @@ bool AsyncEventSourceClient::_queueMessage(const char *message, size_t len) { | ||||
|  | ||||
| #ifdef ESP32 | ||||
|   // length() is not thread-safe, thus acquiring the lock before this call.. | ||||
|   std::lock_guard<std::mutex> lock(_lockmq); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_lockmq); | ||||
| #endif | ||||
|  | ||||
|   _messageQueue.emplace_back(message, len); | ||||
| @@ -241,7 +241,7 @@ bool AsyncEventSourceClient::_queueMessage(AsyncEvent_SharedData_t &&msg) { | ||||
|  | ||||
| #ifdef ESP32 | ||||
|   // length() is not thread-safe, thus acquiring the lock before this call.. | ||||
|   std::lock_guard<std::mutex> lock(_lockmq); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_lockmq); | ||||
| #endif | ||||
|  | ||||
|   _messageQueue.emplace_back(std::move(msg)); | ||||
| @@ -261,7 +261,7 @@ bool AsyncEventSourceClient::_queueMessage(AsyncEvent_SharedData_t &&msg) { | ||||
| void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))) { | ||||
| #ifdef ESP32 | ||||
|   // Same here, acquiring the lock early | ||||
|   std::lock_guard<std::mutex> lock(_lockmq); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_lockmq); | ||||
| #endif | ||||
|  | ||||
|   // adjust in-flight len | ||||
| @@ -290,7 +290,7 @@ void AsyncEventSourceClient::_onPoll() { | ||||
|   if (_messageQueue.size()) { | ||||
| #ifdef ESP32 | ||||
|     // Same here, acquiring the lock early | ||||
|     std::lock_guard<std::mutex> lock(_lockmq); | ||||
|     std::lock_guard<std::recursive_mutex> lock(_lockmq); | ||||
| #endif | ||||
|     _runQueue(); | ||||
|   } | ||||
| @@ -367,7 +367,7 @@ void AsyncEventSource::_addClient(AsyncEventSourceClient *client) { | ||||
|     return; | ||||
|   } | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_client_queue_lock); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_client_queue_lock); | ||||
| #endif | ||||
|   _clients.emplace_back(client); | ||||
|   if (_connectcb) { | ||||
| @@ -382,7 +382,7 @@ void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient *client) { | ||||
|     _disconnectcb(client); | ||||
|   } | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_client_queue_lock); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_client_queue_lock); | ||||
| #endif | ||||
|   for (auto i = _clients.begin(); i != _clients.end(); ++i) { | ||||
|     if (i->get() == client) { | ||||
| @@ -398,10 +398,15 @@ void AsyncEventSource::close() { | ||||
|   // iterator should remain valid even when AsyncEventSource::_handleDisconnect() | ||||
|   // is called very early | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_client_queue_lock); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_client_queue_lock); | ||||
| #endif | ||||
|   for (const auto &c : _clients) { | ||||
|     if (c->connected()) { | ||||
|       /** | ||||
|        * @brief: Fix self-deadlock by using recursive_mutex instead. | ||||
|        * Due to c->close() shall call the callback function _onDisconnect() | ||||
|        * The calling flow _onDisconnect() --> _handleDisconnect() --> deadlock | ||||
|       */ | ||||
|       c->close(); | ||||
|     } | ||||
|   } | ||||
| @@ -412,7 +417,7 @@ size_t AsyncEventSource::avgPacketsWaiting() const { | ||||
|   size_t aql = 0; | ||||
|   uint32_t nConnectedClients = 0; | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_client_queue_lock); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_client_queue_lock); | ||||
| #endif | ||||
|   if (!_clients.size()) { | ||||
|     return 0; | ||||
| @@ -430,7 +435,7 @@ size_t AsyncEventSource::avgPacketsWaiting() const { | ||||
| AsyncEventSource::SendStatus AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect) { | ||||
|   AsyncEvent_SharedData_t shared_msg = std::make_shared<String>(generateEventMessage(message, event, id, reconnect)); | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_client_queue_lock); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_client_queue_lock); | ||||
| #endif | ||||
|   size_t hits = 0; | ||||
|   size_t miss = 0; | ||||
| @@ -446,7 +451,7 @@ AsyncEventSource::SendStatus AsyncEventSource::send(const char *message, const c | ||||
|  | ||||
| size_t AsyncEventSource::count() const { | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_client_queue_lock); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_client_queue_lock); | ||||
| #endif | ||||
|   size_t n_clients{0}; | ||||
|   for (const auto &i : _clients) { | ||||
|   | ||||
| @@ -6,8 +6,13 @@ | ||||
|  | ||||
| #include <Arduino.h> | ||||
|  | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #ifdef LIBRETINY | ||||
| #ifdef round | ||||
| #undef round | ||||
| #endif | ||||
| #endif | ||||
| #include <mutex> | ||||
| #ifndef SSE_MAX_QUEUED_MESSAGES | ||||
| #define SSE_MAX_QUEUED_MESSAGES 32 | ||||
| @@ -129,7 +134,7 @@ private: | ||||
|   size_t _max_inflight{SSE_MAX_INFLIGH};  // max num of unacknowledged bytes that could be written to socket buffer | ||||
|   std::list<AsyncEventSourceMessage> _messageQueue; | ||||
| #ifdef ESP32 | ||||
|   mutable std::mutex _lockmq; | ||||
|   mutable std::recursive_mutex _lockmq; | ||||
| #endif | ||||
|   bool _queueMessage(const char *message, size_t len); | ||||
|   bool _queueMessage(AsyncEvent_SharedData_t &&msg); | ||||
| @@ -230,7 +235,7 @@ private: | ||||
| #ifdef ESP32 | ||||
|   // Same as for individual messages, protect mutations of _clients list | ||||
|   // since simultaneous access from different tasks is possible | ||||
|   mutable std::mutex _client_queue_lock; | ||||
|   mutable std::recursive_mutex _client_queue_lock; | ||||
| #endif | ||||
|   ArEventHandlerFunction _connectcb = nullptr; | ||||
|   ArEventHandlerFunction _disconnectcb = nullptr; | ||||
|   | ||||
| @@ -113,53 +113,77 @@ bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest *request) cons | ||||
|  | ||||
| void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest *request) { | ||||
|   if (_onRequest) { | ||||
|     // GET request: | ||||
|     if (request->method() == HTTP_GET) { | ||||
|       JsonVariant json; | ||||
|       _onRequest(request, json); | ||||
|       return; | ||||
|     } else if (request->_tempObject != NULL) { | ||||
|     } | ||||
|  | ||||
|     // POST / PUT / ... requests: | ||||
|     // check if JSON body is too large, if it is, don't deserialize | ||||
|     if (request->contentLength() > _maxContentLength) { | ||||
| #ifdef ESP32 | ||||
|       log_e("Content length exceeds maximum allowed"); | ||||
| #endif | ||||
|       request->send(413); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (request->_tempObject == NULL) { | ||||
|       // there is no body | ||||
|       request->send(400); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| #if ARDUINOJSON_VERSION_MAJOR == 5 | ||||
|       DynamicJsonBuffer jsonBuffer; | ||||
|       JsonVariant json = jsonBuffer.parse((uint8_t *)(request->_tempObject)); | ||||
|       if (json.success()) { | ||||
|     DynamicJsonBuffer jsonBuffer; | ||||
|     JsonVariant json = jsonBuffer.parse((const char *)request->_tempObject); | ||||
|     if (json.success()) { | ||||
| #elif ARDUINOJSON_VERSION_MAJOR == 6 | ||||
|       DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize); | ||||
|       DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject)); | ||||
|       if (!error) { | ||||
|         JsonVariant json = jsonBuffer.as<JsonVariant>(); | ||||
|     DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize); | ||||
|     DeserializationError error = deserializeJson(jsonBuffer, (const char *)request->_tempObject); | ||||
|     if (!error) { | ||||
|       JsonVariant json = jsonBuffer.as<JsonVariant>(); | ||||
| #else | ||||
|       JsonDocument jsonBuffer; | ||||
|       DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject)); | ||||
|       if (!error) { | ||||
|         JsonVariant json = jsonBuffer.as<JsonVariant>(); | ||||
|     JsonDocument jsonBuffer; | ||||
|     DeserializationError error = deserializeJson(jsonBuffer, (const char *)request->_tempObject); | ||||
|     if (!error) { | ||||
|       JsonVariant json = jsonBuffer.as<JsonVariant>(); | ||||
| #endif | ||||
|  | ||||
|         _onRequest(request, json); | ||||
|         return; | ||||
|       } | ||||
|       _onRequest(request, json); | ||||
|     } else { | ||||
|       // error parsing the body | ||||
|       request->send(400); | ||||
|     } | ||||
|     request->send(_contentLength > _maxContentLength ? 413 : 400); | ||||
|   } else { | ||||
|     request->send(500); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void AsyncCallbackJsonWebHandler::handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { | ||||
|   if (_onRequest) { | ||||
|     _contentLength = total; | ||||
|     if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) { | ||||
|       request->_tempObject = malloc(total); | ||||
|     // ignore callback if size is larger than maxContentLength | ||||
|     if (total > _maxContentLength) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (index == 0) { | ||||
|       // this check allows request->_tempObject to be initialized from a middleware | ||||
|       if (request->_tempObject == NULL) { | ||||
|         request->_tempObject = calloc(total + 1, sizeof(uint8_t));  // null-terminated string | ||||
|         if (request->_tempObject == NULL) { | ||||
| #ifdef ESP32 | ||||
|         log_e("Failed to allocate"); | ||||
|           log_e("Failed to allocate"); | ||||
| #endif | ||||
|         request->abort(); | ||||
|         return; | ||||
|           request->abort(); | ||||
|           return; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (request->_tempObject != NULL) { | ||||
|       memcpy((uint8_t *)(request->_tempObject) + index, data, len); | ||||
|       uint8_t *buffer = (uint8_t *)request->_tempObject; | ||||
|       memcpy(buffer + index, data, len); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -4,8 +4,14 @@ | ||||
| #ifndef ASYNC_JSON_H_ | ||||
| #define ASYNC_JSON_H_ | ||||
|  | ||||
| #if __has_include("ArduinoJson.h") | ||||
| #include <ArduinoJson.h> | ||||
| #if ARDUINOJSON_VERSION_MAJOR >= 5 | ||||
| #define ASYNC_JSON_SUPPORT 1 | ||||
| #else | ||||
| #define ASYNC_JSON_SUPPORT 0 | ||||
| #endif  // ARDUINOJSON_VERSION_MAJOR >= 5 | ||||
| #endif  // __has_include("ArduinoJson.h") | ||||
|  | ||||
| #if ASYNC_JSON_SUPPORT == 1 | ||||
| #include <ESPAsyncWebServer.h> | ||||
| @@ -73,7 +79,6 @@ protected: | ||||
|   String _uri; | ||||
|   WebRequestMethodComposite _method; | ||||
|   ArJsonRequestHandlerFunction _onRequest; | ||||
|   size_t _contentLength; | ||||
| #if ARDUINOJSON_VERSION_MAJOR == 6 | ||||
|   size_t maxJsonBufferSize; | ||||
| #endif | ||||
|   | ||||
| @@ -3,30 +3,32 @@ | ||||
|  | ||||
| #include <ESPAsyncWebServer.h> | ||||
|  | ||||
| AsyncWebHeader::AsyncWebHeader(const String &data) { | ||||
| const AsyncWebHeader AsyncWebHeader::parse(const char *data) { | ||||
|   // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers | ||||
|   // In HTTP/1.X, a header is a case-insensitive name followed by a colon, then optional whitespace which will be ignored, and finally by its value | ||||
|   if (!data) { | ||||
|     return; | ||||
|     return AsyncWebHeader();  // nullptr | ||||
|   } | ||||
|   int index = data.indexOf(':'); | ||||
|   if (index < 0) { | ||||
|     return; | ||||
|   if (data[0] == '\0') { | ||||
|     return AsyncWebHeader();  // empty string | ||||
|   } | ||||
|   _name = data.substring(0, index); | ||||
|   _value = data.substring(index + 2); | ||||
| } | ||||
|  | ||||
| String AsyncWebHeader::toString() const { | ||||
|   String str; | ||||
|   if (str.reserve(_name.length() + _value.length() + 2)) { | ||||
|     str.concat(_name); | ||||
|     str.concat((char)0x3a); | ||||
|     str.concat((char)0x20); | ||||
|     str.concat(_value); | ||||
|     str.concat(asyncsrv::T_rn); | ||||
|   } else { | ||||
| #ifdef ESP32 | ||||
|     log_e("Failed to allocate"); | ||||
| #endif | ||||
|   } | ||||
|   return str; | ||||
|   if (strchr(data, '\n') || strchr(data, '\r')) { | ||||
|     return AsyncWebHeader();  // Invalid header format | ||||
|   } | ||||
|   const char *colon = strchr(data, ':'); | ||||
|   if (!colon) { | ||||
|     return AsyncWebHeader();  // separator not found | ||||
|   } | ||||
|   if (colon == data) { | ||||
|     return AsyncWebHeader();  // Header name cannot be empty | ||||
|   } | ||||
|   const char *startOfValue = colon + 1;  // Skip the colon | ||||
|   // skip one optional whitespace after the colon | ||||
|   if (*startOfValue == ' ') { | ||||
|     startOfValue++; | ||||
|   } | ||||
|   String name; | ||||
|   name.reserve(colon - data); | ||||
|   name.concat(data, colon - data); | ||||
|   return AsyncWebHeader(name, String(startOfValue)); | ||||
| } | ||||
|   | ||||
							
								
								
									
										85
									
								
								src/AsyncWebServerRequest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/AsyncWebServerRequest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| #include <ESPAsyncWebServer.h> | ||||
|  | ||||
| /** | ||||
|  * @brief Sends a file from the filesystem to the client, with optional gzip compression and ETag-based caching. | ||||
|  * | ||||
|  * This method serves files over HTTP from the provided filesystem. If a compressed version of the file | ||||
|  * (with a `.gz` extension) exists and uncompressed version does not exist, it serves the compressed file. | ||||
|  * It also handles ETag caching using the CRC32 value from the gzip trailer, responding with `304 Not Modified` | ||||
|  * if the client's `If-None-Match` header matches the generated ETag. | ||||
|  * | ||||
|  * @param fs Reference to the filesystem (SPIFFS, LittleFS, etc.). | ||||
|  * @param path Path to the file to be served. | ||||
|  * @param contentType Optional MIME type of the file to be sent. | ||||
|  *                    If contentType is "" it will be obtained from the file extension | ||||
|  * @param download If true, forces the file to be sent as a download. | ||||
|  * @param callback Optional template processor for dynamic content generation. | ||||
|  *                 Templates will not be processed in compressed files. | ||||
|  * | ||||
|  * @note If neither the file nor its compressed version exists, responds with `404 Not Found`. | ||||
|  */ | ||||
| void AsyncWebServerRequest::send(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) { | ||||
|   // Check uncompressed file first | ||||
|   if (fs.exists(path)) { | ||||
|     send(beginResponse(fs, path, contentType, download, callback)); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // Handle compressed version | ||||
|   const String gzPath = path + asyncsrv::T__gz; | ||||
|   File gzFile = fs.open(gzPath, fs::FileOpenMode::read); | ||||
|  | ||||
|   // Compressed file not found or invalid | ||||
|   if (!gzFile.seek(gzFile.size() - 8)) { | ||||
|     send(404); | ||||
|     gzFile.close(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // ETag validation | ||||
|   if (this->hasHeader(asyncsrv::T_INM)) { | ||||
|     // Generate server ETag from CRC in gzip trailer | ||||
|     uint8_t crcInTrailer[4]; | ||||
|     gzFile.read(crcInTrailer, 4); | ||||
|     char serverETag[9]; | ||||
|     _getEtag(crcInTrailer, serverETag); | ||||
|  | ||||
|     // Compare with client's ETag | ||||
|     const AsyncWebHeader *inmHeader = this->getHeader(asyncsrv::T_INM); | ||||
|     if (inmHeader && inmHeader->value() == serverETag) { | ||||
|       gzFile.close(); | ||||
|       this->send(304);  // Not Modified | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Send compressed file response | ||||
|   gzFile.close(); | ||||
|   send(beginResponse(fs, path, contentType, download, callback)); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Generates an ETag string from a 4-byte trailer | ||||
|  * | ||||
|  * This function converts a 4-byte array into a hexadecimal ETag string enclosed in quotes. | ||||
|  * | ||||
|  * @param trailer[4] Input array of 4 bytes to convert to hexadecimal | ||||
|  * @param serverETag Output buffer to store the ETag | ||||
|  *                   Must be pre-allocated with minimum 9 bytes (8 hex + 1 null terminator) | ||||
|  */ | ||||
| void AsyncWebServerRequest::_getEtag(uint8_t trailer[4], char *serverETag) { | ||||
|   static constexpr char hexChars[] = "0123456789ABCDEF"; | ||||
|  | ||||
|   uint32_t data; | ||||
|   memcpy(&data, trailer, 4); | ||||
|  | ||||
|   serverETag[0] = hexChars[(data >> 4) & 0x0F]; | ||||
|   serverETag[1] = hexChars[data & 0x0F]; | ||||
|   serverETag[2] = hexChars[(data >> 12) & 0x0F]; | ||||
|   serverETag[3] = hexChars[(data >> 8) & 0x0F]; | ||||
|   serverETag[4] = hexChars[(data >> 20) & 0x0F]; | ||||
|   serverETag[5] = hexChars[(data >> 16) & 0x0F]; | ||||
|   serverETag[6] = hexChars[(data >> 28)]; | ||||
|   serverETag[7] = hexChars[(data >> 24) & 0x0F]; | ||||
|   serverETag[8] = '\0'; | ||||
| } | ||||
| @@ -10,9 +10,9 @@ extern "C" { | ||||
| /** Major version number (X.x.x) */ | ||||
| #define ASYNCWEBSERVER_VERSION_MAJOR 3 | ||||
| /** Minor version number (x.X.x) */ | ||||
| #define ASYNCWEBSERVER_VERSION_MINOR 7 | ||||
| #define ASYNCWEBSERVER_VERSION_MINOR 8 | ||||
| /** Patch version number (x.x.X) */ | ||||
| #define ASYNCWEBSERVER_VERSION_PATCH 5 | ||||
| #define ASYNCWEBSERVER_VERSION_PATCH 0 | ||||
|  | ||||
| /** | ||||
|  * Macro to convert version number into an integer | ||||
|   | ||||
| @@ -17,6 +17,8 @@ | ||||
| #include <rom/ets_sys.h> | ||||
| #elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(ESP8266) | ||||
| #include <Hash.h> | ||||
| #elif defined(LIBRETINY) | ||||
| #include <mbedtls/sha1.h> | ||||
| #endif | ||||
|  | ||||
| using namespace asyncsrv; | ||||
| @@ -118,6 +120,11 @@ size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool | ||||
|   return len; | ||||
| } | ||||
|  | ||||
| size_t AsyncWebSocketControl::send(AsyncClient *client) { | ||||
|   _finished = true; | ||||
|   return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  *    AsyncWebSocketMessageBuffer | ||||
|  */ | ||||
| @@ -144,65 +151,6 @@ bool AsyncWebSocketMessageBuffer::reserve(size_t size) { | ||||
|   return _buffer->capacity() >= size; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Control Frame | ||||
|  */ | ||||
|  | ||||
| class AsyncWebSocketControl { | ||||
| private: | ||||
|   uint8_t _opcode; | ||||
|   uint8_t *_data; | ||||
|   size_t _len; | ||||
|   bool _mask; | ||||
|   bool _finished; | ||||
|  | ||||
| public: | ||||
|   AsyncWebSocketControl(uint8_t opcode, const uint8_t *data = NULL, size_t len = 0, bool mask = false) | ||||
|     : _opcode(opcode), _len(len), _mask(len && mask), _finished(false) { | ||||
|     if (data == NULL) { | ||||
|       _len = 0; | ||||
|     } | ||||
|     if (_len) { | ||||
|       if (_len > 125) { | ||||
|         _len = 125; | ||||
|       } | ||||
|  | ||||
|       _data = (uint8_t *)malloc(_len); | ||||
|  | ||||
|       if (_data == NULL) { | ||||
| #ifdef ESP32 | ||||
|         log_e("Failed to allocate"); | ||||
| #endif | ||||
|         _len = 0; | ||||
|       } else { | ||||
|         memcpy(_data, data, len); | ||||
|       } | ||||
|     } else { | ||||
|       _data = NULL; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ~AsyncWebSocketControl() { | ||||
|     if (_data != NULL) { | ||||
|       free(_data); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   bool finished() const { | ||||
|     return _finished; | ||||
|   } | ||||
|   uint8_t opcode() { | ||||
|     return _opcode; | ||||
|   } | ||||
|   uint8_t len() { | ||||
|     return _len + 2; | ||||
|   } | ||||
|   size_t send(AsyncClient *client) { | ||||
|     _finished = true; | ||||
|     return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * AsyncWebSocketMessage Message | ||||
|  */ | ||||
| @@ -333,7 +281,7 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, Async | ||||
| AsyncWebSocketClient::~AsyncWebSocketClient() { | ||||
|   { | ||||
| #ifdef ESP32 | ||||
|     std::lock_guard<std::mutex> lock(_lock); | ||||
|     std::lock_guard<std::recursive_mutex> lock(_lock); | ||||
| #endif | ||||
|     _messageQueue.clear(); | ||||
|     _controlQueue.clear(); | ||||
| @@ -351,7 +299,7 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) { | ||||
|   _lastMessageTime = millis(); | ||||
|  | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_lock); | ||||
|   std::unique_lock<std::recursive_mutex> lock(_lock); | ||||
| #endif | ||||
|  | ||||
|   if (!_controlQueue.empty()) { | ||||
| @@ -362,6 +310,14 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) { | ||||
|         _controlQueue.pop_front(); | ||||
|         _status = WS_DISCONNECTED; | ||||
|         if (_client) { | ||||
| #ifdef ESP32 | ||||
|           /* | ||||
|             Unlocking has to be called before return execution otherwise std::unique_lock ::~unique_lock() will get an exception pthread_mutex_unlock. | ||||
|             Due to _client->close(true) shall call the callback function _onDisconnect() | ||||
|             The calling flow _onDisconnect() --> _handleDisconnect() --> ~AsyncWebSocketClient() | ||||
|           */ | ||||
|           lock.unlock(); | ||||
| #endif | ||||
|           _client->close(true); | ||||
|         } | ||||
|         return; | ||||
| @@ -385,7 +341,7 @@ void AsyncWebSocketClient::_onPoll() { | ||||
|   } | ||||
|  | ||||
| #ifdef ESP32 | ||||
|   std::unique_lock<std::mutex> lock(_lock); | ||||
|   std::unique_lock<std::recursive_mutex> lock(_lock); | ||||
| #endif | ||||
|   if (_client && _client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) { | ||||
|     _runQueue(); | ||||
| @@ -415,21 +371,21 @@ void AsyncWebSocketClient::_runQueue() { | ||||
|  | ||||
| bool AsyncWebSocketClient::queueIsFull() const { | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_lock); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_lock); | ||||
| #endif | ||||
|   return (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED); | ||||
| } | ||||
|  | ||||
| size_t AsyncWebSocketClient::queueLen() const { | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_lock); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_lock); | ||||
| #endif | ||||
|   return _messageQueue.size(); | ||||
| } | ||||
|  | ||||
| bool AsyncWebSocketClient::canSend() const { | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_lock); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_lock); | ||||
| #endif | ||||
|   return _messageQueue.size() < WS_MAX_QUEUED_MESSAGES; | ||||
| } | ||||
| @@ -440,7 +396,7 @@ bool AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t *data, si | ||||
|   } | ||||
|  | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_lock); | ||||
|   std::lock_guard<std::recursive_mutex> lock(_lock); | ||||
| #endif | ||||
|  | ||||
|   _controlQueue.emplace_back(opcode, data, len, mask); | ||||
| @@ -458,7 +414,7 @@ bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint | ||||
|   } | ||||
|  | ||||
| #ifdef ESP32 | ||||
|   std::lock_guard<std::mutex> lock(_lock); | ||||
|   std::unique_lock<std::recursive_mutex> lock(_lock); | ||||
| #endif | ||||
|  | ||||
|   if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) { | ||||
| @@ -466,6 +422,14 @@ bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint | ||||
|       _status = WS_DISCONNECTED; | ||||
|  | ||||
|       if (_client) { | ||||
| #ifdef ESP32 | ||||
|         /* | ||||
|           Unlocking has to be called before return execution otherwise std::unique_lock ::~unique_lock() will get an exception pthread_mutex_unlock. | ||||
|           Due to _client->close(true) shall call the callback function _onDisconnect() | ||||
|           The calling flow _onDisconnect() --> _handleDisconnect() --> ~AsyncWebSocketClient() | ||||
|         */ | ||||
|         lock.unlock(); | ||||
| #endif | ||||
|         _client->close(true); | ||||
|       } | ||||
|  | ||||
| @@ -551,6 +515,7 @@ void AsyncWebSocketClient::_onTimeout(uint32_t time) { | ||||
| void AsyncWebSocketClient::_onDisconnect() { | ||||
|   // Serial.println("onDis"); | ||||
|   _client = nullptr; | ||||
|   _server->_handleDisconnect(this); | ||||
| } | ||||
|  | ||||
| void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) { | ||||
| @@ -857,6 +822,16 @@ AsyncWebSocketClient *AsyncWebSocket::_newClient(AsyncWebServerRequest *request) | ||||
|   return &_clients.back(); | ||||
| } | ||||
|  | ||||
| void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient *client) { | ||||
|   const auto client_id = client->id(); | ||||
|   const auto iter = std::find_if(std::begin(_clients), std::end(_clients), [client_id](const AsyncWebSocketClient &c) { | ||||
|     return c.id() == client_id; | ||||
|   }); | ||||
|   if (iter != std::end(_clients)) { | ||||
|     _clients.erase(iter); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool AsyncWebSocket::availableForWriteAll() { | ||||
|   return std::none_of(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient &c) { | ||||
|     return c.queueIsFull(); | ||||
| @@ -1300,11 +1275,20 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String &key, AsyncWebSocket | ||||
|   } | ||||
|   k.concat(key); | ||||
|   k.concat(WS_STR_UUID); | ||||
| #ifdef LIBRETINY | ||||
|   mbedtls_sha1_context ctx; | ||||
|   mbedtls_sha1_init(&ctx); | ||||
|   mbedtls_sha1_starts(&ctx); | ||||
|   mbedtls_sha1_update(&ctx, (const uint8_t *)k.c_str(), k.length()); | ||||
|   mbedtls_sha1_finish(&ctx, hash); | ||||
|   mbedtls_sha1_free(&ctx); | ||||
| #else | ||||
|   SHA1Builder sha1; | ||||
|   sha1.begin(); | ||||
|   sha1.add((const uint8_t *)k.c_str(), k.length()); | ||||
|   sha1.calculate(); | ||||
|   sha1.getBytes(hash); | ||||
| #endif | ||||
| #endif | ||||
|   base64_encodestate _state; | ||||
|   base64_init_encodestate(&_state); | ||||
|   | ||||
| @@ -5,8 +5,14 @@ | ||||
| #define ASYNCWEBSOCKET_H_ | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #ifdef ESP32 | ||||
|  | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #ifdef LIBRETINY | ||||
| #ifdef round | ||||
| #undef round | ||||
| #endif | ||||
| #endif | ||||
| #include <mutex> | ||||
| #ifndef WS_MAX_QUEUED_MESSAGES | ||||
| #define WS_MAX_QUEUED_MESSAGES 32 | ||||
| @@ -47,7 +53,62 @@ using AsyncWebSocketSharedBuffer = std::shared_ptr<std::vector<uint8_t>>; | ||||
| class AsyncWebSocket; | ||||
| class AsyncWebSocketResponse; | ||||
| class AsyncWebSocketClient; | ||||
| class AsyncWebSocketControl; | ||||
|  | ||||
| /* | ||||
|  * Control Frame | ||||
|  */ | ||||
|  | ||||
| class AsyncWebSocketControl { | ||||
| private: | ||||
|   uint8_t _opcode; | ||||
|   uint8_t *_data; | ||||
|   size_t _len; | ||||
|   bool _mask; | ||||
|   bool _finished; | ||||
|  | ||||
| public: | ||||
|   AsyncWebSocketControl(uint8_t opcode, const uint8_t *data = NULL, size_t len = 0, bool mask = false) | ||||
|     : _opcode(opcode), _len(len), _mask(len && mask), _finished(false) { | ||||
|     if (data == NULL) { | ||||
|       _len = 0; | ||||
|     } | ||||
|     if (_len) { | ||||
|       if (_len > 125) { | ||||
|         _len = 125; | ||||
|       } | ||||
|  | ||||
|       _data = (uint8_t *)malloc(_len); | ||||
|  | ||||
|       if (_data == NULL) { | ||||
| #ifdef ESP32 | ||||
|         log_e("Failed to allocate"); | ||||
| #endif | ||||
|         _len = 0; | ||||
|       } else { | ||||
|         memcpy(_data, data, len); | ||||
|       } | ||||
|     } else { | ||||
|       _data = NULL; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ~AsyncWebSocketControl() { | ||||
|     if (_data != NULL) { | ||||
|       free(_data); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   bool finished() const { | ||||
|     return _finished; | ||||
|   } | ||||
|   uint8_t opcode() { | ||||
|     return _opcode; | ||||
|   } | ||||
|   uint8_t len() { | ||||
|     return _len + 2; | ||||
|   } | ||||
|   size_t send(AsyncClient *client); | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|   /** Message type as defined by enum AwsFrameType. | ||||
| @@ -152,7 +213,7 @@ private: | ||||
|   uint32_t _clientId; | ||||
|   AwsClientStatus _status; | ||||
| #ifdef ESP32 | ||||
|   mutable std::mutex _lock; | ||||
|   mutable std::recursive_mutex _lock; | ||||
| #endif | ||||
|   std::deque<AsyncWebSocketControl> _controlQueue; | ||||
|   std::deque<AsyncWebSocketMessage> _messageQueue; | ||||
| @@ -385,6 +446,7 @@ public: | ||||
|     return _cNextId++; | ||||
|   } | ||||
|   AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request); | ||||
|   void _handleDisconnect(AsyncWebSocketClient *client); | ||||
|   void _handleEvent(AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len); | ||||
|   bool canHandle(AsyncWebServerRequest *request) const override final; | ||||
|   void handleRequest(AsyncWebServerRequest *request) override final; | ||||
|   | ||||
| @@ -15,16 +15,13 @@ | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
|  | ||||
| #ifdef ESP32 | ||||
| #if defined(ESP32) || defined(LIBRETINY) | ||||
| #include <AsyncTCP.h> | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| #include <ESP8266WiFi.h> | ||||
| #include <ESPAsyncTCP.h> | ||||
| #elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) | ||||
| #include <RPAsyncTCP.h> | ||||
| #include <HTTP_Method.h> | ||||
| #include <WiFi.h> | ||||
| #include <http_parser.h> | ||||
| #else | ||||
| #error Platform not supported | ||||
| @@ -138,12 +135,20 @@ private: | ||||
|   String _value; | ||||
|  | ||||
| public: | ||||
|   AsyncWebHeader() {} | ||||
|   AsyncWebHeader(const AsyncWebHeader &) = default; | ||||
|   AsyncWebHeader(AsyncWebHeader &&) = default; | ||||
|   AsyncWebHeader(const char *name, const char *value) : _name(name), _value(value) {} | ||||
|   AsyncWebHeader(const String &name, const String &value) : _name(name), _value(value) {} | ||||
|   AsyncWebHeader(const String &data); | ||||
|  | ||||
| #ifndef ESP8266 | ||||
|   [[deprecated("Use AsyncWebHeader::parse(data) instead")]] | ||||
| #endif | ||||
|   AsyncWebHeader(const String &data) | ||||
|     : AsyncWebHeader(parse(data)){}; | ||||
|  | ||||
|   AsyncWebHeader &operator=(const AsyncWebHeader &) = default; | ||||
|   AsyncWebHeader &operator=(AsyncWebHeader &&other) = default; | ||||
|  | ||||
|   const String &name() const { | ||||
|     return _name; | ||||
| @@ -151,7 +156,18 @@ public: | ||||
|   const String &value() const { | ||||
|     return _value; | ||||
|   } | ||||
|  | ||||
|   String toString() const; | ||||
|  | ||||
|   // returns true if the header is valid | ||||
|   operator bool() const { | ||||
|     return _name.length(); | ||||
|   } | ||||
|  | ||||
|   static const AsyncWebHeader parse(const String &data) { | ||||
|     return parse(data.c_str()); | ||||
|   } | ||||
|   static const AsyncWebHeader parse(const char *data); | ||||
| }; | ||||
|  | ||||
| /* | ||||
| @@ -187,6 +203,7 @@ class AsyncWebServerRequest { | ||||
|   using FS = fs::FS; | ||||
|   friend class AsyncWebServer; | ||||
|   friend class AsyncCallbackWebHandler; | ||||
|   friend class AsyncFileResponse; | ||||
|  | ||||
| private: | ||||
|   AsyncClient *_client; | ||||
| @@ -258,6 +275,8 @@ private: | ||||
|   void _send(); | ||||
|   void _runMiddlewareChain(); | ||||
|  | ||||
|   static void _getEtag(uint8_t trailer[4], char *serverETag); | ||||
|  | ||||
| public: | ||||
|   File _tempFile; | ||||
|   void *_tempObject; | ||||
| @@ -370,13 +389,7 @@ public: | ||||
|     send(beginResponse(code, contentType, content, len, callback)); | ||||
|   } | ||||
|  | ||||
|   void send(FS &fs, const String &path, const char *contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr) { | ||||
|     if (fs.exists(path) || (!download && fs.exists(path + asyncsrv::T__gz))) { | ||||
|       send(beginResponse(fs, path, contentType, download, callback)); | ||||
|     } else { | ||||
|       send(404); | ||||
|     } | ||||
|   } | ||||
|   void send(FS &fs, const String &path, const char *contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr); | ||||
|   void send(FS &fs, const String &path, const String &contentType, bool download = false, AwsTemplateProcessor callback = nullptr) { | ||||
|     send(fs, path, contentType.c_str(), download, callback); | ||||
|   } | ||||
| @@ -1041,6 +1054,10 @@ public: | ||||
|     setContentType(type.c_str()); | ||||
|   } | ||||
|   void setContentType(const char *type); | ||||
|   bool addHeader(AsyncWebHeader &&header, bool replaceExisting = true); | ||||
|   bool addHeader(const AsyncWebHeader &header, bool replaceExisting = true) { | ||||
|     return header && addHeader(header.name(), header.value(), replaceExisting); | ||||
|   } | ||||
|   bool addHeader(const char *name, const char *value, bool replaceExisting = true); | ||||
|   bool addHeader(const String &name, const String &value, bool replaceExisting = true) { | ||||
|     return addHeader(name.c_str(), value.c_str(), replaceExisting); | ||||
|   | ||||
| @@ -172,7 +172,11 @@ void AsyncLoggingMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNex | ||||
|     return; | ||||
|   } | ||||
|   _out->print(F("* Connection from ")); | ||||
| #ifndef LIBRETINY | ||||
|   _out->print(request->client()->remoteIP().toString()); | ||||
| #else | ||||
|   _out->print(request->client()->remoteIP()); | ||||
| #endif | ||||
|   _out->print(':'); | ||||
|   _out->println(request->client()->remotePort()); | ||||
|   _out->print('>'); | ||||
|   | ||||
| @@ -209,11 +209,14 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) { | ||||
|     char buf[len]; | ||||
|     char *ret = lltoa(lw ^ request->_tempFile.size(), buf, len, 10); | ||||
|     etag = ret ? String(ret) : String(request->_tempFile.size()); | ||||
| #elif defined(LIBRETINY) | ||||
|     long val = lw ^ request->_tempFile.size(); | ||||
|     etag = String(val); | ||||
| #else | ||||
|     etag = lw ^ request->_tempFile.size();  // etag combines file size and lastmod timestamp | ||||
| #endif | ||||
|   } else { | ||||
| #if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) | ||||
| #if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(LIBRETINY) | ||||
|     etag = String(request->_tempFile.size()); | ||||
| #else | ||||
|     etag = request->_tempFile.size(); | ||||
|   | ||||
| @@ -22,10 +22,10 @@ enum { | ||||
| }; | ||||
|  | ||||
| AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer *s, AsyncClient *c) | ||||
|   : _client(c), _server(s), _handler(NULL), _response(NULL), _temp(), _parseState(PARSE_REQ_START), _version(0), _method(HTTP_ANY), _url(), _host(), | ||||
|     _contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP), _authMethod(AsyncAuthType::AUTH_NONE), _isMultipart(false), _isPlainPost(false), | ||||
|     _expectingContinue(false), _contentLength(0), _parsedLength(0), _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0), _itemSize(0), _itemName(), | ||||
|     _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL) { | ||||
|   : _client(c), _server(s), _handler(NULL), _response(NULL), _onDisconnectfn(NULL), _temp(), _parseState(PARSE_REQ_START), _version(0), _method(HTTP_ANY), | ||||
|     _url(), _host(), _contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP), _authMethod(AsyncAuthType::AUTH_NONE), _isMultipart(false), | ||||
|     _isPlainPost(false), _expectingContinue(false), _contentLength(0), _parsedLength(0), _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0), | ||||
|     _itemSize(0), _itemName(), _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL) { | ||||
|   c->onError( | ||||
|     [](void *r, AsyncClient *c, int8_t error) { | ||||
|       (void)c; | ||||
| @@ -341,10 +341,10 @@ bool AsyncWebServerRequest::_parseReqHead() { | ||||
| } | ||||
|  | ||||
| bool AsyncWebServerRequest::_parseReqHeader() { | ||||
|   int index = _temp.indexOf(':'); | ||||
|   if (index) { | ||||
|     String name(_temp.substring(0, index)); | ||||
|     String value(_temp.substring(index + 2)); | ||||
|   AsyncWebHeader header = AsyncWebHeader::parse(_temp); | ||||
|   if (header) { | ||||
|     const String &name = header.name(); | ||||
|     const String &value = header.value(); | ||||
|     if (name.equalsIgnoreCase(T_Host)) { | ||||
|       _host = value; | ||||
|     } else if (name.equalsIgnoreCase(T_Content_Type)) { | ||||
| @@ -392,9 +392,9 @@ bool AsyncWebServerRequest::_parseReqHeader() { | ||||
|         _reqconntype = RCT_EVENT; | ||||
|       } | ||||
|     } | ||||
|     _headers.emplace_back(name, value); | ||||
|     _headers.emplace_back(std::move(header)); | ||||
|   } | ||||
| #if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) | ||||
| #if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(LIBRETINY) | ||||
|   // Ancient PRI core does not have String::clear() method 8-() | ||||
|   _temp = emptyString; | ||||
| #else | ||||
| @@ -419,7 +419,7 @@ void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) { | ||||
|       _params.emplace_back(name, urlDecode(value), true); | ||||
|     } | ||||
|  | ||||
| #if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) | ||||
| #if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(LIBRETINY) | ||||
|     // Ancient PRI core does not have String::clear() method 8-() | ||||
|     _temp = emptyString; | ||||
| #else | ||||
|   | ||||
| @@ -75,7 +75,6 @@ class AsyncFileResponse : public AsyncAbstractResponse { | ||||
|  | ||||
| private: | ||||
|   File _content; | ||||
|   String _path; | ||||
|   void _setContentTypeFromPath(const String &path); | ||||
|  | ||||
| public: | ||||
|   | ||||
| @@ -6,17 +6,6 @@ | ||||
|  | ||||
| using namespace asyncsrv; | ||||
|  | ||||
| // Since ESP8266 does not link memchr by default, here's its implementation. | ||||
| void *memchr(void *ptr, int ch, size_t count) { | ||||
|   unsigned char *p = static_cast<unsigned char *>(ptr); | ||||
|   while (count--) { | ||||
|     if (*p++ == static_cast<unsigned char>(ch)) { | ||||
|       return --p; | ||||
|     } | ||||
|   } | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Abstract Response | ||||
|  * | ||||
| @@ -134,6 +123,30 @@ bool AsyncWebServerResponse::headerMustBePresentOnce(const String &name) { | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool AsyncWebServerResponse::addHeader(AsyncWebHeader &&header, bool replaceExisting) { | ||||
|   if (!header) { | ||||
|     return false;  // invalid header | ||||
|   } | ||||
|   for (auto i = _headers.begin(); i != _headers.end(); ++i) { | ||||
|     if (i->name().equalsIgnoreCase(header.name())) { | ||||
|       // header already set | ||||
|       if (replaceExisting) { | ||||
|         // remove, break and add the new one | ||||
|         _headers.erase(i); | ||||
|         break; | ||||
|       } else if (headerMustBePresentOnce(i->name())) {  // we can have only one header with that name | ||||
|         // do not update | ||||
|         return false; | ||||
|       } else { | ||||
|         break;  // accept multiple headers with the same name | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   // header was not found found, or existing one was removed | ||||
|   _headers.emplace_back(std::move(header)); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool AsyncWebServerResponse::addHeader(const char *name, const char *value, bool replaceExisting) { | ||||
|   for (auto i = _headers.begin(); i != _headers.end(); ++i) { | ||||
|     if (i->name().equalsIgnoreCase(name)) { | ||||
| @@ -595,6 +608,16 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t *data, size | ||||
|  * File Response | ||||
|  * */ | ||||
|  | ||||
| /** | ||||
|  * @brief Sets the content type based on the file path extension | ||||
|  * | ||||
|  * This method determines the appropriate MIME content type for a file based on its | ||||
|  * file extension. It supports both external content type functions (if available) | ||||
|  * and an internal mapping of common file extensions to their corresponding MIME types. | ||||
|  * | ||||
|  * @param path The file path string from which to extract the extension | ||||
|  * @note The method modifies the internal _contentType member variable | ||||
|  */ | ||||
| void AsyncFileResponse::_setContentTypeFromPath(const String &path) { | ||||
| #if HAVE_EXTERN_GET_Content_Type_FUNCTION | ||||
| #ifndef ESP8266 | ||||
| @@ -604,90 +627,138 @@ void AsyncFileResponse::_setContentTypeFromPath(const String &path) { | ||||
| #endif | ||||
|   _contentType = getContentType(path); | ||||
| #else | ||||
|   if (path.endsWith(T__html)) { | ||||
|   const char *cpath = path.c_str(); | ||||
|   const char *dot = strrchr(cpath, '.'); | ||||
|  | ||||
|   if (!dot) { | ||||
|     _contentType = T_application_octet_stream; | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (strcmp(dot, T__html) == 0 || strcmp(dot, T__htm) == 0) { | ||||
|     _contentType = T_text_html; | ||||
|   } else if (path.endsWith(T__htm)) { | ||||
|     _contentType = T_text_html; | ||||
|   } else if (path.endsWith(T__css)) { | ||||
|   } else if (strcmp(dot, T__css) == 0) { | ||||
|     _contentType = T_text_css; | ||||
|   } else if (path.endsWith(T__json)) { | ||||
|     _contentType = T_application_json; | ||||
|   } else if (path.endsWith(T__js)) { | ||||
|   } else if (strcmp(dot, T__js) == 0) { | ||||
|     _contentType = T_application_javascript; | ||||
|   } else if (path.endsWith(T__png)) { | ||||
|   } else if (strcmp(dot, T__json) == 0) { | ||||
|     _contentType = T_application_json; | ||||
|   } else if (strcmp(dot, T__png) == 0) { | ||||
|     _contentType = T_image_png; | ||||
|   } else if (path.endsWith(T__gif)) { | ||||
|     _contentType = T_image_gif; | ||||
|   } else if (path.endsWith(T__jpg)) { | ||||
|     _contentType = T_image_jpeg; | ||||
|   } else if (path.endsWith(T__ico)) { | ||||
|   } else if (strcmp(dot, T__ico) == 0) { | ||||
|     _contentType = T_image_x_icon; | ||||
|   } else if (path.endsWith(T__svg)) { | ||||
|   } else if (strcmp(dot, T__svg) == 0) { | ||||
|     _contentType = T_image_svg_xml; | ||||
|   } else if (path.endsWith(T__eot)) { | ||||
|     _contentType = T_font_eot; | ||||
|   } else if (path.endsWith(T__woff)) { | ||||
|     _contentType = T_font_woff; | ||||
|   } else if (path.endsWith(T__woff2)) { | ||||
|   } else if (strcmp(dot, T__jpg) == 0) { | ||||
|     _contentType = T_image_jpeg; | ||||
|   } else if (strcmp(dot, T__webp) == 0) { | ||||
|     _contentType = T_image_webp; | ||||
|   } else if (strcmp(dot, T__avif) == 0) { | ||||
|     _contentType = T_image_avif; | ||||
|   } else if (strcmp(dot, T__gif) == 0) { | ||||
|     _contentType = T_image_gif; | ||||
|   } else if (strcmp(dot, T__woff2) == 0) { | ||||
|     _contentType = T_font_woff2; | ||||
|   } else if (path.endsWith(T__ttf)) { | ||||
|   } else if (strcmp(dot, T__woff) == 0) { | ||||
|     _contentType = T_font_woff; | ||||
|   } else if (strcmp(dot, T__ttf) == 0) { | ||||
|     _contentType = T_font_ttf; | ||||
|   } else if (path.endsWith(T__xml)) { | ||||
|   } else if (strcmp(dot, T__xml) == 0) { | ||||
|     _contentType = T_text_xml; | ||||
|   } else if (path.endsWith(T__pdf)) { | ||||
|   } else if (strcmp(dot, T__pdf) == 0) { | ||||
|     _contentType = T_application_pdf; | ||||
|   } else if (path.endsWith(T__zip)) { | ||||
|     _contentType = T_application_zip; | ||||
|   } else if (path.endsWith(T__gz)) { | ||||
|     _contentType = T_application_x_gzip; | ||||
|   } else { | ||||
|   } else if (strcmp(dot, T__mp4) == 0) { | ||||
|     _contentType = T_video_mp4; | ||||
|   } else if (strcmp(dot, T__opus) == 0) { | ||||
|     _contentType = T_audio_opus; | ||||
|   } else if (strcmp(dot, T__webm) == 0) { | ||||
|     _contentType = T_video_webm; | ||||
|   } else if (strcmp(dot, T__txt) == 0) { | ||||
|     _contentType = T_text_plain; | ||||
|   } else { | ||||
|     _contentType = T_application_octet_stream; | ||||
|   } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Constructor for AsyncFileResponse that handles file serving with compression support | ||||
|  * | ||||
|  * This constructor creates an AsyncFileResponse object that can serve files from a filesystem, | ||||
|  * with automatic fallback to gzip-compressed versions if the original file is not found. | ||||
|  * It also handles ETag generation for caching and supports both inline and download modes. | ||||
|  * | ||||
|  * @param fs Reference to the filesystem object used to open files | ||||
|  * @param path Path to the file to be served (without compression extension) | ||||
|  * @param contentType MIME type of the file content (empty string for auto-detection) | ||||
|  * @param download If true, file will be served as download attachment; if false, as inline content | ||||
|  * @param callback Template processor callback for dynamic content processing | ||||
|  */ | ||||
| AsyncFileResponse::AsyncFileResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) | ||||
|   : AsyncAbstractResponse(callback) { | ||||
|   _code = 200; | ||||
|   _path = path; | ||||
|  | ||||
|   if (!download && !fs.exists(_path) && fs.exists(_path + T__gz)) { | ||||
|     _path = _path + T__gz; | ||||
|     addHeader(T_Content_Encoding, T_gzip, false); | ||||
|     _callback = nullptr;  // Unable to process zipped templates | ||||
|     _sendContentLength = true; | ||||
|     _chunked = false; | ||||
|   // Try to open the uncompressed version first | ||||
|   _content = fs.open(path, fs::FileOpenMode::read); | ||||
|   if (_content.available()) { | ||||
|     _contentLength = _content.size(); | ||||
|   } else { | ||||
|     // Try to open the compressed version (.gz) | ||||
|     String gzPath; | ||||
|     uint16_t pathLen = path.length(); | ||||
|     gzPath.reserve(pathLen + 3); | ||||
|     gzPath.concat(path); | ||||
|     gzPath.concat(asyncsrv::T__gz); | ||||
|     _content = fs.open(gzPath, fs::FileOpenMode::read); | ||||
|     _contentLength = _content.size(); | ||||
|  | ||||
|     if (_content.seek(_contentLength - 8)) { | ||||
|       addHeader(T_Content_Encoding, T_gzip, false); | ||||
|       _callback = nullptr;  // Unable to process zipped templates | ||||
|       _sendContentLength = true; | ||||
|       _chunked = false; | ||||
|  | ||||
|       // Add ETag and cache headers | ||||
|       uint8_t crcInTrailer[4]; | ||||
|       _content.read(crcInTrailer, sizeof(crcInTrailer)); | ||||
|       char serverETag[9]; | ||||
|       AsyncWebServerRequest::_getEtag(crcInTrailer, serverETag); | ||||
|       addHeader(T_ETag, serverETag, true); | ||||
|       addHeader(T_Cache_Control, T_no_cache, true); | ||||
|  | ||||
|       _content.seek(0); | ||||
|     } else { | ||||
|       // File is corrupted or invalid | ||||
|       _code = 404; | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   _content = fs.open(_path, fs::FileOpenMode::read); | ||||
|   _contentLength = _content.size(); | ||||
|  | ||||
|   if (strlen(contentType) == 0) { | ||||
|   if (*contentType == '\0') { | ||||
|     _setContentTypeFromPath(path); | ||||
|   } else { | ||||
|     _contentType = contentType; | ||||
|   } | ||||
|  | ||||
|   int filenameStart = path.lastIndexOf('/') + 1; | ||||
|   char buf[26 + path.length() - filenameStart]; | ||||
|   char *filename = (char *)path.c_str() + filenameStart; | ||||
|  | ||||
|   if (download) { | ||||
|     // set filename and force download | ||||
|     snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename); | ||||
|     // Extract filename from path and set as download attachment | ||||
|     int filenameStart = path.lastIndexOf('/') + 1; | ||||
|     char buf[26 + path.length() - filenameStart]; | ||||
|     char *filename = (char *)path.c_str() + filenameStart; | ||||
|     snprintf(buf, sizeof(buf), T_attachment, filename); | ||||
|     addHeader(T_Content_Disposition, buf, false); | ||||
|   } else { | ||||
|     // set filename and force rendering | ||||
|     snprintf_P(buf, sizeof(buf), PSTR("inline")); | ||||
|     // Serve file inline (display in browser) | ||||
|     addHeader(T_Content_Disposition, T_inline, false); | ||||
|   } | ||||
|   addHeader(T_Content_Disposition, buf, false); | ||||
|  | ||||
|   _code = 200; | ||||
| } | ||||
|  | ||||
| AsyncFileResponse::AsyncFileResponse(File content, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) | ||||
|   : AsyncAbstractResponse(callback) { | ||||
|   _code = 200; | ||||
|   _path = path; | ||||
|  | ||||
|   if (!download && String(content.name()).endsWith(T__gz) && !path.endsWith(T__gz)) { | ||||
|   if (String(content.name()).endsWith(T__gz) && !path.endsWith(T__gz)) { | ||||
|     addHeader(T_Content_Encoding, T_gzip, false); | ||||
|     _callback = nullptr;  // Unable to process gzipped templates | ||||
|     _sendContentLength = true; | ||||
| @@ -822,7 +893,7 @@ AsyncResponseStream::AsyncResponseStream(const char *contentType, size_t bufferS | ||||
|   _contentType = contentType; | ||||
|   // internal buffer will be null on allocation failure | ||||
|   _content = std::unique_ptr<cbuf>(new cbuf(bufferSize)); | ||||
|   if (_content->size() != bufferSize) { | ||||
|   if (bufferSize && _content->size() < bufferSize) { | ||||
| #ifdef ESP32 | ||||
|     log_e("Failed to allocate"); | ||||
| #endif | ||||
| @@ -840,6 +911,14 @@ size_t AsyncResponseStream::write(const uint8_t *data, size_t len) { | ||||
|   if (len > _content->room()) { | ||||
|     size_t needed = len - _content->room(); | ||||
|     _content->resizeAdd(needed); | ||||
|     // log a warning if allocation failed, but do not return: keep writing the bytes we can | ||||
|     // with _content->write: if len is more than the available size in the buffer, only | ||||
|     // the available size will be written | ||||
|     if (len > _content->room()) { | ||||
| #ifdef ESP32 | ||||
|       log_e("Failed to allocate"); | ||||
| #endif | ||||
|     } | ||||
|   } | ||||
|   size_t written = _content->write((const char *)data, len); | ||||
|   _contentLength += written; | ||||
|   | ||||
| @@ -4,10 +4,18 @@ | ||||
| #include "ESPAsyncWebServer.h" | ||||
| #include "WebHandlerImpl.h" | ||||
|  | ||||
| #if defined(ESP32) || defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(LIBRETINY) | ||||
| #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
| #include <ESP8266WiFi.h> | ||||
| #else | ||||
| #error Platform not supported | ||||
| #endif | ||||
|  | ||||
| using namespace asyncsrv; | ||||
|  | ||||
| bool ON_STA_FILTER(AsyncWebServerRequest *request) { | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   return WiFi.localIP() == request->client()->localIP(); | ||||
| #else | ||||
|   return false; | ||||
| @@ -15,7 +23,7 @@ bool ON_STA_FILTER(AsyncWebServerRequest *request) { | ||||
| } | ||||
|  | ||||
| bool ON_AP_FILTER(AsyncWebServerRequest *request) { | ||||
| #ifndef CONFIG_IDF_TARGET_ESP32H2 | ||||
| #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED || LT_ARD_HAS_WIFI | ||||
|   return WiFi.localIP() != request->client()->localIP(); | ||||
| #else | ||||
|   return false; | ||||
|   | ||||
							
								
								
									
										142
									
								
								src/literals.h
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								src/literals.h
									
									
									
									
									
								
							| @@ -10,39 +10,39 @@ static constexpr const char *empty = ""; | ||||
| static constexpr const char *T__opaque = "\", opaque=\""; | ||||
| static constexpr const char *T_100_CONTINUE = "100-continue"; | ||||
| static constexpr const char *T_13 = "13"; | ||||
| static constexpr const char *T_ACCEPT = "accept"; | ||||
| static constexpr const char *T_Accept_Ranges = "accept-ranges"; | ||||
| static constexpr const char *T_app_xform_urlencoded = "application/x-www-form-urlencoded"; | ||||
| static constexpr const char *T_AUTH = "authorization"; | ||||
| static constexpr const char *T_ACCEPT = "Accept"; | ||||
| static constexpr const char *T_Accept_Ranges = "Accept-Ranges"; | ||||
| static constexpr const char *T_attachment = "attachment; filename=\"%s\""; | ||||
| static constexpr const char *T_AUTH = "Authorization"; | ||||
| static constexpr const char *T_auth_nonce = "\", qop=\"auth\", nonce=\""; | ||||
| static constexpr const char *T_BASIC = "basic"; | ||||
| static constexpr const char *T_BASIC_REALM = "basic realm=\""; | ||||
| static constexpr const char *T_BEARER = "bearer"; | ||||
| static constexpr const char *T_BASIC = "Basic"; | ||||
| static constexpr const char *T_BASIC_REALM = "Basic realm=\""; | ||||
| static constexpr const char *T_BEARER = "Bearer"; | ||||
| static constexpr const char *T_BODY = "body"; | ||||
| static constexpr const char *T_Cache_Control = "cache-control"; | ||||
| static constexpr const char *T_Cache_Control = "Cache-Control"; | ||||
| static constexpr const char *T_chunked = "chunked"; | ||||
| static constexpr const char *T_close = "close"; | ||||
| static constexpr const char *T_cnonce = "cnonce"; | ||||
| static constexpr const char *T_Connection = "connection"; | ||||
| static constexpr const char *T_Content_Disposition = "content-disposition"; | ||||
| static constexpr const char *T_Content_Encoding = "content-encoding"; | ||||
| static constexpr const char *T_Content_Length = "content-length"; | ||||
| static constexpr const char *T_Content_Type = "content-type"; | ||||
| static constexpr const char *T_Content_Location = "content-location"; | ||||
| static constexpr const char *T_Cookie = "cookie"; | ||||
| static constexpr const char *T_CORS_ACAC = "access-control-allow-credentials"; | ||||
| static constexpr const char *T_CORS_ACAH = "access-control-allow-headers"; | ||||
| static constexpr const char *T_CORS_ACAM = "access-control-allow-methods"; | ||||
| static constexpr const char *T_CORS_ACAO = "access-control-allow-origin"; | ||||
| static constexpr const char *T_CORS_ACMA = "access-control-max-age"; | ||||
| static constexpr const char *T_CORS_O = "origin"; | ||||
| static constexpr const char *T_Connection = "Connection"; | ||||
| static constexpr const char *T_Content_Disposition = "Content-Disposition"; | ||||
| static constexpr const char *T_Content_Encoding = "Content-Encoding"; | ||||
| static constexpr const char *T_Content_Length = "Content-Length"; | ||||
| static constexpr const char *T_Content_Type = "Content-Type"; | ||||
| static constexpr const char *T_Content_Location = "Content-Location"; | ||||
| static constexpr const char *T_Cookie = "Cookie"; | ||||
| static constexpr const char *T_CORS_ACAC = "Access-Control-Allow-Credentials"; | ||||
| static constexpr const char *T_CORS_ACAH = "Access-Control-Allow-Headers"; | ||||
| static constexpr const char *T_CORS_ACAM = "Access-Control-Allow-Methods"; | ||||
| static constexpr const char *T_CORS_ACAO = "Access-Control-Allow-Origin"; | ||||
| static constexpr const char *T_CORS_ACMA = "Access-Control-Max-Age"; | ||||
| static constexpr const char *T_CORS_O = "Origin"; | ||||
| static constexpr const char *T_data_ = "data: "; | ||||
| static constexpr const char *T_Date = "date"; | ||||
| static constexpr const char *T_DIGEST = "digest"; | ||||
| static constexpr const char *T_DIGEST_ = "digest "; | ||||
| static constexpr const char *T_ETag = "etag"; | ||||
| static constexpr const char *T_Date = "Date"; | ||||
| static constexpr const char *T_DIGEST = "Digest"; | ||||
| static constexpr const char *T_DIGEST_ = "Digest "; | ||||
| static constexpr const char *T_ETag = "ETag"; | ||||
| static constexpr const char *T_event_ = "event: "; | ||||
| static constexpr const char *T_EXPECT = "expect"; | ||||
| static constexpr const char *T_EXPECT = "Expect"; | ||||
| static constexpr const char *T_FALSE = "false"; | ||||
| static constexpr const char *T_filename = "filename"; | ||||
| static constexpr const char *T_gzip = "gzip"; | ||||
| @@ -50,12 +50,13 @@ static constexpr const char *T_Host = "host"; | ||||
| static constexpr const char *T_HTTP_1_0 = "HTTP/1.0"; | ||||
| static constexpr const char *T_HTTP_100_CONT = "HTTP/1.1 100 Continue\r\n\r\n"; | ||||
| static constexpr const char *T_id__ = "id: "; | ||||
| static constexpr const char *T_IMS = "if-modified-since"; | ||||
| static constexpr const char *T_INM = "if-none-match"; | ||||
| static constexpr const char *T_IMS = "If-Modified-Since"; | ||||
| static constexpr const char *T_INM = "If-None-Match"; | ||||
| static constexpr const char *T_inline = "inline"; | ||||
| static constexpr const char *T_keep_alive = "keep-alive"; | ||||
| static constexpr const char *T_Last_Event_ID = "last-event-id"; | ||||
| static constexpr const char *T_Last_Modified = "last-modified"; | ||||
| static constexpr const char *T_LOCATION = "location"; | ||||
| static constexpr const char *T_Last_Event_ID = "Last-Event-ID"; | ||||
| static constexpr const char *T_Last_Modified = "Last-Modified"; | ||||
| static constexpr const char *T_LOCATION = "Location"; | ||||
| static constexpr const char *T_LOGIN_REQ = "Login Required"; | ||||
| static constexpr const char *T_MULTIPART_ = "multipart/"; | ||||
| static constexpr const char *T_name = "name"; | ||||
| @@ -69,21 +70,20 @@ static constexpr const char *T_realm = "realm"; | ||||
| static constexpr const char *T_realm__ = "realm=\""; | ||||
| static constexpr const char *T_response = "response"; | ||||
| static constexpr const char *T_retry_ = "retry: "; | ||||
| static constexpr const char *T_retry_after = "retry-after"; | ||||
| static constexpr const char *T_retry_after = "Retry-After"; | ||||
| static constexpr const char *T_nn = "\n\n"; | ||||
| static constexpr const char *T_rn = "\r\n"; | ||||
| static constexpr const char *T_rnrn = "\r\n\r\n"; | ||||
| static constexpr const char *T_Server = "server"; | ||||
| static constexpr const char *T_Transfer_Encoding = "transfer-encoding"; | ||||
| static constexpr const char *T_Server = "Server"; | ||||
| static constexpr const char *T_Transfer_Encoding = "Transfer-Encoding"; | ||||
| static constexpr const char *T_TRUE = "true"; | ||||
| static constexpr const char *T_UPGRADE = "upgrade"; | ||||
| static constexpr const char *T_UPGRADE = "Upgrade"; | ||||
| static constexpr const char *T_uri = "uri"; | ||||
| static constexpr const char *T_username = "username"; | ||||
| static constexpr const char *T_WS = "websocket"; | ||||
| static constexpr const char *T_WWW_AUTH = "www-authenticate"; | ||||
| static constexpr const char *T_WWW_AUTH = "WWW-Authenticate"; | ||||
|  | ||||
| // HTTP Methods | ||||
|  | ||||
| static constexpr const char *T_ANY = "ANY"; | ||||
| static constexpr const char *T_GET = "GET"; | ||||
| static constexpr const char *T_POST = "POST"; | ||||
| @@ -103,44 +103,55 @@ static constexpr const char *T_RCT_EVENT = "RCT_EVENT"; | ||||
| static constexpr const char *T_ERROR = "ERROR"; | ||||
|  | ||||
| // extensions & MIME-Types | ||||
| static constexpr const char *T__css = ".css"; | ||||
| static constexpr const char *T__eot = ".eot"; | ||||
| static constexpr const char *T__gif = ".gif"; | ||||
| static constexpr const char *T__gz = ".gz"; | ||||
| static constexpr const char *T__htm = ".htm"; | ||||
| static constexpr const char *T__html = ".html"; | ||||
| static constexpr const char *T__ico = ".ico"; | ||||
| static constexpr const char *T__jpg = ".jpg"; | ||||
| static constexpr const char *T__js = ".js"; | ||||
| static constexpr const char *T__json = ".json"; | ||||
| static constexpr const char *T__pdf = ".pdf"; | ||||
| static constexpr const char *T__png = ".png"; | ||||
| static constexpr const char *T__svg = ".svg"; | ||||
| static constexpr const char *T__ttf = ".ttf"; | ||||
| static constexpr const char *T__woff = ".woff"; | ||||
| static constexpr const char *T__woff2 = ".woff2"; | ||||
| static constexpr const char *T__xml = ".xml"; | ||||
| static constexpr const char *T__zip = ".zip"; | ||||
| static constexpr const char *T_application_javascript = "application/javascript"; | ||||
| static constexpr const char *T__avif = ".avif";    // AVIF: Highly compressed images. Compatible with all modern browsers. | ||||
| static constexpr const char *T__csv = ".csv";      // CSV: Data logging and configuration | ||||
| static constexpr const char *T__css = ".css";      // CSS: Styling for web interfaces | ||||
| static constexpr const char *T__gif = ".gif";      // GIF: Simple animations. Legacy support | ||||
| static constexpr const char *T__gz = ".gz";        // GZ: compressed files | ||||
| static constexpr const char *T__htm = ".htm";      // HTM: Web interface files | ||||
| static constexpr const char *T__html = ".html";    // HTML: Web interface files | ||||
| static constexpr const char *T__ico = ".ico";      // ICO: Favicons, system icons. Legacy support | ||||
| static constexpr const char *T__jpg = ".jpg";      // JPEG/JPG: Photos. Legacy support | ||||
| static constexpr const char *T__js = ".js";        // JavaScript: Interactive functionality | ||||
| static constexpr const char *T__json = ".json";    // JSON: Data exchange format | ||||
| static constexpr const char *T__mp4 = ".mp4";      // MP4: Proprietary format. Worse compression than WEBM. | ||||
| static constexpr const char *T__opus = ".opus";    // OPUS: High compression audio format | ||||
| static constexpr const char *T__pdf = ".pdf";      // PDF: Universal document format | ||||
| static constexpr const char *T__png = ".png";      // PNG: Icons, logos, transparency. Legacy support | ||||
| static constexpr const char *T__svg = ".svg";      // SVG: Vector graphics, icons (scalable, tiny file sizes) | ||||
| static constexpr const char *T__ttf = ".ttf";      // TTF: Font file. Legacy support | ||||
| static constexpr const char *T__txt = ".txt";      // TXT: Plain text files | ||||
| static constexpr const char *T__webm = ".webm";    // WebM: Video. Open source, optimized for web. Compatible with all modern browsers. | ||||
| static constexpr const char *T__webp = ".webp";    // WebP: Highly compressed images. Compatible with all modern browsers. | ||||
| static constexpr const char *T__woff = ".woff";    // WOFF: Font file. Legacy support | ||||
| static constexpr const char *T__woff2 = ".woff2";  // WOFF2: Better compression. Compatible with all modern browsers. | ||||
| static constexpr const char *T__xml = ".xml";      // XML: Configuration and data files | ||||
| static constexpr const char *T_application_javascript = "application/javascript";  // Obsolete type for JavaScript | ||||
| static constexpr const char *T_application_json = "application/json"; | ||||
| static constexpr const char *T_application_msgpack = "application/msgpack"; | ||||
| static constexpr const char *T_application_octet_stream = "application/octet-stream"; | ||||
| static constexpr const char *T_application_pdf = "application/pdf"; | ||||
| static constexpr const char *T_application_x_gzip = "application/x-gzip"; | ||||
| static constexpr const char *T_application_zip = "application/zip"; | ||||
| static constexpr const char *T_font_eot = "font/eot"; | ||||
| static constexpr const char *T_app_xform_urlencoded = "application/x-www-form-urlencoded"; | ||||
| static constexpr const char *T_audio_opus = "audio/opus"; | ||||
| static constexpr const char *T_font_ttf = "font/ttf"; | ||||
| static constexpr const char *T_font_woff = "font/woff"; | ||||
| static constexpr const char *T_font_woff2 = "font/woff2"; | ||||
| static constexpr const char *T_image_avif = "image/avif"; | ||||
| static constexpr const char *T_image_gif = "image/gif"; | ||||
| static constexpr const char *T_image_jpeg = "image/jpeg"; | ||||
| static constexpr const char *T_image_png = "image/png"; | ||||
| static constexpr const char *T_image_svg_xml = "image/svg+xml"; | ||||
| static constexpr const char *T_image_webp = "image/webp"; | ||||
| static constexpr const char *T_image_x_icon = "image/x-icon"; | ||||
| static constexpr const char *T_text_css = "text/css"; | ||||
| static constexpr const char *T_text_csv = "text/csv"; | ||||
| static constexpr const char *T_text_event_stream = "text/event-stream"; | ||||
| static constexpr const char *T_text_html = "text/html"; | ||||
| static constexpr const char *T_text_javascript = "text/javascript"; | ||||
| static constexpr const char *T_text_plain = "text/plain"; | ||||
| static constexpr const char *T_text_xml = "text/xml"; | ||||
| static constexpr const char *T_video_mp4 = "video/mp4"; | ||||
| static constexpr const char *T_video_webm = "video/webm"; | ||||
|  | ||||
| // Response codes | ||||
| static constexpr const char *T_HTTP_CODE_100 = "Continue"; | ||||
| @@ -175,7 +186,7 @@ static constexpr const char *T_HTTP_CODE_412 = "Precondition Failed"; | ||||
| static constexpr const char *T_HTTP_CODE_413 = "Request Entity Too Large"; | ||||
| static constexpr const char *T_HTTP_CODE_414 = "Request-URI Too Large"; | ||||
| static constexpr const char *T_HTTP_CODE_415 = "Unsupported Media Type"; | ||||
| static constexpr const char *T_HTTP_CODE_416 = "Requested range not satisfiable"; | ||||
| static constexpr const char *T_HTTP_CODE_416 = "Requested Range Not Satisfiable"; | ||||
| static constexpr const char *T_HTTP_CODE_417 = "Expectation Failed"; | ||||
| static constexpr const char *T_HTTP_CODE_429 = "Too Many Requests"; | ||||
| static constexpr const char *T_HTTP_CODE_500 = "Internal Server Error"; | ||||
| @@ -183,11 +194,14 @@ static constexpr const char *T_HTTP_CODE_501 = "Not Implemented"; | ||||
| static constexpr const char *T_HTTP_CODE_502 = "Bad Gateway"; | ||||
| static constexpr const char *T_HTTP_CODE_503 = "Service Unavailable"; | ||||
| static constexpr const char *T_HTTP_CODE_504 = "Gateway Time-out"; | ||||
| static constexpr const char *T_HTTP_CODE_505 = "HTTP Version not supported"; | ||||
| static constexpr const char *T_HTTP_CODE_505 = "HTTP Version Not Supported"; | ||||
| static constexpr const char *T_HTTP_CODE_ANY = "Unknown code"; | ||||
|  | ||||
| static constexpr const uint8_t T_only_once_headers_len = 11; | ||||
| static constexpr const char *T_only_once_headers[] = {T_Content_Length,    T_Content_Type,     T_Date,   T_ETag,    T_Last_Modified, T_LOCATION, T_retry_after, | ||||
|                                                       T_Transfer_Encoding, T_Content_Location, T_Server, T_WWW_AUTH}; | ||||
| static constexpr const char *T_only_once_headers[] = { | ||||
|   T_Accept_Ranges,     T_Content_Length,   T_Content_Type, T_Connection, T_CORS_ACAC, T_CORS_ACAH,     T_CORS_ACAM, T_CORS_ACAO, | ||||
|   T_CORS_ACMA,         T_CORS_O,           T_Date,         T_DIGEST,     T_ETag,      T_Last_Modified, T_LOCATION,  T_retry_after, | ||||
|   T_Transfer_Encoding, T_Content_Location, T_Server,       T_WWW_AUTH | ||||
| }; | ||||
| static constexpr size_t T_only_once_headers_len = sizeof(T_only_once_headers) / sizeof(T_only_once_headers[0]); | ||||
|  | ||||
| }  // namespace asyncsrv | ||||
|   | ||||
		Reference in New Issue
	
	Block a user