diff --git a/.clang-format b/.clang-format index 63f29c0be..8f4734879 100644 --- a/.clang-format +++ b/.clang-format @@ -1,22 +1,246 @@ -Language: Cpp +# Clang format version: 18.1.3 +--- BasedOnStyle: LLVM - AccessModifierOffset: -2 -AlignConsecutiveMacros: true -AllowAllArgumentsOnNextLine: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortIfStatementsOnASingleLine: false -AllowShortLambdasOnASingleLine: Inline -BinPackArguments: false -ColumnLimit: 0 +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 -FixNamespaceComments: false -IndentAccessModifiers: true +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 -IndentPPDirectives: BeforeHash +IndentExternBlock: NoIndent +IndentGotoLabels: false +IndentPPDirectives: None +IndentRequiresClause: false IndentWidth: 2 -NamespaceIndentation: All -PointerAlignment: Left -ReferenceAlignment: Left +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 diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 000000000..d26ee41f2 --- /dev/null +++ b/.codespellrc @@ -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 = diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..e22936cb1 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.github/workflows/pre-commit-status.yml b/.github/workflows/pre-commit-status.yml new file mode 100644 index 000000000..d00606684 --- /dev/null +++ b/.github/workflows/pre-commit-status.yml @@ -0,0 +1,64 @@ +# This needs to be in a separate workflow because it requires higher permissions than the calling workflow +name: Report Pre-commit Check Status + +on: + workflow_run: + workflows: [Pre-commit hooks] + types: + - completed + +permissions: + statuses: write + +jobs: + report-success: + name: Report pre-commit success + if: github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + steps: + - name: Report success + uses: actions/github-script@v7 + with: + script: | + const owner = '${{ github.repository_owner }}'; + const repo = '${{ github.repository }}'.split('/')[1]; + const sha = '${{ github.event.workflow_run.head_sha }}'; + core.debug(`owner: ${owner}`); + core.debug(`repo: ${repo}`); + core.debug(`sha: ${sha}`); + const { context: name, state } = (await github.rest.repos.createCommitStatus({ + context: 'Pre-commit checks', + description: 'Pre-commit checks successful', + owner: owner, + repo: repo, + sha: sha, + state: 'success', + target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}' + })).data; + core.info(`${name} is ${state}`); + + report-pending: + name: Report pre-commit pending + if: github.event.workflow_run.conclusion != 'success' + runs-on: ubuntu-latest + steps: + - name: Report pending + uses: actions/github-script@v7 + with: + script: | + const owner = '${{ github.repository_owner }}'; + const repo = '${{ github.repository }}'.split('/')[1]; + const sha = '${{ github.event.workflow_run.head_sha }}'; + core.debug(`owner: ${owner}`); + core.debug(`repo: ${repo}`); + core.debug(`sha: ${sha}`); + const { context: name, state } = (await github.rest.repos.createCommitStatus({ + context: 'Pre-commit checks', + description: 'The pre-commit checks need to be successful before merging', + owner: owner, + repo: repo, + sha: sha, + state: 'pending', + target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}' + })).data; + core.info(`${name} is ${state}`); diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 000000000..b92f283d1 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,80 @@ +name: Pre-commit hooks + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: + types: [opened, reopened, synchronize, labeled] + +concurrency: + group: pre-commit-${{github.event.pull_request.number || github.ref}} + cancel-in-progress: true + +jobs: + lint: + if: | + github.event_name != 'pull_request' || + contains(github.event.pull_request.labels.*.name, 'Status: Pending Merge') || + contains(github.event.pull_request.labels.*.name, 'Re-trigger Pre-commit') + + name: Check if fixes are needed + runs-on: ubuntu-latest + steps: + - name: Checkout latest commit + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Remove Label + if: contains(github.event.pull_request.labels.*.name, 'Re-trigger Pre-commit') + run: gh pr edit ${{ github.event.number }} --remove-label 'Re-trigger Pre-commit' + env: + GH_TOKEN: ${{ github.token }} + + - name: Set up Python 3 + uses: actions/setup-python@v5 + with: + cache-dependency-path: pre-commit.requirements.txt + cache: "pip" + python-version: "3.x" + + - name: Get Python version hash + run: | + echo "Using $(python -VV)" + echo "PY_HASH=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV + + - name: Restore pre-commit cache + uses: actions/cache/restore@v4 + id: restore-cache + with: + path: | + ~/.cache/pre-commit + key: pre-commit-${{ env.PY_HASH }}-${{ hashFiles('.pre-commit-config.yaml', '.github/workflows/pre-commit.yml', 'pre-commit.requirements.txt') }} + + - name: Install python dependencies + run: python -m pip install -r pre-commit.requirements.txt + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v42.0.2 + + - name: Run pre-commit hooks in changed files + run: pre-commit run --color=always --show-diff-on-failure --files ${{ steps.changed-files.outputs.all_changed_files }} + + - name: Save pre-commit cache + uses: actions/cache/save@v4 + if: ${{ always() && steps.restore-cache.outputs.cache-hit != 'true' }} + continue-on-error: true + with: + path: | + ~/.cache/pre-commit + key: ${{ steps.restore-cache.outputs.cache-primary-key }} + + - name: Push changes using pre-commit-ci-lite + uses: pre-commit-ci/lite-action@v1.1.0 + # Only push changes in PRs + if: ${{ always() && github.event_name == 'pull_request' }} + with: + msg: "ci(pre-commit): Apply automatic fixes" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..eb2d62e9f --- /dev/null +++ b/.pre-commit-config.yaml @@ -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$ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 0a5f91417..4fcdc2f52 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -6,7 +6,7 @@ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, +identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation. diff --git a/README.md b/README.md index 0c2d87ccf..548e08de1 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static Fi - (perf) `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full - (perf) `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client - (perf) `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client -- (perf) in-flight buffer control and queue congestion avoidance to help to improve parallel connections handling, high volume data transfers and mitigate poor implemeneted slow user-code callbacks delayes on connctions handling +- (perf) in-flight buffer control and queue congestion avoidance to help to improve parallel connections handling, high volume data transfers and mitigate poor implemented slow user-code callbacks delayes on connections handling - (perf) Code size improvements - (perf) Lot of code cleanup and optimizations - (perf) Performance improvements in terms of memory, speed and size @@ -78,7 +78,7 @@ We plan on creating a next major 4.x version that will: 3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with latest ArduinoJson So if you need one of these feature, you will have to stick with the current 3.x. -All releases we do will not cease to exist: all 3.x releases will stay in the release page. +All releases we do will not cease to exist: all 3.x releases will stay in the release page. That is why we have tags and a release cycle. Maintaining a library for ESP8266 and RP2040 has a real cost and clearly what we see is that most users helping are on ESP32. @@ -187,7 +187,7 @@ Here are some recommendations to avoid them and build-time flags you can change. In some bad network conditions you might consider increasing it. `CONFIG_ASYNC_TCP_QUEUE_SIZE` - defines the length of the queue for events related to connections handling. -Both the server and AsyncTCP library in this fork were optimized to control the queue automatically. Do NOT try blindly increasing the queue size, it does not help you in a way you might think it is. If you receive debug messages about queue throttling, try to optimize your server callbaks code to execute as fast as possible. +Both the server and AsyncTCP library in this fork were optimized to control the queue automatically. Do NOT try blindly increasing the queue size, it does not help you in a way you might think it is. If you receive debug messages about queue throttling, try to optimize your server callbacks code to execute as fast as possible. Read #165 thread, it might give you some hints. `CONFIG_ASYNC_TCP_RUNNING_CORE` - CPU core thread affinity that runs the queue events handling and executes server callbacks. Default is ANY core, so it means that for dualcore SoCs both cores could handle server activities. If your server's code is too heavy and unoptimized or you see that sometimes @@ -196,7 +196,7 @@ server might affect other network activities, you might consider to bind it to t `CONFIG_ASYNC_TCP_STACK_SIZE` - stack size for the thread that runs sever events and callbacks. Default is 16k that is a way too much waste for well-defined short async code or simple static file handling. You might want to cosider reducing it to 4-8k to same RAM usage. If you do not know what this is or not sure about your callback code demands - leave it as default, should be enough even for very hungry callbacks in most cases. > [!NOTE] -> This relates to ESP32 only, ESP8266 uses different ESPAsyncTCP lib that does not has this build options +> This relates to ESP32 only, ESP8266 uses different ESPAsyncTCP lib that does not has this build options I personally use the following configuration in my projects: @@ -300,7 +300,7 @@ AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlew ## How to use authentication with AsyncAuthenticationMiddleware -Do not use the `setUsername()` and `setPassword()` methods on the hanlders anymore. +Do not use the `setUsername()` and `setPassword()` methods on the handlers anymore. They are deprecated. These methods were causing a copy of the username and password for each handler, which is not efficient. @@ -529,8 +529,8 @@ request->version(); // uint8_t: 0 = HTTP/1.0, 1 = HTTP/1.1 request->method(); // enum: HTTP_GET, HTTP_POST, HTTP_DELETE, HTTP_PUT, HTTP_PATCH, HTTP_HEAD, HTTP_OPTIONS request->url(); // String: URL of the request (not including host, port or GET parameters) request->host(); // String: The requested host (can be used for virtual hosting) -request->contentType(); // String: ContentType of the request (not avaiable in Handler::canHandle) -request->contentLength(); // size_t: ContentLength of the request (not avaiable in Handler::canHandle) +request->contentType(); // String: ContentType of the request (not available in Handler::canHandle) +request->contentLength(); // size_t: ContentLength of the request (not available in Handler::canHandle) request->multipart(); // bool: True if the request has content type "multipart" ``` @@ -945,7 +945,7 @@ without memory problems. You need to create a file handler in outer function (to have a single one for request) but use it in a lambda. The catch is that the lambda has it's own lifecycle which may/will cause it's called after the original function is over thus the original file handle is destroyed. Using the -captured `&file` in the lambda then causes segfault (Hello, Exception 9!) and the whole ESP crashes. +captured `&file` in the lambda then causes segfault (Hello, Exception 9!) and the whole ESP crashes. By using this code, you tell the compiler to move the handle into the lambda so it won't be destroyed when outer function (that one where you call `request->send(response)`) ends. @@ -1176,7 +1176,7 @@ To serve files in a directory, the path to the files should specify a directory ```cpp // Serve files in directory "/www/" when request url starts with "/" -// Request to the root or none existing files will try to server the defualt +// Request to the root or none existing files will try to server the default // file name "index.htm" if exists server.serveStatic("/", SPIFFS, "/www/"); @@ -1220,7 +1220,7 @@ with "If-Modified-Since" header with the same value, instead of responding with // Update the date modified string every time files are updated server.serveStatic("/", SPIFFS, "/www/").setLastModified("Mon, 20 Jun 2016 14:00:00 GMT"); -//*** Chage last modified value at a later stage *** +//*** Change last modified value at a later stage *** // During setup - read last modified value from config or EEPROM String date_modified = loadDateModified(); @@ -1253,7 +1253,7 @@ server.serveStatic("/", SPIFFS, "/www/").setTemplateProcessor(processor); #### Serving static files by custom handling -It may happen your static files are too big and the ESP will crash the request before it sends the whole file. +It may happen your static files are too big and the ESP will crash the request before it sends the whole file. In that case, you can handle static files with custom file serving through not found handler. This code below is more-or-less equivalent to this: diff --git a/examples/CaptivePortal/CaptivePortal.ino b/examples/CaptivePortal/CaptivePortal.ino index a813a6465..e2badac5b 100644 --- a/examples/CaptivePortal/CaptivePortal.ino +++ b/examples/CaptivePortal/CaptivePortal.ino @@ -1,13 +1,13 @@ #include #ifdef ESP32 - #include - #include +#include +#include #elif defined(ESP8266) - #include - #include +#include +#include #elif defined(TARGET_RP2040) - #include - #include +#include +#include #endif #include "ESPAsyncWebServer.h" @@ -15,22 +15,22 @@ DNSServer dnsServer; AsyncWebServer server(80); class CaptiveRequestHandler : public AsyncWebHandler { - public: - bool canHandle(__unused AsyncWebServerRequest* request) const override { - return true; - } - - void handleRequest(AsyncWebServerRequest* request) { - AsyncResponseStream* response = request->beginResponseStream("text/html"); - response->print("Captive Portal"); - response->print("

This is our captive portal front page.

"); - response->printf("

You were trying to reach: http://%s%s

", request->host().c_str(), request->url().c_str()); +public: + bool canHandle(__unused AsyncWebServerRequest *request) const override { + return true; + } + + void handleRequest(AsyncWebServerRequest *request) { + AsyncResponseStream *response = request->beginResponseStream("text/html"); + response->print("Captive Portal"); + response->print("

This is our captive portal front page.

"); + response->printf("

You were trying to reach: http://%s%s

", request->host().c_str(), request->url().c_str()); #ifndef CONFIG_IDF_TARGET_ESP32H2 - response->printf("

Try opening this link instead

", WiFi.softAPIP().toString().c_str()); + response->printf("

Try opening this link instead

", WiFi.softAPIP().toString().c_str()); #endif - response->print(""); - request->send(response); - } + response->print(""); + request->send(response); + } }; void setup() { @@ -41,14 +41,13 @@ void setup() { #ifndef CONFIG_IDF_TARGET_ESP32H2 if (!WiFi.softAP("esp-captive")) { Serial.println("Soft AP creation failed."); - while (1) - ; + while (1); } dnsServer.start(53, "*", WiFi.softAPIP()); #endif - server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER); // only when requested from AP + server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER); // only when requested from AP // more handlers... server.begin(); } diff --git a/examples/Filters/Filters.ino b/examples/Filters/Filters.ino index c29103bb1..6a83d75da 100644 --- a/examples/Filters/Filters.ino +++ b/examples/Filters/Filters.ino @@ -2,14 +2,14 @@ #include #ifdef ESP32 - #include - #include +#include +#include #elif defined(ESP8266) - #include - #include +#include +#include #elif defined(TARGET_RP2040) - #include - #include +#include +#include #endif #include "ESPAsyncWebServer.h" @@ -17,22 +17,22 @@ DNSServer dnsServer; AsyncWebServer server(80); class CaptiveRequestHandler : public AsyncWebHandler { - public: - bool canHandle(__unused AsyncWebServerRequest* request) const override { - return true; - } +public: + bool canHandle(__unused AsyncWebServerRequest *request) const override { + return true; + } - void handleRequest(AsyncWebServerRequest* request) override { - AsyncResponseStream* response = request->beginResponseStream("text/html"); - response->print("Captive Portal"); - response->print("

This is out captive portal front page.

"); - response->printf("

You were trying to reach: http://%s%s

", request->host().c_str(), request->url().c_str()); + void handleRequest(AsyncWebServerRequest *request) override { + AsyncResponseStream *response = request->beginResponseStream("text/html"); + response->print("Captive Portal"); + response->print("

This is out captive portal front page.

"); + response->printf("

You were trying to reach: http://%s%s

", request->host().c_str(), request->url().c_str()); #ifndef CONFIG_IDF_TARGET_ESP32H2 - response->printf("

Try opening this link instead

", WiFi.softAPIP().toString().c_str()); + response->printf("

Try opening this link instead

", WiFi.softAPIP().toString().c_str()); #endif - response->print(""); - request->send(response); - } + response->print(""); + request->send(response); + } }; bool hit1 = false; @@ -42,49 +42,55 @@ void setup() { Serial.begin(115200); server - .on("/", HTTP_GET, [](AsyncWebServerRequest* request) { - Serial.println("Captive portal request..."); + .on( + "/", HTTP_GET, + [](AsyncWebServerRequest *request) { + Serial.println("Captive portal request..."); #ifndef CONFIG_IDF_TARGET_ESP32H2 - Serial.println("WiFi.localIP(): " + WiFi.localIP().toString()); + Serial.println("WiFi.localIP(): " + WiFi.localIP().toString()); #endif - Serial.println("request->client()->localIP(): " + request->client()->localIP().toString()); + Serial.println("request->client()->localIP(): " + request->client()->localIP().toString()); #if ESP_IDF_VERSION_MAJOR >= 5 - #ifndef CONFIG_IDF_TARGET_ESP32H2 - Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type())); - #endif - Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type())); +#ifndef CONFIG_IDF_TARGET_ESP32H2 + 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 - 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()); + 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()); #endif - request->send(200, "text/plain", "This is the captive portal"); - hit1 = true; - }) + request->send(200, "text/plain", "This is the captive portal"); + hit1 = true; + } + ) .setFilter(ON_AP_FILTER); server - .on("/", HTTP_GET, [](AsyncWebServerRequest* request) { - Serial.println("Website request..."); + .on( + "/", HTTP_GET, + [](AsyncWebServerRequest *request) { + Serial.println("Website request..."); #ifndef CONFIG_IDF_TARGET_ESP32H2 - Serial.println("WiFi.localIP(): " + WiFi.localIP().toString()); + Serial.println("WiFi.localIP(): " + WiFi.localIP().toString()); #endif - Serial.println("request->client()->localIP(): " + request->client()->localIP().toString()); + Serial.println("request->client()->localIP(): " + request->client()->localIP().toString()); #if ESP_IDF_VERSION_MAJOR >= 5 - #ifndef CONFIG_IDF_TARGET_ESP32H2 - Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type())); - #endif - Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type())); +#ifndef CONFIG_IDF_TARGET_ESP32H2 + 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 - 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()); + 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()); #endif - request->send(200, "text/plain", "This is the website"); - hit2 = true; - }) + request->send(200, "text/plain", "This is the website"); + hit2 = true; + } + ) .setFilter(ON_STA_FILTER); // assert(WiFi.softAP("esp-captive-portal")); @@ -120,5 +126,4 @@ void setup() { // ESP.restart(); } -void loop() { -} +void loop() {} diff --git a/examples/Issue162/Issue162.ino b/examples/Issue162/Issue162.ino index adc82d951..e86d9c550 100644 --- a/examples/Issue162/Issue162.ino +++ b/examples/Issue162/Issue162.ino @@ -7,10 +7,10 @@ */ #include #ifdef ESP8266 - #include +#include #endif #ifdef ESP32 - #include +#include #endif #include @@ -18,7 +18,7 @@ AsyncWebServer server(80); AsyncWebSocket ws("/ws"); -void onWsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { +void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { if (type == WS_EVT_CONNECT) { Serial.printf("Client #%" PRIu32 " connected.\n", client->id()); } else if (type == WS_EVT_DISCONNECT) { @@ -43,18 +43,18 @@ void setup() { ws.onEvent(onWsEvent); server.addHandler(&ws); - server.on("/close_all_ws_clients", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest* request) { + server.on("/close_all_ws_clients", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest *request) { Serial.println("Closing all WebSocket clients..."); ws.closeAll(); request->send(200, "application/json", "{\"status\":\"all clients closed\"}"); }); - server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/html", R"rawliteral( - -

+

)rawliteral"); diff --git a/examples/Issue85/Issue85.ino b/examples/Issue85/Issue85.ino index fa2aa5547..501a1f75c 100644 --- a/examples/Issue85/Issue85.ino +++ b/examples/Issue85/Issue85.ino @@ -7,10 +7,10 @@ */ #include #ifdef ESP8266 - #include +#include #endif #ifdef ESP32 - #include +#include #endif #include @@ -35,7 +35,7 @@ void connect_wifi() { // Serial.println(WiFi.localIP()); } -void notFound(AsyncWebServerRequest* request) { +void notFound(AsyncWebServerRequest *request) { request->send(404, "text/plain", "Not found"); } @@ -43,7 +43,7 @@ AsyncWebServer server(80); AsyncWebSocket ws("/ws"); // initial stack -char* stack_start; +char *stack_start; void printStackSize() { char stack; @@ -60,22 +60,24 @@ void printStackSize() { Serial.println(); } -void onWsEventEmpty(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { +void onWsEventEmpty(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { msgCount++; Serial.printf("count: %d\n", msgCount); times.push_back(millis()); - while (times.size() > window) + while (times.size() > window) { times.pop_front(); - if (times.size() == window) + } + if (times.size() == window) { Serial.printf("%f req/s\n", 1000.0 * window / (times.back() - times.front())); + } printStackSize(); client->text("PONG"); } -void serve_upload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) { +void serve_upload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { Serial.print("> onUpload "); Serial.print("index: "); Serial.print(index); @@ -101,7 +103,13 @@ void setup() { ws.onEvent(onWsEventEmpty); server.addHandler(&ws); - server.on("/upload", HTTP_POST, [](AsyncWebServerRequest* request) { request->send(200); }, serve_upload); + server.on( + "/upload", HTTP_POST, + [](AsyncWebServerRequest *request) { + request->send(200); + }, + serve_upload + ); server.begin(); Serial.println("Server started"); @@ -124,4 +132,4 @@ void loop() { // msg += 'T'; // } // } -} \ No newline at end of file +} diff --git a/examples/SSE_perftest/SSE_perftest.ino b/examples/SSE_perftest/SSE_perftest.ino index ecabd019c..b05e00eac 100644 --- a/examples/SSE_perftest/SSE_perftest.ino +++ b/examples/SSE_perftest/SSE_perftest.ino @@ -6,27 +6,27 @@ #include #ifdef ESP32 - #include - #include +#include +#include #elif defined(ESP8266) - #include - #include +#include +#include #elif defined(TARGET_RP2040) - #include - #include +#include +#include #endif #include #if __has_include("ArduinoJson.h") - #include - #include - #include +#include +#include +#include #endif #include -const char* htmlContent PROGMEM = R"( +const char *htmlContent PROGMEM = R"( @@ -86,7 +86,7 @@ const char* htmlContent PROGMEM = R"( )"; -const char* staticContent PROGMEM = R"( +const char *staticContent PROGMEM = R"( @@ -103,8 +103,8 @@ AsyncEventSource events("/events"); ///////////////////////////////////////////////////////////////////////////////////////////////////// -const char* PARAM_MESSAGE PROGMEM = "message"; -const char* SSE_HTLM PROGMEM = R"( +const char *PARAM_MESSAGE PROGMEM = "message"; +const char *SSE_HTLM PROGMEM = R"( @@ -135,12 +135,13 @@ const char* SSE_HTLM PROGMEM = R"( )"; -static const char* SSE_MSG = R"(Alice felt that this could not be denied, so she tried another question. 'What sort of people live about here?' 'In THAT direction,' the Cat said, waving its right paw round, 'lives a Hatter: and in THAT direction,' waving the other paw, 'lives a March Hare. Visit either you like: they're both mad.' +static const char *SSE_MSG = + R"(Alice felt that this could not be denied, so she tried another question. 'What sort of people live about here?' 'In THAT direction,' the Cat said, waving its right paw round, 'lives a Hatter: and in THAT direction,' waving the other paw, 'lives a March Hare. Visit either you like: they're both mad.' 'But I don't want to go among mad people,' Alice remarked. 'Oh, you can't help that,' said the Cat: 'we're all mad here. I'm mad. You're mad.' 'How do you know I'm mad?' said Alice. 'You must be,' said the Cat, `or you wouldn't have come here.' Alice didn't think that proved it at all; however, she went on 'And how do you know that you're mad?' 'To begin with,' said the Cat, 'a dog's not mad. You grant that?' )"; -void notFound(AsyncWebServerRequest* request) { +void notFound(AsyncWebServerRequest *request) { request->send(404, "text/plain", "Not found"); } @@ -167,18 +168,18 @@ void setup() { WiFi.softAP("esp-captive"); #endif - server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/html", staticContent); }); - events.onConnect([](AsyncEventSourceClient* client) { + events.onConnect([](AsyncEventSourceClient *client) { if (client->lastId()) { Serial.printf("SSE Client reconnected! Last message ID that it gat is: %" PRIu32 "\n", client->lastId()); } client->send("hello!", NULL, millis(), 1000); }); - server.on("/sse", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/sse", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/html", SSE_HTLM); }); @@ -192,17 +193,18 @@ void setup() { uint32_t lastSSE = 0; uint32_t deltaSSE = 25; -uint32_t messagesSSE = 4; // how many messages to q each time +uint32_t messagesSSE = 4; // how many messages to q each time uint32_t sse_disc{0}, sse_enq{0}, sse_penq{0}, sse_second{0}; AsyncEventSource::SendStatus enqueue() { AsyncEventSource::SendStatus state = events.send(SSE_MSG, "message"); - if (state == AsyncEventSource::SendStatus::DISCARDED) + if (state == AsyncEventSource::SendStatus::DISCARDED) { ++sse_disc; - else if (state == AsyncEventSource::SendStatus::ENQUEUED) { + } else if (state == AsyncEventSource::SendStatus::ENQUEUED) { ++sse_enq; - } else + } else { ++sse_penq; + } return state; } @@ -246,10 +248,11 @@ void loop() { Serial.println(s); // if we see discards or partial enqueues, let's decrease message rate, else - increase. So that we can come to a max sustained message rate - if (sse_disc || sse_penq) + if (sse_disc || sse_penq) { ++deltaSSE; - else if (deltaSSE > 5) + } else if (deltaSSE > 5) { --deltaSSE; + } sse_disc = sse_enq = sse_penq = 0; sse_second = now; diff --git a/examples/SimpleServer/SimpleServer.ino b/examples/SimpleServer/SimpleServer.ino index 9464c1c10..a17c46a59 100644 --- a/examples/SimpleServer/SimpleServer.ino +++ b/examples/SimpleServer/SimpleServer.ino @@ -7,27 +7,27 @@ #include #ifdef ESP32 - #include - #include +#include +#include #elif defined(ESP8266) - #include - #include +#include +#include #elif defined(TARGET_RP2040) - #include - #include +#include +#include #endif #include #if __has_include("ArduinoJson.h") - #include - #include - #include +#include +#include +#include #endif #include -const char* htmlContent PROGMEM = R"( +const char *htmlContent PROGMEM = R"( @@ -89,7 +89,7 @@ const char* htmlContent PROGMEM = R"( const size_t htmlContentLength = strlen_P(htmlContent); -const char* staticContent PROGMEM = R"( +const char *staticContent PROGMEM = R"( @@ -133,7 +133,7 @@ AsyncAuthenticationMiddleware digestAuth; AsyncAuthenticationMiddleware digestAuthHash; // complex authentication which adds request attributes for the next middlewares and handler -AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlewareNext next) { +AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest *request, ArMiddlewareNext next) { if (!request->authenticate("user", "password")) { return request->requestAuthentication(); } @@ -145,14 +145,16 @@ AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlew request->getResponse()->addHeader("X-Rate-Limit", "200"); }); -AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest* request) { return request->getAttribute("role") == "staff"; }); +AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest *request) { + return request->getAttribute("role") == "staff"; +}); int wsClients = 0; ///////////////////////////////////////////////////////////////////////////////////////////////////// -const char* PARAM_MESSAGE PROGMEM = "message"; -const char* SSE_HTLM PROGMEM = R"( +const char *PARAM_MESSAGE PROGMEM = "message"; +const char *SSE_HTLM PROGMEM = R"( @@ -184,8 +186,8 @@ const char* SSE_HTLM PROGMEM = R"( )"; #if __has_include("ArduinoJson.h") -AsyncCallbackJsonWebHandler* jsonHandler = new AsyncCallbackJsonWebHandler("/json2"); -AsyncCallbackMessagePackWebHandler* msgPackHandler = new AsyncCallbackMessagePackWebHandler("/msgpack2"); +AsyncCallbackJsonWebHandler *jsonHandler = new AsyncCallbackJsonWebHandler("/json2"); +AsyncCallbackMessagePackWebHandler *msgPackHandler = new AsyncCallbackMessagePackWebHandler("/msgpack2"); #endif static const char characters[] = "0123456789ABCDEF"; @@ -236,7 +238,7 @@ void setup() { } // curl -v -X GET http://192.168.4.1/handler-not-sending-response - server.on("/handler-not-sending-response", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/handler-not-sending-response", HTTP_GET, [](AsyncWebServerRequest *request) { // handler forgot to send a response to the client => 501 Not Implemented }); @@ -244,7 +246,7 @@ void setup() { // the previous one will be deleted. // response sending happens when the handler returns. // curl -v -X GET http://192.168.4.1/replace - server.on("/replace", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/replace", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/plain", "Hello, world"); // oups! finally we want to send a different response request->send(400, "text/plain", "validation error"); @@ -258,21 +260,24 @@ void setup() { /////////////////////////////////////////////////////////////////////// // curl -v -X GET -H "x-remove-me: value" http://192.168.4.1/headers - server.on("/headers", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/headers", HTTP_GET, [](AsyncWebServerRequest *request) { Serial.printf("Request Headers:\n"); - for (auto& h : request->getHeaders()) + for (auto &h : request->getHeaders()) { Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str()); + } // remove x-remove-me header request->removeHeader("x-remove-me"); Serial.printf("Request Headers:\n"); - for (auto& h : request->getHeaders()) + for (auto &h : request->getHeaders()) { Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str()); + } - std::vector headers; + std::vector headers; request->getHeaderNames(headers); - for (auto& h : headers) + for (auto &h : headers) { Serial.printf("Request Header Name: %s\n", h); + } request->send(200); }); @@ -291,7 +296,7 @@ void setup() { basicAuth.generateHash(); basicAuthHash.setUsername("admin"); - basicAuthHash.setPasswordHash("YWRtaW46YWRtaW4="); // BASE64(admin:admin) + basicAuthHash.setPasswordHash("YWRtaW46YWRtaW4="); // BASE64(admin:admin) basicAuthHash.setRealm("MyApp"); basicAuthHash.setAuthFailureMessage("Authentication failed"); basicAuthHash.setAuthType(AsyncAuthType::AUTH_BASIC); @@ -304,7 +309,7 @@ void setup() { digestAuth.generateHash(); digestAuthHash.setUsername("admin"); - digestAuthHash.setPasswordHash("f499b71f9a36d838b79268e145e132f7"); // MD5(user:realm:pass) + digestAuthHash.setPasswordHash("f499b71f9a36d838b79268e145e132f7"); // MD5(user:realm:pass) digestAuthHash.setRealm("MyApp"); digestAuthHash.setAuthFailureMessage("Authentication failed"); digestAuthHash.setAuthType(AsyncAuthType::AUTH_DIGEST); @@ -330,7 +335,7 @@ void setup() { // Test CORS preflight request // curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/middleware/cors - server.on("/middleware/cors", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/middleware/cors", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/plain", "Hello, world!"); }); @@ -338,9 +343,10 @@ void setup() { // - requestLogger will log the incoming headers (including x-remove-me) // - headerFilter will remove x-remove-me header // - handler will log the remaining headers - server.on("/middleware/test-header-filter", HTTP_GET, [](AsyncWebServerRequest* request) { - for (auto& h : request->getHeaders()) + server.on("/middleware/test-header-filter", HTTP_GET, [](AsyncWebServerRequest *request) { + for (auto &h : request->getHeaders()) { Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str()); + } request->send(200); }); @@ -348,57 +354,82 @@ void setup() { // - requestLogger will log the incoming headers (including x-keep-me) // - headerFree will remove all headers except x-keep-me and host // - handler will log the remaining headers (x-keep-me and host) - server.on("/middleware/test-header-free", HTTP_GET, [](AsyncWebServerRequest* request) { - for (auto& h : request->getHeaders()) - Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str()); - request->send(200); - }) + server + .on( + "/middleware/test-header-free", HTTP_GET, + [](AsyncWebServerRequest *request) { + for (auto &h : request->getHeaders()) { + Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str()); + } + request->send(200); + } + ) .addMiddleware(&headerFree); // basic authentication method // curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin http://192.168.4.1/middleware/auth-basic - server.on("/middleware/auth-basic", HTTP_GET, [](AsyncWebServerRequest* request) { - request->send(200, "text/plain", "Hello, world!"); - }) + server + .on( + "/middleware/auth-basic", HTTP_GET, + [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "Hello, world!"); + } + ) .addMiddleware(&basicAuth); // basic authentication method with hash // curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin http://192.168.4.1/middleware/auth-basic-hash - server.on("/middleware/auth-basic-hash", HTTP_GET, [](AsyncWebServerRequest* request) { - request->send(200, "text/plain", "Hello, world!"); - }) + server + .on( + "/middleware/auth-basic-hash", HTTP_GET, + [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "Hello, world!"); + } + ) .addMiddleware(&basicAuthHash); // digest authentication // curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin --digest http://192.168.4.1/middleware/auth-digest - server.on("/middleware/auth-digest", HTTP_GET, [](AsyncWebServerRequest* request) { - request->send(200, "text/plain", "Hello, world!"); - }) + server + .on( + "/middleware/auth-digest", HTTP_GET, + [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "Hello, world!"); + } + ) .addMiddleware(&digestAuth); // digest authentication with hash // curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin --digest http://192.168.4.1/middleware/auth-digest-hash - server.on("/middleware/auth-digest-hash", HTTP_GET, [](AsyncWebServerRequest* request) { - request->send(200, "text/plain", "Hello, world!"); - }) + server + .on( + "/middleware/auth-digest-hash", HTTP_GET, + [](AsyncWebServerRequest *request) { + request->send(200, "text/plain", "Hello, world!"); + } + ) .addMiddleware(&digestAuthHash); // test digest auth with cors // curl -v -X GET -H "origin: http://192.168.4.1" --digest -u user:password http://192.168.4.1/middleware/auth-custom - server.on("/middleware/auth-custom", HTTP_GET, [](AsyncWebServerRequest* request) { - String buffer = "Hello "; - buffer.concat(request->getAttribute("user")); - buffer.concat(" with role: "); - buffer.concat(request->getAttribute("role")); - request->send(200, "text/plain", buffer); - }) + server + .on( + "/middleware/auth-custom", HTTP_GET, + [](AsyncWebServerRequest *request) { + String buffer = "Hello "; + buffer.concat(request->getAttribute("user")); + buffer.concat(" with role: "); + buffer.concat(request->getAttribute("role")); + request->send(200, "text/plain", buffer); + } + ) .addMiddlewares({&complexAuth, &authz}); /////////////////////////////////////////////////////////////////////// // curl -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/redirect // curl -v -X POST -H "origin: http://192.168.4.1" http://192.168.4.1/redirect - server.on("/redirect", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest* request) { + server.on("/redirect", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest *request) { request->redirect("/"); }); @@ -406,7 +437,7 @@ void setup() { // > brew install autocannon // > autocannon -c 10 -w 10 -d 20 http://192.168.4.1 // > autocannon -c 16 -w 16 -d 20 http://192.168.4.1 - server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/html", htmlContent); }); @@ -418,13 +449,13 @@ void setup() { // ServeStatic static is used to serve static output which never changes over time. // This special endpoints automatyically adds caching headers. - // If a template processor is used, it must enure that the outputed content will always be the ame over time and never changes. + // If a template processor is used, it must ensure that the outputted content will always be the same over time and never changes. // Otherwise, do not use serveStatic. // Example below: IP never changes. // curl -v -X GET http://192.168.4.1/index-static.html - server.serveStatic("/index-static.html", LittleFS, "/index.html").setTemplateProcessor([](const String& var) -> String { + server.serveStatic("/index-static.html", LittleFS, "/index.html").setTemplateProcessor([](const String &var) -> String { if (var == "IP") { - // for CI, commented out since H2 board doesn ot support WiFi + // for CI, commented out since H2 board does not support WiFi // return WiFi.localIP().toString(); // return WiFi.softAPIP().toString(); return "127.0.0..1"; @@ -435,10 +466,11 @@ void setup() { // to serve a template with dynamic content (output changes over time), use normal // Example below: content changes over tinme do not use serveStatic. // curl -v -X GET http://192.168.4.1/index-dynamic.html - server.on("/index-dynamic.html", HTTP_GET, [](AsyncWebServerRequest* request) { - request->send(LittleFS, "/index.html", "text/html", false, [](const String& var) -> String { - if (var == "IP") + server.on("/index-dynamic.html", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(LittleFS, "/index.html", "text/html", false, [](const String &var) -> String { + if (var == "IP") { return String(random(0, 1000)); + } return emptyString; }); }); @@ -446,7 +478,7 @@ void setup() { // Issue #14: assert failed: tcp_update_rcv_ann_wnd (needs help to test fix) // > curl -v http://192.168.4.1/issue-14 pinMode(4, OUTPUT); - server.on("/issue-14", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/issue-14", HTTP_GET, [](AsyncWebServerRequest *request) { digitalWrite(4, HIGH); request->send(LittleFS, "/index.txt", "text/pain"); delay(500); @@ -457,10 +489,11 @@ void setup() { Chunked encoding test: sends 16k of characters. ❯ curl -N -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/chunk */ - server.on("/chunk", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [](uint8_t* buffer, size_t maxLen, size_t index) -> size_t { - if (index >= 16384) + server.on("/chunk", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest *request) { + AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + if (index >= 16384) { return 0; + } memset(buffer, characters[charactersIndex], maxLen); charactersIndex = (charactersIndex + 1) % sizeof(characters); return maxLen; @@ -470,7 +503,7 @@ void setup() { // curl -N -v -X GET http://192.168.4.1/chunked.html --output - // curl -N -v -X GET -H "if-none-match: 4272" http://192.168.4.1/chunked.html --output - - server.on("/chunked.html", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/chunked.html", HTTP_GET, [](AsyncWebServerRequest *request) { String len = String(htmlContentLength); if (request->header(asyncsrv::T_INM) == len) { @@ -478,7 +511,7 @@ void setup() { return; } - AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [](uint8_t* buffer, size_t maxLen, size_t index) -> size_t { + AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { Serial.printf("%u / %u\n", index, htmlContentLength); // finished ? @@ -503,15 +536,16 @@ void setup() { }); // time curl -N -v -G -d 'd=3000' -d 'l=10000' http://192.168.4.1/slow.html --output - - server.on("/slow.html", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/slow.html", HTTP_GET, [](AsyncWebServerRequest *request) { uint32_t d = request->getParam("d")->value().toInt(); uint32_t l = request->getParam("l")->value().toInt(); Serial.printf("d = %" PRIu32 ", l = %" PRIu32 "\n", d, l); - AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [d, l](uint8_t* buffer, size_t maxLen, size_t index) -> size_t { + AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [d, l](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { Serial.printf("%u\n", index); // finished ? - if (index >= l) + if (index >= l) { return 0; + } // slow down the task by 2 seconds // to simulate some heavy processing, like SD card reading @@ -534,14 +568,14 @@ void setup() { Accept-Ranges: bytes */ // Ref: https://github.com/mathieucarbou/ESPAsyncWebServer/pull/80 - server.on("/download", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/download", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest *request) { if (request->method() == HTTP_HEAD) { - AsyncWebServerResponse* response = request->beginResponse(200, "application/octet-stream"); + AsyncWebServerResponse *response = request->beginResponse(200, "application/octet-stream"); response->addHeader(asyncsrv::T_Accept_Ranges, "bytes"); response->addHeader(asyncsrv::T_Content_Length, 10); - response->setContentLength(1024); // overrides previous one + response->setContentLength(1024); // overrides previous one response->addHeader(asyncsrv::T_Content_Type, "foo"); - response->setContentType("application/octet-stream"); // overrides previous one + response->setContentType("application/octet-stream"); // overrides previous one // ... request->send(response); } else { @@ -550,7 +584,7 @@ void setup() { }); // Send a GET request to /get?message= - server.on("/get", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/get", HTTP_GET, [](AsyncWebServerRequest *request) { String message; if (request->hasParam(PARAM_MESSAGE)) { message = request->getParam(PARAM_MESSAGE)->value(); @@ -561,7 +595,7 @@ void setup() { }); // Send a POST request to /post with a form field message set to - server.on("/post", HTTP_POST, [](AsyncWebServerRequest* request) { + server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request) { String message; if (request->hasParam(PARAM_MESSAGE, true)) { message = request->getParam(PARAM_MESSAGE, true)->value(); @@ -576,8 +610,8 @@ void setup() { // sends JSON // curl -v -X GET http://192.168.4.1/json1 - server.on("/json1", HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncJsonResponse* response = new AsyncJsonResponse(); + server.on("/json1", HTTP_GET, [](AsyncWebServerRequest *request) { + AsyncJsonResponse *response = new AsyncJsonResponse(); JsonObject root = response->getRoot().to(); root["hello"] = "world"; response->setLength(); @@ -585,8 +619,8 @@ void setup() { }); // curl -v -X GET http://192.168.4.1/json2 - server.on("/json2", HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncResponseStream* response = request->beginResponseStream("application/json"); + server.on("/json2", HTTP_GET, [](AsyncWebServerRequest *request) { + AsyncResponseStream *response = request->beginResponseStream("application/json"); JsonDocument doc; JsonObject root = doc.to(); root["foo"] = "bar"; @@ -597,9 +631,9 @@ void setup() { // 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 jsonHandler->setMethod(HTTP_POST | HTTP_PUT); - jsonHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) { + jsonHandler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) { serializeJson(json, Serial); - AsyncJsonResponse* response = new AsyncJsonResponse(); + AsyncJsonResponse *response = new AsyncJsonResponse(); JsonObject root = response->getRoot().to(); root["hello"] = json.as()["name"]; response->setLength(); @@ -609,11 +643,11 @@ void setup() { // MessagePack // receives MessagePack and sends MessagePack - msgPackHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) { + msgPackHandler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) { // JsonObject jsonObj = json.as(); // ... - AsyncMessagePackResponse* response = new AsyncMessagePackResponse(); + AsyncMessagePackResponse *response = new AsyncMessagePackResponse(); JsonObject root = response->getRoot().to(); root["hello"] = "world"; response->setLength(); @@ -621,8 +655,8 @@ void setup() { }); // sends MessagePack - server.on("/msgpack1", HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncMessagePackResponse* response = new AsyncMessagePackResponse(); + server.on("/msgpack1", HTTP_GET, [](AsyncWebServerRequest *request) { + AsyncMessagePackResponse *response = new AsyncMessagePackResponse(); JsonObject root = response->getRoot().to(); root["hello"] = "world"; response->setLength(); @@ -630,18 +664,18 @@ void setup() { }); #endif - events.onConnect([](AsyncEventSourceClient* client) { + events.onConnect([](AsyncEventSourceClient *client) { if (client->lastId()) { Serial.printf("SSE Client reconnected! Last message ID that it gat is: %" PRIu32 "\n", client->lastId()); } client->send("hello!", NULL, millis(), 1000); }); - server.on("/sse", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/sse", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/html", SSE_HTLM); }); - ws.onEvent([](AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { + ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { (void)len; if (type == WS_EVT_CONNECT) { wsClients++; @@ -658,12 +692,12 @@ void setup() { } else if (type == WS_EVT_PONG) { Serial.println("ws pong"); } else if (type == WS_EVT_DATA) { - AwsFrameInfo* info = (AwsFrameInfo*)arg; + AwsFrameInfo *info = (AwsFrameInfo *)arg; String msg = ""; if (info->final && info->index == 0 && info->len == len) { if (info->opcode == WS_TEXT) { data[len] = 0; - Serial.printf("ws text: %s\n", (char*)data); + Serial.printf("ws text: %s\n", (char *)data); } } } @@ -736,7 +770,7 @@ void setup() { websocat: WebSocketError: WebSocketError: Received unexpected status code (503 Service Unavailable) websocat: error running */ - server.addHandler(&ws).addMiddleware([](AsyncWebServerRequest* request, ArMiddlewareNext next) { + server.addHandler(&ws).addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) { if (ws.count() > 2) { // too many clients - answer back immediately and stop processing next middlewares and handler request->send(503, "text/plain", "Server is busy"); @@ -757,9 +791,10 @@ websocat: error running // catch any request, and send a 404 Not Found response // except for /game_log which is handled by onRequestBody - server.onNotFound([](AsyncWebServerRequest* request) { - if (request->url() == "/game_log") - return; // response object already creted by onRequestBody + server.onNotFound([](AsyncWebServerRequest *request) { + if (request->url() == "/game_log") { + return; // response object already created by onRequestBody + } request->send(404, "text/plain", "Not found"); }); @@ -767,7 +802,7 @@ websocat: error running // https://github.com/ESP32Async/ESPAsyncWebServer/issues/6 // curl -v -X POST http://192.168.4.1/game_log -H "Content-Type: application/json" -d '{"game": "test"}' // catch any POST request and send a 200 OK response - server.onRequestBody([](AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) { + server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { if (request->url() == "/game_log") { request->send(200, "application/json", "{\"status\":\"OK\"}"); } diff --git a/examples/StreamFiles/StreamConcat.h b/examples/StreamFiles/StreamConcat.h index c1e192769..3698dba4a 100644 --- a/examples/StreamFiles/StreamConcat.h +++ b/examples/StreamFiles/StreamConcat.h @@ -3,35 +3,41 @@ #include class StreamConcat : public Stream { - public: - StreamConcat(Stream* s1, Stream* s2) : _s1(s1), _s2(s2) {} - - size_t write(__unused const uint8_t* p, __unused size_t n) override { return 0; } - size_t write(__unused uint8_t c) override { return 0; } - void flush() override {} - - int available() override { return _s1->available() + _s2->available(); } - - int read() override { - int c = _s1->read(); - return c != -1 ? c : _s2->read(); - } +public: + StreamConcat(Stream *s1, Stream *s2) : _s1(s1), _s2(s2) {} + + size_t write(__unused const uint8_t *p, __unused size_t n) override { + return 0; + } + size_t write(__unused uint8_t c) override { + return 0; + } + void flush() override {} + + int available() override { + return _s1->available() + _s2->available(); + } + + int read() override { + int c = _s1->read(); + return c != -1 ? c : _s2->read(); + } #if defined(TARGET_RP2040) - size_t readBytes(char* buffer, size_t length) { + size_t readBytes(char *buffer, size_t length) { #else - size_t readBytes(char* buffer, size_t length) override { + size_t readBytes(char *buffer, size_t length) override { #endif - size_t count = _s1->readBytes(buffer, length); - return count > 0 ? count : _s2->readBytes(buffer, length); - } - - int peek() override { - int c = _s1->peek(); - return c != -1 ? c : _s2->peek(); - } - - private: - Stream* _s1; - Stream* _s2; + size_t count = _s1->readBytes(buffer, length); + return count > 0 ? count : _s2->readBytes(buffer, length); + } + + int peek() override { + int c = _s1->peek(); + return c != -1 ? c : _s2->peek(); + } + +private: + Stream *_s1; + Stream *_s2; }; diff --git a/examples/StreamFiles/StreamFiles.ino b/examples/StreamFiles/StreamFiles.ino index a0d6b9f60..bee4587ee 100644 --- a/examples/StreamFiles/StreamFiles.ino +++ b/examples/StreamFiles/StreamFiles.ino @@ -1,14 +1,14 @@ #include #include #ifdef ESP32 - #include - #include +#include +#include #elif defined(ESP8266) - #include - #include +#include +#include #elif defined(TARGET_RP2040) - #include - #include +#include +#include #endif #include @@ -44,7 +44,7 @@ void setup() { file3.print(""); file3.close(); - server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { File header = LittleFS.open("/header.html", "r"); File body = LittleFS.open("/body.html", "r"); StreamConcat stream1(&header, &body); @@ -66,7 +66,7 @@ void setup() { footer.close(); }); - server.onNotFound([](AsyncWebServerRequest* request) { + server.onNotFound([](AsyncWebServerRequest *request) { request->send(404, "text/plain", "Not found"); }); @@ -86,4 +86,4 @@ void loop() { #endif last = millis(); } -} \ No newline at end of file +} diff --git a/library.properties b/library.properties index 2692f1f8b..63ce3de21 100644 --- a/library.properties +++ b/library.properties @@ -8,4 +8,4 @@ paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, category=Other url=https://github.com/ESP32Async/ESPAsyncWebServer architectures=* -license=LGPL-3.0 \ No newline at end of file +license=LGPL-3.0 diff --git a/pre-commit.requirements.txt b/pre-commit.requirements.txt new file mode 100644 index 000000000..40a16fa94 --- /dev/null +++ b/pre-commit.requirements.txt @@ -0,0 +1 @@ +pre-commit==4.1.0 diff --git a/src/AsyncEventSource.cpp b/src/AsyncEventSource.cpp index 874faa049..61a2650c2 100644 --- a/src/AsyncEventSource.cpp +++ b/src/AsyncEventSource.cpp @@ -19,7 +19,7 @@ */ #include "Arduino.h" #if defined(ESP32) - #include +#include #endif #include "AsyncEventSource.h" @@ -27,46 +27,49 @@ using namespace asyncsrv; -static String generateEventMessage(const char* message, const char* event, uint32_t id, uint32_t reconnect) { +static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect) { String str; size_t len{0}; - if (message) + if (message) { len += strlen(message); + } - if (event) + if (event) { len += strlen(event); + } - len += 42; // give it some overhead + len += 42; // give it some overhead str.reserve(len); if (reconnect) { str += T_retry_; str += reconnect; - str += ASYNC_SSE_NEW_LINE_CHAR; // '\n' + str += ASYNC_SSE_NEW_LINE_CHAR; // '\n' } if (id) { str += T_id__; str += id; - str += ASYNC_SSE_NEW_LINE_CHAR; // '\n' + str += ASYNC_SSE_NEW_LINE_CHAR; // '\n' } if (event != NULL) { str += T_event_; str += event; - str += ASYNC_SSE_NEW_LINE_CHAR; // '\n' + str += ASYNC_SSE_NEW_LINE_CHAR; // '\n' } - if (!message) + if (!message) { return str; + } size_t messageLen = strlen(message); - char* lineStart = (char*)message; - char* lineEnd; + char *lineStart = (char *)message; + char *lineEnd; do { - char* nextN = strchr(lineStart, '\n'); - char* nextR = strchr(lineStart, '\r'); + char *nextN = strchr(lineStart, '\n'); + char *nextR = strchr(lineStart, '\r'); if (nextN == NULL && nextR == NULL) { // a message is a single-line string str += T_data_; @@ -76,10 +79,10 @@ static String generateEventMessage(const char* message, const char* event, uint3 } // a message is a multi-line string - char* nextLine = NULL; - if (nextN != NULL && nextR != NULL) { // windows line-ending \r\n + char *nextLine = NULL; + if (nextN != NULL && nextR != NULL) { // windows line-ending \r\n if (nextR + 1 == nextN) { - // normal \r\n sequense + // normal \r\n sequence lineEnd = nextR; nextLine = nextN + 1; } else { @@ -87,23 +90,23 @@ static String generateEventMessage(const char* message, const char* event, uint3 lineEnd = std::min(nextR, nextN); nextLine = lineEnd + 1; } - } else if (nextN != NULL) { // Unix/Mac OS X LF + } else if (nextN != NULL) { // Unix/Mac OS X LF lineEnd = nextN; nextLine = nextN + 1; - } else { // some ancient garbage + } else { // some ancient garbage lineEnd = nextR; nextLine = nextR + 1; } str += T_data_; str.concat(lineStart, lineEnd - lineStart); - str += ASYNC_SSE_NEW_LINE_CHAR; // \n + str += ASYNC_SSE_NEW_LINE_CHAR; // \n lineStart = nextLine; - } while (lineStart < ((char*)message + messageLen)); + } while (lineStart < ((char *)message + messageLen)); // append another \n to terminate message - str += ASYNC_SSE_NEW_LINE_CHAR; // '\n' + str += ASYNC_SSE_NEW_LINE_CHAR; // '\n' return str; } @@ -123,9 +126,10 @@ size_t AsyncEventSourceMessage::ack(size_t len, __attribute__((unused)) uint32_t return 0; } -size_t AsyncEventSourceMessage::write(AsyncClient* client) { - if (!client) +size_t AsyncEventSourceMessage::write(AsyncClient *client) { + if (!client) { return 0; + } if (_sent >= _data->length() || !client->canSend()) { return 0; @@ -143,31 +147,54 @@ size_t AsyncEventSourceMessage::write(AsyncClient* client) { So let's just keep it enforced ASYNC_WRITE_FLAG_COPY and keep in mind that there is no zero-copy */ - size_t written = client->add(_data->c_str() + _sent, len, ASYNC_WRITE_FLAG_COPY); // ASYNC_WRITE_FLAG_MORE + size_t written = client->add(_data->c_str() + _sent, len, ASYNC_WRITE_FLAG_COPY); // ASYNC_WRITE_FLAG_MORE _sent += written; return written; } -size_t AsyncEventSourceMessage::send(AsyncClient* client) { +size_t AsyncEventSourceMessage::send(AsyncClient *client) { size_t sent = write(client); return sent && client->send() ? sent : 0; } // Client -AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server) - : _client(request->client()), _server(server) { +AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server) : _client(request->client()), _server(server) { - if (request->hasHeader(T_Last_Event_ID)) + if (request->hasHeader(T_Last_Event_ID)) { _lastId = atoi(request->getHeader(T_Last_Event_ID)->value().c_str()); + } _client->setRxTimeout(0); _client->onError(NULL, NULL); - _client->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; static_cast(r)->_onAck(len, time); }, this); - _client->onPoll([](void* r, AsyncClient* c) { (void)c; static_cast(r)->_onPoll(); }, this); + _client->onAck( + [](void *r, AsyncClient *c, size_t len, uint32_t time) { + (void)c; + static_cast(r)->_onAck(len, time); + }, + this + ); + _client->onPoll( + [](void *r, AsyncClient *c) { + (void)c; + static_cast(r)->_onPoll(); + }, + this + ); _client->onData(NULL, NULL); - _client->onTimeout([this](void* r, AsyncClient* c __attribute__((unused)), uint32_t time) { static_cast(r)->_onTimeout(time); }, this); - _client->onDisconnect([this](void* r, AsyncClient* c) { static_cast(r)->_onDisconnect(); delete c; }, this); + _client->onTimeout( + [this](void *r, AsyncClient *c __attribute__((unused)), uint32_t time) { + static_cast(r)->_onTimeout(time); + }, + this + ); + _client->onDisconnect( + [this](void *r, AsyncClient *c) { + static_cast(r)->_onDisconnect(); + delete c; + }, + this + ); _server->_addClient(this); delete request; @@ -183,7 +210,7 @@ AsyncEventSourceClient::~AsyncEventSourceClient() { close(); } -bool AsyncEventSourceClient::_queueMessage(const char* message, size_t len) { +bool AsyncEventSourceClient::_queueMessage(const char *message, size_t len) { if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) { #ifdef ESP8266 ets_printf(String(F("ERROR: Too many messages queued\n")).c_str()); @@ -213,7 +240,7 @@ bool AsyncEventSourceClient::_queueMessage(const char* message, size_t len) { return true; } -bool AsyncEventSourceClient::_queueMessage(AsyncEvent_SharedData_t&& msg) { +bool AsyncEventSourceClient::_queueMessage(AsyncEvent_SharedData_t &&msg) { if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) { #ifdef ESP8266 ets_printf(String(F("ERROR: Too many messages queued\n")).c_str()); @@ -249,10 +276,11 @@ void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t #endif // adjust in-flight len - if (len < _inflight) + if (len < _inflight) { _inflight -= len; - else + } else { _inflight = 0; + } // acknowledge as much messages's data as we got confirmed len from a AsyncTCP while (len && _messageQueue.size()) { @@ -264,8 +292,9 @@ void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t } // try to send another batch of data - if (_messageQueue.size()) + if (_messageQueue.size()) { _runQueue(); + } } void AsyncEventSourceClient::_onPoll() { @@ -279,31 +308,36 @@ void AsyncEventSourceClient::_onPoll() { } void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) { - if (_client) + if (_client) { _client->close(true); + } } void AsyncEventSourceClient::_onDisconnect() { - if (!_client) + if (!_client) { return; + } _client = nullptr; _server->_handleDisconnect(this); } void AsyncEventSourceClient::close() { - if (_client) + if (_client) { _client->close(); + } } -bool AsyncEventSourceClient::send(const char* message, const char* event, uint32_t id, uint32_t reconnect) { - if (!connected()) +bool AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect) { + if (!connected()) { return false; + } return _queueMessage(std::make_shared(generateEventMessage(message, event, id, reconnect))); } void AsyncEventSourceClient::_runQueue() { - if (!_client) + if (!_client) { return; + } // there is no need to lock the mutex here, 'cause all the calls to this method must be already lock'ed size_t total_bytes_written = 0; @@ -320,45 +354,51 @@ void AsyncEventSourceClient::_runQueue() { } // flush socket - if (total_bytes_written) + if (total_bytes_written) { _client->send(); + } } void AsyncEventSourceClient::set_max_inflight_bytes(size_t value) { - if (value >= SSE_MIN_INFLIGH && value <= SSE_MAX_INFLIGH) + if (value >= SSE_MIN_INFLIGH && value <= SSE_MAX_INFLIGH) { _max_inflight = value; + } } /* AsyncEventSource */ void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) { - AsyncAuthorizationMiddleware* m = new AsyncAuthorizationMiddleware(401, cb); + AsyncAuthorizationMiddleware *m = new AsyncAuthorizationMiddleware(401, cb); m->_freeOnRemoval = true; addMiddleware(m); } -void AsyncEventSource::_addClient(AsyncEventSourceClient* client) { - if (!client) +void AsyncEventSource::_addClient(AsyncEventSourceClient *client) { + if (!client) { return; + } #ifdef ESP32 std::lock_guard lock(_client_queue_lock); #endif _clients.emplace_back(client); - if (_connectcb) + if (_connectcb) { _connectcb(client); + } _adjust_inflight_window(); } -void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) { - if (_disconnectcb) +void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient *client) { + if (_disconnectcb) { _disconnectcb(client); + } #ifdef ESP32 std::lock_guard lock(_client_queue_lock); #endif for (auto i = _clients.begin(); i != _clients.end(); ++i) { - if (i->get() == client) + if (i->get() == client) { _clients.erase(i); + } } _adjust_inflight_window(); } @@ -370,9 +410,10 @@ void AsyncEventSource::close() { #ifdef ESP32 std::lock_guard lock(_client_queue_lock); #endif - for (const auto& c : _clients) { - if (c->connected()) + for (const auto &c : _clients) { + if (c->connected()) { c->close(); + } } } @@ -383,31 +424,32 @@ size_t AsyncEventSource::avgPacketsWaiting() const { #ifdef ESP32 std::lock_guard lock(_client_queue_lock); #endif - if (!_clients.size()) + if (!_clients.size()) { return 0; + } - for (const auto& c : _clients) { + for (const auto &c : _clients) { if (c->connected()) { aql += c->packetsWaiting(); ++nConnectedClients; } } - return ((aql) + (nConnectedClients / 2)) / (nConnectedClients); // round up + return ((aql) + (nConnectedClients / 2)) / (nConnectedClients); // round up } -AsyncEventSource::SendStatus AsyncEventSource::send( - const char* message, const char* event, uint32_t id, uint32_t reconnect) { +AsyncEventSource::SendStatus AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect) { AsyncEvent_SharedData_t shared_msg = std::make_shared(generateEventMessage(message, event, id, reconnect)); #ifdef ESP32 std::lock_guard lock(_client_queue_lock); #endif size_t hits = 0; size_t miss = 0; - for (const auto& c : _clients) { - if (c->write(shared_msg)) + for (const auto &c : _clients) { + if (c->write(shared_msg)) { ++hits; - else + } else { ++miss; + } } return hits == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED); } @@ -417,33 +459,36 @@ size_t AsyncEventSource::count() const { std::lock_guard lock(_client_queue_lock); #endif size_t n_clients{0}; - for (const auto& i : _clients) - if (i->connected()) + for (const auto &i : _clients) { + if (i->connected()) { ++n_clients; + } + } return n_clients; } -bool AsyncEventSource::canHandle(AsyncWebServerRequest* request) const { +bool AsyncEventSource::canHandle(AsyncWebServerRequest *request) const { return request->isSSE() && request->url().equals(_url); } -void AsyncEventSource::handleRequest(AsyncWebServerRequest* request) { +void AsyncEventSource::handleRequest(AsyncWebServerRequest *request) { request->send(new AsyncEventSourceResponse(this)); } void AsyncEventSource::_adjust_inflight_window() { if (_clients.size()) { size_t inflight = SSE_MAX_INFLIGH / _clients.size(); - for (const auto& c : _clients) + for (const auto &c : _clients) { c->set_max_inflight_bytes(inflight); + } // Serial.printf("adjusted inflight to: %u\n", inflight); } } /* Response */ -AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource* server) { +AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server) { _server = server; _code = 200; _contentType = T_text_event_stream; @@ -452,14 +497,14 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource* server) { addHeader(T_Connection, T_keep_alive); } -void AsyncEventSourceResponse::_respond(AsyncWebServerRequest* request) { +void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request) { String out; _assembleHead(out, request->version()); request->client()->write(out.c_str(), _headLength); _state = RESPONSE_WAIT_ACK; } -size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time __attribute__((unused))) { +size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))) { if (len) { new AsyncEventSourceClient(request, _server); } diff --git a/src/AsyncEventSource.h b/src/AsyncEventSource.h index 0796faedc..141d95ccb 100644 --- a/src/AsyncEventSource.h +++ b/src/AsyncEventSource.h @@ -23,42 +23,42 @@ #include #ifdef ESP32 - #include - #include - #ifndef SSE_MAX_QUEUED_MESSAGES - #define SSE_MAX_QUEUED_MESSAGES 32 - #endif - #define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets - #define SSE_MAX_INFLIGH 16 * 1024 // but no more than 16k, no need to blow it, since same data is kept in local Q +#include +#include +#ifndef SSE_MAX_QUEUED_MESSAGES +#define SSE_MAX_QUEUED_MESSAGES 32 +#endif +#define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets +#define SSE_MAX_INFLIGH 16 * 1024 // but no more than 16k, no need to blow it, since same data is kept in local Q #elif defined(ESP8266) - #include - #ifndef SSE_MAX_QUEUED_MESSAGES - #define SSE_MAX_QUEUED_MESSAGES 8 - #endif - #define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets - #define SSE_MAX_INFLIGH 8 * 1024 // but no more than 8k, no need to blow it, since same data is kept in local Q +#include +#ifndef SSE_MAX_QUEUED_MESSAGES +#define SSE_MAX_QUEUED_MESSAGES 8 +#endif +#define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets +#define SSE_MAX_INFLIGH 8 * 1024 // but no more than 8k, no need to blow it, since same data is kept in local Q #elif defined(TARGET_RP2040) - #include - #ifndef SSE_MAX_QUEUED_MESSAGES - #define SSE_MAX_QUEUED_MESSAGES 32 - #endif - #define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets - #define SSE_MAX_INFLIGH 16 * 1024 // but no more than 16k, no need to blow it, since same data is kept in local Q +#include +#ifndef SSE_MAX_QUEUED_MESSAGES +#define SSE_MAX_QUEUED_MESSAGES 32 +#endif +#define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets +#define SSE_MAX_INFLIGH 16 * 1024 // but no more than 16k, no need to blow it, since same data is kept in local Q #endif #include #ifdef ESP8266 - #include - #ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library - #include <../src/Hash.h> - #endif +#include +#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library +#include <../src/Hash.h> +#endif #endif class AsyncEventSource; class AsyncEventSourceResponse; class AsyncEventSourceClient; -using ArEventHandlerFunction = std::function; +using ArEventHandlerFunction = std::function; using ArAuthorizeConnectHandler = ArAuthorizeFunction; // shared message object container using AsyncEvent_SharedData_t = std::shared_ptr; @@ -69,55 +69,61 @@ using AsyncEvent_SharedData_t = std::shared_ptr; */ class AsyncEventSourceMessage { - private: - const AsyncEvent_SharedData_t _data; - size_t _sent{0}; // num of bytes already sent - size_t _acked{0}; // num of bytes acked +private: + const AsyncEvent_SharedData_t _data; + size_t _sent{0}; // num of bytes already sent + size_t _acked{0}; // num of bytes acked - public: - AsyncEventSourceMessage(AsyncEvent_SharedData_t data) : _data(data) {}; +public: + AsyncEventSourceMessage(AsyncEvent_SharedData_t data) : _data(data){}; #ifdef ESP32 - AsyncEventSourceMessage(const char* data, size_t len) : _data(std::make_shared(data, len)) {}; + AsyncEventSourceMessage(const char *data, size_t len) : _data(std::make_shared(data, len)){}; #else - // esp8266's String does not have constructor with data/length arguments. Use a concat method here - AsyncEventSourceMessage(const char* data, size_t len) { _data->concat(data, len); }; + // esp8266's String does not have constructor with data/length arguments. Use a concat method here + AsyncEventSourceMessage(const char *data, size_t len) { + _data->concat(data, len); + }; #endif - /** + /** * @brief acknowledge sending len bytes of data * @note if num of bytes to ack is larger then the unacknowledged message length the number of carried over bytes are returned * - * @param len bytes to acknowlegde + * @param len bytes to acknowledge * @param time * @return size_t number of extra bytes carried over */ - size_t ack(size_t len, uint32_t time = 0); + size_t ack(size_t len, uint32_t time = 0); - /** + /** * @brief write message data to client's buffer * @note this method does NOT call client's send * * @param client * @return size_t number of bytes written */ - size_t write(AsyncClient* client); + size_t write(AsyncClient *client); - /** + /** * @brief writes message data to client's buffer and calls client's send method * * @param client * @return size_t returns num of bytes the clien was able to send() */ - size_t send(AsyncClient* client); + size_t send(AsyncClient *client); - // returns true if full message's length were acked - bool finished() { return _acked == _data->length(); } + // returns true if full message's length were acked + bool finished() { + return _acked == _data->length(); + } - /** + /** * @brief returns true if all data has been sent already * */ - bool sent() { return _sent == _data->length(); } + bool sent() { + return _sent == _data->length(); + } }; /** @@ -125,25 +131,25 @@ class AsyncEventSourceMessage { * */ class AsyncEventSourceClient { - private: - AsyncClient* _client; - AsyncEventSource* _server; - uint32_t _lastId{0}; - size_t _inflight{0}; // num of unacknowledged bytes that has been written to socket buffer - size_t _max_inflight{SSE_MAX_INFLIGH}; // max num of unacknowledged bytes that could be written to socket buffer - std::list _messageQueue; +private: + AsyncClient *_client; + AsyncEventSource *_server; + uint32_t _lastId{0}; + size_t _inflight{0}; // num of unacknowledged bytes that has been written to socket buffer + size_t _max_inflight{SSE_MAX_INFLIGH}; // max num of unacknowledged bytes that could be written to socket buffer + std::list _messageQueue; #ifdef ESP32 - mutable std::mutex _lockmq; + mutable std::mutex _lockmq; #endif - bool _queueMessage(const char* message, size_t len); - bool _queueMessage(AsyncEvent_SharedData_t&& msg); - void _runQueue(); + bool _queueMessage(const char *message, size_t len); + bool _queueMessage(AsyncEvent_SharedData_t &&msg); + void _runQueue(); - public: - AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server); - ~AsyncEventSourceClient(); +public: + AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server); + ~AsyncEventSourceClient(); - /** + /** * @brief Send an SSE message to client * it will craft an SSE message and place it to client's message queue * @@ -154,11 +160,15 @@ class AsyncEventSourceClient { * @return true if message was placed in a queue * @return false if queue is full */ - bool send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0); - bool send(const String& message, const String& event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event.c_str(), id, reconnect); } - bool send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event, id, reconnect); } - - /** + bool send(const char *message, const char *event = NULL, uint32_t id = 0, uint32_t reconnect = 0); + bool send(const String &message, const String &event, uint32_t id = 0, uint32_t reconnect = 0) { + return send(message.c_str(), event.c_str(), id, reconnect); + } + bool send(const String &message, const char *event, uint32_t id = 0, uint32_t reconnect = 0) { + return send(message.c_str(), event, id, reconnect); + } + + /** * @brief place supplied preformatted SSE message to the message queue * @note message must a properly formatted SSE string according to https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events * @@ -166,42 +176,56 @@ class AsyncEventSourceClient { * @return true on success * @return false on queue overflow or no client connected */ - bool write(AsyncEvent_SharedData_t message) { return connected() && _queueMessage(std::move(message)); }; - - [[deprecated("Use _write(AsyncEvent_SharedData_t message) instead to share same data with multiple SSE clients")]] - bool write(const char* message, size_t len) { return connected() && _queueMessage(message, len); }; - - // close client's connection - void close(); - - // getters - - AsyncClient* client() { return _client; } - bool connected() const { return _client && _client->connected(); } - uint32_t lastId() const { return _lastId; } - size_t packetsWaiting() const { return _messageQueue.size(); }; - - /** + bool write(AsyncEvent_SharedData_t message) { + return connected() && _queueMessage(std::move(message)); + }; + + [[deprecated("Use _write(AsyncEvent_SharedData_t message) instead to share same data with multiple SSE clients")]] + bool write(const char *message, size_t len) { + return connected() && _queueMessage(message, len); + }; + + // close client's connection + void close(); + + // getters + + AsyncClient *client() { + return _client; + } + bool connected() const { + return _client && _client->connected(); + } + uint32_t lastId() const { + return _lastId; + } + size_t packetsWaiting() const { + return _messageQueue.size(); + }; + + /** * @brief Sets max amount of bytes that could be written to client's socket while awaiting delivery acknowledge * used to throttle message delivery length to tradeoff memory consumption * @note actual amount of data written could possible be a bit larger but no more than available socket buff space * * @param value */ - void set_max_inflight_bytes(size_t value); + void set_max_inflight_bytes(size_t value); - /** + /** * @brief Get current max inflight bytes value * * @return size_t */ - size_t get_max_inflight_bytes() const { return _max_inflight; } - - // system callbacks (do not call if from user code!) - void _onAck(size_t len, uint32_t time); - void _onPoll(); - void _onTimeout(uint32_t time); - void _onDisconnect(); + size_t get_max_inflight_bytes() const { + return _max_inflight; + } + + // system callbacks (do not call if from user code!) + void _onAck(size_t len, uint32_t time); + void _onPoll(); + void _onTimeout(uint32_t time); + void _onDisconnect(); }; /** @@ -210,44 +234,50 @@ class AsyncEventSourceClient { * */ class AsyncEventSource : public AsyncWebHandler { - private: - String _url; - std::list> _clients; +private: + String _url; + std::list> _clients; #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; + // Same as for individual messages, protect mutations of _clients list + // since simultaneous access from different tasks is possible + mutable std::mutex _client_queue_lock; #endif - ArEventHandlerFunction _connectcb = nullptr; - ArEventHandlerFunction _disconnectcb = nullptr; - - // this method manipulates in-fligh data size for connected client depending on number of active connections - void _adjust_inflight_window(); - - public: - typedef enum { - DISCARDED = 0, - ENQUEUED = 1, - PARTIALLY_ENQUEUED = 2, - } SendStatus; - - AsyncEventSource(const char* url) : _url(url) {}; - AsyncEventSource(const String& url) : _url(url) {}; - ~AsyncEventSource() { close(); }; - - const char* url() const { return _url.c_str(); } - // close all connected clients - void close(); - - /** + ArEventHandlerFunction _connectcb = nullptr; + ArEventHandlerFunction _disconnectcb = nullptr; + + // this method manipulates in-fligh data size for connected client depending on number of active connections + void _adjust_inflight_window(); + +public: + typedef enum { + DISCARDED = 0, + ENQUEUED = 1, + PARTIALLY_ENQUEUED = 2, + } SendStatus; + + AsyncEventSource(const char *url) : _url(url){}; + AsyncEventSource(const String &url) : _url(url){}; + ~AsyncEventSource() { + close(); + }; + + const char *url() const { + return _url.c_str(); + } + // close all connected clients + void close(); + + /** * @brief set on-connect callback for the client * used to deliver messages to client on first connect * * @param cb */ - void onConnect(ArEventHandlerFunction cb) { _connectcb = cb; } + void onConnect(ArEventHandlerFunction cb) { + _connectcb = cb; + } - /** + /** * @brief Send an SSE message to client * it will craft an SSE message and place it to all connected client's message queues * @@ -257,36 +287,44 @@ class AsyncEventSource : public AsyncWebHandler { * @param reconnect client's reconnect timeout * @return SendStatus if message was placed in any/all/part of the client's queues */ - SendStatus send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0); - SendStatus send(const String& message, const String& event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event.c_str(), id, reconnect); } - SendStatus send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event, id, reconnect); } - - // The client pointer sent to the callback is only for reference purposes. DO NOT CALL ANY METHOD ON IT ! - void onDisconnect(ArEventHandlerFunction cb) { _disconnectcb = cb; } - void authorizeConnect(ArAuthorizeConnectHandler cb); - - // returns number of connected clients - size_t count() const; - - // returns average number of messages pending in all client's queues - size_t avgPacketsWaiting() const; - - // system callbacks (do not call from user code!) - void _addClient(AsyncEventSourceClient* client); - void _handleDisconnect(AsyncEventSourceClient* client); - bool canHandle(AsyncWebServerRequest* request) const override final; - void handleRequest(AsyncWebServerRequest* request) override final; + SendStatus send(const char *message, const char *event = NULL, uint32_t id = 0, uint32_t reconnect = 0); + SendStatus send(const String &message, const String &event, uint32_t id = 0, uint32_t reconnect = 0) { + return send(message.c_str(), event.c_str(), id, reconnect); + } + SendStatus send(const String &message, const char *event, uint32_t id = 0, uint32_t reconnect = 0) { + return send(message.c_str(), event, id, reconnect); + } + + // The client pointer sent to the callback is only for reference purposes. DO NOT CALL ANY METHOD ON IT ! + void onDisconnect(ArEventHandlerFunction cb) { + _disconnectcb = cb; + } + void authorizeConnect(ArAuthorizeConnectHandler cb); + + // returns number of connected clients + size_t count() const; + + // returns average number of messages pending in all client's queues + size_t avgPacketsWaiting() const; + + // system callbacks (do not call from user code!) + void _addClient(AsyncEventSourceClient *client); + void _handleDisconnect(AsyncEventSourceClient *client); + bool canHandle(AsyncWebServerRequest *request) const override final; + void handleRequest(AsyncWebServerRequest *request) override final; }; class AsyncEventSourceResponse : public AsyncWebServerResponse { - private: - AsyncEventSource* _server; - - public: - AsyncEventSourceResponse(AsyncEventSource* server); - void _respond(AsyncWebServerRequest* request); - size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); - bool _sourceValid() const { return true; } +private: + AsyncEventSource *_server; + +public: + AsyncEventSourceResponse(AsyncEventSource *server); + void _respond(AsyncWebServerRequest *request); + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + bool _sourceValid() const { + return true; + } }; #endif /* ASYNCEVENTSOURCE_H_ */ diff --git a/src/AsyncJson.cpp b/src/AsyncJson.cpp index 42956537d..e9cba6e56 100644 --- a/src/AsyncJson.cpp +++ b/src/AsyncJson.cpp @@ -2,107 +2,113 @@ #if ASYNC_JSON_SUPPORT == 1 - #if ARDUINOJSON_VERSION_MAJOR == 5 +#if ARDUINOJSON_VERSION_MAJOR == 5 AsyncJsonResponse::AsyncJsonResponse(bool isArray) : _isValid{false} { _code = 200; _contentType = asyncsrv::T_application_json; - if (isArray) + if (isArray) { _root = _jsonBuffer.createArray(); - else + } else { _root = _jsonBuffer.createObject(); + } } - #elif ARDUINOJSON_VERSION_MAJOR == 6 +#elif ARDUINOJSON_VERSION_MAJOR == 6 AsyncJsonResponse::AsyncJsonResponse(bool isArray, size_t maxJsonBufferSize) : _jsonBuffer(maxJsonBufferSize), _isValid{false} { _code = 200; _contentType = asyncsrv::T_application_json; - if (isArray) + if (isArray) { _root = _jsonBuffer.createNestedArray(); - else + } else { _root = _jsonBuffer.createNestedObject(); + } } - #else +#else AsyncJsonResponse::AsyncJsonResponse(bool isArray) : _isValid{false} { _code = 200; _contentType = asyncsrv::T_application_json; - if (isArray) + if (isArray) { _root = _jsonBuffer.add(); - else + } else { _root = _jsonBuffer.add(); + } } - #endif +#endif size_t AsyncJsonResponse::setLength() { - #if ARDUINOJSON_VERSION_MAJOR == 5 +#if ARDUINOJSON_VERSION_MAJOR == 5 _contentLength = _root.measureLength(); - #else +#else _contentLength = measureJson(_root); - #endif +#endif if (_contentLength) { _isValid = true; } return _contentLength; } -size_t AsyncJsonResponse::_fillBuffer(uint8_t* data, size_t len) { +size_t AsyncJsonResponse::_fillBuffer(uint8_t *data, size_t len) { ChunkPrint dest(data, _sentLength, len); - #if ARDUINOJSON_VERSION_MAJOR == 5 +#if ARDUINOJSON_VERSION_MAJOR == 5 _root.printTo(dest); - #else +#else serializeJson(_root, dest); - #endif +#endif return len; } - #if ARDUINOJSON_VERSION_MAJOR == 6 +#if ARDUINOJSON_VERSION_MAJOR == 6 PrettyAsyncJsonResponse::PrettyAsyncJsonResponse(bool isArray, size_t maxJsonBufferSize) : AsyncJsonResponse{isArray, maxJsonBufferSize} {} - #else +#else PrettyAsyncJsonResponse::PrettyAsyncJsonResponse(bool isArray) : AsyncJsonResponse{isArray} {} - #endif +#endif size_t PrettyAsyncJsonResponse::setLength() { - #if ARDUINOJSON_VERSION_MAJOR == 5 +#if ARDUINOJSON_VERSION_MAJOR == 5 _contentLength = _root.measurePrettyLength(); - #else +#else _contentLength = measureJsonPretty(_root); - #endif +#endif if (_contentLength) { _isValid = true; } return _contentLength; } -size_t PrettyAsyncJsonResponse::_fillBuffer(uint8_t* data, size_t len) { +size_t PrettyAsyncJsonResponse::_fillBuffer(uint8_t *data, size_t len) { ChunkPrint dest(data, _sentLength, len); - #if ARDUINOJSON_VERSION_MAJOR == 5 +#if ARDUINOJSON_VERSION_MAJOR == 5 _root.prettyPrintTo(dest); - #else +#else serializeJsonPretty(_root, dest); - #endif +#endif return len; } - #if ARDUINOJSON_VERSION_MAJOR == 6 -AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize) - : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {} - #else -AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) - : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} - #endif +#if ARDUINOJSON_VERSION_MAJOR == 6 +AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(const String &uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize) + : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {} +#else +AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(const String &uri, ArJsonRequestHandlerFunction onRequest) + : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} +#endif -bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest* request) const { - if (!_onRequest || !request->isHTTP() || !(_method & request->method())) +bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest *request) const { + if (!_onRequest || !request->isHTTP() || !(_method & request->method())) { return false; + } - if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) + if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) { return false; + } - if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_json)) + if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_json)) { return false; + } return true; } -void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest* request) { +void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest *request) { if (_onRequest) { if (request->method() == HTTP_GET) { JsonVariant json; @@ -110,21 +116,21 @@ void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest* request) return; } else if (request->_tempObject != NULL) { - #if ARDUINOJSON_VERSION_MAJOR == 5 +#if ARDUINOJSON_VERSION_MAJOR == 5 DynamicJsonBuffer jsonBuffer; - JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject)); + JsonVariant json = jsonBuffer.parse((uint8_t *)(request->_tempObject)); if (json.success()) { - #elif ARDUINOJSON_VERSION_MAJOR == 6 +#elif ARDUINOJSON_VERSION_MAJOR == 6 DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize); - DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject)); + DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject)); if (!error) { JsonVariant json = jsonBuffer.as(); - #else +#else JsonDocument jsonBuffer; - DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject)); + DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject)); if (!error) { JsonVariant json = jsonBuffer.as(); - #endif +#endif _onRequest(request, json); return; @@ -136,16 +142,16 @@ void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest* request) } } -void AsyncCallbackJsonWebHandler::handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) { +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); } if (request->_tempObject != NULL) { - memcpy((uint8_t*)(request->_tempObject) + index, data, len); + memcpy((uint8_t *)(request->_tempObject) + index, data, len); } } } -#endif // ASYNC_JSON_SUPPORT +#endif // ASYNC_JSON_SUPPORT diff --git a/src/AsyncJson.h b/src/AsyncJson.h index a54935adb..7fbd67bbd 100644 --- a/src/AsyncJson.h +++ b/src/AsyncJson.h @@ -36,96 +36,115 @@ #define ASYNC_JSON_H_ #if __has_include("ArduinoJson.h") - #include - #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") +#include +#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 +#include - #include "ChunkPrint.h" +#include "ChunkPrint.h" - #if ARDUINOJSON_VERSION_MAJOR == 6 - #ifndef DYNAMIC_JSON_DOCUMENT_SIZE - #define DYNAMIC_JSON_DOCUMENT_SIZE 1024 - #endif - #endif +#if ARDUINOJSON_VERSION_MAJOR == 6 +#ifndef DYNAMIC_JSON_DOCUMENT_SIZE +#define DYNAMIC_JSON_DOCUMENT_SIZE 1024 +#endif +#endif class AsyncJsonResponse : public AsyncAbstractResponse { - protected: - #if ARDUINOJSON_VERSION_MAJOR == 5 - DynamicJsonBuffer _jsonBuffer; - #elif ARDUINOJSON_VERSION_MAJOR == 6 - DynamicJsonDocument _jsonBuffer; - #else - JsonDocument _jsonBuffer; - #endif - - JsonVariant _root; - bool _isValid; - - public: - #if ARDUINOJSON_VERSION_MAJOR == 6 - AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE); - #else - AsyncJsonResponse(bool isArray = false); - #endif - JsonVariant& getRoot() { return _root; } - bool _sourceValid() const { return _isValid; } - size_t setLength(); - size_t getSize() const { return _jsonBuffer.size(); } - size_t _fillBuffer(uint8_t* data, size_t len); - #if ARDUINOJSON_VERSION_MAJOR >= 6 - bool overflowed() const { return _jsonBuffer.overflowed(); } - #endif +protected: +#if ARDUINOJSON_VERSION_MAJOR == 5 + DynamicJsonBuffer _jsonBuffer; +#elif ARDUINOJSON_VERSION_MAJOR == 6 + DynamicJsonDocument _jsonBuffer; +#else + JsonDocument _jsonBuffer; +#endif + + JsonVariant _root; + bool _isValid; + +public: +#if ARDUINOJSON_VERSION_MAJOR == 6 + AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE); +#else + AsyncJsonResponse(bool isArray = false); +#endif + JsonVariant &getRoot() { + return _root; + } + bool _sourceValid() const { + return _isValid; + } + size_t setLength(); + size_t getSize() const { + return _jsonBuffer.size(); + } + size_t _fillBuffer(uint8_t *data, size_t len); +#if ARDUINOJSON_VERSION_MAJOR >= 6 + bool overflowed() const { + return _jsonBuffer.overflowed(); + } +#endif }; class PrettyAsyncJsonResponse : public AsyncJsonResponse { - public: - #if ARDUINOJSON_VERSION_MAJOR == 6 - PrettyAsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE); - #else - PrettyAsyncJsonResponse(bool isArray = false); - #endif - size_t setLength(); - size_t _fillBuffer(uint8_t* data, size_t len); +public: +#if ARDUINOJSON_VERSION_MAJOR == 6 + PrettyAsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE); +#else + PrettyAsyncJsonResponse(bool isArray = false); +#endif + size_t setLength(); + size_t _fillBuffer(uint8_t *data, size_t len); }; -typedef std::function ArJsonRequestHandlerFunction; +typedef std::function ArJsonRequestHandlerFunction; class AsyncCallbackJsonWebHandler : public AsyncWebHandler { - protected: - String _uri; - WebRequestMethodComposite _method; - ArJsonRequestHandlerFunction _onRequest; - size_t _contentLength; - #if ARDUINOJSON_VERSION_MAJOR == 6 - size_t maxJsonBufferSize; - #endif - size_t _maxContentLength; - - public: - #if ARDUINOJSON_VERSION_MAJOR == 6 - AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest = nullptr, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE); - #else - AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest = nullptr); - #endif - - void setMethod(WebRequestMethodComposite method) { _method = method; } - void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; } - void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; } - - bool canHandle(AsyncWebServerRequest* request) const override final; - void handleRequest(AsyncWebServerRequest* request) override final; - void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {} - void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final; - bool isRequestHandlerTrivial() const override final { return !_onRequest; } +protected: + String _uri; + WebRequestMethodComposite _method; + ArJsonRequestHandlerFunction _onRequest; + size_t _contentLength; +#if ARDUINOJSON_VERSION_MAJOR == 6 + size_t maxJsonBufferSize; +#endif + size_t _maxContentLength; + +public: +#if ARDUINOJSON_VERSION_MAJOR == 6 + AsyncCallbackJsonWebHandler(const String &uri, ArJsonRequestHandlerFunction onRequest = nullptr, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE); +#else + AsyncCallbackJsonWebHandler(const String &uri, ArJsonRequestHandlerFunction onRequest = nullptr); +#endif + + void setMethod(WebRequestMethodComposite method) { + _method = method; + } + void setMaxContentLength(int maxContentLength) { + _maxContentLength = maxContentLength; + } + void onRequest(ArJsonRequestHandlerFunction fn) { + _onRequest = fn; + } + + bool canHandle(AsyncWebServerRequest *request) const override final; + void handleRequest(AsyncWebServerRequest *request) override final; + void handleUpload( + __unused AsyncWebServerRequest *request, __unused const String &filename, __unused size_t index, __unused uint8_t *data, __unused size_t len, + __unused bool final + ) override final {} + void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final; + bool isRequestHandlerTrivial() const override final { + return !_onRequest; + } }; -#endif // ASYNC_JSON_SUPPORT == 1 +#endif // ASYNC_JSON_SUPPORT == 1 -#endif // ASYNC_JSON_H_ +#endif // ASYNC_JSON_H_ diff --git a/src/AsyncMessagePack.cpp b/src/AsyncMessagePack.cpp index 85af67148..d4b74ed02 100644 --- a/src/AsyncMessagePack.cpp +++ b/src/AsyncMessagePack.cpp @@ -2,25 +2,27 @@ #if ASYNC_MSG_PACK_SUPPORT == 1 - #if ARDUINOJSON_VERSION_MAJOR == 6 +#if ARDUINOJSON_VERSION_MAJOR == 6 AsyncMessagePackResponse::AsyncMessagePackResponse(bool isArray, size_t maxJsonBufferSize) : _jsonBuffer(maxJsonBufferSize), _isValid{false} { _code = 200; _contentType = asyncsrv::T_application_msgpack; - if (isArray) + if (isArray) { _root = _jsonBuffer.createNestedArray(); - else + } else { _root = _jsonBuffer.createNestedObject(); + } } - #else +#else AsyncMessagePackResponse::AsyncMessagePackResponse(bool isArray) : _isValid{false} { _code = 200; _contentType = asyncsrv::T_application_msgpack; - if (isArray) + if (isArray) { _root = _jsonBuffer.add(); - else + } else { _root = _jsonBuffer.add(); + } } - #endif +#endif size_t AsyncMessagePackResponse::setLength() { _contentLength = measureMsgPack(_root); @@ -30,34 +32,39 @@ size_t AsyncMessagePackResponse::setLength() { return _contentLength; } -size_t AsyncMessagePackResponse::_fillBuffer(uint8_t* data, size_t len) { +size_t AsyncMessagePackResponse::_fillBuffer(uint8_t *data, size_t len) { ChunkPrint dest(data, _sentLength, len); serializeMsgPack(_root, dest); return len; } - #if ARDUINOJSON_VERSION_MAJOR == 6 -AsyncCallbackMessagePackWebHandler::AsyncCallbackMessagePackWebHandler(const String& uri, ArMessagePackRequestHandlerFunction onRequest, size_t maxJsonBufferSize) - : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {} - #else -AsyncCallbackMessagePackWebHandler::AsyncCallbackMessagePackWebHandler(const String& uri, ArMessagePackRequestHandlerFunction onRequest) - : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} - #endif +#if ARDUINOJSON_VERSION_MAJOR == 6 +AsyncCallbackMessagePackWebHandler::AsyncCallbackMessagePackWebHandler( + const String &uri, ArMessagePackRequestHandlerFunction onRequest, size_t maxJsonBufferSize +) + : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {} +#else +AsyncCallbackMessagePackWebHandler::AsyncCallbackMessagePackWebHandler(const String &uri, ArMessagePackRequestHandlerFunction onRequest) + : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} +#endif -bool AsyncCallbackMessagePackWebHandler::canHandle(AsyncWebServerRequest* request) const { - if (!_onRequest || !request->isHTTP() || !(_method & request->method())) +bool AsyncCallbackMessagePackWebHandler::canHandle(AsyncWebServerRequest *request) const { + if (!_onRequest || !request->isHTTP() || !(_method & request->method())) { return false; + } - if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) + if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) { return false; + } - if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack)) + if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack)) { return false; + } return true; } -void AsyncCallbackMessagePackWebHandler::handleRequest(AsyncWebServerRequest* request) { +void AsyncCallbackMessagePackWebHandler::handleRequest(AsyncWebServerRequest *request) { if (_onRequest) { if (request->method() == HTTP_GET) { JsonVariant json; @@ -65,17 +72,17 @@ void AsyncCallbackMessagePackWebHandler::handleRequest(AsyncWebServerRequest* re return; } else if (request->_tempObject != NULL) { - #if ARDUINOJSON_VERSION_MAJOR == 6 +#if ARDUINOJSON_VERSION_MAJOR == 6 DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize); - DeserializationError error = deserializeMsgPack(jsonBuffer, (uint8_t*)(request->_tempObject)); + DeserializationError error = deserializeMsgPack(jsonBuffer, (uint8_t *)(request->_tempObject)); if (!error) { JsonVariant json = jsonBuffer.as(); - #else +#else JsonDocument jsonBuffer; - DeserializationError error = deserializeMsgPack(jsonBuffer, (uint8_t*)(request->_tempObject)); + DeserializationError error = deserializeMsgPack(jsonBuffer, (uint8_t *)(request->_tempObject)); if (!error) { JsonVariant json = jsonBuffer.as(); - #endif +#endif _onRequest(request, json); return; @@ -87,16 +94,16 @@ void AsyncCallbackMessagePackWebHandler::handleRequest(AsyncWebServerRequest* re } } -void AsyncCallbackMessagePackWebHandler::handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) { +void AsyncCallbackMessagePackWebHandler::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); } if (request->_tempObject != NULL) { - memcpy((uint8_t*)(request->_tempObject) + index, data, len); + memcpy((uint8_t *)(request->_tempObject) + index, data, len); } } } -#endif // ASYNC_MSG_PACK_SUPPORT +#endif // ASYNC_MSG_PACK_SUPPORT diff --git a/src/AsyncMessagePack.h b/src/AsyncMessagePack.h index 78fde92fc..91b832b2a 100644 --- a/src/AsyncMessagePack.h +++ b/src/AsyncMessagePack.h @@ -22,81 +22,102 @@ */ #if __has_include("ArduinoJson.h") - #include - #if ARDUINOJSON_VERSION_MAJOR >= 6 - #define ASYNC_MSG_PACK_SUPPORT 1 - #else - #define ASYNC_MSG_PACK_SUPPORT 0 - #endif // ARDUINOJSON_VERSION_MAJOR >= 6 -#endif // __has_include("ArduinoJson.h") +#include +#if ARDUINOJSON_VERSION_MAJOR >= 6 +#define ASYNC_MSG_PACK_SUPPORT 1 +#else +#define ASYNC_MSG_PACK_SUPPORT 0 +#endif // ARDUINOJSON_VERSION_MAJOR >= 6 +#endif // __has_include("ArduinoJson.h") #if ASYNC_MSG_PACK_SUPPORT == 1 - #include +#include - #include "ChunkPrint.h" +#include "ChunkPrint.h" - #if ARDUINOJSON_VERSION_MAJOR == 6 - #ifndef DYNAMIC_JSON_DOCUMENT_SIZE - #define DYNAMIC_JSON_DOCUMENT_SIZE 1024 - #endif - #endif +#if ARDUINOJSON_VERSION_MAJOR == 6 +#ifndef DYNAMIC_JSON_DOCUMENT_SIZE +#define DYNAMIC_JSON_DOCUMENT_SIZE 1024 +#endif +#endif class AsyncMessagePackResponse : public AsyncAbstractResponse { - protected: - #if ARDUINOJSON_VERSION_MAJOR == 6 - DynamicJsonDocument _jsonBuffer; - #else - JsonDocument _jsonBuffer; - #endif - - JsonVariant _root; - bool _isValid; - - public: - #if ARDUINOJSON_VERSION_MAJOR == 6 - AsyncMessagePackResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE); - #else - AsyncMessagePackResponse(bool isArray = false); - #endif - JsonVariant& getRoot() { return _root; } - bool _sourceValid() const { return _isValid; } - size_t setLength(); - size_t getSize() const { return _jsonBuffer.size(); } - size_t _fillBuffer(uint8_t* data, size_t len); - #if ARDUINOJSON_VERSION_MAJOR >= 6 - bool overflowed() const { return _jsonBuffer.overflowed(); } - #endif +protected: +#if ARDUINOJSON_VERSION_MAJOR == 6 + DynamicJsonDocument _jsonBuffer; +#else + JsonDocument _jsonBuffer; +#endif + + JsonVariant _root; + bool _isValid; + +public: +#if ARDUINOJSON_VERSION_MAJOR == 6 + AsyncMessagePackResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE); +#else + AsyncMessagePackResponse(bool isArray = false); +#endif + JsonVariant &getRoot() { + return _root; + } + bool _sourceValid() const { + return _isValid; + } + size_t setLength(); + size_t getSize() const { + return _jsonBuffer.size(); + } + size_t _fillBuffer(uint8_t *data, size_t len); +#if ARDUINOJSON_VERSION_MAJOR >= 6 + bool overflowed() const { + return _jsonBuffer.overflowed(); + } +#endif }; -typedef std::function ArMessagePackRequestHandlerFunction; +typedef std::function ArMessagePackRequestHandlerFunction; class AsyncCallbackMessagePackWebHandler : public AsyncWebHandler { - protected: - String _uri; - WebRequestMethodComposite _method; - ArMessagePackRequestHandlerFunction _onRequest; - size_t _contentLength; - #if ARDUINOJSON_VERSION_MAJOR == 6 - size_t maxJsonBufferSize; - #endif - size_t _maxContentLength; - - public: - #if ARDUINOJSON_VERSION_MAJOR == 6 - AsyncCallbackMessagePackWebHandler(const String& uri, ArMessagePackRequestHandlerFunction onRequest = nullptr, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE); - #else - AsyncCallbackMessagePackWebHandler(const String& uri, ArMessagePackRequestHandlerFunction onRequest = nullptr); - #endif - - void setMethod(WebRequestMethodComposite method) { _method = method; } - void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; } - void onRequest(ArMessagePackRequestHandlerFunction fn) { _onRequest = fn; } - - bool canHandle(AsyncWebServerRequest* request) const override final; - void handleRequest(AsyncWebServerRequest* request) override final; - void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {} - void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final; - bool isRequestHandlerTrivial() const override final { return !_onRequest; } +protected: + String _uri; + WebRequestMethodComposite _method; + ArMessagePackRequestHandlerFunction _onRequest; + size_t _contentLength; +#if ARDUINOJSON_VERSION_MAJOR == 6 + size_t maxJsonBufferSize; +#endif + size_t _maxContentLength; + +public: +#if ARDUINOJSON_VERSION_MAJOR == 6 + AsyncCallbackMessagePackWebHandler( + const String &uri, ArMessagePackRequestHandlerFunction onRequest = nullptr, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE + ); +#else + AsyncCallbackMessagePackWebHandler(const String &uri, ArMessagePackRequestHandlerFunction onRequest = nullptr); +#endif + + void setMethod(WebRequestMethodComposite method) { + _method = method; + } + void setMaxContentLength(int maxContentLength) { + _maxContentLength = maxContentLength; + } + void onRequest(ArMessagePackRequestHandlerFunction fn) { + _onRequest = fn; + } + + bool canHandle(AsyncWebServerRequest *request) const override final; + void handleRequest(AsyncWebServerRequest *request) override final; + void handleUpload( + __unused AsyncWebServerRequest *request, __unused const String &filename, __unused size_t index, __unused uint8_t *data, __unused size_t len, + __unused bool final + ) override final {} + void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final; + bool isRequestHandlerTrivial() const override final { + return !_onRequest; + } }; -#endif // ASYNC_MSG_PACK_SUPPORT == 1 +#endif // ASYNC_MSG_PACK_SUPPORT == 1 diff --git a/src/AsyncWebHeader.cpp b/src/AsyncWebHeader.cpp index ba271a3b6..b3e174095 100644 --- a/src/AsyncWebHeader.cpp +++ b/src/AsyncWebHeader.cpp @@ -1,11 +1,13 @@ #include -AsyncWebHeader::AsyncWebHeader(const String& data) { - if (!data) +AsyncWebHeader::AsyncWebHeader(const String &data) { + if (!data) { return; + } int index = data.indexOf(':'); - if (index < 0) + if (index < 0) { return; + } _name = data.substring(0, index); _value = data.substring(index + 2); } diff --git a/src/AsyncWebSocket.cpp b/src/AsyncWebSocket.cpp index 9deb74a9d..c9e58501c 100644 --- a/src/AsyncWebSocket.cpp +++ b/src/AsyncWebSocket.cpp @@ -26,28 +26,30 @@ #include #if defined(ESP32) - #if ESP_IDF_VERSION_MAJOR < 5 - #include "BackPort_SHA1Builder.h" - #else - #include - #endif - #include +#if ESP_IDF_VERSION_MAJOR < 5 +#include "BackPort_SHA1Builder.h" +#else +#include +#endif +#include #elif defined(TARGET_RP2040) || defined(ESP8266) - #include +#include #endif using namespace asyncsrv; -size_t webSocketSendFrameWindow(AsyncClient* client) { - if (!client || !client->canSend()) +size_t webSocketSendFrameWindow(AsyncClient *client) { + if (!client || !client->canSend()) { return 0; + } size_t space = client->space(); - if (space < 9) + if (space < 9) { return 0; + } return space - 8; } -size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool mask, uint8_t* data, size_t len) { +size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool mask, uint8_t *data, size_t len) { if (!client || !client->canSend()) { // Serial.println("SF 1"); return 0; @@ -66,18 +68,20 @@ size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool mbuf[2] = rand() % 0xFF; mbuf[3] = rand() % 0xFF; } - if (len > 125) + if (len > 125) { headLen += 2; + } if (space < headLen) { // Serial.println("SF 2"); return 0; } space -= headLen; - if (len > space) + if (len > space) { len = space; + } - uint8_t* buf = (uint8_t*)malloc(headLen); + uint8_t *buf = (uint8_t *)malloc(headLen); if (buf == NULL) { // os_printf("could not malloc %u bytes for frame header\n", headLen); // Serial.println("SF 3"); @@ -85,11 +89,12 @@ size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool } buf[0] = opcode & 0x0F; - if (final) + if (final) { buf[0] |= 0x80; - if (len < 126) + } + if (len < 126) { buf[1] = len & 0x7F; - else { + } else { buf[1] = 126; buf[2] = (uint8_t)((len >> 8) & 0xFF); buf[3] = (uint8_t)(len & 0xFF); @@ -98,7 +103,7 @@ size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool buf[1] |= 0x80; memcpy(buf + (headLen - 4), mbuf, 4); } - if (client->add((const char*)buf, headLen) != headLen) { + if (client->add((const char *)buf, headLen) != headLen) { // os_printf("error adding %lu header bytes\n", headLen); free(buf); // Serial.println("SF 4"); @@ -109,10 +114,11 @@ size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool if (len) { if (len && mask) { size_t i; - for (i = 0; i < len; i++) + for (i = 0; i < len; i++) { data[i] = data[i] ^ mbuf[i % 4]; + } } - if (client->add((const char*)data, len) != len) { + if (client->add((const char *)data, len) != len) { // os_printf("error adding %lu data bytes\n", len); // Serial.println("SF 5"); return 0; @@ -131,8 +137,7 @@ size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool * AsyncWebSocketMessageBuffer */ -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const uint8_t* data, size_t size) - : _buffer(std::make_shared>(size)) { +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const uint8_t *data, size_t size) : _buffer(std::make_shared>(size)) { if (_buffer->capacity() < size) { _buffer->reserve(size); } else { @@ -140,16 +145,16 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const uint8_t* data, si } } -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) - : _buffer(std::make_shared>(size)) { +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) : _buffer(std::make_shared>(size)) { if (_buffer->capacity() < size) { _buffer->reserve(size); } } bool AsyncWebSocketMessageBuffer::reserve(size_t size) { - if (_buffer->capacity() >= size) + if (_buffer->capacity() >= size) { return true; + } _buffer->reserve(size); return _buffer->capacity() >= size; } @@ -159,55 +164,63 @@ bool AsyncWebSocketMessageBuffer::reserve(size_t size) { */ 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) - _len = 0; - else - memcpy(_data, data, len); - } else - _data = NULL; +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); - ~AsyncWebSocketControl() { - if (_data != NULL) - free(_data); + if (_data == NULL) { + _len = 0; + } else { + memcpy(_data, data, len); + } + } else { + _data = NULL; } + } - 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); + ~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 */ -AsyncWebSocketMessage::AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) : _WSbuffer{buffer}, - _opcode(opcode & 0x07), - _mask{mask}, - _status{_WSbuffer ? WS_MSG_SENDING : WS_MSG_ERROR} { -} +AsyncWebSocketMessage::AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) + : _WSbuffer{buffer}, _opcode(opcode & 0x07), _mask{mask}, _status{_WSbuffer ? WS_MSG_SENDING : WS_MSG_ERROR} {} void AsyncWebSocketMessage::ack(size_t len, uint32_t time) { (void)time; @@ -218,18 +231,21 @@ void AsyncWebSocketMessage::ack(size_t len, uint32_t time) { // ets_printf("A: %u\n", len); } -size_t AsyncWebSocketMessage::send(AsyncClient* client) { - if (!client) +size_t AsyncWebSocketMessage::send(AsyncClient *client) { + if (!client) { return 0; + } - if (_status != WS_MSG_SENDING) + if (_status != WS_MSG_SENDING) { return 0; + } if (_acked < _ack) { return 0; } if (_sent == _WSbuffer->size()) { - if (_acked == _ack) + if (_acked == _ack) { _status = WS_MSG_SENT; + } return 0; } if (_sent > _WSbuffer->size()) { @@ -251,7 +267,7 @@ size_t AsyncWebSocketMessage::send(AsyncClient* client) { // ets_printf("W: %u %u\n", _sent - toSend, toSend); bool final = (_sent == _WSbuffer->size()); - uint8_t* dPtr = (uint8_t*)(_WSbuffer->data() + (_sent - toSend)); + uint8_t *dPtr = (uint8_t *)(_WSbuffer->data() + (_sent - toSend)); uint8_t opCode = (toSend && _sent == toSend) ? _opcode : (uint8_t)WS_CONTINUATION; size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend); @@ -268,11 +284,10 @@ size_t AsyncWebSocketMessage::send(AsyncClient* client) { /* * Async WebSocket Client */ -const char* AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING"; +const char *AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING"; const size_t AWSC_PING_PAYLOAD_LEN = 22; -AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest* request, AsyncWebSocket* server) - : _tempObject(NULL) { +AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server) : _tempObject(NULL) { _client = request->client(); _server = server; _clientId = _server->_getNextId(); @@ -281,12 +296,48 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest* request, Async _lastMessageTime = millis(); _keepAlivePeriod = 0; _client->setRxTimeout(0); - _client->onError([](void* r, AsyncClient* c, int8_t error) { (void)c; ((AsyncWebSocketClient*)(r))->_onError(error); }, this); - _client->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; ((AsyncWebSocketClient*)(r))->_onAck(len, time); }, this); - _client->onDisconnect([](void* r, AsyncClient* c) { ((AsyncWebSocketClient*)(r))->_onDisconnect(); delete c; }, this); - _client->onTimeout([](void* r, AsyncClient* c, uint32_t time) { (void)c; ((AsyncWebSocketClient*)(r))->_onTimeout(time); }, this); - _client->onData([](void* r, AsyncClient* c, void* buf, size_t len) { (void)c; ((AsyncWebSocketClient*)(r))->_onData(buf, len); }, this); - _client->onPoll([](void* r, AsyncClient* c) { (void)c; ((AsyncWebSocketClient*)(r))->_onPoll(); }, this); + _client->onError( + [](void *r, AsyncClient *c, int8_t error) { + (void)c; + ((AsyncWebSocketClient *)(r))->_onError(error); + }, + this + ); + _client->onAck( + [](void *r, AsyncClient *c, size_t len, uint32_t time) { + (void)c; + ((AsyncWebSocketClient *)(r))->_onAck(len, time); + }, + this + ); + _client->onDisconnect( + [](void *r, AsyncClient *c) { + ((AsyncWebSocketClient *)(r))->_onDisconnect(); + delete c; + }, + this + ); + _client->onTimeout( + [](void *r, AsyncClient *c, uint32_t time) { + (void)c; + ((AsyncWebSocketClient *)(r))->_onTimeout(time); + }, + this + ); + _client->onData( + [](void *r, AsyncClient *c, void *buf, size_t len) { + (void)c; + ((AsyncWebSocketClient *)(r))->_onData(buf, len); + }, + this + ); + _client->onPoll( + [](void *r, AsyncClient *c) { + (void)c; + ((AsyncWebSocketClient *)(r))->_onPoll(); + }, + this + ); delete request; memset(&_pinfo, 0, sizeof(_pinfo)); } @@ -303,8 +354,9 @@ AsyncWebSocketClient::~AsyncWebSocketClient() { } void AsyncWebSocketClient::_clearQueue() { - while (!_messageQueue.empty() && _messageQueue.front().finished()) + while (!_messageQueue.empty() && _messageQueue.front().finished()) { _messageQueue.pop_front(); + } } void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) { @@ -315,14 +367,15 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) { #endif if (!_controlQueue.empty()) { - auto& head = _controlQueue.front(); + auto &head = _controlQueue.front(); if (head.finished()) { len -= head.len(); if (_status == WS_DISCONNECTING && head.opcode() == WS_DISCONNECT) { _controlQueue.pop_front(); _status = WS_DISCONNECTED; - if (_client) + if (_client) { _client->close(true); + } return; } _controlQueue.pop_front(); @@ -339,8 +392,9 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) { } void AsyncWebSocketClient::_onPoll() { - if (!_client) + if (!_client) { return; + } #ifdef ESP32 std::unique_lock lock(_lock); @@ -351,18 +405,20 @@ void AsyncWebSocketClient::_onPoll() { #ifdef ESP32 lock.unlock(); #endif - ping((uint8_t*)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN); + ping((uint8_t *)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN); } } void AsyncWebSocketClient::_runQueue() { // all calls to this method MUST be protected by a mutex lock! - if (!_client) + if (!_client) { return; + } _clearQueue(); - if (!_controlQueue.empty() && (_messageQueue.empty() || _messageQueue.front().betweenFrames()) && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front().len() - 1)) { + if (!_controlQueue.empty() && (_messageQueue.empty() || _messageQueue.front().betweenFrames()) + && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front().len() - 1)) { _controlQueue.front().send(_client); } else if (!_messageQueue.empty() && _messageQueue.front().betweenFrames() && webSocketSendFrameWindow(_client)) { _messageQueue.front().send(_client); @@ -390,9 +446,10 @@ bool AsyncWebSocketClient::canSend() const { return _messageQueue.size() < WS_MAX_QUEUED_MESSAGES; } -bool AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t* data, size_t len, bool mask) { - if (!_client) +bool AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t *data, size_t len, bool mask) { + if (!_client) { return false; + } #ifdef ESP32 std::lock_guard lock(_lock); @@ -400,15 +457,17 @@ bool AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t* data, si _controlQueue.emplace_back(opcode, data, len, mask); - if (_client && _client->canSend()) + if (_client && _client->canSend()) { _runQueue(); + } return true; } bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) { - if (!_client || buffer->size() == 0 || _status != WS_CONNECTED) + if (!_client || buffer->size() == 0 || _status != WS_CONNECTED) { return false; + } #ifdef ESP32 std::lock_guard lock(_lock); @@ -418,8 +477,9 @@ bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint if (closeWhenFull) { _status = WS_DISCONNECTED; - if (_client) + if (_client) { _client->close(true); + } #ifdef ESP8266 ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: closing connection\n"); @@ -440,15 +500,17 @@ bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint _messageQueue.emplace_back(buffer, opcode, mask); - if (_client && _client->canSend()) + if (_client && _client->canSend()) { _runQueue(); + } return true; } -void AsyncWebSocketClient::close(uint16_t code, const char* message) { - if (_status != WS_CONNECTED) +void AsyncWebSocketClient::close(uint16_t code, const char *message) { + if (_status != WS_CONNECTED) { return; + } _status = WS_DISCONNECTING; @@ -456,18 +518,19 @@ void AsyncWebSocketClient::close(uint16_t code, const char* message) { uint8_t packetLen = 2; if (message != NULL) { size_t mlen = strlen(message); - if (mlen > 123) + if (mlen > 123) { mlen = 123; + } packetLen += mlen; } - char* buf = (char*)malloc(packetLen); + char *buf = (char *)malloc(packetLen); if (buf != NULL) { buf[0] = (uint8_t)(code >> 8); buf[1] = (uint8_t)(code & 0xFF); if (message != NULL) { memcpy(buf + 2, message, packetLen - 2); } - _queueControl(WS_DISCONNECT, (uint8_t*)buf, packetLen); + _queueControl(WS_DISCONNECT, (uint8_t *)buf, packetLen); free(buf); return; } @@ -475,7 +538,7 @@ void AsyncWebSocketClient::close(uint16_t code, const char* message) { _queueControl(WS_DISCONNECT); } -bool AsyncWebSocketClient::ping(const uint8_t* data, size_t len) { +bool AsyncWebSocketClient::ping(const uint8_t *data, size_t len) { return _status == WS_CONNECTED && _queueControl(WS_PING, data, len); } @@ -484,8 +547,9 @@ void AsyncWebSocketClient::_onError(int8_t) { } void AsyncWebSocketClient::_onTimeout(uint32_t time) { - if (!_client) + if (!_client) { return; + } // Serial.println("onTime"); (void)time; _client->close(true); @@ -496,12 +560,12 @@ void AsyncWebSocketClient::_onDisconnect() { _client = nullptr; } -void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { +void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) { _lastMessageTime = millis(); - uint8_t* data = (uint8_t*)pbuf; + uint8_t *data = (uint8_t *)pbuf; while (plen > 0) { if (!_pstate) { - const uint8_t* fdata = data; + const uint8_t *fdata = data; _pinfo.index = 0; _pinfo.final = (fdata[0] & 0x80) != 0; @@ -522,12 +586,14 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { plen -= 2; } else if (_pinfo.len == 127 && plen >= 8) { - _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56; + _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 + | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56; data += 8; plen -= 8; } - if (_pinfo.masked && plen >= 4) { // if ws.close() is called, Safari sends a close frame with plen 2 and masked bit set. We must not decrement plen which is already 0. + if (_pinfo.masked + && plen >= 4) { // if ws.close() is called, Safari sends a close frame with plen 2 and masked bit set. We must not decrement plen which is already 0. memcpy(_pinfo.mask, data, 4); data += 4; plen -= 4; @@ -538,8 +604,9 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { const auto datalast = data[datalen]; if (_pinfo.masked) { - for (size_t i = 0; i < datalen; i++) + for (size_t i = 0; i < datalen; i++) { data[i] ^= _pinfo.mask[(_pinfo.index + i) % 4]; + } } if ((datalen + _pinfo.index) < _pinfo.len) { @@ -551,8 +618,9 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { _pinfo.num = 0; } } - if (datalen > 0) - _server->_handleEvent(this, WS_EVT_DATA, (void*)&_pinfo, data, datalen); + if (datalen > 0) { + _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen); + } _pinfo.index += datalen; } else if ((datalen + _pinfo.index) == _pinfo.len) { @@ -560,33 +628,37 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { if (_pinfo.opcode == WS_DISCONNECT) { if (datalen) { uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1]; - char* reasonString = (char*)(data + 2); + char *reasonString = (char *)(data + 2); if (reasonCode > 1001) { - _server->_handleEvent(this, WS_EVT_ERROR, (void*)&reasonCode, (uint8_t*)reasonString, strlen(reasonString)); + _server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t *)reasonString, strlen(reasonString)); } } if (_status == WS_DISCONNECTING) { _status = WS_DISCONNECTED; - if (_client) + if (_client) { _client->close(true); + } } else { _status = WS_DISCONNECTING; - if (_client) + if (_client) { _client->ackLater(); + } _queueControl(WS_DISCONNECT, data, datalen); } } else if (_pinfo.opcode == WS_PING) { _server->_handleEvent(this, WS_EVT_PING, NULL, NULL, 0); _queueControl(WS_PONG, data, datalen); } else if (_pinfo.opcode == WS_PONG) { - if (datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) + if (datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) { _server->_handleEvent(this, WS_EVT_PONG, NULL, NULL, 0); - } else if (_pinfo.opcode < WS_DISCONNECT) { // continuation or text/binary frame - _server->_handleEvent(this, WS_EVT_DATA, (void*)&_pinfo, data, datalen); - if (_pinfo.final) + } + } else if (_pinfo.opcode < WS_DISCONNECT) { // continuation or text/binary frame + _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen); + if (_pinfo.final) { _pinfo.num = 0; - else + } else { _pinfo.num += 1; + } } } else { // os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len); @@ -595,27 +667,30 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { } // restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0; - if (datalen) + if (datalen) { data[datalen] = datalast; + } data += datalen; plen -= datalen; } } -size_t AsyncWebSocketClient::printf(const char* format, ...) { +size_t AsyncWebSocketClient::printf(const char *format, ...) { va_list arg; va_start(arg, format); size_t len = vsnprintf(nullptr, 0, format, arg); va_end(arg); - if (len == 0) + if (len == 0) { return 0; + } - char* buffer = new char[len + 1]; + char *buffer = new char[len + 1]; - if (!buffer) + if (!buffer) { return 0; + } va_start(arg, format); len = vsnprintf(buffer, len + 1, format, arg); @@ -633,13 +708,15 @@ size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) { size_t len = vsnprintf_P(nullptr, 0, formatP, arg); va_end(arg); - if (len == 0) + if (len == 0) { return 0; + } - char* buffer = new char[len + 1]; + char *buffer = new char[len + 1]; - if (!buffer) + if (!buffer) { return 0; + } va_start(arg, formatP); len = vsnprintf_P(buffer, len + 1, formatP, arg); @@ -652,14 +729,14 @@ size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) { #endif namespace { - AsyncWebSocketSharedBuffer makeSharedBuffer(const uint8_t* message, size_t len) { - auto buffer = std::make_shared>(len); - std::memcpy(buffer->data(), message, len); - return buffer; - } +AsyncWebSocketSharedBuffer makeSharedBuffer(const uint8_t *message, size_t len) { + auto buffer = std::make_shared>(len); + std::memcpy(buffer->data(), message, len); + return buffer; } +} // namespace -bool AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer* buffer) { +bool AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer *buffer) { bool enqueued = false; if (buffer) { enqueued = text(std::move(buffer->_buffer)); @@ -672,34 +749,35 @@ bool AsyncWebSocketClient::text(AsyncWebSocketSharedBuffer buffer) { return _queueMessage(buffer); } -bool AsyncWebSocketClient::text(const uint8_t* message, size_t len) { +bool AsyncWebSocketClient::text(const uint8_t *message, size_t len) { return text(makeSharedBuffer(message, len)); } -bool AsyncWebSocketClient::text(const char* message, size_t len) { - return text((const uint8_t*)message, len); +bool AsyncWebSocketClient::text(const char *message, size_t len) { + return text((const uint8_t *)message, len); } -bool AsyncWebSocketClient::text(const char* message) { +bool AsyncWebSocketClient::text(const char *message) { return text(message, strlen(message)); } -bool AsyncWebSocketClient::text(const String& message) { +bool AsyncWebSocketClient::text(const String &message) { return text(message.c_str(), message.length()); } #ifdef ESP8266 -bool AsyncWebSocketClient::text(const __FlashStringHelper* data) { +bool AsyncWebSocketClient::text(const __FlashStringHelper *data) { PGM_P p = reinterpret_cast(data); size_t n = 0; while (1) { - if (pgm_read_byte(p + n) == 0) + if (pgm_read_byte(p + n) == 0) { break; + } n += 1; } - char* message = (char*)malloc(n + 1); + char *message = (char *)malloc(n + 1); bool enqueued = false; if (message) { memcpy_P(message, p, n); @@ -709,9 +787,9 @@ bool AsyncWebSocketClient::text(const __FlashStringHelper* data) { } return enqueued; } -#endif // ESP8266 +#endif // ESP8266 -bool AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer* buffer) { +bool AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer *buffer) { bool enqueued = false; if (buffer) { enqueued = binary(std::move(buffer->_buffer)); @@ -724,26 +802,26 @@ bool AsyncWebSocketClient::binary(AsyncWebSocketSharedBuffer buffer) { return _queueMessage(buffer, WS_BINARY); } -bool AsyncWebSocketClient::binary(const uint8_t* message, size_t len) { +bool AsyncWebSocketClient::binary(const uint8_t *message, size_t len) { return binary(makeSharedBuffer(message, len)); } -bool AsyncWebSocketClient::binary(const char* message, size_t len) { - return binary((const uint8_t*)message, len); +bool AsyncWebSocketClient::binary(const char *message, size_t len) { + return binary((const uint8_t *)message, len); } -bool AsyncWebSocketClient::binary(const char* message) { +bool AsyncWebSocketClient::binary(const char *message) { return binary(message, strlen(message)); } -bool AsyncWebSocketClient::binary(const String& message) { +bool AsyncWebSocketClient::binary(const String &message) { return binary(message.c_str(), message.length()); } #ifdef ESP8266 -bool AsyncWebSocketClient::binary(const __FlashStringHelper* data, size_t len) { +bool AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len) { PGM_P p = reinterpret_cast(data); - char* message = (char*)malloc(len); + char *message = (char *)malloc(len); bool enqueued = false; if (message) { memcpy_P(message, p, len); @@ -755,15 +833,17 @@ bool AsyncWebSocketClient::binary(const __FlashStringHelper* data, size_t len) { #endif IPAddress AsyncWebSocketClient::remoteIP() const { - if (!_client) + if (!_client) { return IPAddress((uint32_t)0U); + } return _client->remoteIP(); } uint16_t AsyncWebSocketClient::remotePort() const { - if (!_client) + if (!_client) { return 0; + } return _client->remotePort(); } @@ -772,106 +852,124 @@ uint16_t AsyncWebSocketClient::remotePort() const { * Async Web Socket - Each separate socket location */ -void AsyncWebSocket::_handleEvent(AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { +void AsyncWebSocket::_handleEvent(AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { if (_eventHandler != NULL) { _eventHandler(this, client, type, arg, data, len); } } -AsyncWebSocketClient* AsyncWebSocket::_newClient(AsyncWebServerRequest* request) { +AsyncWebSocketClient *AsyncWebSocket::_newClient(AsyncWebServerRequest *request) { _clients.emplace_back(request, this); _handleEvent(&_clients.back(), WS_EVT_CONNECT, request, NULL, 0); return &_clients.back(); } bool AsyncWebSocket::availableForWriteAll() { - return std::none_of(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient& c) { return c.queueIsFull(); }); + return std::none_of(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient &c) { + return c.queueIsFull(); + }); } bool AsyncWebSocket::availableForWrite(uint32_t id) { - const auto iter = std::find_if(std::begin(_clients), std::end(_clients), [id](const AsyncWebSocketClient& c) { return c.id() == id; }); - if (iter == std::end(_clients)) + const auto iter = std::find_if(std::begin(_clients), std::end(_clients), [id](const AsyncWebSocketClient &c) { + return c.id() == id; + }); + if (iter == std::end(_clients)) { return true; + } return !iter->queueIsFull(); } size_t AsyncWebSocket::count() const { - return std::count_if(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient& c) { return c.status() == WS_CONNECTED; }); + return std::count_if(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient &c) { + return c.status() == WS_CONNECTED; + }); } -AsyncWebSocketClient* AsyncWebSocket::client(uint32_t id) { - const auto iter = std::find_if(_clients.begin(), _clients.end(), [id](const AsyncWebSocketClient& c) { return c.id() == id && c.status() == WS_CONNECTED; }); - if (iter == std::end(_clients)) +AsyncWebSocketClient *AsyncWebSocket::client(uint32_t id) { + const auto iter = std::find_if(_clients.begin(), _clients.end(), [id](const AsyncWebSocketClient &c) { + return c.id() == id && c.status() == WS_CONNECTED; + }); + if (iter == std::end(_clients)) { return nullptr; + } return &(*iter); } -void AsyncWebSocket::close(uint32_t id, uint16_t code, const char* message) { - if (AsyncWebSocketClient* c = client(id)) +void AsyncWebSocket::close(uint32_t id, uint16_t code, const char *message) { + if (AsyncWebSocketClient *c = client(id)) { c->close(code, message); + } } -void AsyncWebSocket::closeAll(uint16_t code, const char* message) { - for (auto& c : _clients) - if (c.status() == WS_CONNECTED) +void AsyncWebSocket::closeAll(uint16_t code, const char *message) { + for (auto &c : _clients) { + if (c.status() == WS_CONNECTED) { c.close(code, message); + } + } } void AsyncWebSocket::cleanupClients(uint16_t maxClients) { - if (count() > maxClients) + if (count() > maxClients) { _clients.front().close(); + } for (auto iter = std::begin(_clients); iter != std::end(_clients);) { - if (iter->shouldBeDeleted()) + if (iter->shouldBeDeleted()) { iter = _clients.erase(iter); - else + } else { iter++; + } } } -bool AsyncWebSocket::ping(uint32_t id, const uint8_t* data, size_t len) { - AsyncWebSocketClient* c = client(id); +bool AsyncWebSocket::ping(uint32_t id, const uint8_t *data, size_t len) { + AsyncWebSocketClient *c = client(id); return c && c->ping(data, len); } -AsyncWebSocket::SendStatus AsyncWebSocket::pingAll(const uint8_t* data, size_t len) { +AsyncWebSocket::SendStatus AsyncWebSocket::pingAll(const uint8_t *data, size_t len) { size_t hit = 0; size_t miss = 0; - for (auto& c : _clients) - if (c.status() == WS_CONNECTED && c.ping(data, len)) + for (auto &c : _clients) { + if (c.status() == WS_CONNECTED && c.ping(data, len)) { hit++; - else + } else { miss++; + } + } return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED); } -bool AsyncWebSocket::text(uint32_t id, const uint8_t* message, size_t len) { - AsyncWebSocketClient* c = client(id); +bool AsyncWebSocket::text(uint32_t id, const uint8_t *message, size_t len) { + AsyncWebSocketClient *c = client(id); return c && c->text(makeSharedBuffer(message, len)); } -bool AsyncWebSocket::text(uint32_t id, const char* message, size_t len) { - return text(id, (const uint8_t*)message, len); +bool AsyncWebSocket::text(uint32_t id, const char *message, size_t len) { + return text(id, (const uint8_t *)message, len); } -bool AsyncWebSocket::text(uint32_t id, const char* message) { +bool AsyncWebSocket::text(uint32_t id, const char *message) { return text(id, message, strlen(message)); } -bool AsyncWebSocket::text(uint32_t id, const String& message) { +bool AsyncWebSocket::text(uint32_t id, const String &message) { return text(id, message.c_str(), message.length()); } #ifdef ESP8266 -bool AsyncWebSocket::text(uint32_t id, const __FlashStringHelper* data) { +bool AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *data) { PGM_P p = reinterpret_cast(data); size_t n = 0; while (true) { - if (pgm_read_byte(p + n) == 0) + if (pgm_read_byte(p + n) == 0) { break; + } n += 1; } - char* message = (char*)malloc(n + 1); + char *message = (char *)malloc(n + 1); bool enqueued = false; if (message) { memcpy_P(message, p, n); @@ -881,9 +979,9 @@ bool AsyncWebSocket::text(uint32_t id, const __FlashStringHelper* data) { } return enqueued; } -#endif // ESP8266 +#endif // ESP8266 -bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { +bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer *buffer) { bool enqueued = false; if (buffer) { enqueued = text(id, std::move(buffer->_buffer)); @@ -892,34 +990,35 @@ bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { return enqueued; } bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketSharedBuffer buffer) { - AsyncWebSocketClient* c = client(id); + AsyncWebSocketClient *c = client(id); return c && c->text(buffer); } -AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const uint8_t* message, size_t len) { +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const uint8_t *message, size_t len) { return textAll(makeSharedBuffer(message, len)); } -AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char* message, size_t len) { - return textAll((const uint8_t*)message, len); +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char *message, size_t len) { + return textAll((const uint8_t *)message, len); } -AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char* message) { +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char *message) { return textAll(message, strlen(message)); } -AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const String& message) { +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const String &message) { return textAll(message.c_str(), message.length()); } #ifdef ESP8266 -AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const __FlashStringHelper* data) { +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const __FlashStringHelper *data) { PGM_P p = reinterpret_cast(data); size_t n = 0; while (1) { - if (pgm_read_byte(p + n) == 0) + if (pgm_read_byte(p + n) == 0) { break; + } n += 1; } - char* message = (char*)malloc(n + 1); + char *message = (char *)malloc(n + 1); AsyncWebSocket::SendStatus status = DISCARDED; if (message) { memcpy_P(message, p, n); @@ -929,8 +1028,8 @@ AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const __FlashStringHelper* da } return status; } -#endif // ESP8266 -AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer* buffer) { +#endif // ESP8266 +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer *buffer) { AsyncWebSocket::SendStatus status = DISCARDED; if (buffer) { status = textAll(std::move(buffer->_buffer)); @@ -942,32 +1041,34 @@ AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer* AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketSharedBuffer buffer) { size_t hit = 0; size_t miss = 0; - for (auto& c : _clients) - if (c.status() == WS_CONNECTED && c.text(buffer)) + for (auto &c : _clients) { + if (c.status() == WS_CONNECTED && c.text(buffer)) { hit++; - else + } else { miss++; + } + } return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED); } -bool AsyncWebSocket::binary(uint32_t id, const uint8_t* message, size_t len) { - AsyncWebSocketClient* c = client(id); +bool AsyncWebSocket::binary(uint32_t id, const uint8_t *message, size_t len) { + AsyncWebSocketClient *c = client(id); return c && c->binary(makeSharedBuffer(message, len)); } -bool AsyncWebSocket::binary(uint32_t id, const char* message, size_t len) { - return binary(id, (const uint8_t*)message, len); +bool AsyncWebSocket::binary(uint32_t id, const char *message, size_t len) { + return binary(id, (const uint8_t *)message, len); } -bool AsyncWebSocket::binary(uint32_t id, const char* message) { +bool AsyncWebSocket::binary(uint32_t id, const char *message) { return binary(id, message, strlen(message)); } -bool AsyncWebSocket::binary(uint32_t id, const String& message) { +bool AsyncWebSocket::binary(uint32_t id, const String &message) { return binary(id, message.c_str(), message.length()); } #ifdef ESP8266 -bool AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper* data, size_t len) { +bool AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper *data, size_t len) { PGM_P p = reinterpret_cast(data); - char* message = (char*)malloc(len); + char *message = (char *)malloc(len); bool enqueued = false; if (message) { memcpy_P(message, p, len); @@ -976,9 +1077,9 @@ bool AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper* data, size_t } return enqueued; } -#endif // ESP8266 +#endif // ESP8266 -bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { +bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer *buffer) { bool enqueued = false; if (buffer) { enqueued = binary(id, std::move(buffer->_buffer)); @@ -987,27 +1088,27 @@ bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { return enqueued; } bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketSharedBuffer buffer) { - AsyncWebSocketClient* c = client(id); + AsyncWebSocketClient *c = client(id); return c && c->binary(buffer); } -AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const uint8_t* message, size_t len) { +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const uint8_t *message, size_t len) { return binaryAll(makeSharedBuffer(message, len)); } -AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const char* message, size_t len) { - return binaryAll((const uint8_t*)message, len); +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const char *message, size_t len) { + return binaryAll((const uint8_t *)message, len); } -AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const char* message) { +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const char *message) { return binaryAll(message, strlen(message)); } -AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const String& message) { +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const String &message) { return binaryAll(message.c_str(), message.length()); } #ifdef ESP8266 -AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const __FlashStringHelper* data, size_t len) { +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const __FlashStringHelper *data, size_t len) { PGM_P p = reinterpret_cast(data); - char* message = (char*)malloc(len); + char *message = (char *)malloc(len); AsyncWebSocket::SendStatus status = DISCARDED; if (message) { memcpy_P(message, p, len); @@ -1016,9 +1117,9 @@ AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const __FlashStringHelper* } return status; } -#endif // ESP8266 +#endif // ESP8266 -AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer* buffer) { +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer *buffer) { AsyncWebSocket::SendStatus status = DISCARDED; if (buffer) { status = binaryAll(std::move(buffer->_buffer)); @@ -1029,16 +1130,18 @@ AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketSharedBuffer buffer) { size_t hit = 0; size_t miss = 0; - for (auto& c : _clients) - if (c.status() == WS_CONNECTED && c.binary(buffer)) + for (auto &c : _clients) { + if (c.status() == WS_CONNECTED && c.binary(buffer)) { hit++; - else + } else { miss++; + } + } return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED); } -size_t AsyncWebSocket::printf(uint32_t id, const char* format, ...) { - AsyncWebSocketClient* c = client(id); +size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...) { + AsyncWebSocketClient *c = client(id); if (c) { va_list arg; va_start(arg, format); @@ -1049,19 +1152,21 @@ size_t AsyncWebSocket::printf(uint32_t id, const char* format, ...) { return 0; } -size_t AsyncWebSocket::printfAll(const char* format, ...) { +size_t AsyncWebSocket::printfAll(const char *format, ...) { va_list arg; va_start(arg, format); size_t len = vsnprintf(nullptr, 0, format, arg); va_end(arg); - if (len == 0) + if (len == 0) { return 0; + } - char* buffer = new char[len + 1]; + char *buffer = new char[len + 1]; - if (!buffer) + if (!buffer) { return 0; + } va_start(arg, format); len = vsnprintf(buffer, len + 1, format, arg); @@ -1074,7 +1179,7 @@ size_t AsyncWebSocket::printfAll(const char* format, ...) { #ifdef ESP8266 size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...) { - AsyncWebSocketClient* c = client(id); + AsyncWebSocketClient *c = client(id); if (c != NULL) { va_list arg; va_start(arg, formatP); @@ -1091,13 +1196,15 @@ size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) { size_t len = vsnprintf_P(nullptr, 0, formatP, arg); va_end(arg); - if (len == 0) + if (len == 0) { return 0; + } - char* buffer = new char[len + 1]; + char *buffer = new char[len + 1]; - if (!buffer) + if (!buffer) { return 0; + } va_start(arg, formatP); len = vsnprintf_P(buffer, len + 1, formatP, arg); @@ -1131,11 +1238,11 @@ const char __WS_STR_UUID[] PROGMEM = {"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"}; #define WS_STR_ACCEPT FPSTR(__WS_STR_ACCEPT) #define WS_STR_UUID FPSTR(__WS_STR_UUID) -bool AsyncWebSocket::canHandle(AsyncWebServerRequest* request) const { +bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request) const { return _enabled && request->isWebSocketUpgrade() && request->url().equals(_url); } -void AsyncWebSocket::handleRequest(AsyncWebServerRequest* request) { +void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request) { if (!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)) { request->send(400); return; @@ -1146,25 +1253,25 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest* request) { return; } } - const AsyncWebHeader* version = request->getHeader(WS_STR_VERSION); + const AsyncWebHeader *version = request->getHeader(WS_STR_VERSION); if (version->value().toInt() != 13) { - AsyncWebServerResponse* response = request->beginResponse(400); + AsyncWebServerResponse *response = request->beginResponse(400); response->addHeader(WS_STR_VERSION, T_13); request->send(response); return; } - const AsyncWebHeader* key = request->getHeader(WS_STR_KEY); - AsyncWebServerResponse* response = new AsyncWebSocketResponse(key->value(), this); + const AsyncWebHeader *key = request->getHeader(WS_STR_KEY); + AsyncWebServerResponse *response = new AsyncWebSocketResponse(key->value(), this); if (request->hasHeader(WS_STR_PROTOCOL)) { - const AsyncWebHeader* protocol = request->getHeader(WS_STR_PROTOCOL); + const AsyncWebHeader *protocol = request->getHeader(WS_STR_PROTOCOL); // ToDo: check protocol response->addHeader(WS_STR_PROTOCOL, protocol->value()); } request->send(response); } -AsyncWebSocketMessageBuffer* AsyncWebSocket::makeBuffer(size_t size) { - AsyncWebSocketMessageBuffer* buffer = new AsyncWebSocketMessageBuffer(size); +AsyncWebSocketMessageBuffer *AsyncWebSocket::makeBuffer(size_t size) { + AsyncWebSocketMessageBuffer *buffer = new AsyncWebSocketMessageBuffer(size); if (buffer->length() != size) { delete buffer; return nullptr; @@ -1173,8 +1280,8 @@ AsyncWebSocketMessageBuffer* AsyncWebSocket::makeBuffer(size_t size) { } } -AsyncWebSocketMessageBuffer* AsyncWebSocket::makeBuffer(const uint8_t* data, size_t size) { - AsyncWebSocketMessageBuffer* buffer = new AsyncWebSocketMessageBuffer(data, size); +AsyncWebSocketMessageBuffer *AsyncWebSocket::makeBuffer(const uint8_t *data, size_t size) { + AsyncWebSocketMessageBuffer *buffer = new AsyncWebSocketMessageBuffer(data, size); if (buffer->length() != size) { delete buffer; return nullptr; @@ -1188,7 +1295,7 @@ AsyncWebSocketMessageBuffer* AsyncWebSocket::makeBuffer(const uint8_t* data, siz * Authentication code from https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L480 */ -AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket* server) { +AsyncWebSocketResponse::AsyncWebSocketResponse(const String &key, AsyncWebSocket *server) { _server = server; _code = 101; _sendContentLength = false; @@ -1205,20 +1312,20 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket k.concat(WS_STR_UUID); SHA1Builder sha1; sha1.begin(); - sha1.add((const uint8_t*)k.c_str(), k.length()); + sha1.add((const uint8_t *)k.c_str(), k.length()); sha1.calculate(); sha1.getBytes(hash); #endif base64_encodestate _state; base64_init_encodestate(&_state); - int len = base64_encode_block((const char*)hash, 20, buffer, &_state); + int len = base64_encode_block((const char *)hash, 20, buffer, &_state); len = base64_encode_blockend((buffer + len), &_state); addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE); addHeader(WS_STR_UPGRADE, T_WS); addHeader(WS_STR_ACCEPT, buffer); } -void AsyncWebSocketResponse::_respond(AsyncWebServerRequest* request) { +void AsyncWebSocketResponse::_respond(AsyncWebServerRequest *request) { if (_state == RESPONSE_FAILED) { request->client()->close(true); return; @@ -1229,11 +1336,12 @@ void AsyncWebSocketResponse::_respond(AsyncWebServerRequest* request) { _state = RESPONSE_WAIT_ACK; } -size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) { +size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { (void)time; - if (len) + if (len) { _server->_newClient(request); + } return 0; } diff --git a/src/AsyncWebSocket.h b/src/AsyncWebSocket.h index 671c8664f..541ee7415 100644 --- a/src/AsyncWebSocket.h +++ b/src/AsyncWebSocket.h @@ -23,21 +23,21 @@ #include #ifdef ESP32 - #include - #include - #ifndef WS_MAX_QUEUED_MESSAGES - #define WS_MAX_QUEUED_MESSAGES 32 - #endif +#include +#include +#ifndef WS_MAX_QUEUED_MESSAGES +#define WS_MAX_QUEUED_MESSAGES 32 +#endif #elif defined(ESP8266) - #include - #ifndef WS_MAX_QUEUED_MESSAGES - #define WS_MAX_QUEUED_MESSAGES 8 - #endif +#include +#ifndef WS_MAX_QUEUED_MESSAGES +#define WS_MAX_QUEUED_MESSAGES 8 +#endif #elif defined(TARGET_RP2040) - #include - #ifndef WS_MAX_QUEUED_MESSAGES - #define WS_MAX_QUEUED_MESSAGES 32 - #endif +#include +#ifndef WS_MAX_QUEUED_MESSAGES +#define WS_MAX_QUEUED_MESSAGES 32 +#endif #endif #include @@ -45,18 +45,18 @@ #include #ifdef ESP8266 - #include - #ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library - #include <../src/Hash.h> - #endif +#include +#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library +#include <../src/Hash.h> +#endif #endif #ifndef DEFAULT_MAX_WS_CLIENTS - #ifdef ESP32 - #define DEFAULT_MAX_WS_CLIENTS 8 - #else - #define DEFAULT_MAX_WS_CLIENTS 4 - #endif +#ifdef ESP32 +#define DEFAULT_MAX_WS_CLIENTS 8 +#else +#define DEFAULT_MAX_WS_CLIENTS 4 +#endif #endif using AsyncWebSocketSharedBuffer = std::shared_ptr>; @@ -67,311 +67,367 @@ class AsyncWebSocketClient; class AsyncWebSocketControl; typedef struct { - /** Message type as defined by enum AwsFrameType. + /** Message type as defined by enum AwsFrameType. * Note: Applications will only see WS_TEXT and WS_BINARY. * All other types are handled by the library. */ - uint8_t message_opcode; - /** Frame number of a fragmented message. */ - uint32_t num; - /** Is this the last frame in a fragmented message ?*/ - uint8_t final; - /** Is this frame masked? */ - uint8_t masked; - /** Message type as defined by enum AwsFrameType. + uint8_t message_opcode; + /** Frame number of a fragmented message. */ + uint32_t num; + /** Is this the last frame in a fragmented message ?*/ + uint8_t final; + /** Is this frame masked? */ + uint8_t masked; + /** Message type as defined by enum AwsFrameType. * This value is the same as message_opcode for non-fragmented * messages, but may also be WS_CONTINUATION in a fragmented message. */ - uint8_t opcode; - /** Length of the current frame. + uint8_t opcode; + /** Length of the current frame. * This equals the total length of the message if num == 0 && final == true */ - uint64_t len; - /** Mask key */ - uint8_t mask[4]; - /** Offset of the data inside the current frame. */ - uint64_t index; + uint64_t len; + /** Mask key */ + uint8_t mask[4]; + /** Offset of the data inside the current frame. */ + uint64_t index; } AwsFrameInfo; -typedef enum { WS_DISCONNECTED, - WS_CONNECTED, - WS_DISCONNECTING } AwsClientStatus; -typedef enum { WS_CONTINUATION, - WS_TEXT, - WS_BINARY, - WS_DISCONNECT = 0x08, - WS_PING, - WS_PONG } AwsFrameType; -typedef enum { WS_MSG_SENDING, - WS_MSG_SENT, - WS_MSG_ERROR } AwsMessageStatus; -typedef enum { WS_EVT_CONNECT, - WS_EVT_DISCONNECT, - WS_EVT_PING, - WS_EVT_PONG, - WS_EVT_ERROR, - WS_EVT_DATA } AwsEventType; +typedef enum { + WS_DISCONNECTED, + WS_CONNECTED, + WS_DISCONNECTING +} AwsClientStatus; +typedef enum { + WS_CONTINUATION, + WS_TEXT, + WS_BINARY, + WS_DISCONNECT = 0x08, + WS_PING, + WS_PONG +} AwsFrameType; +typedef enum { + WS_MSG_SENDING, + WS_MSG_SENT, + WS_MSG_ERROR +} AwsMessageStatus; +typedef enum { + WS_EVT_CONNECT, + WS_EVT_DISCONNECT, + WS_EVT_PING, + WS_EVT_PONG, + WS_EVT_ERROR, + WS_EVT_DATA +} AwsEventType; class AsyncWebSocketMessageBuffer { - friend AsyncWebSocket; - friend AsyncWebSocketClient; - - private: - AsyncWebSocketSharedBuffer _buffer; - - public: - AsyncWebSocketMessageBuffer() {} - explicit AsyncWebSocketMessageBuffer(size_t size); - AsyncWebSocketMessageBuffer(const uint8_t* data, size_t size); - //~AsyncWebSocketMessageBuffer(); - bool reserve(size_t size); - uint8_t* get() { return _buffer->data(); } - size_t length() const { return _buffer->size(); } + friend AsyncWebSocket; + friend AsyncWebSocketClient; + +private: + AsyncWebSocketSharedBuffer _buffer; + +public: + AsyncWebSocketMessageBuffer() {} + explicit AsyncWebSocketMessageBuffer(size_t size); + AsyncWebSocketMessageBuffer(const uint8_t *data, size_t size); + //~AsyncWebSocketMessageBuffer(); + bool reserve(size_t size); + uint8_t *get() { + return _buffer->data(); + } + size_t length() const { + return _buffer->size(); + } }; class AsyncWebSocketMessage { - private: - AsyncWebSocketSharedBuffer _WSbuffer; - uint8_t _opcode{WS_TEXT}; - bool _mask{false}; - AwsMessageStatus _status{WS_MSG_ERROR}; - size_t _sent{}; - size_t _ack{}; - size_t _acked{}; - - public: - AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false); - - bool finished() const { return _status != WS_MSG_SENDING; } - bool betweenFrames() const { return _acked == _ack; } - - void ack(size_t len, uint32_t time); - size_t send(AsyncClient* client); +private: + AsyncWebSocketSharedBuffer _WSbuffer; + uint8_t _opcode{WS_TEXT}; + bool _mask{false}; + AwsMessageStatus _status{WS_MSG_ERROR}; + size_t _sent{}; + size_t _ack{}; + size_t _acked{}; + +public: + AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false); + + bool finished() const { + return _status != WS_MSG_SENDING; + } + bool betweenFrames() const { + return _acked == _ack; + } + + void ack(size_t len, uint32_t time); + size_t send(AsyncClient *client); }; class AsyncWebSocketClient { - private: - AsyncClient* _client; - AsyncWebSocket* _server; - uint32_t _clientId; - AwsClientStatus _status; +private: + AsyncClient *_client; + AsyncWebSocket *_server; + uint32_t _clientId; + AwsClientStatus _status; #ifdef ESP32 - mutable std::mutex _lock; + mutable std::mutex _lock; #endif - std::deque _controlQueue; - std::deque _messageQueue; - bool closeWhenFull = true; - - uint8_t _pstate; - AwsFrameInfo _pinfo; - - uint32_t _lastMessageTime; - uint32_t _keepAlivePeriod; - - bool _queueControl(uint8_t opcode, const uint8_t* data = NULL, size_t len = 0, bool mask = false); - bool _queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false); - void _runQueue(); - void _clearQueue(); - - public: - void* _tempObject; - - AsyncWebSocketClient(AsyncWebServerRequest* request, AsyncWebSocket* server); - ~AsyncWebSocketClient(); - - // client id increments for the given server - uint32_t id() const { return _clientId; } - AwsClientStatus status() const { return _status; } - AsyncClient* client() { return _client; } - const AsyncClient* client() const { return _client; } - AsyncWebSocket* server() { return _server; } - const AsyncWebSocket* server() const { return _server; } - AwsFrameInfo const& pinfo() const { return _pinfo; } - - // - If "true" (default), the connection will be closed if the message queue is full. - // This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection. - // The big issue with this behavior is that is can cause the UI to automatically re-create a new WS connection, which can be filled again, - // and so on, causing a resource exhaustion. - // - // - If "false", the incoming message will be discarded if the queue is full. - // This is the default behavior in the original ESPAsyncWebServer library from me-no-dev. - // This behavior allows the best performance at the expense of unreliable message delivery in case the queue is full (some messages may be lost). - // - // - In any case, when the queue is full, a message is logged. - // - IT is recommended to use the methods queueIsFull(), availableForWriteAll(), availableForWrite(clientId) to check if the queue is full before sending a message. - // - // Usage: - // - can be set in the onEvent listener when connecting (event type is: WS_EVT_CONNECT) - // - // Use cases:, - // - if using websocket to send logging messages, maybe some loss is acceptable. - // - But if using websocket to send UI update messages, maybe the connection should be closed and the UI redrawn. - void setCloseClientOnQueueFull(bool close) { closeWhenFull = close; } - bool willCloseClientOnQueueFull() const { return closeWhenFull; } - - IPAddress remoteIP() const; - uint16_t remotePort() const; - - bool shouldBeDeleted() const { return !_client; } - - // control frames - void close(uint16_t code = 0, const char* message = NULL); - bool ping(const uint8_t* data = NULL, size_t len = 0); - - // set auto-ping period in seconds. disabled if zero (default) - void keepAlivePeriod(uint16_t seconds) { - _keepAlivePeriod = seconds * 1000; - } - uint16_t keepAlivePeriod() { - return (uint16_t)(_keepAlivePeriod / 1000); - } - - // data packets - void message(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false) { _queueMessage(buffer, opcode, mask); } - bool queueIsFull() const; - size_t queueLen() const; - - size_t printf(const char* format, ...) __attribute__((format(printf, 2, 3))); - - bool text(AsyncWebSocketSharedBuffer buffer); - bool text(const uint8_t* message, size_t len); - bool text(const char* message, size_t len); - bool text(const char* message); - bool text(const String& message); - bool text(AsyncWebSocketMessageBuffer* buffer); - - bool binary(AsyncWebSocketSharedBuffer buffer); - bool binary(const uint8_t* message, size_t len); - bool binary(const char* message, size_t len); - bool binary(const char* message); - bool binary(const String& message); - bool binary(AsyncWebSocketMessageBuffer* buffer); - - bool canSend() const; - - // system callbacks (do not call) - void _onAck(size_t len, uint32_t time); - void _onError(int8_t); - void _onPoll(); - void _onTimeout(uint32_t time); - void _onDisconnect(); - void _onData(void* pbuf, size_t plen); + std::deque _controlQueue; + std::deque _messageQueue; + bool closeWhenFull = true; + + uint8_t _pstate; + AwsFrameInfo _pinfo; + + uint32_t _lastMessageTime; + uint32_t _keepAlivePeriod; + + bool _queueControl(uint8_t opcode, const uint8_t *data = NULL, size_t len = 0, bool mask = false); + bool _queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false); + void _runQueue(); + void _clearQueue(); + +public: + void *_tempObject; + + AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server); + ~AsyncWebSocketClient(); + + // client id increments for the given server + uint32_t id() const { + return _clientId; + } + AwsClientStatus status() const { + return _status; + } + AsyncClient *client() { + return _client; + } + const AsyncClient *client() const { + return _client; + } + AsyncWebSocket *server() { + return _server; + } + const AsyncWebSocket *server() const { + return _server; + } + AwsFrameInfo const &pinfo() const { + return _pinfo; + } + + // - If "true" (default), the connection will be closed if the message queue is full. + // This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection. + // The big issue with this behavior is that is can cause the UI to automatically re-create a new WS connection, which can be filled again, + // and so on, causing a resource exhaustion. + // + // - If "false", the incoming message will be discarded if the queue is full. + // This is the default behavior in the original ESPAsyncWebServer library from me-no-dev. + // This behavior allows the best performance at the expense of unreliable message delivery in case the queue is full (some messages may be lost). + // + // - In any case, when the queue is full, a message is logged. + // - IT is recommended to use the methods queueIsFull(), availableForWriteAll(), availableForWrite(clientId) to check if the queue is full before sending a message. + // + // Usage: + // - can be set in the onEvent listener when connecting (event type is: WS_EVT_CONNECT) + // + // Use cases:, + // - if using websocket to send logging messages, maybe some loss is acceptable. + // - But if using websocket to send UI update messages, maybe the connection should be closed and the UI redrawn. + void setCloseClientOnQueueFull(bool close) { + closeWhenFull = close; + } + bool willCloseClientOnQueueFull() const { + return closeWhenFull; + } + + IPAddress remoteIP() const; + uint16_t remotePort() const; + + bool shouldBeDeleted() const { + return !_client; + } + + // control frames + void close(uint16_t code = 0, const char *message = NULL); + bool ping(const uint8_t *data = NULL, size_t len = 0); + + // set auto-ping period in seconds. disabled if zero (default) + void keepAlivePeriod(uint16_t seconds) { + _keepAlivePeriod = seconds * 1000; + } + uint16_t keepAlivePeriod() { + return (uint16_t)(_keepAlivePeriod / 1000); + } + + // data packets + void message(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false) { + _queueMessage(buffer, opcode, mask); + } + bool queueIsFull() const; + size_t queueLen() const; + + size_t printf(const char *format, ...) __attribute__((format(printf, 2, 3))); + + bool text(AsyncWebSocketSharedBuffer buffer); + bool text(const uint8_t *message, size_t len); + bool text(const char *message, size_t len); + bool text(const char *message); + bool text(const String &message); + bool text(AsyncWebSocketMessageBuffer *buffer); + + bool binary(AsyncWebSocketSharedBuffer buffer); + bool binary(const uint8_t *message, size_t len); + bool binary(const char *message, size_t len); + bool binary(const char *message); + bool binary(const String &message); + bool binary(AsyncWebSocketMessageBuffer *buffer); + + bool canSend() const; + + // system callbacks (do not call) + void _onAck(size_t len, uint32_t time); + void _onError(int8_t); + void _onPoll(); + void _onTimeout(uint32_t time); + void _onDisconnect(); + void _onData(void *pbuf, size_t plen); #ifdef ESP8266 - size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); - bool text(const __FlashStringHelper* message); - bool binary(const __FlashStringHelper* message, size_t len); + size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); + bool text(const __FlashStringHelper *message); + bool binary(const __FlashStringHelper *message, size_t len); #endif }; -using AwsHandshakeHandler = std::function; -using AwsEventHandler = std::function; +using AwsHandshakeHandler = std::function; +using AwsEventHandler = std::function; // WebServer Handler implementation that plays the role of a socket server class AsyncWebSocket : public AsyncWebHandler { - private: - String _url; - std::list _clients; - uint32_t _cNextId; - AwsEventHandler _eventHandler{nullptr}; - AwsHandshakeHandler _handshakeHandler; - bool _enabled; +private: + String _url; + std::list _clients; + uint32_t _cNextId; + AwsEventHandler _eventHandler{nullptr}; + AwsHandshakeHandler _handshakeHandler; + bool _enabled; #ifdef ESP32 - mutable std::mutex _lock; + mutable std::mutex _lock; #endif - public: - typedef enum { - DISCARDED = 0, - ENQUEUED = 1, - PARTIALLY_ENQUEUED = 2, - } SendStatus; - - explicit AsyncWebSocket(const char* url) : _url(url), _cNextId(1), _enabled(true) {} - AsyncWebSocket(const String& url) : _url(url), _cNextId(1), _enabled(true) {} - ~AsyncWebSocket() {}; - const char* url() const { return _url.c_str(); } - void enable(bool e) { _enabled = e; } - bool enabled() const { return _enabled; } - bool availableForWriteAll(); - bool availableForWrite(uint32_t id); - - size_t count() const; - AsyncWebSocketClient* client(uint32_t id); - bool hasClient(uint32_t id) { return client(id) != nullptr; } - - void close(uint32_t id, uint16_t code = 0, const char* message = NULL); - void closeAll(uint16_t code = 0, const char* message = NULL); - void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS); - - bool ping(uint32_t id, const uint8_t* data = NULL, size_t len = 0); - SendStatus pingAll(const uint8_t* data = NULL, size_t len = 0); // done - - bool text(uint32_t id, const uint8_t* message, size_t len); - bool text(uint32_t id, const char* message, size_t len); - bool text(uint32_t id, const char* message); - bool text(uint32_t id, const String& message); - bool text(uint32_t id, AsyncWebSocketMessageBuffer* buffer); - bool text(uint32_t id, AsyncWebSocketSharedBuffer buffer); - - SendStatus textAll(const uint8_t* message, size_t len); - SendStatus textAll(const char* message, size_t len); - SendStatus textAll(const char* message); - SendStatus textAll(const String& message); - SendStatus textAll(AsyncWebSocketMessageBuffer* buffer); - SendStatus textAll(AsyncWebSocketSharedBuffer buffer); - - bool binary(uint32_t id, const uint8_t* message, size_t len); - bool binary(uint32_t id, const char* message, size_t len); - bool binary(uint32_t id, const char* message); - bool binary(uint32_t id, const String& message); - bool binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer); - bool binary(uint32_t id, AsyncWebSocketSharedBuffer buffer); - - SendStatus binaryAll(const uint8_t* message, size_t len); - SendStatus binaryAll(const char* message, size_t len); - SendStatus binaryAll(const char* message); - SendStatus binaryAll(const String& message); - SendStatus binaryAll(AsyncWebSocketMessageBuffer* buffer); - SendStatus binaryAll(AsyncWebSocketSharedBuffer buffer); - - size_t printf(uint32_t id, const char* format, ...) __attribute__((format(printf, 3, 4))); - size_t printfAll(const char* format, ...) __attribute__((format(printf, 2, 3))); +public: + typedef enum { + DISCARDED = 0, + ENQUEUED = 1, + PARTIALLY_ENQUEUED = 2, + } SendStatus; + + explicit AsyncWebSocket(const char *url) : _url(url), _cNextId(1), _enabled(true) {} + AsyncWebSocket(const String &url) : _url(url), _cNextId(1), _enabled(true) {} + ~AsyncWebSocket(){}; + const char *url() const { + return _url.c_str(); + } + void enable(bool e) { + _enabled = e; + } + bool enabled() const { + return _enabled; + } + bool availableForWriteAll(); + bool availableForWrite(uint32_t id); + + size_t count() const; + AsyncWebSocketClient *client(uint32_t id); + bool hasClient(uint32_t id) { + return client(id) != nullptr; + } + + void close(uint32_t id, uint16_t code = 0, const char *message = NULL); + void closeAll(uint16_t code = 0, const char *message = NULL); + void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS); + + bool ping(uint32_t id, const uint8_t *data = NULL, size_t len = 0); + SendStatus pingAll(const uint8_t *data = NULL, size_t len = 0); // done + + bool text(uint32_t id, const uint8_t *message, size_t len); + bool text(uint32_t id, const char *message, size_t len); + bool text(uint32_t id, const char *message); + bool text(uint32_t id, const String &message); + bool text(uint32_t id, AsyncWebSocketMessageBuffer *buffer); + bool text(uint32_t id, AsyncWebSocketSharedBuffer buffer); + + SendStatus textAll(const uint8_t *message, size_t len); + SendStatus textAll(const char *message, size_t len); + SendStatus textAll(const char *message); + SendStatus textAll(const String &message); + SendStatus textAll(AsyncWebSocketMessageBuffer *buffer); + SendStatus textAll(AsyncWebSocketSharedBuffer buffer); + + bool binary(uint32_t id, const uint8_t *message, size_t len); + bool binary(uint32_t id, const char *message, size_t len); + bool binary(uint32_t id, const char *message); + bool binary(uint32_t id, const String &message); + bool binary(uint32_t id, AsyncWebSocketMessageBuffer *buffer); + bool binary(uint32_t id, AsyncWebSocketSharedBuffer buffer); + + SendStatus binaryAll(const uint8_t *message, size_t len); + SendStatus binaryAll(const char *message, size_t len); + SendStatus binaryAll(const char *message); + SendStatus binaryAll(const String &message); + SendStatus binaryAll(AsyncWebSocketMessageBuffer *buffer); + SendStatus binaryAll(AsyncWebSocketSharedBuffer buffer); + + size_t printf(uint32_t id, const char *format, ...) __attribute__((format(printf, 3, 4))); + size_t printfAll(const char *format, ...) __attribute__((format(printf, 2, 3))); #ifdef ESP8266 - bool text(uint32_t id, const __FlashStringHelper* message); - SendStatus textAll(const __FlashStringHelper* message); - bool binary(uint32_t id, const __FlashStringHelper* message, size_t len); - SendStatus binaryAll(const __FlashStringHelper* message, size_t len); - size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4))); - size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); + bool text(uint32_t id, const __FlashStringHelper *message); + SendStatus textAll(const __FlashStringHelper *message); + bool binary(uint32_t id, const __FlashStringHelper *message, size_t len); + SendStatus binaryAll(const __FlashStringHelper *message, size_t len); + size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4))); + size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); #endif - void onEvent(AwsEventHandler handler) { _eventHandler = handler; } - void handleHandshake(AwsHandshakeHandler handler) { _handshakeHandler = handler; } - - // system callbacks (do not call) - uint32_t _getNextId() { return _cNextId++; } - AsyncWebSocketClient* _newClient(AsyncWebServerRequest* request); - 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; - - // messagebuffer functions/objects. - AsyncWebSocketMessageBuffer* makeBuffer(size_t size = 0); - AsyncWebSocketMessageBuffer* makeBuffer(const uint8_t* data, size_t size); - - std::list& getClients() { return _clients; } + void onEvent(AwsEventHandler handler) { + _eventHandler = handler; + } + void handleHandshake(AwsHandshakeHandler handler) { + _handshakeHandler = handler; + } + + // system callbacks (do not call) + uint32_t _getNextId() { + return _cNextId++; + } + AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request); + 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; + + // messagebuffer functions/objects. + AsyncWebSocketMessageBuffer *makeBuffer(size_t size = 0); + AsyncWebSocketMessageBuffer *makeBuffer(const uint8_t *data, size_t size); + + std::list &getClients() { + return _clients; + } }; // WebServer response to authenticate the socket and detach the tcp client from the web server request class AsyncWebSocketResponse : public AsyncWebServerResponse { - private: - String _content; - AsyncWebSocket* _server; - - public: - AsyncWebSocketResponse(const String& key, AsyncWebSocket* server); - void _respond(AsyncWebServerRequest* request); - size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); - bool _sourceValid() const { return true; } +private: + String _content; + AsyncWebSocket *_server; + +public: + AsyncWebSocketResponse(const String &key, AsyncWebSocket *server); + void _respond(AsyncWebServerRequest *request); + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + bool _sourceValid() const { + return true; + } }; #endif /* ASYNCWEBSOCKET_H_ */ diff --git a/src/BackPort_SHA1Builder.cpp b/src/BackPort_SHA1Builder.cpp index 08ba32ca2..06a73a5b8 100644 --- a/src/BackPort_SHA1Builder.cpp +++ b/src/BackPort_SHA1Builder.cpp @@ -281,4 +281,4 @@ void SHA1Builder::getBytes(uint8_t *output) { memcpy(output, hash, SHA1_HASH_SIZE); } -#endif // ESP_IDF_VERSION_MAJOR < 5 +#endif // ESP_IDF_VERSION_MAJOR < 5 diff --git a/src/BackPort_SHA1Builder.h b/src/BackPort_SHA1Builder.h index 8f518259b..e7eafbef9 100644 --- a/src/BackPort_SHA1Builder.h +++ b/src/BackPort_SHA1Builder.h @@ -24,21 +24,21 @@ #define SHA1_HASH_SIZE 20 class SHA1Builder { - private: - uint32_t total[2]; /* number of bytes processed */ - uint32_t state[5]; /* intermediate digest state */ - unsigned char buffer[64]; /* data block being processed */ - uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */ - - void process(const uint8_t* data); - - public: - void begin(); - void add(const uint8_t* data, size_t len); - void calculate(); - void getBytes(uint8_t* output); +private: + uint32_t total[2]; /* number of bytes processed */ + uint32_t state[5]; /* intermediate digest state */ + unsigned char buffer[64]; /* data block being processed */ + uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */ + + void process(const uint8_t *data); + +public: + void begin(); + void add(const uint8_t *data, size_t len); + void calculate(); + void getBytes(uint8_t *output); }; -#endif // SHA1Builder_h +#endif // SHA1Builder_h -#endif // ESP_IDF_VERSION_MAJOR < 5 +#endif // ESP_IDF_VERSION_MAJOR < 5 diff --git a/src/ChunkPrint.cpp b/src/ChunkPrint.cpp index 8c9717a56..ab1082b27 100644 --- a/src/ChunkPrint.cpp +++ b/src/ChunkPrint.cpp @@ -1,7 +1,6 @@ #include -ChunkPrint::ChunkPrint(uint8_t* destination, size_t from, size_t len) - : _destination(destination), _to_skip(from), _to_write(len), _pos{0} {} +ChunkPrint::ChunkPrint(uint8_t *destination, size_t from, size_t len) : _destination(destination), _to_skip(from), _to_write(len), _pos{0} {} size_t ChunkPrint::write(uint8_t c) { if (_to_skip > 0) { @@ -13,4 +12,4 @@ size_t ChunkPrint::write(uint8_t c) { return 1; } return 0; -} \ No newline at end of file +} diff --git a/src/ChunkPrint.h b/src/ChunkPrint.h index 47988e17c..7afa93245 100644 --- a/src/ChunkPrint.h +++ b/src/ChunkPrint.h @@ -4,15 +4,17 @@ #include class ChunkPrint : public Print { - private: - uint8_t* _destination; - size_t _to_skip; - size_t _to_write; - size_t _pos; +private: + uint8_t *_destination; + size_t _to_skip; + size_t _to_write; + size_t _pos; - public: - ChunkPrint(uint8_t* destination, size_t from, size_t len); - size_t write(uint8_t c); - size_t write(const uint8_t* buffer, size_t size) { return this->Print::write(buffer, size); } +public: + ChunkPrint(uint8_t *destination, size_t from, size_t len); + size_t write(uint8_t c); + size_t write(const uint8_t *buffer, size_t size) { + return this->Print::write(buffer, size); + } }; #endif diff --git a/src/ESPAsyncWebServer.h b/src/ESPAsyncWebServer.h index a1fc4b368..e0031cf11 100644 --- a/src/ESPAsyncWebServer.h +++ b/src/ESPAsyncWebServer.h @@ -32,18 +32,18 @@ #include #ifdef ESP32 - #include - #include +#include +#include #elif defined(ESP8266) - #include - #include +#include +#include #elif defined(TARGET_RP2040) - #include - #include - #include - #include +#include +#include +#include +#include #else - #error Platform not supported +#error Platform not supported #endif #include "literals.h" @@ -55,15 +55,15 @@ #define ASYNCWEBSERVER_FORK_ESP32Async #ifdef ASYNCWEBSERVER_REGEX - #define ASYNCWEBSERVER_REGEX_ATTRIBUTE +#define ASYNCWEBSERVER_REGEX_ATTRIBUTE #else - #define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined"))) +#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined"))) #endif // See https://github.com/ESP32Async/ESPAsyncWebServer/commit/3d3456e9e81502a477f6498c44d0691499dda8f9#diff-646b25b11691c11dce25529e3abce843f0ba4bd07ab75ec9eee7e72b06dbf13fR388-R392 // This setting slowdown chunk serving but avoids crashing or deadlocks in the case where slow chunk responses are created, like file serving form SD Card #ifndef ASYNCWEBSERVER_USE_CHUNK_INFLIGHT - #define ASYNCWEBSERVER_USE_CHUNK_INFLIGHT 1 +#define ASYNCWEBSERVER_USE_CHUNK_INFLIGHT 1 #endif class AsyncWebServer; @@ -81,7 +81,7 @@ class AsyncMiddlewareChain; #if defined(TARGET_RP2040) typedef enum http_method WebRequestMethod; #else - #ifndef WEBSERVER_H +#ifndef WEBSERVER_H typedef enum { HTTP_GET = 0b00000001, HTTP_POST = 0b00000010, @@ -92,20 +92,20 @@ typedef enum { HTTP_OPTIONS = 0b01000000, HTTP_ANY = 0b01111111, } WebRequestMethod; - #endif +#endif #endif #ifndef HAVE_FS_FILE_OPEN_MODE namespace fs { - class FileOpenMode { - public: - static const char* read; - static const char* write; - static const char* append; - }; +class FileOpenMode { +public: + static const char *read; + static const char *write; + static const char *append; }; +}; // namespace fs #else - #include "FileOpenMode.h" +#include "FileOpenMode.h" #endif // if this value is returned when asked for data, packet will not be sent and you will be asked for data again @@ -120,20 +120,31 @@ typedef std::function ArDisconnectHandler; * */ class AsyncWebParameter { - private: - String _name; - String _value; - size_t _size; - bool _isForm; - bool _isFile; - - public: - AsyncWebParameter(const String& name, const String& value, bool form = false, bool file = false, size_t size = 0) : _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {} - const String& name() const { return _name; } - const String& value() const { return _value; } - size_t size() const { return _size; } - bool isPost() const { return _isForm; } - bool isFile() const { return _isFile; } +private: + String _name; + String _value; + size_t _size; + bool _isForm; + bool _isFile; + +public: + AsyncWebParameter(const String &name, const String &value, bool form = false, bool file = false, size_t size = 0) + : _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {} + const String &name() const { + return _name; + } + const String &value() const { + return _value; + } + size_t size() const { + return _size; + } + bool isPost() const { + return _isForm; + } + bool isFile() const { + return _isFile; + } }; /* @@ -141,264 +152,353 @@ class AsyncWebParameter { * */ class AsyncWebHeader { - private: - String _name; - String _value; - - public: - AsyncWebHeader(const 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); - - AsyncWebHeader& operator=(const AsyncWebHeader&) = default; - - const String& name() const { return _name; } - const String& value() const { return _value; } - String toString() const; +private: + String _name; + String _value; + +public: + AsyncWebHeader(const 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); + + AsyncWebHeader &operator=(const AsyncWebHeader &) = default; + + const String &name() const { + return _name; + } + const String &value() const { + return _value; + } + String toString() const; }; /* * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect * */ -typedef enum { RCT_NOT_USED = -1, - RCT_DEFAULT = 0, - RCT_HTTP, - RCT_WS, - RCT_EVENT, - RCT_MAX } RequestedConnectionType; +typedef enum { + RCT_NOT_USED = -1, + RCT_DEFAULT = 0, + RCT_HTTP, + RCT_WS, + RCT_EVENT, + RCT_MAX +} RequestedConnectionType; // this enum is similar to Arduino WebServer's AsyncAuthType and PsychicHttp typedef enum { - AUTH_NONE = 0, // always allow + AUTH_NONE = 0, // always allow AUTH_BASIC = 1, AUTH_DIGEST = 2, AUTH_BEARER = 3, AUTH_OTHER = 4, - AUTH_DENIED = 255, // always returns 401 + AUTH_DENIED = 255, // always returns 401 } AsyncAuthType; -typedef std::function AwsResponseFiller; -typedef std::function AwsTemplateProcessor; +typedef std::function AwsResponseFiller; +typedef std::function AwsTemplateProcessor; class AsyncWebServerRequest { - using File = fs::File; - using FS = fs::FS; - friend class AsyncWebServer; - friend class AsyncCallbackWebHandler; - - private: - AsyncClient* _client; - AsyncWebServer* _server; - AsyncWebHandler* _handler; - AsyncWebServerResponse* _response; - ArDisconnectHandler _onDisconnectfn; - - // response is sent - bool _sent = false; - - String _temp; - uint8_t _parseState; - - uint8_t _version; - WebRequestMethodComposite _method; - String _url; - String _host; - String _contentType; - String _boundary; - String _authorization; - RequestedConnectionType _reqconntype; - AsyncAuthType _authMethod = AsyncAuthType::AUTH_NONE; - bool _isMultipart; - bool _isPlainPost; - bool _expectingContinue; - size_t _contentLength; - size_t _parsedLength; - - std::list _headers; - std::list _params; - std::vector _pathParams; - - std::unordered_map, std::equal_to> _attributes; - - uint8_t _multiParseState; - uint8_t _boundaryPosition; - size_t _itemStartIndex; - size_t _itemSize; - String _itemName; - String _itemFilename; - String _itemType; - String _itemValue; - uint8_t* _itemBuffer; - size_t _itemBufferIndex; - bool _itemIsFile; - - void _onPoll(); - void _onAck(size_t len, uint32_t time); - void _onError(int8_t error); - void _onTimeout(uint32_t time); - void _onDisconnect(); - void _onData(void* buf, size_t len); - - void _addPathParam(const char* param); - - bool _parseReqHead(); - bool _parseReqHeader(); - void _parseLine(); - void _parsePlainPostChar(uint8_t data); - void _parseMultipartPostByte(uint8_t data, bool last); - void _addGetParams(const String& params); - - void _handleUploadStart(); - void _handleUploadByte(uint8_t data, bool last); - void _handleUploadEnd(); - - public: - File _tempFile; - void* _tempObject; - - AsyncWebServerRequest(AsyncWebServer*, AsyncClient*); - ~AsyncWebServerRequest(); - - AsyncClient* client() { return _client; } - uint8_t version() const { return _version; } - WebRequestMethodComposite method() const { return _method; } - const String& url() const { return _url; } - const String& host() const { return _host; } - const String& contentType() const { return _contentType; } - size_t contentLength() const { return _contentLength; } - bool multipart() const { return _isMultipart; } - - const char* methodToString() const; - const char* requestedConnTypeToString() const; - - RequestedConnectionType requestedConnType() const { return _reqconntype; } - bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED) const; - bool isWebSocketUpgrade() const { return _method == HTTP_GET && isExpectedRequestedConnType(RCT_WS); } - bool isSSE() const { return _method == HTTP_GET && isExpectedRequestedConnType(RCT_EVENT); } - bool isHTTP() const { return isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP); } - void onDisconnect(ArDisconnectHandler fn); - - // hash is the string representation of: - // base64(user:pass) for basic or - // user:realm:md5(user:realm:pass) for digest - bool authenticate(const char* hash) const; - bool authenticate(const char* username, const char* credentials, const char* realm = NULL, bool isHash = false) const; - void requestAuthentication(const char* realm = nullptr, bool isDigest = true) { requestAuthentication(isDigest ? AsyncAuthType::AUTH_DIGEST : AsyncAuthType::AUTH_BASIC, realm); } - void requestAuthentication(AsyncAuthType method, const char* realm = nullptr, const char* _authFailMsg = nullptr); - - void setHandler(AsyncWebHandler* handler) { _handler = handler; } + using File = fs::File; + using FS = fs::FS; + friend class AsyncWebServer; + friend class AsyncCallbackWebHandler; + +private: + AsyncClient *_client; + AsyncWebServer *_server; + AsyncWebHandler *_handler; + AsyncWebServerResponse *_response; + ArDisconnectHandler _onDisconnectfn; + + // response is sent + bool _sent = false; + + String _temp; + uint8_t _parseState; + + uint8_t _version; + WebRequestMethodComposite _method; + String _url; + String _host; + String _contentType; + String _boundary; + String _authorization; + RequestedConnectionType _reqconntype; + AsyncAuthType _authMethod = AsyncAuthType::AUTH_NONE; + bool _isMultipart; + bool _isPlainPost; + bool _expectingContinue; + size_t _contentLength; + size_t _parsedLength; + + std::list _headers; + std::list _params; + std::vector _pathParams; + + std::unordered_map, std::equal_to> _attributes; + + uint8_t _multiParseState; + uint8_t _boundaryPosition; + size_t _itemStartIndex; + size_t _itemSize; + String _itemName; + String _itemFilename; + String _itemType; + String _itemValue; + uint8_t *_itemBuffer; + size_t _itemBufferIndex; + bool _itemIsFile; + + void _onPoll(); + void _onAck(size_t len, uint32_t time); + void _onError(int8_t error); + void _onTimeout(uint32_t time); + void _onDisconnect(); + void _onData(void *buf, size_t len); + + void _addPathParam(const char *param); + + bool _parseReqHead(); + bool _parseReqHeader(); + void _parseLine(); + void _parsePlainPostChar(uint8_t data); + void _parseMultipartPostByte(uint8_t data, bool last); + void _addGetParams(const String ¶ms); + + void _handleUploadStart(); + void _handleUploadByte(uint8_t data, bool last); + void _handleUploadEnd(); + +public: + File _tempFile; + void *_tempObject; + + AsyncWebServerRequest(AsyncWebServer *, AsyncClient *); + ~AsyncWebServerRequest(); + + AsyncClient *client() { + return _client; + } + uint8_t version() const { + return _version; + } + WebRequestMethodComposite method() const { + return _method; + } + const String &url() const { + return _url; + } + const String &host() const { + return _host; + } + const String &contentType() const { + return _contentType; + } + size_t contentLength() const { + return _contentLength; + } + bool multipart() const { + return _isMultipart; + } + + const char *methodToString() const; + const char *requestedConnTypeToString() const; + + RequestedConnectionType requestedConnType() const { + return _reqconntype; + } + bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED) + const; + bool isWebSocketUpgrade() const { + return _method == HTTP_GET && isExpectedRequestedConnType(RCT_WS); + } + bool isSSE() const { + return _method == HTTP_GET && isExpectedRequestedConnType(RCT_EVENT); + } + bool isHTTP() const { + return isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP); + } + void onDisconnect(ArDisconnectHandler fn); + + // hash is the string representation of: + // base64(user:pass) for basic or + // user:realm:md5(user:realm:pass) for digest + bool authenticate(const char *hash) const; + bool authenticate(const char *username, const char *credentials, const char *realm = NULL, bool isHash = false) const; + void requestAuthentication(const char *realm = nullptr, bool isDigest = true) { + requestAuthentication(isDigest ? AsyncAuthType::AUTH_DIGEST : AsyncAuthType::AUTH_BASIC, realm); + } + void requestAuthentication(AsyncAuthType method, const char *realm = nullptr, const char *_authFailMsg = nullptr); + + void setHandler(AsyncWebHandler *handler) { + _handler = handler; + } #ifndef ESP8266 - [[deprecated("All headers are now collected. Use removeHeader(name) or AsyncHeaderFreeMiddleware if you really need to free some headers.")]] + [[deprecated("All headers are now collected. Use removeHeader(name) or AsyncHeaderFreeMiddleware if you really need to free some headers.")]] #endif - void addInterestingHeader(__unused const char* name) { - } + void addInterestingHeader(__unused const char *name) { + } #ifndef ESP8266 - [[deprecated("All headers are now collected. Use removeHeader(name) or AsyncHeaderFreeMiddleware if you really need to free some headers.")]] + [[deprecated("All headers are now collected. Use removeHeader(name) or AsyncHeaderFreeMiddleware if you really need to free some headers.")]] #endif - void addInterestingHeader(__unused const String& name) { - } + void addInterestingHeader(__unused const String &name) { + } - /** - * @brief issue HTTP redirect responce with Location header + /** + * @brief issue HTTP redirect response with Location header * * @param url - url to redirect to - * @param code - responce code, default is 302 : temporary redirect + * @param code - response code, default is 302 : temporary redirect */ - void redirect(const char* url, int code = 302); - void redirect(const String& url, int code = 302) { return redirect(url.c_str(), code); }; - - void send(AsyncWebServerResponse* response); - AsyncWebServerResponse* getResponse() const { return _response; } - - void send(int code, const char* contentType = asyncsrv::empty, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType, content, callback)); } - void send(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType.c_str(), content, callback)); } - void send(int code, const String& contentType, const String& content, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType.c_str(), content.c_str(), callback)); } - - void send(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType, content, len, callback)); } - void send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType, content, len, callback)); } + void redirect(const char *url, int code = 302); + void redirect(const String &url, int code = 302) { + return redirect(url.c_str(), code); + }; - 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(AsyncWebServerResponse *response); + AsyncWebServerResponse *getResponse() const { + return _response; + } + + void send(int code, const char *contentType = asyncsrv::empty, const char *content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { + send(beginResponse(code, contentType, content, callback)); + } + void send(int code, const String &contentType, const char *content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { + send(beginResponse(code, contentType.c_str(), content, callback)); + } + void send(int code, const String &contentType, const String &content, AwsTemplateProcessor callback = nullptr) { + send(beginResponse(code, contentType.c_str(), content.c_str(), callback)); + } + + void send(int code, const char *contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback = nullptr) { + send(beginResponse(code, contentType, content, len, callback)); + } + void send(int code, const String &contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback = nullptr) { + 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 String& contentType, bool download = false, AwsTemplateProcessor callback = nullptr) { send(fs, path, contentType.c_str(), download, callback); } - - void send(File content, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr) { - if (content) { - send(beginResponse(content, path, contentType, download, callback)); - } else - send(404); + } + void send(FS &fs, const String &path, const String &contentType, bool download = false, AwsTemplateProcessor callback = nullptr) { + send(fs, path, contentType.c_str(), download, callback); + } + + void send(File content, const String &path, const char *contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr) { + if (content) { + send(beginResponse(content, path, contentType, download, callback)); + } else { + send(404); } - void send(File content, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callback = nullptr) { send(content, path, contentType.c_str(), download, callback); } - - void send(Stream& stream, const char* contentType, size_t len, AwsTemplateProcessor callback = nullptr) { send(beginResponse(stream, contentType, len, callback)); } - void send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr) { send(beginResponse(stream, contentType, len, callback)); } - - void send(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { send(beginResponse(contentType, len, callback, templateCallback)); } - void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { send(beginResponse(contentType, len, callback, templateCallback)); } - - void sendChunked(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { send(beginChunkedResponse(contentType, callback, templateCallback)); } - void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { send(beginChunkedResponse(contentType, callback, templateCallback)); } + } + void send(File content, const String &path, const String &contentType, bool download = false, AwsTemplateProcessor callback = nullptr) { + send(content, path, contentType.c_str(), download, callback); + } + + void send(Stream &stream, const char *contentType, size_t len, AwsTemplateProcessor callback = nullptr) { + send(beginResponse(stream, contentType, len, callback)); + } + void send(Stream &stream, const String &contentType, size_t len, AwsTemplateProcessor callback = nullptr) { + send(beginResponse(stream, contentType, len, callback)); + } + + void send(const char *contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { + send(beginResponse(contentType, len, callback, templateCallback)); + } + void send(const String &contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { + send(beginResponse(contentType, len, callback, templateCallback)); + } + + void sendChunked(const char *contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { + send(beginChunkedResponse(contentType, callback, templateCallback)); + } + void sendChunked(const String &contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { + send(beginChunkedResponse(contentType, callback, templateCallback)); + } #ifndef ESP8266 - [[deprecated("Replaced by send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr)")]] + [[deprecated("Replaced by send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr)")]] #endif - void send_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { - send(code, contentType, content, len, callback); - } + void send_P(int code, const String &contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback = nullptr) { + send(code, contentType, content, len, callback); + } #ifndef ESP8266 - [[deprecated("Replaced by send(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)")]] - void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr) { - send(code, contentType, content, callback); - } + [[deprecated("Replaced by send(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)")]] + void send_P(int code, const String &contentType, PGM_P content, AwsTemplateProcessor callback = nullptr) { + send(code, contentType, content, callback); + } #else - void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr) { - send(beginResponse_P(code, contentType, content, callback)); - } + void send_P(int code, const String &contentType, PGM_P content, AwsTemplateProcessor callback = nullptr) { + send(beginResponse_P(code, contentType, content, callback)); + } #endif - AsyncWebServerResponse* beginResponse(int code, const char* contentType = asyncsrv::empty, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse* beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { return beginResponse(code, contentType.c_str(), content, callback); } - AsyncWebServerResponse* beginResponse(int code, const String& contentType, const String& content, AwsTemplateProcessor callback = nullptr) { return beginResponse(code, contentType.c_str(), content.c_str(), callback); } - - AsyncWebServerResponse* beginResponse(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse* beginResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { return beginResponse(code, contentType.c_str(), content, len, callback); } - - AsyncWebServerResponse* beginResponse(FS& fs, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse* beginResponse(FS& fs, const String& path, const String& contentType = emptyString, bool download = false, AwsTemplateProcessor callback = nullptr) { return beginResponse(fs, path, contentType.c_str(), download, callback); } - - AsyncWebServerResponse* beginResponse(File content, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse* beginResponse(File content, const String& path, const String& contentType = emptyString, bool download = false, AwsTemplateProcessor callback = nullptr) { return beginResponse(content, path, contentType.c_str(), download, callback); } - - AsyncWebServerResponse* beginResponse(Stream& stream, const char* contentType, size_t len, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse* beginResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr) { return beginResponse(stream, contentType.c_str(), len, callback); } - - AsyncWebServerResponse* beginResponse(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - AsyncWebServerResponse* beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { return beginResponse(contentType.c_str(), len, callback, templateCallback); } - - AsyncWebServerResponse* beginChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - AsyncWebServerResponse* beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - - AsyncResponseStream* beginResponseStream(const char* contentType, size_t bufferSize = RESPONSE_STREAM_BUFFER_SIZE); - AsyncResponseStream* beginResponseStream(const String& contentType, size_t bufferSize = RESPONSE_STREAM_BUFFER_SIZE) { return beginResponseStream(contentType.c_str(), bufferSize); } + AsyncWebServerResponse * + beginResponse(int code, const char *contentType = asyncsrv::empty, const char *content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse *beginResponse(int code, const String &contentType, const char *content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { + return beginResponse(code, contentType.c_str(), content, callback); + } + AsyncWebServerResponse *beginResponse(int code, const String &contentType, const String &content, AwsTemplateProcessor callback = nullptr) { + return beginResponse(code, contentType.c_str(), content.c_str(), callback); + } + + AsyncWebServerResponse *beginResponse(int code, const char *contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse *beginResponse(int code, const String &contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback = nullptr) { + return beginResponse(code, contentType.c_str(), content, len, callback); + } + + AsyncWebServerResponse * + beginResponse(FS &fs, const String &path, const char *contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse * + beginResponse(FS &fs, const String &path, const String &contentType = emptyString, bool download = false, AwsTemplateProcessor callback = nullptr) { + return beginResponse(fs, path, contentType.c_str(), download, callback); + } + + AsyncWebServerResponse * + beginResponse(File content, const String &path, const char *contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse * + beginResponse(File content, const String &path, const String &contentType = emptyString, bool download = false, AwsTemplateProcessor callback = nullptr) { + return beginResponse(content, path, contentType.c_str(), download, callback); + } + + AsyncWebServerResponse *beginResponse(Stream &stream, const char *contentType, size_t len, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse *beginResponse(Stream &stream, const String &contentType, size_t len, AwsTemplateProcessor callback = nullptr) { + return beginResponse(stream, contentType.c_str(), len, callback); + } + + AsyncWebServerResponse *beginResponse(const char *contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncWebServerResponse *beginResponse(const String &contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { + return beginResponse(contentType.c_str(), len, callback, templateCallback); + } + + AsyncWebServerResponse *beginChunkedResponse(const char *contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncWebServerResponse *beginChunkedResponse(const String &contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + + AsyncResponseStream *beginResponseStream(const char *contentType, size_t bufferSize = RESPONSE_STREAM_BUFFER_SIZE); + AsyncResponseStream *beginResponseStream(const String &contentType, size_t bufferSize = RESPONSE_STREAM_BUFFER_SIZE) { + return beginResponseStream(contentType.c_str(), bufferSize); + } #ifndef ESP8266 - [[deprecated("Replaced by beginResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr)")]] + [[deprecated("Replaced by beginResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr)")]] #endif - AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { - return beginResponse(code, contentType.c_str(), content, len, callback); - } + AsyncWebServerResponse *beginResponse_P(int code, const String &contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback = nullptr) { + return beginResponse(code, contentType.c_str(), content, len, callback); + } #ifndef ESP8266 - [[deprecated("Replaced by beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)")]] + [[deprecated("Replaced by beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)" + )]] #endif - AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse *beginResponse_P(int code, const String &contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); - /** + /** * @brief Get the Request parameter by name * * @param name @@ -406,113 +506,147 @@ class AsyncWebServerRequest { * @param file * @return const AsyncWebParameter* */ - const AsyncWebParameter* getParam(const char* name, bool post = false, bool file = false) const; + const AsyncWebParameter *getParam(const char *name, bool post = false, bool file = false) const; - const AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const { return getParam(name.c_str(), post, file); }; + const AsyncWebParameter *getParam(const String &name, bool post = false, bool file = false) const { + return getParam(name.c_str(), post, file); + }; #ifdef ESP8266 - const AsyncWebParameter* getParam(const __FlashStringHelper* data, bool post, bool file) const; + const AsyncWebParameter *getParam(const __FlashStringHelper *data, bool post, bool file) const; #endif - /** + /** * @brief Get request parameter by number * i.e., n-th parameter * @param num * @return const AsyncWebParameter* */ - const AsyncWebParameter* getParam(size_t num) const; + const AsyncWebParameter *getParam(size_t num) const; - size_t args() const { return params(); } // get arguments count + size_t args() const { + return params(); + } // get arguments count - // get request argument value by name - const String& arg(const char* name) const; - // get request argument value by name - const String& arg(const String& name) const { return arg(name.c_str()); }; + // get request argument value by name + const String &arg(const char *name) const; + // get request argument value by name + const String &arg(const String &name) const { + return arg(name.c_str()); + }; #ifdef ESP8266 - const String& arg(const __FlashStringHelper* data) const; // get request argument value by F(name) + const String &arg(const __FlashStringHelper *data) const; // get request argument value by F(name) #endif - const String& arg(size_t i) const; // get request argument value by number - const String& argName(size_t i) const; // get request argument name by number - bool hasArg(const char* name) const; // check if argument exists - bool hasArg(const String& name) const { return hasArg(name.c_str()); }; + const String &arg(size_t i) const; // get request argument value by number + const String &argName(size_t i) const; // get request argument name by number + bool hasArg(const char *name) const; // check if argument exists + bool hasArg(const String &name) const { + return hasArg(name.c_str()); + }; #ifdef ESP8266 - bool hasArg(const __FlashStringHelper* data) const; // check if F(argument) exists + bool hasArg(const __FlashStringHelper *data) const; // check if F(argument) exists #endif - const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const; + const String &ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const; - // get request header value by name - const String& header(const char* name) const; - const String& header(const String& name) const { return header(name.c_str()); }; + // get request header value by name + const String &header(const char *name) const; + const String &header(const String &name) const { + return header(name.c_str()); + }; #ifdef ESP8266 - const String& header(const __FlashStringHelper* data) const; // get request header value by F(name) + const String &header(const __FlashStringHelper *data) const; // get request header value by F(name) #endif - const String& header(size_t i) const; // get request header value by number - const String& headerName(size_t i) const; // get request header name by number + const String &header(size_t i) const; // get request header value by number + const String &headerName(size_t i) const; // get request header name by number - size_t headers() const; // get header count + size_t headers() const; // get header count - // check if header exists - bool hasHeader(const char* name) const; - bool hasHeader(const String& name) const { return hasHeader(name.c_str()); }; + // check if header exists + bool hasHeader(const char *name) const; + bool hasHeader(const String &name) const { + return hasHeader(name.c_str()); + }; #ifdef ESP8266 - bool hasHeader(const __FlashStringHelper* data) const; // check if header exists + bool hasHeader(const __FlashStringHelper *data) const; // check if header exists #endif - const AsyncWebHeader* getHeader(const char* name) const; - const AsyncWebHeader* getHeader(const String& name) const { return getHeader(name.c_str()); }; + const AsyncWebHeader *getHeader(const char *name) const; + const AsyncWebHeader *getHeader(const String &name) const { + return getHeader(name.c_str()); + }; #ifdef ESP8266 - const AsyncWebHeader* getHeader(const __FlashStringHelper* data) const; + const AsyncWebHeader *getHeader(const __FlashStringHelper *data) const; #endif - const AsyncWebHeader* getHeader(size_t num) const; + const AsyncWebHeader *getHeader(size_t num) const; - const std::list& getHeaders() const { return _headers; } + const std::list &getHeaders() const { + return _headers; + } - size_t getHeaderNames(std::vector& names) const; + size_t getHeaderNames(std::vector &names) const; - // Remove a header from the request. - // It will free the memory and prevent the header to be seen during request processing. - bool removeHeader(const char* name); - // Remove all request headers. - void removeHeaders() { _headers.clear(); } + // Remove a header from the request. + // It will free the memory and prevent the header to be seen during request processing. + bool removeHeader(const char *name); + // Remove all request headers. + void removeHeaders() { + _headers.clear(); + } - size_t params() const; // get arguments count - bool hasParam(const char* name, bool post = false, bool file = false) const; - bool hasParam(const String& name, bool post = false, bool file = false) const { return hasParam(name.c_str(), post, file); }; + size_t params() const; // get arguments count + bool hasParam(const char *name, bool post = false, bool file = false) const; + bool hasParam(const String &name, bool post = false, bool file = false) const { + return hasParam(name.c_str(), post, file); + }; #ifdef ESP8266 - bool hasParam(const __FlashStringHelper* data, bool post = false, bool file = false) const { return hasParam(String(data).c_str(), post, file); }; + bool hasParam(const __FlashStringHelper *data, bool post = false, bool file = false) const { + return hasParam(String(data).c_str(), post, file); + }; #endif - // REQUEST ATTRIBUTES - - void setAttribute(const char* name, const char* value) { _attributes[name] = value; } - void setAttribute(const char* name, bool value) { _attributes[name] = value ? "1" : emptyString; } - void setAttribute(const char* name, long value) { _attributes[name] = String(value); } - void setAttribute(const char* name, float value, unsigned int decimalPlaces = 2) { _attributes[name] = String(value, decimalPlaces); } - void setAttribute(const char* name, double value, unsigned int decimalPlaces = 2) { _attributes[name] = String(value, decimalPlaces); } - - bool hasAttribute(const char* name) const { return _attributes.find(name) != _attributes.end(); } - - const String& getAttribute(const char* name, const String& defaultValue = emptyString) const; - bool getAttribute(const char* name, bool defaultValue) const; - long getAttribute(const char* name, long defaultValue) const; - float getAttribute(const char* name, float defaultValue) const; - double getAttribute(const char* name, double defaultValue) const; - - String urlDecode(const String& text) const; + // REQUEST ATTRIBUTES + + void setAttribute(const char *name, const char *value) { + _attributes[name] = value; + } + void setAttribute(const char *name, bool value) { + _attributes[name] = value ? "1" : emptyString; + } + void setAttribute(const char *name, long value) { + _attributes[name] = String(value); + } + void setAttribute(const char *name, float value, unsigned int decimalPlaces = 2) { + _attributes[name] = String(value, decimalPlaces); + } + void setAttribute(const char *name, double value, unsigned int decimalPlaces = 2) { + _attributes[name] = String(value, decimalPlaces); + } + + bool hasAttribute(const char *name) const { + return _attributes.find(name) != _attributes.end(); + } + + const String &getAttribute(const char *name, const String &defaultValue = emptyString) const; + bool getAttribute(const char *name, bool defaultValue) const; + long getAttribute(const char *name, long defaultValue) const; + float getAttribute(const char *name, float defaultValue) const; + double getAttribute(const char *name, double defaultValue) const; + + String urlDecode(const String &text) const; }; /* * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server) * */ -using ArRequestFilterFunction = std::function; +using ArRequestFilterFunction = std::function; -bool ON_STA_FILTER(AsyncWebServerRequest* request); +bool ON_STA_FILTER(AsyncWebServerRequest *request); -bool ON_AP_FILTER(AsyncWebServerRequest* request); +bool ON_AP_FILTER(AsyncWebServerRequest *request); /* * MIDDLEWARE :: Request interceptor, assigned to a AsyncWebHandler (or the server), which can be used: @@ -521,178 +655,220 @@ bool ON_AP_FILTER(AsyncWebServerRequest* request); * */ using ArMiddlewareNext = std::function; -using ArMiddlewareCallback = std::function; +using ArMiddlewareCallback = std::function; // Middleware is a base class for all middleware class AsyncMiddleware { - public: - virtual ~AsyncMiddleware() {} - virtual void run(__unused AsyncWebServerRequest* request, __unused ArMiddlewareNext next) { return next(); }; - - private: - friend class AsyncWebHandler; - friend class AsyncEventSource; - friend class AsyncMiddlewareChain; - bool _freeOnRemoval = false; +public: + virtual ~AsyncMiddleware() {} + virtual void run(__unused AsyncWebServerRequest *request, __unused ArMiddlewareNext next) { + return next(); + }; + +private: + friend class AsyncWebHandler; + friend class AsyncEventSource; + friend class AsyncMiddlewareChain; + bool _freeOnRemoval = false; }; // Create a custom middleware by providing an anonymous callback function class AsyncMiddlewareFunction : public AsyncMiddleware { - public: - AsyncMiddlewareFunction(ArMiddlewareCallback fn) : _fn(fn) {} - void run(AsyncWebServerRequest* request, ArMiddlewareNext next) override { return _fn(request, next); }; +public: + AsyncMiddlewareFunction(ArMiddlewareCallback fn) : _fn(fn) {} + void run(AsyncWebServerRequest *request, ArMiddlewareNext next) override { + return _fn(request, next); + }; - private: - ArMiddlewareCallback _fn; +private: + ArMiddlewareCallback _fn; }; // For internal use only: super class to add/remove middleware to server or handlers class AsyncMiddlewareChain { - public: - ~AsyncMiddlewareChain(); +public: + ~AsyncMiddlewareChain(); - void addMiddleware(ArMiddlewareCallback fn); - void addMiddleware(AsyncMiddleware* middleware); - void addMiddlewares(std::vector middlewares); - bool removeMiddleware(AsyncMiddleware* middleware); + void addMiddleware(ArMiddlewareCallback fn); + void addMiddleware(AsyncMiddleware *middleware); + void addMiddlewares(std::vector middlewares); + bool removeMiddleware(AsyncMiddleware *middleware); - // For internal use only - void _runChain(AsyncWebServerRequest* request, ArMiddlewareNext finalizer); + // For internal use only + void _runChain(AsyncWebServerRequest *request, ArMiddlewareNext finalizer); - protected: - std::list _middlewares; +protected: + std::list _middlewares; }; // AsyncAuthenticationMiddleware is a middleware that checks if the request is authenticated class AsyncAuthenticationMiddleware : public AsyncMiddleware { - public: - void setUsername(const char* username); - void setPassword(const char* password); - void setPasswordHash(const char* hash); - - void setRealm(const char* realm) { _realm = realm; } - void setAuthFailureMessage(const char* message) { _authFailMsg = message; } - - // set the authentication method to use - // default is AUTH_NONE: no authentication required - // AUTH_BASIC: basic authentication - // AUTH_DIGEST: digest authentication - // AUTH_BEARER: bearer token authentication - // AUTH_OTHER: other authentication method - // AUTH_DENIED: always return 401 Unauthorized - // if a method is set but no username or password is set, authentication will be ignored - void setAuthType(AsyncAuthType authMethod) { _authMethod = authMethod; } - - // precompute and store the hash value based on the username, password, realm. - // can be used for DIGEST and BASIC to avoid recomputing the hash for each request. - // returns true if the hash was successfully generated and replaced - bool generateHash(); - - // returns true if the username and password (or hash) are set - bool hasCredentials() const { return _hasCreds; } - - bool allowed(AsyncWebServerRequest* request) const; - - void run(AsyncWebServerRequest* request, ArMiddlewareNext next); - - private: - String _username; - String _credentials; - bool _hash = false; - - String _realm = asyncsrv::T_LOGIN_REQ; - AsyncAuthType _authMethod = AsyncAuthType::AUTH_NONE; - String _authFailMsg; - bool _hasCreds = false; +public: + void setUsername(const char *username); + void setPassword(const char *password); + void setPasswordHash(const char *hash); + + void setRealm(const char *realm) { + _realm = realm; + } + void setAuthFailureMessage(const char *message) { + _authFailMsg = message; + } + + // set the authentication method to use + // default is AUTH_NONE: no authentication required + // AUTH_BASIC: basic authentication + // AUTH_DIGEST: digest authentication + // AUTH_BEARER: bearer token authentication + // AUTH_OTHER: other authentication method + // AUTH_DENIED: always return 401 Unauthorized + // if a method is set but no username or password is set, authentication will be ignored + void setAuthType(AsyncAuthType authMethod) { + _authMethod = authMethod; + } + + // precompute and store the hash value based on the username, password, realm. + // can be used for DIGEST and BASIC to avoid recomputing the hash for each request. + // returns true if the hash was successfully generated and replaced + bool generateHash(); + + // returns true if the username and password (or hash) are set + bool hasCredentials() const { + return _hasCreds; + } + + bool allowed(AsyncWebServerRequest *request) const; + + void run(AsyncWebServerRequest *request, ArMiddlewareNext next); + +private: + String _username; + String _credentials; + bool _hash = false; + + String _realm = asyncsrv::T_LOGIN_REQ; + AsyncAuthType _authMethod = AsyncAuthType::AUTH_NONE; + String _authFailMsg; + bool _hasCreds = false; }; -using ArAuthorizeFunction = std::function; +using ArAuthorizeFunction = std::function; // AsyncAuthorizationMiddleware is a middleware that checks if the request is authorized class AsyncAuthorizationMiddleware : public AsyncMiddleware { - public: - AsyncAuthorizationMiddleware(ArAuthorizeFunction authorizeConnectHandler) : _code(403), _authz(authorizeConnectHandler) {} - AsyncAuthorizationMiddleware(int code, ArAuthorizeFunction authorizeConnectHandler) : _code(code), _authz(authorizeConnectHandler) {} +public: + AsyncAuthorizationMiddleware(ArAuthorizeFunction authorizeConnectHandler) : _code(403), _authz(authorizeConnectHandler) {} + AsyncAuthorizationMiddleware(int code, ArAuthorizeFunction authorizeConnectHandler) : _code(code), _authz(authorizeConnectHandler) {} - void run(AsyncWebServerRequest* request, ArMiddlewareNext next) { return _authz && !_authz(request) ? request->send(_code) : next(); } + void run(AsyncWebServerRequest *request, ArMiddlewareNext next) { + return _authz && !_authz(request) ? request->send(_code) : next(); + } - private: - int _code; - ArAuthorizeFunction _authz; +private: + int _code; + ArAuthorizeFunction _authz; }; // remove all headers from the incoming request except the ones provided in the constructor class AsyncHeaderFreeMiddleware : public AsyncMiddleware { - public: - void keep(const char* name) { _toKeep.push_back(name); } - void unKeep(const char* name) { _toKeep.erase(std::remove(_toKeep.begin(), _toKeep.end(), name), _toKeep.end()); } - - void run(AsyncWebServerRequest* request, ArMiddlewareNext next); - - private: - std::vector _toKeep; +public: + void keep(const char *name) { + _toKeep.push_back(name); + } + void unKeep(const char *name) { + _toKeep.erase(std::remove(_toKeep.begin(), _toKeep.end(), name), _toKeep.end()); + } + + void run(AsyncWebServerRequest *request, ArMiddlewareNext next); + +private: + std::vector _toKeep; }; // filter out specific headers from the incoming request class AsyncHeaderFilterMiddleware : public AsyncMiddleware { - public: - void filter(const char* name) { _toRemove.push_back(name); } - void unFilter(const char* name) { _toRemove.erase(std::remove(_toRemove.begin(), _toRemove.end(), name), _toRemove.end()); } - - void run(AsyncWebServerRequest* request, ArMiddlewareNext next); - - private: - std::vector _toRemove; +public: + void filter(const char *name) { + _toRemove.push_back(name); + } + void unFilter(const char *name) { + _toRemove.erase(std::remove(_toRemove.begin(), _toRemove.end(), name), _toRemove.end()); + } + + void run(AsyncWebServerRequest *request, ArMiddlewareNext next); + +private: + std::vector _toRemove; }; // curl-like logging of incoming requests class AsyncLoggingMiddleware : public AsyncMiddleware { - public: - void setOutput(Print& output) { _out = &output; } - void setEnabled(bool enabled) { _enabled = enabled; } - bool isEnabled() const { return _enabled && _out; } - - void run(AsyncWebServerRequest* request, ArMiddlewareNext next); - - private: - Print* _out = nullptr; - bool _enabled = true; +public: + void setOutput(Print &output) { + _out = &output; + } + void setEnabled(bool enabled) { + _enabled = enabled; + } + bool isEnabled() const { + return _enabled && _out; + } + + void run(AsyncWebServerRequest *request, ArMiddlewareNext next); + +private: + Print *_out = nullptr; + bool _enabled = true; }; // CORS Middleware class AsyncCorsMiddleware : public AsyncMiddleware { - public: - void setOrigin(const char* origin) { _origin = origin; } - void setMethods(const char* methods) { _methods = methods; } - void setHeaders(const char* headers) { _headers = headers; } - void setAllowCredentials(bool credentials) { _credentials = credentials; } - void setMaxAge(uint32_t seconds) { _maxAge = seconds; } - - void addCORSHeaders(AsyncWebServerResponse* response); - - void run(AsyncWebServerRequest* request, ArMiddlewareNext next); - - private: - String _origin = "*"; - String _methods = "*"; - String _headers = "*"; - bool _credentials = true; - uint32_t _maxAge = 86400; +public: + void setOrigin(const char *origin) { + _origin = origin; + } + void setMethods(const char *methods) { + _methods = methods; + } + void setHeaders(const char *headers) { + _headers = headers; + } + void setAllowCredentials(bool credentials) { + _credentials = credentials; + } + void setMaxAge(uint32_t seconds) { + _maxAge = seconds; + } + + void addCORSHeaders(AsyncWebServerResponse *response); + + void run(AsyncWebServerRequest *request, ArMiddlewareNext next); + +private: + String _origin = "*"; + String _methods = "*"; + String _headers = "*"; + bool _credentials = true; + uint32_t _maxAge = 86400; }; // Rate limit Middleware class AsyncRateLimitMiddleware : public AsyncMiddleware { - public: - void setMaxRequests(size_t maxRequests) { _maxRequests = maxRequests; } - void setWindowSize(uint32_t seconds) { _windowSizeMillis = seconds * 1000; } - - bool isRequestAllowed(uint32_t& retryAfterSeconds); - - void run(AsyncWebServerRequest* request, ArMiddlewareNext next); - - private: - size_t _maxRequests = 0; - uint32_t _windowSizeMillis = 0; - std::list _requestTimes; +public: + void setMaxRequests(size_t maxRequests) { + _maxRequests = maxRequests; + } + void setWindowSize(uint32_t seconds) { + _windowSizeMillis = seconds * 1000; + } + + bool isRequestAllowed(uint32_t &retryAfterSeconds); + + void run(AsyncWebServerRequest *request, ArMiddlewareNext next); + +private: + size_t _maxRequests = 0; + uint32_t _windowSizeMillis = 0; + std::list _requestTimes; }; /* @@ -700,30 +876,40 @@ class AsyncRateLimitMiddleware : public AsyncMiddleware { * */ class AsyncWebRewrite { - protected: - String _from; - String _toUrl; - String _params; - ArRequestFilterFunction _filter{nullptr}; - - public: - AsyncWebRewrite(const char* from, const char* to) : _from(from), _toUrl(to) { - int index = _toUrl.indexOf('?'); - if (index > 0) { - _params = _toUrl.substring(index + 1); - _toUrl = _toUrl.substring(0, index); - } +protected: + String _from; + String _toUrl; + String _params; + ArRequestFilterFunction _filter{nullptr}; + +public: + AsyncWebRewrite(const char *from, const char *to) : _from(from), _toUrl(to) { + int index = _toUrl.indexOf('?'); + if (index > 0) { + _params = _toUrl.substring(index + 1); + _toUrl = _toUrl.substring(0, index); } - virtual ~AsyncWebRewrite() {} - AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { - _filter = fn; - return *this; - } - bool filter(AsyncWebServerRequest* request) const { return _filter == NULL || _filter(request); } - const String& from(void) const { return _from; } - const String& toUrl(void) const { return _toUrl; } - const String& params(void) const { return _params; } - virtual bool match(AsyncWebServerRequest* request) { return from() == request->url() && filter(request); } + } + virtual ~AsyncWebRewrite() {} + AsyncWebRewrite &setFilter(ArRequestFilterFunction fn) { + _filter = fn; + return *this; + } + bool filter(AsyncWebServerRequest *request) const { + return _filter == NULL || _filter(request); + } + const String &from(void) const { + return _from; + } + const String &toUrl(void) const { + return _toUrl; + } + const String ¶ms(void) const { + return _params; + } + virtual bool match(AsyncWebServerRequest *request) { + return from() == request->url() && filter(request); + } }; /* @@ -731,22 +917,33 @@ class AsyncWebRewrite { * */ class AsyncWebHandler : public AsyncMiddlewareChain { - protected: - ArRequestFilterFunction _filter = nullptr; - AsyncAuthenticationMiddleware* _authMiddleware = nullptr; - - public: - AsyncWebHandler() {} - virtual ~AsyncWebHandler() {} - AsyncWebHandler& setFilter(ArRequestFilterFunction fn); - AsyncWebHandler& setAuthentication(const char* username, const char* password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST); - AsyncWebHandler& setAuthentication(const String& username, const String& password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST) { return setAuthentication(username.c_str(), password.c_str(), authMethod); }; - bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); } - virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) const { return false; } - virtual void handleRequest(__unused AsyncWebServerRequest* request) {} - virtual void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) {} - virtual void handleBody(__unused AsyncWebServerRequest* request, __unused uint8_t* data, __unused size_t len, __unused size_t index, __unused size_t total) {} - virtual bool isRequestHandlerTrivial() const { return true; } +protected: + ArRequestFilterFunction _filter = nullptr; + AsyncAuthenticationMiddleware *_authMiddleware = nullptr; + +public: + AsyncWebHandler() {} + virtual ~AsyncWebHandler() {} + AsyncWebHandler &setFilter(ArRequestFilterFunction fn); + AsyncWebHandler &setAuthentication(const char *username, const char *password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST); + AsyncWebHandler &setAuthentication(const String &username, const String &password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST) { + return setAuthentication(username.c_str(), password.c_str(), authMethod); + }; + bool filter(AsyncWebServerRequest *request) { + return _filter == NULL || _filter(request); + } + virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))) const { + return false; + } + virtual void handleRequest(__unused AsyncWebServerRequest *request) {} + virtual void handleUpload( + __unused AsyncWebServerRequest *request, __unused const String &filename, __unused size_t index, __unused uint8_t *data, __unused size_t len, + __unused bool final + ) {} + virtual void handleBody(__unused AsyncWebServerRequest *request, __unused uint8_t *data, __unused size_t len, __unused size_t index, __unused size_t total) {} + virtual bool isRequestHandlerTrivial() const { + return true; + } }; /* @@ -763,86 +960,99 @@ typedef enum { } WebResponseState; class AsyncWebServerResponse { - protected: - int _code; - std::list _headers; - String _contentType; - size_t _contentLength; - bool _sendContentLength; - bool _chunked; - size_t _headLength; - size_t _sentLength; - size_t _ackedLength; - size_t _writtenLength; - WebResponseState _state; - - public: - static const char* responseCodeToString(int code); - - public: - AsyncWebServerResponse(); - virtual ~AsyncWebServerResponse() {} - void setCode(int code); - int code() const { return _code; } - void setContentLength(size_t len); - void setContentType(const String& type) { setContentType(type.c_str()); } - void setContentType(const char* type); - 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); } - bool addHeader(const char* name, long value, bool replaceExisting = true) { return addHeader(name, String(value), replaceExisting); } - bool addHeader(const String& name, long value, bool replaceExisting = true) { return addHeader(name.c_str(), value, replaceExisting); } - bool removeHeader(const char* name); - const AsyncWebHeader* getHeader(const char* name) const; - const std::list& getHeaders() const { return _headers; } +protected: + int _code; + std::list _headers; + String _contentType; + size_t _contentLength; + bool _sendContentLength; + bool _chunked; + size_t _headLength; + size_t _sentLength; + size_t _ackedLength; + size_t _writtenLength; + WebResponseState _state; + +public: + static const char *responseCodeToString(int code); + +public: + AsyncWebServerResponse(); + virtual ~AsyncWebServerResponse() {} + void setCode(int code); + int code() const { + return _code; + } + void setContentLength(size_t len); + void setContentType(const String &type) { + setContentType(type.c_str()); + } + void setContentType(const char *type); + 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); + } + bool addHeader(const char *name, long value, bool replaceExisting = true) { + return addHeader(name, String(value), replaceExisting); + } + bool addHeader(const String &name, long value, bool replaceExisting = true) { + return addHeader(name.c_str(), value, replaceExisting); + } + bool removeHeader(const char *name); + const AsyncWebHeader *getHeader(const char *name) const; + const std::list &getHeaders() const { + return _headers; + } #ifndef ESP8266 - [[deprecated("Use instead: _assembleHead(String& buffer, uint8_t version)")]] + [[deprecated("Use instead: _assembleHead(String& buffer, uint8_t version)")]] #endif - String _assembleHead(uint8_t version) { - String buffer; - _assembleHead(buffer, version); - return buffer; - } - void _assembleHead(String& buffer, uint8_t version); - - virtual bool _started() const; - virtual bool _finished() const; - virtual bool _failed() const; - virtual bool _sourceValid() const; - virtual void _respond(AsyncWebServerRequest* request); - virtual size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); + String _assembleHead(uint8_t version) { + String buffer; + _assembleHead(buffer, version); + return buffer; + } + void _assembleHead(String &buffer, uint8_t version); + + virtual bool _started() const; + virtual bool _finished() const; + virtual bool _failed() const; + virtual bool _sourceValid() const; + virtual void _respond(AsyncWebServerRequest *request); + virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); }; /* * SERVER :: One instance * */ -typedef std::function ArRequestHandlerFunction; -typedef std::function ArUploadHandlerFunction; -typedef std::function ArBodyHandlerFunction; +typedef std::function ArRequestHandlerFunction; +typedef std::function + ArUploadHandlerFunction; +typedef std::function ArBodyHandlerFunction; class AsyncWebServer : public AsyncMiddlewareChain { - protected: - AsyncServer _server; - std::list> _rewrites; - std::list> _handlers; - AsyncCallbackWebHandler* _catchAllHandler; +protected: + AsyncServer _server; + std::list> _rewrites; + std::list> _handlers; + AsyncCallbackWebHandler *_catchAllHandler; - public: - AsyncWebServer(uint16_t port); - ~AsyncWebServer(); +public: + AsyncWebServer(uint16_t port); + ~AsyncWebServer(); - void begin(); - void end(); + void begin(); + void end(); #if ASYNC_TCP_SSL_ENABLED - void onSslFileRequest(AcSSlFileHandler cb, void* arg); - void beginSecure(const char* cert, const char* private_key_file, const char* password); + void onSslFileRequest(AcSSlFileHandler cb, void *arg); + void beginSecure(const char *cert, const char *private_key_file, const char *password); #endif - AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite); + AsyncWebRewrite &addRewrite(AsyncWebRewrite *rewrite); - /** + /** * @brief (compat) Add url rewrite rule by pointer * a deep copy of the pointer object will be created, * it is up to user to manage further lifetime of the object in argument @@ -850,18 +1060,18 @@ class AsyncWebServer : public AsyncMiddlewareChain { * @param rewrite pointer to rewrite object to copy setting from * @return AsyncWebRewrite& reference to a newly created rewrite rule */ - AsyncWebRewrite& addRewrite(std::shared_ptr rewrite); + AsyncWebRewrite &addRewrite(std::shared_ptr rewrite); - /** + /** * @brief add url rewrite rule * * @param from * @param to * @return AsyncWebRewrite& */ - AsyncWebRewrite& rewrite(const char* from, const char* to); + AsyncWebRewrite &rewrite(const char *from, const char *to); - /** + /** * @brief (compat) remove rewrite rule via referenced object * this will NOT deallocate pointed object itself, internal rule with same from/to urls will be removed if any * it's a compat method, better use `removeRewrite(const char* from, const char* to)` @@ -869,9 +1079,9 @@ class AsyncWebServer : public AsyncMiddlewareChain { * @return true * @return false */ - bool removeRewrite(AsyncWebRewrite* rewrite); + bool removeRewrite(AsyncWebRewrite *rewrite); - /** + /** * @brief remove rewrite rule * * @param from @@ -879,50 +1089,59 @@ class AsyncWebServer : public AsyncMiddlewareChain { * @return true * @return false */ - bool removeRewrite(const char* from, const char* to); + bool removeRewrite(const char *from, const char *to); - AsyncWebHandler& addHandler(AsyncWebHandler* handler); - bool removeHandler(AsyncWebHandler* handler); + AsyncWebHandler &addHandler(AsyncWebHandler *handler); + bool removeHandler(AsyncWebHandler *handler); - AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest) { return on(uri, HTTP_ANY, onRequest); } - AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr, ArBodyHandlerFunction onBody = nullptr); + AsyncCallbackWebHandler &on(const char *uri, ArRequestHandlerFunction onRequest) { + return on(uri, HTTP_ANY, onRequest); + } + AsyncCallbackWebHandler &on( + const char *uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr, + ArBodyHandlerFunction onBody = nullptr + ); - AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL); + AsyncStaticWebHandler &serveStatic(const char *uri, fs::FS &fs, const char *path, const char *cache_control = NULL); - void onNotFound(ArRequestHandlerFunction fn); // called when handler is not assigned - void onFileUpload(ArUploadHandlerFunction fn); // handle file uploads - void onRequestBody(ArBodyHandlerFunction fn); // handle posts with plain body content (JSON often transmitted this way as a request) + void onNotFound(ArRequestHandlerFunction fn); // called when handler is not assigned + void onFileUpload(ArUploadHandlerFunction fn); // handle file uploads + void onRequestBody(ArBodyHandlerFunction fn); // handle posts with plain body content (JSON often transmitted this way as a request) - void reset(); // remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody + void reset(); // remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody - void _handleDisconnect(AsyncWebServerRequest* request); - void _attachHandler(AsyncWebServerRequest* request); - void _rewriteRequest(AsyncWebServerRequest* request); + void _handleDisconnect(AsyncWebServerRequest *request); + void _attachHandler(AsyncWebServerRequest *request); + void _rewriteRequest(AsyncWebServerRequest *request); }; class DefaultHeaders { - using headers_t = std::list; - headers_t _headers; + using headers_t = std::list; + headers_t _headers; - public: - DefaultHeaders() = default; +public: + DefaultHeaders() = default; - using ConstIterator = headers_t::const_iterator; + using ConstIterator = headers_t::const_iterator; - void addHeader(const String& name, const String& value) { - _headers.emplace_back(name, value); - } + void addHeader(const String &name, const String &value) { + _headers.emplace_back(name, value); + } - ConstIterator begin() const { return _headers.begin(); } - ConstIterator end() const { return _headers.end(); } + ConstIterator begin() const { + return _headers.begin(); + } + ConstIterator end() const { + return _headers.end(); + } - DefaultHeaders(DefaultHeaders const&) = delete; - DefaultHeaders& operator=(DefaultHeaders const&) = delete; + DefaultHeaders(DefaultHeaders const &) = delete; + DefaultHeaders &operator=(DefaultHeaders const &) = delete; - static DefaultHeaders& Instance() { - static DefaultHeaders instance; - return instance; - } + static DefaultHeaders &Instance() { + static DefaultHeaders instance; + return instance; + } }; #include "AsyncEventSource.h" diff --git a/src/Middleware.cpp b/src/Middleware.cpp index 1eb8e50f5..b410f554b 100644 --- a/src/Middleware.cpp +++ b/src/Middleware.cpp @@ -2,69 +2,81 @@ #include AsyncMiddlewareChain::~AsyncMiddlewareChain() { - for (AsyncMiddleware* m : _middlewares) - if (m->_freeOnRemoval) + for (AsyncMiddleware *m : _middlewares) { + if (m->_freeOnRemoval) { delete m; + } + } } void AsyncMiddlewareChain::addMiddleware(ArMiddlewareCallback fn) { - AsyncMiddlewareFunction* m = new AsyncMiddlewareFunction(fn); + AsyncMiddlewareFunction *m = new AsyncMiddlewareFunction(fn); m->_freeOnRemoval = true; _middlewares.emplace_back(m); } -void AsyncMiddlewareChain::addMiddleware(AsyncMiddleware* middleware) { - if (middleware) +void AsyncMiddlewareChain::addMiddleware(AsyncMiddleware *middleware) { + if (middleware) { _middlewares.emplace_back(middleware); + } } -void AsyncMiddlewareChain::addMiddlewares(std::vector middlewares) { - for (AsyncMiddleware* m : middlewares) +void AsyncMiddlewareChain::addMiddlewares(std::vector middlewares) { + for (AsyncMiddleware *m : middlewares) { addMiddleware(m); + } } -bool AsyncMiddlewareChain::removeMiddleware(AsyncMiddleware* middleware) { +bool AsyncMiddlewareChain::removeMiddleware(AsyncMiddleware *middleware) { // remove all middlewares from _middlewares vector being equal to middleware, delete them having _freeOnRemoval flag to true and resize the vector. const size_t size = _middlewares.size(); - _middlewares.erase(std::remove_if(_middlewares.begin(), _middlewares.end(), [middleware](AsyncMiddleware* m) { - if (m == middleware) { - if (m->_freeOnRemoval) - delete m; - return true; - } - return false; - }), - _middlewares.end()); + _middlewares.erase( + std::remove_if( + _middlewares.begin(), _middlewares.end(), + [middleware](AsyncMiddleware *m) { + if (m == middleware) { + if (m->_freeOnRemoval) { + delete m; + } + return true; + } + return false; + } + ), + _middlewares.end() + ); return size != _middlewares.size(); } -void AsyncMiddlewareChain::_runChain(AsyncWebServerRequest* request, ArMiddlewareNext finalizer) { - if (!_middlewares.size()) +void AsyncMiddlewareChain::_runChain(AsyncWebServerRequest *request, ArMiddlewareNext finalizer) { + if (!_middlewares.size()) { return finalizer(); + } ArMiddlewareNext next; - std::list::iterator it = _middlewares.begin(); + std::list::iterator it = _middlewares.begin(); next = [this, &next, &it, request, finalizer]() { - if (it == _middlewares.end()) + if (it == _middlewares.end()) { return finalizer(); - AsyncMiddleware* m = *it; + } + AsyncMiddleware *m = *it; it++; return m->run(request, next); }; return next(); } -void AsyncAuthenticationMiddleware::setUsername(const char* username) { +void AsyncAuthenticationMiddleware::setUsername(const char *username) { _username = username; _hasCreds = _username.length() && _credentials.length(); } -void AsyncAuthenticationMiddleware::setPassword(const char* password) { +void AsyncAuthenticationMiddleware::setPassword(const char *password) { _credentials = password; _hash = false; _hasCreds = _username.length() && _credentials.length(); } -void AsyncAuthenticationMiddleware::setPasswordHash(const char* hash) { +void AsyncAuthenticationMiddleware::setPasswordHash(const char *hash) { _credentials = hash; _hash = _credentials.length(); _hasCreds = _username.length() && _credentials.length(); @@ -72,12 +84,14 @@ void AsyncAuthenticationMiddleware::setPasswordHash(const char* hash) { bool AsyncAuthenticationMiddleware::generateHash() { // ensure we have all the necessary data - if (!_hasCreds) + if (!_hasCreds) { return false; + } // if we already have a hash, do nothing - if (_hash) + if (_hash) { return false; + } switch (_authMethod) { case AsyncAuthType::AUTH_DIGEST: @@ -90,34 +104,36 @@ bool AsyncAuthenticationMiddleware::generateHash() { _hash = true; return true; - default: - return false; + default: return false; } } -bool AsyncAuthenticationMiddleware::allowed(AsyncWebServerRequest* request) const { - if (_authMethod == AsyncAuthType::AUTH_NONE) +bool AsyncAuthenticationMiddleware::allowed(AsyncWebServerRequest *request) const { + if (_authMethod == AsyncAuthType::AUTH_NONE) { return true; + } - if (_authMethod == AsyncAuthType::AUTH_DENIED) + if (_authMethod == AsyncAuthType::AUTH_DENIED) { return false; + } - if (!_hasCreds) + if (!_hasCreds) { return true; + } return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash); } -void AsyncAuthenticationMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) { +void AsyncAuthenticationMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext next) { return allowed(request) ? next() : request->requestAuthentication(_authMethod, _realm.c_str(), _authFailMsg.c_str()); } -void AsyncHeaderFreeMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) { - std::vector reqHeaders; +void AsyncHeaderFreeMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext next) { + std::vector reqHeaders; request->getHeaderNames(reqHeaders); - for (const char* h : reqHeaders) { + for (const char *h : reqHeaders) { bool keep = false; - for (const char* k : _toKeep) { + for (const char *k : _toKeep) { if (strcasecmp(h, k) == 0) { keep = true; break; @@ -130,13 +146,14 @@ void AsyncHeaderFreeMiddleware::run(AsyncWebServerRequest* request, ArMiddleware next(); } -void AsyncHeaderFilterMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) { - for (auto it = _toRemove.begin(); it != _toRemove.end(); ++it) +void AsyncHeaderFilterMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext next) { + for (auto it = _toRemove.begin(); it != _toRemove.end(); ++it) { request->removeHeader(*it); + } next(); } -void AsyncLoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) { +void AsyncLoggingMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext next) { if (!isEnabled()) { next(); return; @@ -152,7 +169,7 @@ void AsyncLoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNex _out->print(request->url().c_str()); _out->print(F(" HTTP/1.")); _out->println(request->version()); - for (auto& h : request->getHeaders()) { + for (auto &h : request->getHeaders()) { if (h.value().length()) { _out->print('>'); _out->print(' '); @@ -166,7 +183,7 @@ void AsyncLoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNex uint32_t elapsed = millis(); next(); elapsed = millis() - elapsed; - AsyncWebServerResponse* response = request->getResponse(); + AsyncWebServerResponse *response = request->getResponse(); if (response) { _out->print(F("* Processed in ")); _out->print(elapsed); @@ -178,7 +195,7 @@ void AsyncLoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNex _out->print(response->code()); _out->print(' '); _out->println(AsyncWebServerResponse::responseCodeToString(response->code())); - for (auto& h : response->getHeaders()) { + for (auto &h : response->getHeaders()) { if (h.value().length()) { _out->print('<'); _out->print(' '); @@ -194,7 +211,7 @@ void AsyncLoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNex } } -void AsyncCorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) { +void AsyncCorsMiddleware::addCORSHeaders(AsyncWebServerResponse *response) { response->addHeader(asyncsrv::T_CORS_ACAO, _origin.c_str()); response->addHeader(asyncsrv::T_CORS_ACAM, _methods.c_str()); response->addHeader(asyncsrv::T_CORS_ACAH, _headers.c_str()); @@ -202,12 +219,12 @@ void AsyncCorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) { response->addHeader(asyncsrv::T_CORS_ACMA, String(_maxAge).c_str()); } -void AsyncCorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) { +void AsyncCorsMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext next) { // Origin header ? => CORS handling if (request->hasHeader(asyncsrv::T_CORS_O)) { // check if this is a preflight request => handle it and return if (request->method() == HTTP_OPTIONS) { - AsyncWebServerResponse* response = request->beginResponse(200); + AsyncWebServerResponse *response = request->beginResponse(200); addCORSHeaders(response); request->send(response); return; @@ -215,7 +232,7 @@ void AsyncCorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext n // CORS request, no options => let the request pass and add CORS headers after next(); - AsyncWebServerResponse* response = request->getResponse(); + AsyncWebServerResponse *response = request->getResponse(); if (response) { addCORSHeaders(response); } @@ -226,11 +243,12 @@ void AsyncCorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext n } } -bool AsyncRateLimitMiddleware::isRequestAllowed(uint32_t& retryAfterSeconds) { +bool AsyncRateLimitMiddleware::isRequestAllowed(uint32_t &retryAfterSeconds) { uint32_t now = millis(); - while (!_requestTimes.empty() && _requestTimes.front() <= now - _windowSizeMillis) + while (!_requestTimes.empty() && _requestTimes.front() <= now - _windowSizeMillis) { _requestTimes.pop_front(); + } _requestTimes.push_back(now); @@ -244,12 +262,12 @@ bool AsyncRateLimitMiddleware::isRequestAllowed(uint32_t& retryAfterSeconds) { return true; } -void AsyncRateLimitMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) { +void AsyncRateLimitMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext next) { uint32_t retryAfterSeconds; if (isRequestAllowed(retryAfterSeconds)) { next(); } else { - AsyncWebServerResponse* response = request->beginResponse(429); + AsyncWebServerResponse *response = request->beginResponse(429); response->addHeader(asyncsrv::T_retry_after, retryAfterSeconds); request->send(response); } diff --git a/src/WebAuthentication.cpp b/src/WebAuthentication.cpp index ebcc38e55..e8885777c 100644 --- a/src/WebAuthentication.cpp +++ b/src/WebAuthentication.cpp @@ -21,9 +21,9 @@ #include "WebAuthentication.h" #include #if defined(ESP32) || defined(TARGET_RP2040) - #include +#include #else - #include "md5.h" +#include "md5.h" #endif #include "literals.h" @@ -31,23 +31,25 @@ using namespace asyncsrv; // Basic Auth hash = base64("username:password") -bool checkBasicAuthentication(const char* hash, const char* username, const char* password) { - if (username == NULL || password == NULL || hash == NULL) +bool checkBasicAuthentication(const char *hash, const char *username, const char *password) { + if (username == NULL || password == NULL || hash == NULL) { return false; + } return generateBasicHash(username, password).equalsIgnoreCase(hash); } -String generateBasicHash(const char* username, const char* password) { - if (username == NULL || password == NULL) +String generateBasicHash(const char *username, const char *password) { + if (username == NULL || password == NULL) { return emptyString; + } size_t toencodeLen = strlen(username) + strlen(password) + 1; - char* toencode = new char[toencodeLen + 1]; + char *toencode = new char[toencodeLen + 1]; if (toencode == NULL) { return emptyString; } - char* encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; + char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; if (encoded == NULL) { delete[] toencode; return emptyString; @@ -64,7 +66,7 @@ String generateBasicHash(const char* username, const char* password) { return emptyString; } -static bool getMD5(uint8_t* data, uint16_t len, char* output) { // 33 bytes or more +static bool getMD5(uint8_t *data, uint16_t len, char *output) { // 33 bytes or more #if defined(ESP32) || defined(TARGET_RP2040) MD5Builder md5; md5.begin(); @@ -74,9 +76,10 @@ static bool getMD5(uint8_t* data, uint16_t len, char* output) { // 33 bytes or m #else md5_context_t _ctx; - uint8_t* _buf = (uint8_t*)malloc(16); - if (_buf == NULL) + uint8_t *_buf = (uint8_t *)malloc(16); + if (_buf == NULL) { return false; + } memset(_buf, 0x00, 16); MD5Init(&_ctx); @@ -98,28 +101,30 @@ String genRandomMD5() { #else uint32_t r = rand(); #endif - char* out = (char*)malloc(33); - if (out == NULL || !getMD5((uint8_t*)(&r), 4, out)) + char *out = (char *)malloc(33); + if (out == NULL || !getMD5((uint8_t *)(&r), 4, out)) { return emptyString; + } String res = String(out); free(out); return res; } -static String stringMD5(const String& in) { - char* out = (char*)malloc(33); - if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) +static String stringMD5(const String &in) { + char *out = (char *)malloc(33); + if (out == NULL || !getMD5((uint8_t *)(in.c_str()), in.length(), out)) { return emptyString; + } String res = String(out); free(out); return res; } -String generateDigestHash(const char* username, const char* password, const char* realm) { +String generateDigestHash(const char *username, const char *password, const char *realm) { if (username == NULL || password == NULL || realm == NULL) { return emptyString; } - char* out = (char*)malloc(33); + char *out = (char *)malloc(33); String in; in.reserve(strlen(username) + strlen(realm) + strlen(password) + 2); @@ -129,18 +134,21 @@ String generateDigestHash(const char* username, const char* password, const char in.concat(':'); in.concat(password); - if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) + if (out == NULL || !getMD5((uint8_t *)(in.c_str()), in.length(), out)) { return emptyString; + } in = String(out); free(out); return in; } -bool checkDigestAuthentication(const char* header, const char* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri) -{ +bool checkDigestAuthentication( + const char *header, const char *method, const char *username, const char *password, const char *realm, bool passwordIsHash, const char *nonce, + const char *opaque, const char *uri +) { if (username == NULL || password == NULL || header == NULL || method == NULL) { - // os_printf("AUTH FAIL: missing requred fields\n"); + // os_printf("AUTH FAIL: missing required fields\n"); return false; } @@ -160,8 +168,8 @@ bool checkDigestAuthentication(const char* header, const char* method, const cha String myNc; String myCnonce; - myHeader += (char)0x2c; // ',' - myHeader += (char)0x20; // ' ' + myHeader += (char)0x2c; // ',' + myHeader += (char)0x20; // ' ' do { String avLine(myHeader.substring(0, nextBreak)); avLine.trim(); diff --git a/src/WebAuthentication.h b/src/WebAuthentication.h index 68bed0c28..45fec4dc6 100644 --- a/src/WebAuthentication.h +++ b/src/WebAuthentication.h @@ -24,14 +24,17 @@ #include "Arduino.h" -bool checkBasicAuthentication(const char* header, const char* username, const char* password); +bool checkBasicAuthentication(const char *header, const char *username, const char *password); -bool checkDigestAuthentication(const char* header, const char* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri); +bool checkDigestAuthentication( + const char *header, const char *method, const char *username, const char *password, const char *realm, bool passwordIsHash, const char *nonce, + const char *opaque, const char *uri +); // for storing hashed versions on the device that can be authenticated against -String generateDigestHash(const char* username, const char* password, const char* realm); +String generateDigestHash(const char *username, const char *password, const char *realm); -String generateBasicHash(const char* username, const char* password); +String generateBasicHash(const char *username, const char *password); String genRandomMD5(); diff --git a/src/WebHandlerImpl.h b/src/WebHandlerImpl.h index 134006f63..75a60be6d 100644 --- a/src/WebHandlerImpl.h +++ b/src/WebHandlerImpl.h @@ -23,79 +23,89 @@ #include #ifdef ASYNCWEBSERVER_REGEX - #include +#include #endif #include "stddef.h" #include class AsyncStaticWebHandler : public AsyncWebHandler { - using File = fs::File; - using FS = fs::FS; - - private: - bool _getFile(AsyncWebServerRequest* request) const; - bool _searchFile(AsyncWebServerRequest* request, const String& path); - uint8_t _countBits(const uint8_t value) const; - - protected: - FS _fs; - String _uri; - String _path; - String _default_file; - String _cache_control; - String _last_modified; - AwsTemplateProcessor _callback; - bool _isDir; - bool _tryGzipFirst = true; - - public: - AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control); - bool canHandle(AsyncWebServerRequest* request) const override final; - void handleRequest(AsyncWebServerRequest* request) override final; - AsyncStaticWebHandler& setTryGzipFirst(bool value); - AsyncStaticWebHandler& setIsDir(bool isDir); - AsyncStaticWebHandler& setDefaultFile(const char* filename); - AsyncStaticWebHandler& setCacheControl(const char* cache_control); - - /** + using File = fs::File; + using FS = fs::FS; + +private: + bool _getFile(AsyncWebServerRequest *request) const; + bool _searchFile(AsyncWebServerRequest *request, const String &path); + uint8_t _countBits(const uint8_t value) const; + +protected: + FS _fs; + String _uri; + String _path; + String _default_file; + String _cache_control; + String _last_modified; + AwsTemplateProcessor _callback; + bool _isDir; + bool _tryGzipFirst = true; + +public: + AsyncStaticWebHandler(const char *uri, FS &fs, const char *path, const char *cache_control); + bool canHandle(AsyncWebServerRequest *request) const override final; + void handleRequest(AsyncWebServerRequest *request) override final; + AsyncStaticWebHandler &setTryGzipFirst(bool value); + AsyncStaticWebHandler &setIsDir(bool isDir); + AsyncStaticWebHandler &setDefaultFile(const char *filename); + AsyncStaticWebHandler &setCacheControl(const char *cache_control); + + /** * @brief Set the Last-Modified time for the object - * - * @param last_modified - * @return AsyncStaticWebHandler& + * + * @param last_modified + * @return AsyncStaticWebHandler& */ - AsyncStaticWebHandler& setLastModified(const char* last_modified); - AsyncStaticWebHandler& setLastModified(struct tm* last_modified); - AsyncStaticWebHandler& setLastModified(time_t last_modified); - // sets to current time. Make sure sntp is runing and time is updated - AsyncStaticWebHandler& setLastModified(); + AsyncStaticWebHandler &setLastModified(const char *last_modified); + AsyncStaticWebHandler &setLastModified(struct tm *last_modified); + AsyncStaticWebHandler &setLastModified(time_t last_modified); + // sets to current time. Make sure sntp is running and time is updated + AsyncStaticWebHandler &setLastModified(); - AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback); + AsyncStaticWebHandler &setTemplateProcessor(AwsTemplateProcessor newCallback); }; class AsyncCallbackWebHandler : public AsyncWebHandler { - private: - protected: - String _uri; - WebRequestMethodComposite _method; - ArRequestHandlerFunction _onRequest; - ArUploadHandlerFunction _onUpload; - ArBodyHandlerFunction _onBody; - bool _isRegex; - - public: - AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {} - void setUri(const String& uri); - void setMethod(WebRequestMethodComposite method) { _method = method; } - void onRequest(ArRequestHandlerFunction fn) { _onRequest = fn; } - void onUpload(ArUploadHandlerFunction fn) { _onUpload = fn; } - void onBody(ArBodyHandlerFunction fn) { _onBody = fn; } - - bool canHandle(AsyncWebServerRequest* request) const override final; - void handleRequest(AsyncWebServerRequest* request) override final; - void handleUpload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) override final; - void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final; - bool isRequestHandlerTrivial() const override final { return !_onRequest; } +private: +protected: + String _uri; + WebRequestMethodComposite _method; + ArRequestHandlerFunction _onRequest; + ArUploadHandlerFunction _onUpload; + ArBodyHandlerFunction _onBody; + bool _isRegex; + +public: + AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {} + void setUri(const String &uri); + void setMethod(WebRequestMethodComposite method) { + _method = method; + } + void onRequest(ArRequestHandlerFunction fn) { + _onRequest = fn; + } + void onUpload(ArUploadHandlerFunction fn) { + _onUpload = fn; + } + void onBody(ArBodyHandlerFunction fn) { + _onBody = fn; + } + + bool canHandle(AsyncWebServerRequest *request) const override final; + void handleRequest(AsyncWebServerRequest *request) override final; + void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) override final; + void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final; + bool isRequestHandlerTrivial() const override final { + return !_onRequest; + } }; #endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ diff --git a/src/WebHandlers.cpp b/src/WebHandlers.cpp index c3efab705..bdb01aaaa 100644 --- a/src/WebHandlers.cpp +++ b/src/WebHandlers.cpp @@ -23,11 +23,11 @@ using namespace asyncsrv; -AsyncWebHandler& AsyncWebHandler::setFilter(ArRequestFilterFunction fn) { +AsyncWebHandler &AsyncWebHandler::setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } -AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const char* password, AsyncAuthType authMethod) { +AsyncWebHandler &AsyncWebHandler::setAuthentication(const char *username, const char *password, AsyncAuthType authMethod) { if (!_authMiddleware) { _authMiddleware = new AsyncAuthenticationMiddleware(); _authMiddleware->_freeOnRemoval = true; @@ -39,13 +39,15 @@ AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const return *this; }; -AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control) - : _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr) { +AsyncStaticWebHandler::AsyncStaticWebHandler(const char *uri, FS &fs, const char *path, const char *cache_control) + : _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr) { // Ensure leading '/' - if (_uri.length() == 0 || _uri[0] != '/') + if (_uri.length() == 0 || _uri[0] != '/') { _uri = String('/') + _uri; - if (_path.length() == 0 || _path[0] != '/') + } + if (_path.length() == 0 || _path[0] != '/') { _path = String('/') + _path; + } // If path ends with '/' we assume a hint that this is a directory to improve performance. // However - if it does not end with '/' we, can't assume a file, path can still be a directory. @@ -53,45 +55,47 @@ AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char // Remove the trailing '/' so we can handle default file // Notice that root will be "" not "/" - if (_uri[_uri.length() - 1] == '/') + if (_uri[_uri.length() - 1] == '/') { _uri = _uri.substring(0, _uri.length() - 1); - if (_path[_path.length() - 1] == '/') + } + if (_path[_path.length() - 1] == '/') { _path = _path.substring(0, _path.length() - 1); + } } -AsyncStaticWebHandler& AsyncStaticWebHandler::setTryGzipFirst(bool value) { +AsyncStaticWebHandler &AsyncStaticWebHandler::setTryGzipFirst(bool value) { _tryGzipFirst = value; return *this; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) { +AsyncStaticWebHandler &AsyncStaticWebHandler::setIsDir(bool isDir) { _isDir = isDir; return *this; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename) { +AsyncStaticWebHandler &AsyncStaticWebHandler::setDefaultFile(const char *filename) { _default_file = filename; return *this; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control) { +AsyncStaticWebHandler &AsyncStaticWebHandler::setCacheControl(const char *cache_control) { _cache_control = cache_control; return *this; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified) { +AsyncStaticWebHandler &AsyncStaticWebHandler::setLastModified(const char *last_modified) { _last_modified = last_modified; return *this; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified) { +AsyncStaticWebHandler &AsyncStaticWebHandler::setLastModified(struct tm *last_modified) { char result[30]; #ifdef ESP8266 auto formatP = PSTR("%a, %d %b %Y %H:%M:%S GMT"); char format[strlen_P(formatP) + 1]; strcpy_P(format, formatP); #else - static constexpr const char* format = "%a, %d %b %Y %H:%M:%S GMT"; + static constexpr const char *format = "%a, %d %b %Y %H:%M:%S GMT"; #endif strftime(result, sizeof(result), format, last_modified); @@ -99,22 +103,23 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_mo return *this; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified) { - return setLastModified((struct tm*)gmtime(&last_modified)); +AsyncStaticWebHandler &AsyncStaticWebHandler::setLastModified(time_t last_modified) { + return setLastModified((struct tm *)gmtime(&last_modified)); } -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() { +AsyncStaticWebHandler &AsyncStaticWebHandler::setLastModified() { time_t last_modified; - if (time(&last_modified) == 0) // time is not yet set + if (time(&last_modified) == 0) { // time is not yet set return *this; + } return setLastModified(last_modified); } -bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) const { +bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) const { return request->isHTTP() && request->method() == HTTP_GET && request->url().startsWith(_uri) && _getFile(request); } -bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) const { +bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request) const { // Remove the found uri String path = request->url().substring(_uri.length()); @@ -124,28 +129,31 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) const { path = _path + path; // Do we have a file or .gz file - if (!canSkipFileCheck && const_cast(this)->_searchFile(request, path)) + if (!canSkipFileCheck && const_cast(this)->_searchFile(request, path)) { return true; + } // Can't handle if not default file - if (_default_file.length() == 0) + if (_default_file.length() == 0) { return false; + } - // Try to add default file, ensure there is a trailing '/' ot the path. - if (path.length() == 0 || path[path.length() - 1] != '/') + // Try to add default file, ensure there is a trailing '/' to the path. + if (path.length() == 0 || path[path.length() - 1] != '/') { path += String('/'); + } path += _default_file; - return const_cast(this)->_searchFile(request, path); + return const_cast(this)->_searchFile(request, path); } #ifdef ESP32 - #define FILE_IS_REAL(f) (f == true && !f.isDirectory()) +#define FILE_IS_REAL(f) (f == true && !f.isDirectory()) #else - #define FILE_IS_REAL(f) (f == true) +#define FILE_IS_REAL(f) (f == true) #endif -bool AsyncStaticWebHandler::_searchFile(AsyncWebServerRequest* request, const String& path) { +bool AsyncStaticWebHandler::_searchFile(AsyncWebServerRequest *request, const String &path) { bool fileFound = false; bool gzipFound = false; @@ -180,9 +188,9 @@ bool AsyncStaticWebHandler::_searchFile(AsyncWebServerRequest* request, const St if (found) { // Extract the file name from the path and keep it in _tempObject size_t pathLen = path.length(); - char* _tempPath = (char*)malloc(pathLen + 1); + char *_tempPath = (char *)malloc(pathLen + 1); snprintf_P(_tempPath, pathLen + 1, PSTR("%s"), path.c_str()); - request->_tempObject = (void*)_tempPath; + request->_tempObject = (void *)_tempPath; } return found; @@ -191,81 +199,85 @@ bool AsyncStaticWebHandler::_searchFile(AsyncWebServerRequest* request, const St uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const { uint8_t w = value; uint8_t n; - for (n = 0; w != 0; n++) + for (n = 0; w != 0; n++) { w &= w - 1; + } return n; } -void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) { +void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) { // Get the filename from request->_tempObject and free it - String filename((char*)request->_tempObject); + String filename((char *)request->_tempObject); free(request->_tempObject); request->_tempObject = NULL; - if (request->_tempFile != true){ + if (request->_tempFile != true) { request->send(404); return; } - time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS) - // set etag to lastmod timestamp if available, otherwise to size - String etag; - if (lw) { - setLastModified(lw); + time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS) + // set etag to lastmod timestamp if available, otherwise to size + String etag; + if (lw) { + setLastModified(lw); #if defined(TARGET_RP2040) - // time_t == long long int - constexpr size_t len = 1 + 8 * sizeof(time_t); - char buf[len]; - char* ret = lltoa(lw ^ request->_tempFile.size(), buf, len, 10); - etag = ret ? String(ret) : String(request->_tempFile.size()); + // time_t == long long int + constexpr size_t len = 1 + 8 * sizeof(time_t); + char buf[len]; + char *ret = lltoa(lw ^ request->_tempFile.size(), buf, len, 10); + etag = ret ? String(ret) : String(request->_tempFile.size()); #else - etag = lw ^ request->_tempFile.size(); // etag combines file size and lastmod timestamp + etag = lw ^ request->_tempFile.size(); // etag combines file size and lastmod timestamp #endif - } else { - etag = request->_tempFile.size(); - } + } else { + etag = request->_tempFile.size(); + } - bool not_modified = false; + bool not_modified = false; - // if-none-match has precedence over if-modified-since - if (request->hasHeader(T_INM)) - not_modified = request->header(T_INM).equals(etag); - else if (_last_modified.length()) - not_modified = request->header(T_IMS).equals(_last_modified); + // if-none-match has precedence over if-modified-since + if (request->hasHeader(T_INM)) { + not_modified = request->header(T_INM).equals(etag); + } else if (_last_modified.length()) { + not_modified = request->header(T_IMS).equals(_last_modified); + } - AsyncWebServerResponse* response; + AsyncWebServerResponse *response; - if (not_modified){ - request->_tempFile.close(); - response = new AsyncBasicResponse(304); // Not modified - } else { - response = new AsyncFileResponse(request->_tempFile, filename, emptyString, false, _callback); - } + if (not_modified) { + request->_tempFile.close(); + response = new AsyncBasicResponse(304); // Not modified + } else { + response = new AsyncFileResponse(request->_tempFile, filename, emptyString, false, _callback); + } - response->addHeader(T_ETag, etag.c_str()); + response->addHeader(T_ETag, etag.c_str()); - if (_last_modified.length()) - response->addHeader(T_Last_Modified, _last_modified.c_str()); - if (_cache_control.length()) - response->addHeader(T_Cache_Control, _cache_control.c_str()); - - request->send(response); + if (_last_modified.length()) { + response->addHeader(T_Last_Modified, _last_modified.c_str()); + } + if (_cache_control.length()) { + response->addHeader(T_Cache_Control, _cache_control.c_str()); + } + request->send(response); } -AsyncStaticWebHandler& AsyncStaticWebHandler::setTemplateProcessor(AwsTemplateProcessor newCallback) { +AsyncStaticWebHandler &AsyncStaticWebHandler::setTemplateProcessor(AwsTemplateProcessor newCallback) { _callback = newCallback; return *this; } -void AsyncCallbackWebHandler::setUri(const String& uri) { +void AsyncCallbackWebHandler::setUri(const String &uri) { _uri = uri; _isRegex = uri.startsWith("^") && uri.endsWith("$"); } -bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest* request) const { - if (!_onRequest || !request->isHTTP() || !(_method & request->method())) +bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest *request) const { + if (!_onRequest || !request->isHTTP() || !(_method & request->method())) { return false; + } #ifdef ASYNCWEBSERVER_REGEX if (_isRegex) { @@ -273,7 +285,7 @@ bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest* request) const { std::smatch matches; std::string s(request->url().c_str()); if (std::regex_search(s, matches, pattern)) { - for (size_t i = 1; i < matches.size(); ++i) { // start from 1 + for (size_t i = 1; i < matches.size(); ++i) { // start from 1 request->_addPathParam(matches[i].str().c_str()); } } else { @@ -284,31 +296,37 @@ bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest* request) const { if (_uri.length() && _uri.startsWith("/*.")) { String uriTemplate = String(_uri); uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf(".")); - if (!request->url().endsWith(uriTemplate)) + if (!request->url().endsWith(uriTemplate)) { return false; + } } else if (_uri.length() && _uri.endsWith("*")) { String uriTemplate = String(_uri); uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1); - if (!request->url().startsWith(uriTemplate)) + if (!request->url().startsWith(uriTemplate)) { return false; - } else if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) + } + } else if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) { return false; + } return true; } -void AsyncCallbackWebHandler::handleRequest(AsyncWebServerRequest* request) { - if (_onRequest) +void AsyncCallbackWebHandler::handleRequest(AsyncWebServerRequest *request) { + if (_onRequest) { _onRequest(request); - else + } else { request->send(500); + } } -void AsyncCallbackWebHandler::handleUpload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) { - if (_onUpload) +void AsyncCallbackWebHandler::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { + if (_onUpload) { _onUpload(request, filename, index, data, len, final); + } } -void AsyncCallbackWebHandler::handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) { +void AsyncCallbackWebHandler::handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { // ESP_LOGD("AsyncWebServer", "AsyncCallbackWebHandler::handleBody"); - if (_onBody) + if (_onBody) { _onBody(request, data, len, index, total); -} \ No newline at end of file + } +} diff --git a/src/WebRequest.cpp b/src/WebRequest.cpp index 5fd39bb4f..8fb3bf4d9 100644 --- a/src/WebRequest.cpp +++ b/src/WebRequest.cpp @@ -28,20 +28,67 @@ using namespace asyncsrv; -enum { PARSE_REQ_START = 0, - PARSE_REQ_HEADERS = 1, - PARSE_REQ_BODY = 2, - PARSE_REQ_END = 3, - PARSE_REQ_FAIL = 4 }; - -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) { - c->onError([](void* r, AsyncClient* c, int8_t error) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); - c->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); - c->onDisconnect([](void* r, AsyncClient* c) { AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this); - c->onTimeout([](void* r, AsyncClient* c, uint32_t time) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); - c->onData([](void* r, AsyncClient* c, void* buf, size_t len) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); - c->onPoll([](void* r, AsyncClient* c) { (void)c; AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this); +enum { + PARSE_REQ_START = 0, + PARSE_REQ_HEADERS = 1, + PARSE_REQ_BODY = 2, + PARSE_REQ_END = 3, + PARSE_REQ_FAIL = 4 +}; + +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) { + c->onError( + [](void *r, AsyncClient *c, int8_t error) { + (void)c; + AsyncWebServerRequest *req = (AsyncWebServerRequest *)r; + req->_onError(error); + }, + this + ); + c->onAck( + [](void *r, AsyncClient *c, size_t len, uint32_t time) { + (void)c; + AsyncWebServerRequest *req = (AsyncWebServerRequest *)r; + req->_onAck(len, time); + }, + this + ); + c->onDisconnect( + [](void *r, AsyncClient *c) { + AsyncWebServerRequest *req = (AsyncWebServerRequest *)r; + req->_onDisconnect(); + delete c; + }, + this + ); + c->onTimeout( + [](void *r, AsyncClient *c, uint32_t time) { + (void)c; + AsyncWebServerRequest *req = (AsyncWebServerRequest *)r; + req->_onTimeout(time); + }, + this + ); + c->onData( + [](void *r, AsyncClient *c, void *buf, size_t len) { + (void)c; + AsyncWebServerRequest *req = (AsyncWebServerRequest *)r; + req->_onData(buf, len); + }, + this + ); + c->onPoll( + [](void *r, AsyncClient *c) { + (void)c; + AsyncWebServerRequest *req = (AsyncWebServerRequest *)r; + req->_onPoll(); + }, + this + ); } AsyncWebServerRequest::~AsyncWebServerRequest() { @@ -49,7 +96,7 @@ AsyncWebServerRequest::~AsyncWebServerRequest() { _pathParams.clear(); - AsyncWebServerResponse* r = _response; + AsyncWebServerResponse *r = _response; _response = NULL; delete r; @@ -66,13 +113,13 @@ AsyncWebServerRequest::~AsyncWebServerRequest() { } } -void AsyncWebServerRequest::_onData(void* buf, size_t len) { +void AsyncWebServerRequest::_onData(void *buf, size_t len) { // SSL/TLS handshake detection #ifndef ASYNC_TCP_SSL_ENABLED - if (_parseState == PARSE_REQ_START && len && ((uint8_t*)buf)[0] == 0x16) { // 0x16 indicates a Handshake message (SSL/TLS). - #ifdef ESP32 + if (_parseState == PARSE_REQ_START && len && ((uint8_t *)buf)[0] == 0x16) { // 0x16 indicates a Handshake message (SSL/TLS). +#ifdef ESP32 log_d("SSL/TLS handshake detected: resetting connection"); - #endif +#endif _parseState = PARSE_REQ_FAIL; _client->abort(); return; @@ -84,7 +131,7 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) { if (_parseState < PARSE_REQ_BODY) { // Find new line in buf - char* str = (char*)buf; + char *str = (char *)buf; for (i = 0; i < len; i++) { // Check for null characters in header if (!str[i]) { @@ -96,14 +143,14 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) { break; } } - if (i == len) { // No new line, just add the buffer in _temp + if (i == len) { // No new line, just add the buffer in _temp char ch = str[len - 1]; str[len - 1] = 0; _temp.reserve(_temp.length() + len); _temp.concat(str); _temp.concat(ch); - } else { // Found new line - extract it and parse - str[i] = 0; // Terminate the string at the end of the line. + } else { // Found new line - extract it and parse + str[i] = 0; // Terminate the string at the end of the line. _temp.concat(str); _temp.trim(); _parseLine(); @@ -122,34 +169,35 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) { if (needParse) { size_t i; for (i = 0; i < len; i++) { - _parseMultipartPostByte(((uint8_t*)buf)[i], i == len - 1); + _parseMultipartPostByte(((uint8_t *)buf)[i], i == len - 1); _parsedLength++; } - } else + } else { _parsedLength += len; + } } else { if (_parsedLength == 0) { if (_contentType.startsWith(T_app_xform_urlencoded)) { _isPlainPost = true; - } else if (_contentType == T_text_plain && __is_param_char(((char*)buf)[0])) { + } else if (_contentType == T_text_plain && __is_param_char(((char *)buf)[0])) { size_t i = 0; - while (i < len && __is_param_char(((char*)buf)[i++])) - ; - if (i < len && ((char*)buf)[i - 1] == '=') { + while (i < len && __is_param_char(((char *)buf)[i++])); + if (i < len && ((char *)buf)[i - 1] == '=') { _isPlainPost = true; } } } if (!_isPlainPost) { // ESP_LOGD("AsyncWebServer", "_isPlainPost: %d, _handler: %p", _isPlainPost, _handler); - if (_handler) - _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength); + if (_handler) { + _handler->handleBody(this, (uint8_t *)buf, len, _parsedLength, _contentLength); + } _parsedLength += len; } else if (needParse) { size_t i; for (i = 0; i < len; i++) { _parsedLength++; - _parsePlainPostChar(((uint8_t*)buf)[i]); + _parsePlainPostChar(((uint8_t *)buf)[i]); } } else { _parsedLength += len; @@ -157,12 +205,21 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) { } if (_parsedLength == _contentLength) { _parseState = PARSE_REQ_END; - _server->_runChain(this, [this]() { return _handler ? _handler->_runChain(this, [this]() { _handler->handleRequest(this); }) : send(501); }); + _server->_runChain(this, [this]() { + return _handler ? _handler->_runChain( + this, + [this]() { + _handler->handleRequest(this); + } + ) + : send(501); + }); if (!_sent) { - if (!_response) + if (!_response) { send(501, T_text_plain, "Handler did not handle the request"); - else if (!_response->_sourceValid()) + } else if (!_response->_sourceValid()) { send(500, T_text_plain, "Invalid data in handler"); + } _client->setRxTimeout(0); _response->_respond(this); _sent = true; @@ -179,7 +236,7 @@ void AsyncWebServerRequest::_onPoll() { if (!_response->_finished()) { _response->_ack(this, 0, 0); } else { - AsyncWebServerResponse* r = _response; + AsyncWebServerResponse *r = _response; _response = NULL; delete r; @@ -194,7 +251,7 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) { if (!_response->_finished()) { _response->_ack(this, len, time); } else if (_response->_finished()) { - AsyncWebServerResponse* r = _response; + AsyncWebServerResponse *r = _response; _response = NULL; delete r; @@ -225,19 +282,21 @@ void AsyncWebServerRequest::_onDisconnect() { _server->_handleDisconnect(this); } -void AsyncWebServerRequest::_addPathParam(const char* p) { +void AsyncWebServerRequest::_addPathParam(const char *p) { _pathParams.emplace_back(p); } -void AsyncWebServerRequest::_addGetParams(const String& params) { +void AsyncWebServerRequest::_addGetParams(const String ¶ms) { size_t start = 0; while (start < params.length()) { int end = params.indexOf('&', start); - if (end < 0) + if (end < 0) { end = params.length(); + } int equal = params.indexOf('=', start); - if (equal < 0 || equal > end) + if (equal < 0 || equal > end) { equal = end; + } String name(params.substring(start, equal)); String value(equal + 1 < end ? params.substring(equal + 1, end) : String()); _params.emplace_back(urlDecode(name), urlDecode(value)); @@ -280,11 +339,13 @@ bool AsyncWebServerRequest::_parseReqHead() { _url = urlDecode(u); _addGetParams(g); - if (!_url.length()) + if (!_url.length()) { return false; + } - if (!_temp.startsWith(T_HTTP_1_0)) + if (!_temp.startsWith(T_HTTP_1_0)) { _version = 1; + } _temp = emptyString; return true; @@ -333,9 +394,9 @@ bool AsyncWebServerRequest::_parseReqHeader() { String lowcase(value); lowcase.toLowerCase(); #ifndef ESP8266 - const char* substr = std::strstr(lowcase.c_str(), T_text_event_stream); + const char *substr = std::strstr(lowcase.c_str(), T_text_event_stream); #else - const char* substr = std::strstr(lowcase.c_str(), String(T_text_event_stream).c_str()); + const char *substr = std::strstr(lowcase.c_str(), String(T_text_event_stream).c_str()); #endif if (substr != NULL) { // WebEvent request can be uniquely identified by header: [Accept: text/event-stream] @@ -354,8 +415,9 @@ bool AsyncWebServerRequest::_parseReqHeader() { } void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) { - if (data && (char)data != '&') + if (data && (char)data != '&') { _temp += (char)data; + } if (!data || (char)data == '&' || _parsedLength == _contentLength) { String name(T_BODY); String value(_temp); @@ -379,8 +441,9 @@ void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) { if (last || _itemBufferIndex == RESPONSE_STREAM_BUFFER_SIZE) { // check if authenticated before calling the upload - if (_handler) + if (_handler) { _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false); + } _itemBufferIndex = 0; } } @@ -442,8 +505,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { _itemIsFile = false; } } else if (_multiParseState == PARSE_HEADERS) { - if ((char)data != '\r' && (char)data != '\n') + if ((char)data != '\r' && (char)data != '\n') { _temp += (char)data; + } if ((char)data == '\n') { if (_temp.length()) { if (_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(T_Content_Type)) { @@ -479,9 +543,10 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { _itemStartIndex = _parsedLength; _itemValue = emptyString; if (_itemIsFile) { - if (_itemBuffer) + if (_itemBuffer) { free(_itemBuffer); - _itemBuffer = (uint8_t*)malloc(RESPONSE_STREAM_BUFFER_SIZE); + } + _itemBuffer = (uint8_t *)malloc(RESPONSE_STREAM_BUFFER_SIZE); if (_itemBuffer == NULL) { _multiParseState = PARSE_ERROR; return; @@ -526,8 +591,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { itemWriteByte('-'); itemWriteByte('-'); uint8_t i; - for (i = 0; i < _boundaryPosition; i++) + for (i = 0; i < _boundaryPosition; i++) { itemWriteByte(_boundary.c_str()[i]); + } _parseMultipartPostByte(data, last); } else if (_boundaryPosition == _boundary.length() - 1) { _multiParseState = DASH3_OR_RETURN2; @@ -535,8 +601,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { _params.emplace_back(_itemName, _itemValue, true); } else { if (_itemSize) { - if (_handler) + if (_handler) { _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true); + } _itemBufferIndex = 0; _params.emplace_back(_itemName, _itemFilename, true, true, _itemSize); } @@ -550,7 +617,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { } else if (_multiParseState == DASH3_OR_RETURN2) { if (data == '-' && (_contentLength - _parsedLength - 4) != 0) { // os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4); - _contentLength = _parsedLength + 4; // lets close the request gracefully + _contentLength = _parsedLength + 4; // lets close the request gracefully } if (data == '\r') { _multiParseState = EXPECT_FEED2; @@ -563,8 +630,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { itemWriteByte('-'); itemWriteByte('-'); uint8_t i; - for (i = 0; i < _boundary.length(); i++) + for (i = 0; i < _boundary.length(); i++) { itemWriteByte(_boundary.c_str()[i]); + } _parseMultipartPostByte(data, last); } } else if (_multiParseState == EXPECT_FEED2) { @@ -578,8 +646,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { itemWriteByte('-'); itemWriteByte('-'); uint8_t i; - for (i = 0; i < _boundary.length(); i++) + for (i = 0; i < _boundary.length(); i++) { itemWriteByte(_boundary.c_str()[i]); + } itemWriteByte('\r'); _parseMultipartPostByte(data, last); } @@ -615,19 +684,29 @@ void AsyncWebServerRequest::_parseLine() { _parseState = PARSE_REQ_BODY; } else { _parseState = PARSE_REQ_END; - _server->_runChain(this, [this]() { return _handler ? _handler->_runChain(this, [this]() { _handler->handleRequest(this); }) : send(501); }); + _server->_runChain(this, [this]() { + return _handler ? _handler->_runChain( + this, + [this]() { + _handler->handleRequest(this); + } + ) + : send(501); + }); if (!_sent) { - if (!_response) + if (!_response) { send(501, T_text_plain, "Handler did not handle the request"); - else if (!_response->_sourceValid()) + } else if (!_response->_sourceValid()) { send(500, T_text_plain, "Invalid data in handler"); + } _client->setRxTimeout(0); _response->_respond(this); _sent = true; } } - } else + } else { _parseReqHeader(); + } } } @@ -635,8 +714,8 @@ size_t AsyncWebServerRequest::headers() const { return _headers.size(); } -bool AsyncWebServerRequest::hasHeader(const char* name) const { - for (const auto& h : _headers) { +bool AsyncWebServerRequest::hasHeader(const char *name) const { + for (const auto &h : _headers) { if (h.name().equalsIgnoreCase(name)) { return true; } @@ -645,24 +724,26 @@ bool AsyncWebServerRequest::hasHeader(const char* name) const { } #ifdef ESP8266 -bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper* data) const { +bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper *data) const { return hasHeader(String(data)); } #endif -const AsyncWebHeader* AsyncWebServerRequest::getHeader(const char* name) const { - auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); }); +const AsyncWebHeader *AsyncWebServerRequest::getHeader(const char *name) const { + auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader &header) { + return header.name().equalsIgnoreCase(name); + }); return (iter == std::end(_headers)) ? nullptr : &(*iter); } #ifdef ESP8266 -const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper* data) const { +const AsyncWebHeader *AsyncWebServerRequest::getHeader(const __FlashStringHelper *data) const { PGM_P p = reinterpret_cast(data); size_t n = strlen_P(p); - char* name = (char*)malloc(n + 1); + char *name = (char *)malloc(n + 1); if (name) { strcpy_P(name, p); - const AsyncWebHeader* result = getHeader(String(name)); + const AsyncWebHeader *result = getHeader(String(name)); free(name); return result; } else { @@ -671,24 +752,27 @@ const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper } #endif -const AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const { - if (num >= _headers.size()) +const AsyncWebHeader *AsyncWebServerRequest::getHeader(size_t num) const { + if (num >= _headers.size()) { return nullptr; + } return &(*std::next(_headers.cbegin(), num)); } -size_t AsyncWebServerRequest::getHeaderNames(std::vector& names) const { +size_t AsyncWebServerRequest::getHeaderNames(std::vector &names) const { const size_t size = _headers.size(); names.reserve(size); - for (const auto& h : _headers) { + for (const auto &h : _headers) { names.push_back(h.name().c_str()); } return size; } -bool AsyncWebServerRequest::removeHeader(const char* name) { +bool AsyncWebServerRequest::removeHeader(const char *name) { const size_t size = _headers.size(); - _headers.remove_if([name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); }); + _headers.remove_if([name](const AsyncWebHeader &header) { + return header.name().equalsIgnoreCase(name); + }); return size != _headers.size(); } @@ -696,8 +780,8 @@ size_t AsyncWebServerRequest::params() const { return _params.size(); } -bool AsyncWebServerRequest::hasParam(const char* name, bool post, bool file) const { - for (const auto& p : _params) { +bool AsyncWebServerRequest::hasParam(const char *name, bool post, bool file) const { + for (const auto &p : _params) { if (p.name().equals(name) && p.isPost() == post && p.isFile() == file) { return true; } @@ -705,8 +789,8 @@ bool AsyncWebServerRequest::hasParam(const char* name, bool post, bool file) con return false; } -const AsyncWebParameter* AsyncWebServerRequest::getParam(const char* name, bool post, bool file) const { - for (const auto& p : _params) { +const AsyncWebParameter *AsyncWebServerRequest::getParam(const char *name, bool post, bool file) const { + for (const auto &p : _params) { if (p.name() == name && p.isPost() == post && p.isFile() == file) { return &p; } @@ -715,122 +799,138 @@ const AsyncWebParameter* AsyncWebServerRequest::getParam(const char* name, bool } #ifdef ESP8266 -const AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper* data, bool post, bool file) const { +const AsyncWebParameter *AsyncWebServerRequest::getParam(const __FlashStringHelper *data, bool post, bool file) const { return getParam(String(data), post, file); } #endif -const AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const { - if (num >= _params.size()) +const AsyncWebParameter *AsyncWebServerRequest::getParam(size_t num) const { + if (num >= _params.size()) { return nullptr; + } return &(*std::next(_params.cbegin(), num)); } -const String& AsyncWebServerRequest::getAttribute(const char* name, const String& defaultValue) const { +const String &AsyncWebServerRequest::getAttribute(const char *name, const String &defaultValue) const { auto it = _attributes.find(name); return it != _attributes.end() ? it->second : defaultValue; } -bool AsyncWebServerRequest::getAttribute(const char* name, bool defaultValue) const { +bool AsyncWebServerRequest::getAttribute(const char *name, bool defaultValue) const { auto it = _attributes.find(name); return it != _attributes.end() ? it->second == "1" : defaultValue; } -long AsyncWebServerRequest::getAttribute(const char* name, long defaultValue) const { +long AsyncWebServerRequest::getAttribute(const char *name, long defaultValue) const { auto it = _attributes.find(name); return it != _attributes.end() ? it->second.toInt() : defaultValue; } -float AsyncWebServerRequest::getAttribute(const char* name, float defaultValue) const { +float AsyncWebServerRequest::getAttribute(const char *name, float defaultValue) const { auto it = _attributes.find(name); return it != _attributes.end() ? it->second.toFloat() : defaultValue; } -double AsyncWebServerRequest::getAttribute(const char* name, double defaultValue) const { +double AsyncWebServerRequest::getAttribute(const char *name, double defaultValue) const { auto it = _attributes.find(name); return it != _attributes.end() ? it->second.toDouble() : defaultValue; } -AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(int code, const char* contentType, const char* content, AwsTemplateProcessor callback) { - if (callback) - return new AsyncProgmemResponse(code, contentType, (const uint8_t*)content, strlen(content), callback); +AsyncWebServerResponse *AsyncWebServerRequest::beginResponse(int code, const char *contentType, const char *content, AwsTemplateProcessor callback) { + if (callback) { + return new AsyncProgmemResponse(code, contentType, (const uint8_t *)content, strlen(content), callback); + } return new AsyncBasicResponse(code, contentType, content); } -AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback) { +AsyncWebServerResponse * + AsyncWebServerRequest::beginResponse(int code, const char *contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback) { return new AsyncProgmemResponse(code, contentType, content, len, callback); } -AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(FS& fs, const String& path, const char* contentType, bool download, AwsTemplateProcessor callback) { - if (fs.exists(path) || (!download && fs.exists(path + T__gz))) +AsyncWebServerResponse * + AsyncWebServerRequest::beginResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) { + if (fs.exists(path) || (!download && fs.exists(path + T__gz))) { return new AsyncFileResponse(fs, path, contentType, download, callback); + } return NULL; } -AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(File content, const String& path, const char* contentType, bool download, AwsTemplateProcessor callback) { - if (content == true) +AsyncWebServerResponse * + AsyncWebServerRequest::beginResponse(File content, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) { + if (content == true) { return new AsyncFileResponse(content, path, contentType, download, callback); + } return NULL; } -AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(Stream& stream, const char* contentType, size_t len, AwsTemplateProcessor callback) { +AsyncWebServerResponse *AsyncWebServerRequest::beginResponse(Stream &stream, const char *contentType, size_t len, AwsTemplateProcessor callback) { return new AsyncStreamResponse(stream, contentType, len, callback); } -AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { +AsyncWebServerResponse * + AsyncWebServerRequest::beginResponse(const char *contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { return new AsyncCallbackResponse(contentType, len, callback, templateCallback); } -AsyncWebServerResponse* AsyncWebServerRequest::beginChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { - if (_version) +AsyncWebServerResponse * + AsyncWebServerRequest::beginChunkedResponse(const char *contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { + if (_version) { return new AsyncChunkedResponse(contentType, callback, templateCallback); + } return new AsyncCallbackResponse(contentType, 0, callback, templateCallback); } -AsyncResponseStream* AsyncWebServerRequest::beginResponseStream(const char* contentType, size_t bufferSize) { +AsyncResponseStream *AsyncWebServerRequest::beginResponseStream(const char *contentType, size_t bufferSize) { return new AsyncResponseStream(contentType, bufferSize); } -AsyncWebServerResponse* AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback) { - return new AsyncProgmemResponse(code, contentType, (const uint8_t*)content, strlen_P(content), callback); +AsyncWebServerResponse *AsyncWebServerRequest::beginResponse_P(int code, const String &contentType, PGM_P content, AwsTemplateProcessor callback) { + return new AsyncProgmemResponse(code, contentType, (const uint8_t *)content, strlen_P(content), callback); } -void AsyncWebServerRequest::send(AsyncWebServerResponse* response) { - if (_sent) +void AsyncWebServerRequest::send(AsyncWebServerResponse *response) { + if (_sent) { return; - if (_response) + } + if (_response) { delete _response; + } _response = response; } -void AsyncWebServerRequest::redirect(const char* url, int code) { - AsyncWebServerResponse* response = beginResponse(code); +void AsyncWebServerRequest::redirect(const char *url, int code) { + AsyncWebServerResponse *response = beginResponse(code); response->addHeader(T_LOCATION, url); send(response); } -bool AsyncWebServerRequest::authenticate(const char* username, const char* password, const char* realm, bool passwordIsHash) const { +bool AsyncWebServerRequest::authenticate(const char *username, const char *password, const char *realm, bool passwordIsHash) const { if (_authorization.length()) { - if (_authMethod == AsyncAuthType::AUTH_DIGEST) + if (_authMethod == AsyncAuthType::AUTH_DIGEST) { return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL); - else if (!passwordIsHash) + } else if (!passwordIsHash) { return checkBasicAuthentication(_authorization.c_str(), username, password); - else + } else { return _authorization.equals(password); + } } return false; } -bool AsyncWebServerRequest::authenticate(const char* hash) const { - if (!_authorization.length() || hash == NULL) +bool AsyncWebServerRequest::authenticate(const char *hash) const { + if (!_authorization.length() || hash == NULL) { return false; + } if (_authMethod == AsyncAuthType::AUTH_DIGEST) { String hStr = String(hash); int separator = hStr.indexOf(':'); - if (separator <= 0) + if (separator <= 0) { return false; + } String username = hStr.substring(0, separator); hStr = hStr.substring(separator + 1); separator = hStr.indexOf(':'); - if (separator <= 0) + if (separator <= 0) { return false; + } String realm = hStr.substring(0, separator); hStr = hStr.substring(separator + 1); return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL); @@ -840,14 +940,16 @@ bool AsyncWebServerRequest::authenticate(const char* hash) const { return (_authorization.equals(hash)); } -void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const char* realm, const char* _authFailMsg) { - if (!realm) +void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const char *realm, const char *_authFailMsg) { + if (!realm) { realm = T_LOGIN_REQ; + } - AsyncWebServerResponse* r = _authFailMsg ? beginResponse(401, T_text_html, _authFailMsg) : beginResponse(401); + AsyncWebServerResponse *r = _authFailMsg ? beginResponse(401, T_text_html, _authFailMsg) : beginResponse(401); switch (method) { - case AsyncAuthType::AUTH_BASIC: { + case AsyncAuthType::AUTH_BASIC: + { String header; header.reserve(strlen(T_BASIC_REALM) + strlen(realm) + 1); header.concat(T_BASIC_REALM); @@ -856,7 +958,8 @@ void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const ch r->addHeader(T_WWW_AUTH, header.c_str()); break; } - case AsyncAuthType::AUTH_DIGEST: { + case AsyncAuthType::AUTH_DIGEST: + { size_t len = strlen(T_DIGEST_) + strlen(T_realm__) + strlen(T_auth_nonce) + 32 + strlen(T__opaque) + 32 + 1; String header; header.reserve(len + strlen(realm)); @@ -867,19 +970,18 @@ void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const ch header.concat(genRandomMD5()); header.concat(T__opaque); header.concat(genRandomMD5()); - header.concat((char)0x22); // '"' + header.concat((char)0x22); // '"' r->addHeader(T_WWW_AUTH, header.c_str()); break; } - default: - break; + default: break; } send(r); } -bool AsyncWebServerRequest::hasArg(const char* name) const { - for (const auto& arg : _params) { +bool AsyncWebServerRequest::hasArg(const char *name) const { + for (const auto &arg : _params) { if (arg.name() == name) { return true; } @@ -888,13 +990,13 @@ bool AsyncWebServerRequest::hasArg(const char* name) const { } #ifdef ESP8266 -bool AsyncWebServerRequest::hasArg(const __FlashStringHelper* data) const { +bool AsyncWebServerRequest::hasArg(const __FlashStringHelper *data) const { return hasArg(String(data).c_str()); } #endif -const String& AsyncWebServerRequest::arg(const char* name) const { - for (const auto& arg : _params) { +const String &AsyncWebServerRequest::arg(const char *name) const { + for (const auto &arg : _params) { if (arg.name() == name) { return arg.value(); } @@ -903,50 +1005,50 @@ const String& AsyncWebServerRequest::arg(const char* name) const { } #ifdef ESP8266 -const String& AsyncWebServerRequest::arg(const __FlashStringHelper* data) const { +const String &AsyncWebServerRequest::arg(const __FlashStringHelper *data) const { return arg(String(data).c_str()); } #endif -const String& AsyncWebServerRequest::arg(size_t i) const { +const String &AsyncWebServerRequest::arg(size_t i) const { return getParam(i)->value(); } -const String& AsyncWebServerRequest::argName(size_t i) const { +const String &AsyncWebServerRequest::argName(size_t i) const { return getParam(i)->name(); } -const String& AsyncWebServerRequest::pathArg(size_t i) const { +const String &AsyncWebServerRequest::pathArg(size_t i) const { return i < _pathParams.size() ? _pathParams[i] : emptyString; } -const String& AsyncWebServerRequest::header(const char* name) const { - const AsyncWebHeader* h = getHeader(name); +const String &AsyncWebServerRequest::header(const char *name) const { + const AsyncWebHeader *h = getHeader(name); return h ? h->value() : emptyString; } #ifdef ESP8266 -const String& AsyncWebServerRequest::header(const __FlashStringHelper* data) const { +const String &AsyncWebServerRequest::header(const __FlashStringHelper *data) const { return header(String(data).c_str()); }; #endif -const String& AsyncWebServerRequest::header(size_t i) const { - const AsyncWebHeader* h = getHeader(i); +const String &AsyncWebServerRequest::header(size_t i) const { + const AsyncWebHeader *h = getHeader(i); return h ? h->value() : emptyString; } -const String& AsyncWebServerRequest::headerName(size_t i) const { - const AsyncWebHeader* h = getHeader(i); +const String &AsyncWebServerRequest::headerName(size_t i) const { + const AsyncWebHeader *h = getHeader(i); return h ? h->name() : emptyString; } -String AsyncWebServerRequest::urlDecode(const String& text) const { +String AsyncWebServerRequest::urlDecode(const String &text) const { char temp[] = "0x00"; unsigned int len = text.length(); unsigned int i = 0; String decoded; - decoded.reserve(len); // Allocate the string internal buffer - never longer from source text + decoded.reserve(len); // Allocate the string internal buffer - never longer from source text while (i < len) { char decodedChar; char encodedChar = text.charAt(i++); @@ -957,52 +1059,53 @@ String AsyncWebServerRequest::urlDecode(const String& text) const { } else if (encodedChar == '+') { decodedChar = ' '; } else { - decodedChar = encodedChar; // normal ascii char + decodedChar = encodedChar; // normal ascii char } decoded.concat(decodedChar); } return decoded; } -const char* AsyncWebServerRequest::methodToString() const { - if (_method == HTTP_ANY) +const char *AsyncWebServerRequest::methodToString() const { + if (_method == HTTP_ANY) { return T_ANY; - if (_method & HTTP_GET) + } + if (_method & HTTP_GET) { return T_GET; - if (_method & HTTP_POST) + } + if (_method & HTTP_POST) { return T_POST; - if (_method & HTTP_DELETE) + } + if (_method & HTTP_DELETE) { return T_DELETE; - if (_method & HTTP_PUT) + } + if (_method & HTTP_PUT) { return T_PUT; - if (_method & HTTP_PATCH) + } + if (_method & HTTP_PATCH) { return T_PATCH; - if (_method & HTTP_HEAD) + } + if (_method & HTTP_HEAD) { return T_HEAD; - if (_method & HTTP_OPTIONS) + } + if (_method & HTTP_OPTIONS) { return T_OPTIONS; + } return T_UNKNOWN; } -const char* AsyncWebServerRequest::requestedConnTypeToString() const { +const char *AsyncWebServerRequest::requestedConnTypeToString() const { switch (_reqconntype) { - case RCT_NOT_USED: - return T_RCT_NOT_USED; - case RCT_DEFAULT: - return T_RCT_DEFAULT; - case RCT_HTTP: - return T_RCT_HTTP; - case RCT_WS: - return T_RCT_WS; - case RCT_EVENT: - return T_RCT_EVENT; - default: - return T_ERROR; + case RCT_NOT_USED: return T_RCT_NOT_USED; + case RCT_DEFAULT: return T_RCT_DEFAULT; + case RCT_HTTP: return T_RCT_HTTP; + case RCT_WS: return T_RCT_WS; + case RCT_EVENT: return T_RCT_EVENT; + default: return T_ERROR; } } bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) const { - return ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) || - ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) || - ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)); + return ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) || ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) + || ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)); } diff --git a/src/WebResponseImpl.h b/src/WebResponseImpl.h index c93aa4d15..340e3e3c7 100644 --- a/src/WebResponseImpl.h +++ b/src/WebResponseImpl.h @@ -22,9 +22,9 @@ #define ASYNCWEBSERVERRESPONSEIMPL_H_ #ifdef Arduino_h - // arduino is not compatible with std::vector - #undef min - #undef max +// arduino is not compatible with std::vector +#undef min +#undef max #endif #include "literals.h" #include @@ -34,129 +34,158 @@ // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. class AsyncBasicResponse : public AsyncWebServerResponse { - private: - String _content; - - public: - explicit AsyncBasicResponse(int code, const char* contentType = asyncsrv::empty, const char* content = asyncsrv::empty); - AsyncBasicResponse(int code, const String& contentType, const String& content = emptyString) : AsyncBasicResponse(code, contentType.c_str(), content.c_str()) {} - void _respond(AsyncWebServerRequest* request) override final; - size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time) override final; - bool _sourceValid() const override final { return true; } +private: + String _content; + +public: + explicit AsyncBasicResponse(int code, const char *contentType = asyncsrv::empty, const char *content = asyncsrv::empty); + AsyncBasicResponse(int code, const String &contentType, const String &content = emptyString) + : AsyncBasicResponse(code, contentType.c_str(), content.c_str()) {} + void _respond(AsyncWebServerRequest *request) override final; + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time) override final; + bool _sourceValid() const override final { + return true; + } }; class AsyncAbstractResponse : public AsyncWebServerResponse { - private: +private: #if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT - // amount of responce data in-flight, i.e. sent, but not acked yet - size_t _in_flight{0}; - // in-flight queue credits - size_t _in_flight_credit{2}; + // amount of response data in-flight, i.e. sent, but not acked yet + size_t _in_flight{0}; + // in-flight queue credits + size_t _in_flight_credit{2}; #endif - String _head; - // Data is inserted into cache at begin(). - // This is inefficient with vector, but if we use some other container, - // we won't be able to access it as contiguous array of bytes when reading from it, - // so by gaining performance in one place, we'll lose it in another. - std::vector _cache; - size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); - size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); - - protected: - AwsTemplateProcessor _callback; - - public: - AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr); - virtual ~AsyncAbstractResponse() {} - void _respond(AsyncWebServerRequest* request) override final; - size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time) override final; - virtual bool _sourceValid() const { return false; } - virtual size_t _fillBuffer(uint8_t* buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } + String _head; + // Data is inserted into cache at begin(). + // This is inefficient with vector, but if we use some other container, + // we won't be able to access it as contiguous array of bytes when reading from it, + // so by gaining performance in one place, we'll lose it in another. + std::vector _cache; + size_t _readDataFromCacheOrContent(uint8_t *data, const size_t len); + size_t _fillBufferAndProcessTemplates(uint8_t *buf, size_t maxLen); + +protected: + AwsTemplateProcessor _callback; + +public: + AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr); + virtual ~AsyncAbstractResponse() {} + void _respond(AsyncWebServerRequest *request) override final; + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time) override final; + virtual bool _sourceValid() const { + return false; + } + virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { + return 0; + } }; #ifndef TEMPLATE_PLACEHOLDER - #define TEMPLATE_PLACEHOLDER '%' +#define TEMPLATE_PLACEHOLDER '%' #endif #define TEMPLATE_PARAM_NAME_LENGTH 32 class AsyncFileResponse : public AsyncAbstractResponse { - using File = fs::File; - using FS = fs::FS; - - private: - File _content; - String _path; - void _setContentTypeFromPath(const String& path); - - public: - AsyncFileResponse(FS& fs, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr); - AsyncFileResponse(FS& fs, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callback = nullptr) : AsyncFileResponse(fs, path, contentType.c_str(), download, callback) {} - AsyncFileResponse(File content, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr); - AsyncFileResponse(File content, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callack = nullptr) : AsyncFileResponse(content, path, contentType.c_str(), download, callack) {} - ~AsyncFileResponse() { _content.close(); } - bool _sourceValid() const override final { return !!(_content); } - size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; + using File = fs::File; + using FS = fs::FS; + +private: + File _content; + String _path; + void _setContentTypeFromPath(const String &path); + +public: + AsyncFileResponse(FS &fs, const String &path, const char *contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr); + AsyncFileResponse(FS &fs, const String &path, const String &contentType, bool download = false, AwsTemplateProcessor callback = nullptr) + : AsyncFileResponse(fs, path, contentType.c_str(), download, callback) {} + AsyncFileResponse( + File content, const String &path, const char *contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr + ); + AsyncFileResponse(File content, const String &path, const String &contentType, bool download = false, AwsTemplateProcessor callback = nullptr) + : AsyncFileResponse(content, path, contentType.c_str(), download, callback) {} + ~AsyncFileResponse() { + _content.close(); + } + bool _sourceValid() const override final { + return !!(_content); + } + size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final; }; class AsyncStreamResponse : public AsyncAbstractResponse { - private: - Stream* _content; - - public: - AsyncStreamResponse(Stream& stream, const char* contentType, size_t len, AwsTemplateProcessor callback = nullptr); - AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr) : AsyncStreamResponse(stream, contentType.c_str(), len, callback) {} - bool _sourceValid() const override final { return !!(_content); } - size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; +private: + Stream *_content; + +public: + AsyncStreamResponse(Stream &stream, const char *contentType, size_t len, AwsTemplateProcessor callback = nullptr); + AsyncStreamResponse(Stream &stream, const String &contentType, size_t len, AwsTemplateProcessor callback = nullptr) + : AsyncStreamResponse(stream, contentType.c_str(), len, callback) {} + bool _sourceValid() const override final { + return !!(_content); + } + size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final; }; class AsyncCallbackResponse : public AsyncAbstractResponse { - private: - AwsResponseFiller _content; - size_t _filledLength; - - public: - AsyncCallbackResponse(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncCallbackResponse(contentType.c_str(), len, callback, templateCallback) {} - bool _sourceValid() const override final { return !!(_content); } - size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; +private: + AwsResponseFiller _content; + size_t _filledLength; + +public: + AsyncCallbackResponse(const char *contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncCallbackResponse(const String &contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) + : AsyncCallbackResponse(contentType.c_str(), len, callback, templateCallback) {} + bool _sourceValid() const override final { + return !!(_content); + } + size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final; }; class AsyncChunkedResponse : public AsyncAbstractResponse { - private: - AwsResponseFiller _content; - size_t _filledLength; - - public: - AsyncChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncChunkedResponse(contentType.c_str(), callback, templateCallback) {} - bool _sourceValid() const override final { return !!(_content); } - size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; +private: + AwsResponseFiller _content; + size_t _filledLength; + +public: + AsyncChunkedResponse(const char *contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncChunkedResponse(const String &contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) + : AsyncChunkedResponse(contentType.c_str(), callback, templateCallback) {} + bool _sourceValid() const override final { + return !!(_content); + } + size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final; }; class AsyncProgmemResponse : public AsyncAbstractResponse { - private: - const uint8_t* _content; - size_t _readLength; - - public: - AsyncProgmemResponse(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr); - AsyncProgmemResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) : AsyncProgmemResponse(code, contentType.c_str(), content, len, callback) {} - bool _sourceValid() const override final { return true; } - size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; +private: + const uint8_t *_content; + size_t _readLength; + +public: + AsyncProgmemResponse(int code, const char *contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback = nullptr); + AsyncProgmemResponse(int code, const String &contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback = nullptr) + : AsyncProgmemResponse(code, contentType.c_str(), content, len, callback) {} + bool _sourceValid() const override final { + return true; + } + size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final; }; class AsyncResponseStream : public AsyncAbstractResponse, public Print { - private: - StreamString _content; - - public: - AsyncResponseStream(const char* contentType, size_t bufferSize); - AsyncResponseStream(const String& contentType, size_t bufferSize) : AsyncResponseStream(contentType.c_str(), bufferSize) {} - bool _sourceValid() const override final { return (_state < RESPONSE_END); } - size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; - size_t write(const uint8_t* data, size_t len); - size_t write(uint8_t data); - using Print::write; +private: + StreamString _content; + +public: + AsyncResponseStream(const char *contentType, size_t bufferSize); + AsyncResponseStream(const String &contentType, size_t bufferSize) : AsyncResponseStream(contentType.c_str(), bufferSize) {} + bool _sourceValid() const override final { + return (_state < RESPONSE_END); + } + size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final; + size_t write(const uint8_t *data, size_t len); + size_t write(uint8_t data); + using Print::write; }; #endif /* ASYNCWEBSERVERRESPONSEIMPL_H_ */ diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp index 09a7f8d85..a1558fdac 100644 --- a/src/WebResponses.cpp +++ b/src/WebResponses.cpp @@ -24,11 +24,13 @@ 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(ptr); - while (count--) - if (*p++ == static_cast(ch)) +void *memchr(void *ptr, int ch, size_t count) { + unsigned char *p = static_cast(ptr); + while (count--) { + if (*p++ == static_cast(ch)) { return --p; + } + } return nullptr; } @@ -37,118 +39,80 @@ void* memchr(void* ptr, int ch, size_t count) { * */ -const char* AsyncWebServerResponse::responseCodeToString(int code) { +const char *AsyncWebServerResponse::responseCodeToString(int code) { switch (code) { - case 100: - return T_HTTP_CODE_100; - case 101: - return T_HTTP_CODE_101; - case 200: - return T_HTTP_CODE_200; - case 201: - return T_HTTP_CODE_201; - case 202: - return T_HTTP_CODE_202; - case 203: - return T_HTTP_CODE_203; - case 204: - return T_HTTP_CODE_204; - case 205: - return T_HTTP_CODE_205; - case 206: - return T_HTTP_CODE_206; - case 300: - return T_HTTP_CODE_300; - case 301: - return T_HTTP_CODE_301; - case 302: - return T_HTTP_CODE_302; - case 303: - return T_HTTP_CODE_303; - case 304: - return T_HTTP_CODE_304; - case 305: - return T_HTTP_CODE_305; - case 307: - return T_HTTP_CODE_307; - case 400: - return T_HTTP_CODE_400; - case 401: - return T_HTTP_CODE_401; - case 402: - return T_HTTP_CODE_402; - case 403: - return T_HTTP_CODE_403; - case 404: - return T_HTTP_CODE_404; - case 405: - return T_HTTP_CODE_405; - case 406: - return T_HTTP_CODE_406; - case 407: - return T_HTTP_CODE_407; - case 408: - return T_HTTP_CODE_408; - case 409: - return T_HTTP_CODE_409; - case 410: - return T_HTTP_CODE_410; - case 411: - return T_HTTP_CODE_411; - case 412: - return T_HTTP_CODE_412; - case 413: - return T_HTTP_CODE_413; - case 414: - return T_HTTP_CODE_414; - case 415: - return T_HTTP_CODE_415; - case 416: - return T_HTTP_CODE_416; - case 417: - return T_HTTP_CODE_417; - case 429: - return T_HTTP_CODE_429; - case 500: - return T_HTTP_CODE_500; - case 501: - return T_HTTP_CODE_501; - case 502: - return T_HTTP_CODE_502; - case 503: - return T_HTTP_CODE_503; - case 504: - return T_HTTP_CODE_504; - case 505: - return T_HTTP_CODE_505; - default: - return T_HTTP_CODE_ANY; + case 100: return T_HTTP_CODE_100; + case 101: return T_HTTP_CODE_101; + case 200: return T_HTTP_CODE_200; + case 201: return T_HTTP_CODE_201; + case 202: return T_HTTP_CODE_202; + case 203: return T_HTTP_CODE_203; + case 204: return T_HTTP_CODE_204; + case 205: return T_HTTP_CODE_205; + case 206: return T_HTTP_CODE_206; + case 300: return T_HTTP_CODE_300; + case 301: return T_HTTP_CODE_301; + case 302: return T_HTTP_CODE_302; + case 303: return T_HTTP_CODE_303; + case 304: return T_HTTP_CODE_304; + case 305: return T_HTTP_CODE_305; + case 307: return T_HTTP_CODE_307; + case 400: return T_HTTP_CODE_400; + case 401: return T_HTTP_CODE_401; + case 402: return T_HTTP_CODE_402; + case 403: return T_HTTP_CODE_403; + case 404: return T_HTTP_CODE_404; + case 405: return T_HTTP_CODE_405; + case 406: return T_HTTP_CODE_406; + case 407: return T_HTTP_CODE_407; + case 408: return T_HTTP_CODE_408; + case 409: return T_HTTP_CODE_409; + case 410: return T_HTTP_CODE_410; + case 411: return T_HTTP_CODE_411; + case 412: return T_HTTP_CODE_412; + case 413: return T_HTTP_CODE_413; + case 414: return T_HTTP_CODE_414; + case 415: return T_HTTP_CODE_415; + case 416: return T_HTTP_CODE_416; + case 417: return T_HTTP_CODE_417; + case 429: return T_HTTP_CODE_429; + case 500: return T_HTTP_CODE_500; + case 501: return T_HTTP_CODE_501; + case 502: return T_HTTP_CODE_502; + case 503: return T_HTTP_CODE_503; + case 504: return T_HTTP_CODE_504; + case 505: return T_HTTP_CODE_505; + default: return T_HTTP_CODE_ANY; } } AsyncWebServerResponse::AsyncWebServerResponse() - : _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0), _state(RESPONSE_SETUP) { - for (const auto& header : DefaultHeaders::Instance()) { + : _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0), + _state(RESPONSE_SETUP) { + for (const auto &header : DefaultHeaders::Instance()) { _headers.emplace_back(header); } } void AsyncWebServerResponse::setCode(int code) { - if (_state == RESPONSE_SETUP) + if (_state == RESPONSE_SETUP) { _code = code; + } } void AsyncWebServerResponse::setContentLength(size_t len) { - if (_state == RESPONSE_SETUP && addHeader(T_Content_Length, len, true)) + if (_state == RESPONSE_SETUP && addHeader(T_Content_Length, len, true)) { _contentLength = len; + } } -void AsyncWebServerResponse::setContentType(const char* type) { - if (_state == RESPONSE_SETUP && addHeader(T_Content_Type, type, true)) +void AsyncWebServerResponse::setContentType(const char *type) { + if (_state == RESPONSE_SETUP && addHeader(T_Content_Type, type, true)) { _contentType = type; + } } -bool AsyncWebServerResponse::removeHeader(const char* name) { +bool AsyncWebServerResponse::removeHeader(const char *name) { for (auto i = _headers.begin(); i != _headers.end(); ++i) { if (i->name().equalsIgnoreCase(name)) { _headers.erase(i); @@ -158,12 +122,14 @@ bool AsyncWebServerResponse::removeHeader(const char* name) { return false; } -const AsyncWebHeader* AsyncWebServerResponse::getHeader(const char* name) const { - auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); }); +const AsyncWebHeader *AsyncWebServerResponse::getHeader(const char *name) const { + auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader &header) { + return header.name().equalsIgnoreCase(name); + }); return (iter == std::end(_headers)) ? nullptr : &(*iter); } -bool AsyncWebServerResponse::addHeader(const char* name, const char* value, bool replaceExisting) { +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)) { // header already set @@ -182,24 +148,28 @@ bool AsyncWebServerResponse::addHeader(const char* name, const char* value, bool return true; } -void AsyncWebServerResponse::_assembleHead(String& buffer, uint8_t version) { +void AsyncWebServerResponse::_assembleHead(String &buffer, uint8_t version) { if (version) { addHeader(T_Accept_Ranges, T_none, false); - if (_chunked) + if (_chunked) { addHeader(T_Transfer_Encoding, T_chunked, false); + } } - if (_sendContentLength) + if (_sendContentLength) { addHeader(T_Content_Length, String(_contentLength), false); + } - if (_contentType.length()) + if (_contentType.length()) { addHeader(T_Content_Type, _contentType.c_str(), false); + } // precompute buffer size to avoid reallocations by String class size_t len = 0; - len += 50; // HTTP/1.1 200 \r\n - for (const auto& header : _headers) + len += 50; // HTTP/1.1 200 \r\n + for (const auto &header : _headers) { len += header.name().length() + header.value().length() + 4; + } // prepare buffer buffer.reserve(len); @@ -218,7 +188,7 @@ void AsyncWebServerResponse::_assembleHead(String& buffer, uint8_t version) { buffer.concat(T_rn); // Add headers - for (const auto& header : _headers) { + for (const auto &header : _headers) { buffer.concat(header.name()); #ifdef ESP8266 buffer.concat(PSTR(": ")); @@ -233,15 +203,23 @@ void AsyncWebServerResponse::_assembleHead(String& buffer, uint8_t version) { _headLength = buffer.length(); } -bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; } -bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; } -bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; } -bool AsyncWebServerResponse::_sourceValid() const { return false; } -void AsyncWebServerResponse::_respond(AsyncWebServerRequest* request) { +bool AsyncWebServerResponse::_started() const { + return _state > RESPONSE_SETUP; +} +bool AsyncWebServerResponse::_finished() const { + return _state > RESPONSE_WAIT_ACK; +} +bool AsyncWebServerResponse::_failed() const { + return _state == RESPONSE_FAILED; +} +bool AsyncWebServerResponse::_sourceValid() const { + return false; +} +void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request) { _state = RESPONSE_END; request->client()->close(); } -size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) { +size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { (void)request; (void)len; (void)time; @@ -251,19 +229,20 @@ size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest* request, size_t len, /* * String/Code Response * */ -AsyncBasicResponse::AsyncBasicResponse(int code, const char* contentType, const char* content) { +AsyncBasicResponse::AsyncBasicResponse(int code, const char *contentType, const char *content) { _code = code; _content = content; _contentType = contentType; if (_content.length()) { _contentLength = _content.length(); - if (!_contentType.length()) + if (!_contentType.length()) { _contentType = T_text_plain; + } } addHeader(T_Connection, T_close, false); } -void AsyncBasicResponse::_respond(AsyncWebServerRequest* request) { +void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) { _state = RESPONSE_HEADERS; String out; _assembleHead(out, request->version()); @@ -298,7 +277,7 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest* request) { } } -size_t AsyncBasicResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) { +size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { (void)time; _ackedLength += len; if (_state == RESPONSE_CONTENT) { @@ -338,14 +317,14 @@ AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback) : _c } } -void AsyncAbstractResponse::_respond(AsyncWebServerRequest* request) { +void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request) { addHeader(T_Connection, T_close, false); _assembleHead(_head, request->version()); _state = RESPONSE_HEADERS; _ack(request, 0, 0); } -size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) { +size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { (void)time; if (!_sourceValid()) { _state = RESPONSE_FAILED; @@ -355,14 +334,15 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u #if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT // return a credit for each chunk of acked data (polls does not give any credits) - if (len) + if (len) { ++_in_flight_credit; + } // for chunked responses ignore acks if there are no _in_flight_credits left if (_chunked && !_in_flight_credit) { - #ifdef ESP32 +#ifdef ESP32 log_d("(chunk) out of in-flight credits"); - #endif +#endif return 0; } @@ -384,7 +364,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u _writtenLength += request->client()->write(out.c_str(), out.length()); #if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT _in_flight += out.length(); - --_in_flight_credit; // take a credit + --_in_flight_credit; // take a credit #endif return out.length(); } @@ -399,8 +379,9 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u if (_in_flight > space) { // log_d("defer user call %u/%u", _in_flight, space); // take the credit back since we are ignoring this ack and rely on other inflight data - if (len) + if (len) { --_in_flight_credit; + } return 0; } #endif @@ -418,7 +399,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u outLen = ((_contentLength - _sentLength) > space) ? space : (_contentLength - _sentLength); } - uint8_t* buf = (uint8_t*)malloc(outLen + headLen); + uint8_t *buf = (uint8_t *)malloc(outLen + headLen); if (!buf) { // os_printf("_ack malloc %d failed\n", outLen+headLen); return 0; @@ -438,7 +419,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u free(buf); return 0; } - outLen = sprintf((char*)buf + headLen, "%04x", readLen) + headLen; + outLen = sprintf((char *)buf + headLen, "%04x", readLen) + headLen; buf[outLen++] = '\r'; buf[outLen++] = '\n'; outLen += readLen; @@ -458,10 +439,10 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u } if (outLen) { - _writtenLength += request->client()->write((const char*)buf, outLen); + _writtenLength += request->client()->write((const char *)buf, outLen); #if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT _in_flight += outLen; - --_in_flight_credit; // take a credit + --_in_flight_credit; // take a credit #endif } @@ -481,14 +462,15 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u } else if (_state == RESPONSE_WAIT_ACK) { if (!_sendContentLength || _ackedLength >= _writtenLength) { _state = RESPONSE_END; - if (!_chunked && !_sendContentLength) + if (!_chunked && !_sendContentLength) { request->client()->close(true); + } } } return 0; } -size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) { +size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t *data, const size_t len) { // If we have something in cache, copy it to buffer const size_t readFromCache = std::min(len, _cache.size()); if (readFromCache) { @@ -501,17 +483,20 @@ size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const s return readFromCache + readFromContent; } -size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) { - if (!_callback) +size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t *data, size_t len) { + if (!_callback) { return _fillBuffer(data, len); + } const size_t originalLen = len; len = _readDataFromCacheOrContent(data, len); // Now we've read 'len' bytes, either from cache or from file // Search for template placeholders - uint8_t* pTemplateStart = data; - while ((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1] - uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr; + uint8_t *pTemplateStart = data; + while ((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t *)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1)) + ) { // data[0] ... data[len - 1] + uint8_t *pTemplateEnd = + (pTemplateStart < &data[len - 1]) ? (uint8_t *)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr; // temporary buffer to hold parameter name uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; String paramName; @@ -522,35 +507,39 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size if (paramNameLength) { memcpy(buf, pTemplateStart + 1, paramNameLength); buf[paramNameLength] = 0; - paramName = String(reinterpret_cast(buf)); - } else { // double percent sign encountered, this is single percent sign escaped. + paramName = String(reinterpret_cast(buf)); + } else { // double percent sign encountered, this is single percent sign escaped. // remove the 2nd percent sign memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1; ++pTemplateStart; } - } else if (&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data + } else if (&data[len - 1] - pTemplateStart + 1 + < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart); - const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); + const size_t readFromCacheOrContent = + _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); if (readFromCacheOrContent) { - pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent); + pTemplateEnd = (uint8_t *)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent); if (pTemplateEnd) { // prepare argument to callback *pTemplateEnd = 0; - paramName = String(reinterpret_cast(buf)); + paramName = String(reinterpret_cast(buf)); // Copy remaining read-ahead data into cache _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); pTemplateEnd = &data[len - 1]; - } else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position + } else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position { // but first, store read file data in cache _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); ++pTemplateStart; } - } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position + } else { // closing placeholder not found in content data, store found percent symbol as is and advance to the next position ++pTemplateStart; - } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position + } + } else { // closing placeholder not found in content data, store found percent symbol as is and advance to the next position ++pTemplateStart; + } if (paramName.length()) { // call callback and replace with result. // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value. @@ -558,7 +547,7 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size // The first byte of data after placeholder is located at pTemplateEnd + 1. // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value). const String paramValue(_callback(paramName)); - const char* pvstr = paramValue.c_str(); + const char *pvstr = paramValue.c_str(); const unsigned int pvlen = paramValue.length(); const size_t numBytesCopied = std::min(pvlen, static_cast(&data[originalLen - 1] - pTemplateStart + 1)); // make room for param value @@ -567,27 +556,28 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); // 2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied); - len = originalLen; // fix issue with truncated data, not sure if it has any side effects - } else if (pTemplateEnd + 1 != pTemplateStart + numBytesCopied) + len = originalLen; // fix issue with truncated data, not sure if it has any side effects + } else if (pTemplateEnd + 1 != pTemplateStart + numBytesCopied) { // 2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit. // Move the entire data after the placeholder memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); + } // 3. replace placeholder with actual value memcpy(pTemplateStart, pvstr, numBytesCopied); // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer) if (numBytesCopied < pvlen) { _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen); - } else if (pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... + } else if (pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... // there is some free room, fill it from cache const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied; const size_t totalFreeRoom = originalLen - len + roomFreed; len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed; - } else { // result is copied fully; it is longer than placeholder text + } else { // result is copied fully; it is longer than placeholder text const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1; len = std::min(len + roomTaken, originalLen); } } - } // while(pTemplateStart) + } // while(pTemplateStart) return len; } @@ -595,64 +585,66 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size * File Response * */ -void AsyncFileResponse::_setContentTypeFromPath(const String& path) { +void AsyncFileResponse::_setContentTypeFromPath(const String &path) { #if HAVE_EXTERN_GET_Content_Type_FUNCTION - #ifndef ESP8266 - extern const char* getContentType(const String& path); - #else - extern const __FlashStringHelper* getContentType(const String& path); - #endif +#ifndef ESP8266 + extern const char *getContentType(const String &path); +#else + extern const __FlashStringHelper *getContentType(const String &path); +#endif _contentType = getContentType(path); #else - if (path.endsWith(T__html)) + if (path.endsWith(T__html)) { _contentType = T_text_html; - else if (path.endsWith(T__htm)) + } else if (path.endsWith(T__htm)) { _contentType = T_text_html; - else if (path.endsWith(T__css)) + } else if (path.endsWith(T__css)) { _contentType = T_text_css; - else if (path.endsWith(T__json)) + } else if (path.endsWith(T__json)) { _contentType = T_application_json; - else if (path.endsWith(T__js)) + } else if (path.endsWith(T__js)) { _contentType = T_application_javascript; - else if (path.endsWith(T__png)) + } else if (path.endsWith(T__png)) { _contentType = T_image_png; - else if (path.endsWith(T__gif)) + } else if (path.endsWith(T__gif)) { _contentType = T_image_gif; - else if (path.endsWith(T__jpg)) + } else if (path.endsWith(T__jpg)) { _contentType = T_image_jpeg; - else if (path.endsWith(T__ico)) + } else if (path.endsWith(T__ico)) { _contentType = T_image_x_icon; - else if (path.endsWith(T__svg)) + } else if (path.endsWith(T__svg)) { _contentType = T_image_svg_xml; - else if (path.endsWith(T__eot)) + } else if (path.endsWith(T__eot)) { _contentType = T_font_eot; - else if (path.endsWith(T__woff)) + } else if (path.endsWith(T__woff)) { _contentType = T_font_woff; - else if (path.endsWith(T__woff2)) + } else if (path.endsWith(T__woff2)) { _contentType = T_font_woff2; - else if (path.endsWith(T__ttf)) + } else if (path.endsWith(T__ttf)) { _contentType = T_font_ttf; - else if (path.endsWith(T__xml)) + } else if (path.endsWith(T__xml)) { _contentType = T_text_xml; - else if (path.endsWith(T__pdf)) + } else if (path.endsWith(T__pdf)) { _contentType = T_application_pdf; - else if (path.endsWith(T__zip)) + } else if (path.endsWith(T__zip)) { _contentType = T_application_zip; - else if (path.endsWith(T__gz)) + } else if (path.endsWith(T__gz)) { _contentType = T_application_x_gzip; - else + } else { _contentType = T_text_plain; + } #endif } -AsyncFileResponse::AsyncFileResponse(FS& fs, const String& path, const char* contentType, bool download, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) { +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 + _callback = nullptr; // Unable to process zipped templates _sendContentLength = true; _chunked = false; } @@ -660,14 +652,15 @@ AsyncFileResponse::AsyncFileResponse(FS& fs, const String& path, const char* con _content = fs.open(_path, fs::FileOpenMode::read); _contentLength = _content.size(); - if (strlen(contentType) == 0) + if (strlen(contentType) == 0) { _setContentTypeFromPath(path); - else + } else { _contentType = contentType; + } int filenameStart = path.lastIndexOf('/') + 1; char buf[26 + path.length() - filenameStart]; - char* filename = (char*)path.c_str() + filenameStart; + char *filename = (char *)path.c_str() + filenameStart; if (download) { // set filename and force download @@ -679,13 +672,14 @@ AsyncFileResponse::AsyncFileResponse(FS& fs, const String& path, const char* con addHeader(T_Content_Disposition, buf, false); } -AsyncFileResponse::AsyncFileResponse(File content, const String& path, const char* contentType, bool download, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) { +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)) { addHeader(T_Content_Encoding, T_gzip, false); - _callback = nullptr; // Unable to process gzipped templates + _callback = nullptr; // Unable to process gzipped templates _sendContentLength = true; _chunked = false; } @@ -693,14 +687,15 @@ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const cha _content = content; _contentLength = _content.size(); - if (strlen(contentType) == 0) + if (strlen(contentType) == 0) { _setContentTypeFromPath(path); - else + } else { _contentType = contentType; + } int filenameStart = path.lastIndexOf('/') + 1; char buf[26 + path.length() - filenameStart]; - char* filename = (char*)path.c_str() + filenameStart; + char *filename = (char *)path.c_str() + filenameStart; if (download) { snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename); @@ -710,7 +705,7 @@ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const cha addHeader(T_Content_Disposition, buf, false); } -size_t AsyncFileResponse::_fillBuffer(uint8_t* data, size_t len) { +size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len) { return _content.read(data, len); } @@ -718,19 +713,20 @@ size_t AsyncFileResponse::_fillBuffer(uint8_t* data, size_t len) { * Stream Response * */ -AsyncStreamResponse::AsyncStreamResponse(Stream& stream, const char* contentType, size_t len, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) { +AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const char *contentType, size_t len, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) { _code = 200; _content = &stream; _contentLength = len; _contentType = contentType; } -size_t AsyncStreamResponse::_fillBuffer(uint8_t* data, size_t len) { +size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len) { size_t available = _content->available(); size_t outLen = (available > len) ? len : available; size_t i; - for (i = 0; i < outLen; i++) + for (i = 0; i < outLen; i++) { data[i] = _content->read(); + } return outLen; } @@ -738,17 +734,19 @@ size_t AsyncStreamResponse::_fillBuffer(uint8_t* data, size_t len) { * Callback Response * */ -AsyncCallbackResponse::AsyncCallbackResponse(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) : AsyncAbstractResponse(templateCallback) { +AsyncCallbackResponse::AsyncCallbackResponse(const char *contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) + : AsyncAbstractResponse(templateCallback) { _code = 200; _content = callback; _contentLength = len; - if (!len) + if (!len) { _sendContentLength = false; + } _contentType = contentType; _filledLength = 0; } -size_t AsyncCallbackResponse::_fillBuffer(uint8_t* data, size_t len) { +size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len) { size_t ret = _content(data, len, _filledLength); if (ret != RESPONSE_TRY_AGAIN) { _filledLength += ret; @@ -760,7 +758,8 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t* data, size_t len) { * Chunked Response * */ -AsyncChunkedResponse::AsyncChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback) : AsyncAbstractResponse(processorCallback) { +AsyncChunkedResponse::AsyncChunkedResponse(const char *contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback) + : AsyncAbstractResponse(processorCallback) { _code = 200; _content = callback; _contentLength = 0; @@ -770,7 +769,7 @@ AsyncChunkedResponse::AsyncChunkedResponse(const char* contentType, AwsResponseF _filledLength = 0; } -size_t AsyncChunkedResponse::_fillBuffer(uint8_t* data, size_t len) { +size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len) { size_t ret = _content(data, len, _filledLength); if (ret != RESPONSE_TRY_AGAIN) { _filledLength += ret; @@ -782,7 +781,8 @@ size_t AsyncChunkedResponse::_fillBuffer(uint8_t* data, size_t len) { * Progmem Response * */ -AsyncProgmemResponse::AsyncProgmemResponse(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) { +AsyncProgmemResponse::AsyncProgmemResponse(int code, const char *contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback) + : AsyncAbstractResponse(callback) { _code = code; _content = content; _contentType = contentType; @@ -790,7 +790,7 @@ AsyncProgmemResponse::AsyncProgmemResponse(int code, const char* contentType, co _readLength = 0; } -size_t AsyncProgmemResponse::_fillBuffer(uint8_t* data, size_t len) { +size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len) { size_t left = _contentLength - _readLength; if (left > len) { memcpy_P(data, _content + _readLength, len); @@ -806,20 +806,21 @@ size_t AsyncProgmemResponse::_fillBuffer(uint8_t* data, size_t len) { * Response Stream (You can print/write/printf to it, up to the contentLen bytes) * */ -AsyncResponseStream::AsyncResponseStream(const char* contentType, size_t bufferSize) { +AsyncResponseStream::AsyncResponseStream(const char *contentType, size_t bufferSize) { _code = 200; _contentLength = 0; _contentType = contentType; _content.reserve(bufferSize); } -size_t AsyncResponseStream::_fillBuffer(uint8_t* buf, size_t maxLen) { - return _content.readBytes((char*)buf, maxLen); +size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen) { + return _content.readBytes((char *)buf, maxLen); } -size_t AsyncResponseStream::write(const uint8_t* data, size_t len) { - if (_started()) +size_t AsyncResponseStream::write(const uint8_t *data, size_t len) { + if (_started()) { return 0; + } size_t written = _content.write(data, len); _contentLength += written; return written; diff --git a/src/WebServer.cpp b/src/WebServer.cpp index 2442c8a6f..dfeac2067 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -23,7 +23,7 @@ using namespace asyncsrv; -bool ON_STA_FILTER(AsyncWebServerRequest* request) { +bool ON_STA_FILTER(AsyncWebServerRequest *request) { #ifndef CONFIG_IDF_TARGET_ESP32H2 return WiFi.localIP() == request->client()->localIP(); #else @@ -31,7 +31,7 @@ bool ON_STA_FILTER(AsyncWebServerRequest* request) { #endif } -bool ON_AP_FILTER(AsyncWebServerRequest* request) { +bool ON_AP_FILTER(AsyncWebServerRequest *request) { #ifndef CONFIG_IDF_TARGET_ESP32H2 return WiFi.localIP() != request->client()->localIP(); #else @@ -40,51 +40,55 @@ bool ON_AP_FILTER(AsyncWebServerRequest* request) { } #ifndef HAVE_FS_FILE_OPEN_MODE -const char* fs::FileOpenMode::read = "r"; -const char* fs::FileOpenMode::write = "w"; -const char* fs::FileOpenMode::append = "a"; +const char *fs::FileOpenMode::read = "r"; +const char *fs::FileOpenMode::write = "w"; +const char *fs::FileOpenMode::append = "a"; #endif -AsyncWebServer::AsyncWebServer(uint16_t port) - : _server(port) { +AsyncWebServer::AsyncWebServer(uint16_t port) : _server(port) { _catchAllHandler = new AsyncCallbackWebHandler(); - if (_catchAllHandler == NULL) + if (_catchAllHandler == NULL) { return; - _server.onClient([](void* s, AsyncClient* c) { - if (c == NULL) - return; - c->setRxTimeout(3); - AsyncWebServerRequest* r = new AsyncWebServerRequest((AsyncWebServer*)s, c); - if (r == NULL) { - c->abort(); - delete c; - } - }, - this); + } + _server.onClient( + [](void *s, AsyncClient *c) { + if (c == NULL) { + return; + } + c->setRxTimeout(3); + AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer *)s, c); + if (r == NULL) { + c->abort(); + delete c; + } + }, + this + ); } AsyncWebServer::~AsyncWebServer() { reset(); end(); - if (_catchAllHandler) + if (_catchAllHandler) { delete _catchAllHandler; + } } -AsyncWebRewrite& AsyncWebServer::addRewrite(std::shared_ptr rewrite) { +AsyncWebRewrite &AsyncWebServer::addRewrite(std::shared_ptr rewrite) { _rewrites.emplace_back(rewrite); return *_rewrites.back().get(); } -AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite) { +AsyncWebRewrite &AsyncWebServer::addRewrite(AsyncWebRewrite *rewrite) { _rewrites.emplace_back(rewrite); return *_rewrites.back().get(); } -bool AsyncWebServer::removeRewrite(AsyncWebRewrite* rewrite) { +bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite) { return removeRewrite(rewrite->from().c_str(), rewrite->toUrl().c_str()); } -bool AsyncWebServer::removeRewrite(const char* from, const char* to) { +bool AsyncWebServer::removeRewrite(const char *from, const char *to) { for (auto r = _rewrites.begin(); r != _rewrites.end(); ++r) { if (r->get()->from() == from && r->get()->toUrl() == to) { _rewrites.erase(r); @@ -94,17 +98,17 @@ bool AsyncWebServer::removeRewrite(const char* from, const char* to) { return false; } -AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to) { +AsyncWebRewrite &AsyncWebServer::rewrite(const char *from, const char *to) { _rewrites.emplace_back(std::make_shared(from, to)); return *_rewrites.back().get(); } -AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler) { +AsyncWebHandler &AsyncWebServer::addHandler(AsyncWebHandler *handler) { _handlers.emplace_back(handler); return *(_handlers.back().get()); } -bool AsyncWebServer::removeHandler(AsyncWebHandler* handler) { +bool AsyncWebServer::removeHandler(AsyncWebHandler *handler) { for (auto i = _handlers.begin(); i != _handlers.end(); ++i) { if (i->get() == handler) { _handlers.erase(i); @@ -124,21 +128,21 @@ void AsyncWebServer::end() { } #if ASYNC_TCP_SSL_ENABLED -void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg) { +void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void *arg) { _server.onSslFileRequest(cb, arg); } -void AsyncWebServer::beginSecure(const char* cert, const char* key, const char* password) { +void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password) { _server.beginSecure(cert, key, password); } #endif -void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest* request) { +void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request) { delete request; } -void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest* request) { - for (const auto& r : _rewrites) { +void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request) { + for (const auto &r : _rewrites) { if (r->match(request)) { request->_url = r->toUrl(); request->_addGetParams(r->params()); @@ -146,8 +150,8 @@ void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest* request) { } } -void AsyncWebServer::_attachHandler(AsyncWebServerRequest* request) { - for (auto& h : _handlers) { +void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request) { + for (auto &h : _handlers) { if (h->filter(request) && h->canHandle(request)) { request->setHandler(h.get()); return; @@ -157,8 +161,10 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest* request) { request->setHandler(_catchAllHandler); } -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) { - AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); +AsyncCallbackWebHandler &AsyncWebServer::on( + const char *uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody +) { + AsyncCallbackWebHandler *handler = new AsyncCallbackWebHandler(); handler->setUri(uri); handler->setMethod(method); handler->onRequest(onRequest); @@ -168,8 +174,8 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom return *handler; } -AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control) { - AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control); +AsyncStaticWebHandler &AsyncWebServer::serveStatic(const char *uri, fs::FS &fs, const char *path, const char *cache_control) { + AsyncStaticWebHandler *handler = new AsyncStaticWebHandler(uri, fs, path, cache_control); addHandler(handler); return *handler; } diff --git a/src/literals.h b/src/literals.h index e9244836e..d7a5b6e2c 100644 --- a/src/literals.h +++ b/src/literals.h @@ -2,182 +2,182 @@ namespace asyncsrv { - static constexpr const char* empty = ""; +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_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_BODY = "body"; - 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_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_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_FALSE = "false"; - static constexpr const char* T_filename = "filename"; - static constexpr const char* T_gzip = "gzip"; - 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_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_LOGIN_REQ = "Login Required"; - static constexpr const char* T_MULTIPART_ = "multipart/"; - static constexpr const char* T_name = "name"; - static constexpr const char* T_nc = "nc"; - static constexpr const char* T_no_cache = "no-cache"; - static constexpr const char* T_nonce = "nonce"; - static constexpr const char* T_none = "none"; - static constexpr const char* T_opaque = "opaque"; - static constexpr const char* T_qop = "qop"; - 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_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_Transfer_Encoding = "transfer-encoding"; - static constexpr const char* T_TRUE = "true"; - 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__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_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_BODY = "body"; +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_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_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_FALSE = "false"; +static constexpr const char *T_filename = "filename"; +static constexpr const char *T_gzip = "gzip"; +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_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_LOGIN_REQ = "Login Required"; +static constexpr const char *T_MULTIPART_ = "multipart/"; +static constexpr const char *T_name = "name"; +static constexpr const char *T_nc = "nc"; +static constexpr const char *T_no_cache = "no-cache"; +static constexpr const char *T_nonce = "nonce"; +static constexpr const char *T_none = "none"; +static constexpr const char *T_opaque = "opaque"; +static constexpr const char *T_qop = "qop"; +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_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_Transfer_Encoding = "transfer-encoding"; +static constexpr const char *T_TRUE = "true"; +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"; - // HTTP Methods +// HTTP Methods - static constexpr const char* T_ANY = "ANY"; - static constexpr const char* T_GET = "GET"; - static constexpr const char* T_POST = "POST"; - static constexpr const char* T_PUT = "PUT"; - static constexpr const char* T_DELETE = "DELETE"; - static constexpr const char* T_PATCH = "PATCH"; - static constexpr const char* T_HEAD = "HEAD"; - static constexpr const char* T_OPTIONS = "OPTIONS"; - static constexpr const char* T_UNKNOWN = "UNKNOWN"; +static constexpr const char *T_ANY = "ANY"; +static constexpr const char *T_GET = "GET"; +static constexpr const char *T_POST = "POST"; +static constexpr const char *T_PUT = "PUT"; +static constexpr const char *T_DELETE = "DELETE"; +static constexpr const char *T_PATCH = "PATCH"; +static constexpr const char *T_HEAD = "HEAD"; +static constexpr const char *T_OPTIONS = "OPTIONS"; +static constexpr const char *T_UNKNOWN = "UNKNOWN"; - // Req content types - static constexpr const char* T_RCT_NOT_USED = "RCT_NOT_USED"; - static constexpr const char* T_RCT_DEFAULT = "RCT_DEFAULT"; - static constexpr const char* T_RCT_HTTP = "RCT_HTTP"; - static constexpr const char* T_RCT_WS = "RCT_WS"; - static constexpr const char* T_RCT_EVENT = "RCT_EVENT"; - static constexpr const char* T_ERROR = "ERROR"; +// Req content types +static constexpr const char *T_RCT_NOT_USED = "RCT_NOT_USED"; +static constexpr const char *T_RCT_DEFAULT = "RCT_DEFAULT"; +static constexpr const char *T_RCT_HTTP = "RCT_HTTP"; +static constexpr const char *T_RCT_WS = "RCT_WS"; +static constexpr const char *T_RCT_EVENT = "RCT_EVENT"; +static constexpr const char *T_ERROR = "ERROR"; - // extentions & 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_application_json = "application/json"; - static constexpr const char* T_application_msgpack = "application/msgpack"; - 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_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_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_x_icon = "image/x-icon"; - static constexpr const char* T_text_css = "text/css"; - 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_plain = "text/plain"; - static constexpr const char* T_text_xml = "text/xml"; +// 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_application_json = "application/json"; +static constexpr const char *T_application_msgpack = "application/msgpack"; +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_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_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_x_icon = "image/x-icon"; +static constexpr const char *T_text_css = "text/css"; +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_plain = "text/plain"; +static constexpr const char *T_text_xml = "text/xml"; - // Responce codes - static constexpr const char* T_HTTP_CODE_100 = "Continue"; - static constexpr const char* T_HTTP_CODE_101 = "Switching Protocols"; - static constexpr const char* T_HTTP_CODE_200 = "OK"; - static constexpr const char* T_HTTP_CODE_201 = "Created"; - static constexpr const char* T_HTTP_CODE_202 = "Accepted"; - static constexpr const char* T_HTTP_CODE_203 = "Non-Authoritative Information"; - static constexpr const char* T_HTTP_CODE_204 = "No Content"; - static constexpr const char* T_HTTP_CODE_205 = "Reset Content"; - static constexpr const char* T_HTTP_CODE_206 = "Partial Content"; - static constexpr const char* T_HTTP_CODE_300 = "Multiple Choices"; - static constexpr const char* T_HTTP_CODE_301 = "Moved Permanently"; - static constexpr const char* T_HTTP_CODE_302 = "Found"; - static constexpr const char* T_HTTP_CODE_303 = "See Other"; - static constexpr const char* T_HTTP_CODE_304 = "Not Modified"; - static constexpr const char* T_HTTP_CODE_305 = "Use Proxy"; - static constexpr const char* T_HTTP_CODE_307 = "Temporary Redirect"; - static constexpr const char* T_HTTP_CODE_400 = "Bad Request"; - static constexpr const char* T_HTTP_CODE_401 = "Unauthorized"; - static constexpr const char* T_HTTP_CODE_402 = "Payment Required"; - static constexpr const char* T_HTTP_CODE_403 = "Forbidden"; - static constexpr const char* T_HTTP_CODE_404 = "Not Found"; - static constexpr const char* T_HTTP_CODE_405 = "Method Not Allowed"; - static constexpr const char* T_HTTP_CODE_406 = "Not Acceptable"; - static constexpr const char* T_HTTP_CODE_407 = "Proxy Authentication Required"; - static constexpr const char* T_HTTP_CODE_408 = "Request Time-out"; - static constexpr const char* T_HTTP_CODE_409 = "Conflict"; - static constexpr const char* T_HTTP_CODE_410 = "Gone"; - static constexpr const char* T_HTTP_CODE_411 = "Length Required"; - 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_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"; - 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_ANY = "Unknown code"; +// Response codes +static constexpr const char *T_HTTP_CODE_100 = "Continue"; +static constexpr const char *T_HTTP_CODE_101 = "Switching Protocols"; +static constexpr const char *T_HTTP_CODE_200 = "OK"; +static constexpr const char *T_HTTP_CODE_201 = "Created"; +static constexpr const char *T_HTTP_CODE_202 = "Accepted"; +static constexpr const char *T_HTTP_CODE_203 = "Non-Authoritative Information"; +static constexpr const char *T_HTTP_CODE_204 = "No Content"; +static constexpr const char *T_HTTP_CODE_205 = "Reset Content"; +static constexpr const char *T_HTTP_CODE_206 = "Partial Content"; +static constexpr const char *T_HTTP_CODE_300 = "Multiple Choices"; +static constexpr const char *T_HTTP_CODE_301 = "Moved Permanently"; +static constexpr const char *T_HTTP_CODE_302 = "Found"; +static constexpr const char *T_HTTP_CODE_303 = "See Other"; +static constexpr const char *T_HTTP_CODE_304 = "Not Modified"; +static constexpr const char *T_HTTP_CODE_305 = "Use Proxy"; +static constexpr const char *T_HTTP_CODE_307 = "Temporary Redirect"; +static constexpr const char *T_HTTP_CODE_400 = "Bad Request"; +static constexpr const char *T_HTTP_CODE_401 = "Unauthorized"; +static constexpr const char *T_HTTP_CODE_402 = "Payment Required"; +static constexpr const char *T_HTTP_CODE_403 = "Forbidden"; +static constexpr const char *T_HTTP_CODE_404 = "Not Found"; +static constexpr const char *T_HTTP_CODE_405 = "Method Not Allowed"; +static constexpr const char *T_HTTP_CODE_406 = "Not Acceptable"; +static constexpr const char *T_HTTP_CODE_407 = "Proxy Authentication Required"; +static constexpr const char *T_HTTP_CODE_408 = "Request Time-out"; +static constexpr const char *T_HTTP_CODE_409 = "Conflict"; +static constexpr const char *T_HTTP_CODE_410 = "Gone"; +static constexpr const char *T_HTTP_CODE_411 = "Length Required"; +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_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"; +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_ANY = "Unknown code"; -} // namespace asyncsrv {} +} // namespace asyncsrv