├── .clang-format ├── .clang-tidy ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ ├── documentation.yml │ └── feature-request.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yml │ ├── clang-format.yml │ ├── closed-issue-message.yml │ ├── handle-stale-discussions.yml │ ├── issue-regression-labeler.yml │ └── stale_issue.yml ├── .gitignore ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── bin ├── CMakeLists.txt ├── event_stream_pipe.c └── event_stream_write_test_case.c ├── builder.json ├── clang-tidy └── run-clang-tidy.sh ├── cmake └── aws-c-event-stream-config.cmake ├── docs └── images │ └── encoding.png ├── format-check.py ├── include └── aws │ └── event-stream │ ├── event_stream.h │ ├── event_stream_channel_handler.h │ ├── event_stream_exports.h │ ├── event_stream_rpc.h │ ├── event_stream_rpc_client.h │ ├── event_stream_rpc_server.h │ └── private │ ├── event_stream_rpc_priv.h │ └── event_stream_rpc_test_helper.h ├── source ├── event_stream.c ├── event_stream_channel_handler.c ├── event_stream_rpc.c ├── event_stream_rpc_client.c └── event_stream_rpc_server.c └── tests ├── CMakeLists.txt ├── channel_handler_test.c ├── event_stream_rpc_client_connection_test.c ├── event_stream_rpc_server_connection_test.c ├── message_deserializer_test.c ├── message_serializer_test.c └── message_streaming_decoder_test.c /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Mozilla 4 | AlignAfterOpenBracket: AlwaysBreak 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlines: Right 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: Inline 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: false 18 | BinPackArguments: false 19 | BinPackParameters: false 20 | BreakBeforeBinaryOperators: None 21 | BreakBeforeBraces: Attach 22 | BreakBeforeTernaryOperators: true 23 | BreakStringLiterals: true 24 | ColumnLimit: 120 25 | ContinuationIndentWidth: 4 26 | DerivePointerAlignment: false 27 | IncludeBlocks: Preserve 28 | IndentCaseLabels: true 29 | IndentPPDirectives: AfterHash 30 | IndentWidth: 4 31 | IndentWrappedFunctionNames: true 32 | KeepEmptyLinesAtTheStartOfBlocks: true 33 | MacroBlockBegin: '' 34 | MacroBlockEnd: '' 35 | MaxEmptyLinesToKeep: 1 36 | PenaltyBreakAssignment: 2 37 | PenaltyBreakBeforeFirstCallParameter: 19 38 | PenaltyBreakComment: 300 39 | PenaltyBreakFirstLessLess: 120 40 | PenaltyBreakString: 1000 41 | PenaltyExcessCharacter: 1000000 42 | PenaltyReturnTypeOnItsOwnLine: 100000 43 | PointerAlignment: Right 44 | ReflowComments: true 45 | SortIncludes: true 46 | SpaceAfterCStyleCast: false 47 | SpaceBeforeAssignmentOperators: true 48 | SpaceBeforeParens: ControlStatements 49 | SpaceInEmptyParentheses: false 50 | SpacesInContainerLiterals: true 51 | SpacesInCStyleCastParentheses: false 52 | SpacesInParentheses: false 53 | SpacesInSquareBrackets: false 54 | Standard: Cpp11 55 | TabWidth: 4 56 | UseTab: Never 57 | ... 58 | 59 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: 'clang-diagnostic-*,clang-analyzer-*,readability-*,modernize-*,bugprone-*,misc-*,google-runtime-int,fuchsia-restrict-system-includes,-clang-analyzer-valist.Uninitialized,-clang-analyzer-security.insecureAPI.rand,-clang-analyzer-alpha.*' 3 | WarningsAsErrors: '*' 4 | HeaderFilterRegex: '\./*' 5 | FormatStyle: 'file' 6 | # Use empty line filter to skip linting code we don't own 7 | CheckOptions: 8 | - key: readability-braces-around-statements.ShortStatementLines 9 | value: '1' 10 | - key: google-runtime-int.TypeSufix 11 | value: '_t' 12 | - key: fuchsia-restrict-system-includes.Includes 13 | value: '*,-stdint.h,-stdbool.h,-assert.h' 14 | 15 | ... 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug Report" 3 | description: Report a bug 4 | title: "(short issue description)" 5 | labels: [bug, needs-triage] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the bug 12 | description: What is the problem? A clear and concise description of the bug. 13 | validations: 14 | required: true 15 | - type: checkboxes 16 | id: regression 17 | attributes: 18 | label: Regression Issue 19 | description: What is a regression? If it worked in a previous version but doesn't in the latest version, it's considered a regression. In this case, please provide specific version number in the report. 20 | options: 21 | - label: Select this option if this issue appears to be a regression. 22 | required: false 23 | - type: textarea 24 | id: expected 25 | attributes: 26 | label: Expected Behavior 27 | description: | 28 | What did you expect to happen? 29 | validations: 30 | required: true 31 | - type: textarea 32 | id: current 33 | attributes: 34 | label: Current Behavior 35 | description: | 36 | What actually happened? 37 | 38 | Please include full errors, uncaught exceptions, stack traces, and relevant logs. 39 | If service responses are relevant, please include wire logs. 40 | validations: 41 | required: true 42 | - type: textarea 43 | id: reproduction 44 | attributes: 45 | label: Reproduction Steps 46 | description: | 47 | Provide a self-contained, concise snippet of code that can be used to reproduce the issue. 48 | For more complex issues provide a repo with the smallest sample that reproduces the bug. 49 | 50 | Avoid including business logic or unrelated code, it makes diagnosis more difficult. 51 | The code sample should be an SSCCE. See http://sscce.org/ for details. In short, please provide a code sample that we can copy/paste, run and reproduce. 52 | validations: 53 | required: true 54 | - type: textarea 55 | id: solution 56 | attributes: 57 | label: Possible Solution 58 | description: | 59 | Suggest a fix/reason for the bug 60 | validations: 61 | required: false 62 | - type: textarea 63 | id: context 64 | attributes: 65 | label: Additional Information/Context 66 | description: | 67 | Anything else that might be relevant for troubleshooting this bug. Providing context helps us come up with a solution that is most useful in the real world. 68 | validations: 69 | required: false 70 | 71 | - type: input 72 | id: aws-c-event-stream-version 73 | attributes: 74 | label: aws-c-event-stream version used 75 | validations: 76 | required: true 77 | 78 | - type: input 79 | id: compiler-version 80 | attributes: 81 | label: Compiler and version used 82 | validations: 83 | required: true 84 | 85 | - type: input 86 | id: operating-system 87 | attributes: 88 | label: Operating System and version 89 | validations: 90 | required: true 91 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 💬 General Question 4 | url: https://github.com/awslabs/aws-c-event-stream/discussions/categories/q-a 5 | about: Please ask and answer questions as a discussion thread 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "📕 Documentation Issue" 3 | description: Report an issue in the API Reference documentation or Developer Guide 4 | title: "(short issue description)" 5 | labels: [documentation, needs-triage] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the issue 12 | description: A clear and concise description of the issue. 13 | validations: 14 | required: true 15 | 16 | - type: textarea 17 | id: links 18 | attributes: 19 | label: Links 20 | description: | 21 | Include links to affected documentation page(s). 22 | validations: 23 | required: true 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature Request 3 | description: Suggest an idea for this project 4 | title: "(short issue description)" 5 | labels: [feature-request, needs-triage] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the feature 12 | description: A clear and concise description of the feature you are proposing. 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: use-case 17 | attributes: 18 | label: Use Case 19 | description: | 20 | Why do you need this feature? For example: "I'm always frustrated when..." 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: solution 25 | attributes: 26 | label: Proposed Solution 27 | description: | 28 | Suggest how to implement the addition or change. Please include prototype/workaround/sketch/reference implementation. 29 | validations: 30 | required: false 31 | - type: textarea 32 | id: other 33 | attributes: 34 | label: Other Information 35 | description: | 36 | Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc. 37 | validations: 38 | required: false 39 | - type: checkboxes 40 | id: ack 41 | attributes: 42 | label: Acknowledgements 43 | options: 44 | - label: I may be able to implement this feature request 45 | required: false 46 | - label: This feature might incur a breaking change 47 | required: false 48 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - 'main' 7 | 8 | env: 9 | BUILDER_VERSION: v0.9.72 10 | BUILDER_SOURCE: releases 11 | BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net 12 | PACKAGE_NAME: aws-c-event-stream 13 | LINUX_BASE_IMAGE: ubuntu-18-x64 14 | RUN: ${{ github.run_id }}-${{ github.run_number }} 15 | CRT_CI_ROLE: ${{ secrets.CRT_CI_ROLE_ARN }} 16 | AWS_DEFAULT_REGION: us-east-1 17 | 18 | permissions: 19 | id-token: write # This is required for requesting the JWT 20 | 21 | jobs: 22 | linux-compat: 23 | runs-on: ubuntu-24.04 # latest 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | image: 28 | - manylinux1-x64 29 | - manylinux1-x86 30 | - manylinux2014-x64 31 | - manylinux2014-x86 32 | - al2-x64 33 | - fedora-34-x64 34 | - opensuse-leap 35 | - rhel8-x64 36 | steps: 37 | - uses: aws-actions/configure-aws-credentials@v4 38 | with: 39 | role-to-assume: ${{ env.CRT_CI_ROLE }} 40 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 41 | # We can't use the `uses: docker://image` version yet, GitHub lacks authentication for actions -> packages 42 | - name: Build ${{ env.PACKAGE_NAME }} 43 | run: | 44 | aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh 45 | ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ matrix.image }} build -p ${{ env.PACKAGE_NAME }} 46 | 47 | linux-compiler-compat: 48 | runs-on: ubuntu-24.04 # latest 49 | strategy: 50 | matrix: 51 | compiler: 52 | - clang-3 53 | - clang-6 54 | - clang-8 55 | - clang-9 56 | - clang-10 57 | - clang-11 58 | - clang-15 59 | - clang-17 60 | - gcc-4.8 61 | - gcc-5 62 | - gcc-6 63 | - gcc-7 64 | - gcc-8 65 | - gcc-11 66 | - gcc-13 67 | steps: 68 | - uses: aws-actions/configure-aws-credentials@v4 69 | with: 70 | role-to-assume: ${{ env.CRT_CI_ROLE }} 71 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 72 | # We can't use the `uses: docker://image` version yet, GitHub lacks authentication for actions -> packages 73 | - name: Build ${{ env.PACKAGE_NAME }} 74 | run: | 75 | aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh 76 | ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ env.LINUX_BASE_IMAGE }} build -p ${{ env.PACKAGE_NAME }} --compiler=${{ matrix.compiler }} 77 | 78 | clang-sanitizers: 79 | runs-on: ubuntu-24.04 # latest 80 | strategy: 81 | matrix: 82 | sanitizers: [",thread", ",address,undefined"] 83 | steps: 84 | - uses: aws-actions/configure-aws-credentials@v4 85 | with: 86 | role-to-assume: ${{ env.CRT_CI_ROLE }} 87 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 88 | # We can't use the `uses: docker://image` version yet, GitHub lacks authentication for actions -> packages 89 | - name: Build ${{ env.PACKAGE_NAME }} 90 | run: | 91 | aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh 92 | ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ env.LINUX_BASE_IMAGE }} build -p ${{ env.PACKAGE_NAME }} --compiler=clang-11 --cmake-extra=-DENABLE_SANITIZERS=ON --cmake-extra=-DSANITIZERS="${{ matrix.sanitizers }}" 93 | 94 | linux-shared-libs: 95 | runs-on: ubuntu-24.04 # latest 96 | steps: 97 | - uses: aws-actions/configure-aws-credentials@v4 98 | with: 99 | role-to-assume: ${{ env.CRT_CI_ROLE }} 100 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 101 | # We can't use the `uses: docker://image` version yet, GitHub lacks authentication for actions -> packages 102 | - name: Build ${{ env.PACKAGE_NAME }} 103 | run: | 104 | aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh 105 | ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ env.LINUX_BASE_IMAGE }} build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-DBUILD_SHARED_LIBS=ON 106 | 107 | windows: 108 | runs-on: windows-2022 # latest 109 | steps: 110 | - uses: aws-actions/configure-aws-credentials@v4 111 | with: 112 | role-to-assume: ${{ env.CRT_CI_ROLE }} 113 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 114 | - name: Build ${{ env.PACKAGE_NAME }} + consumers 115 | run: | 116 | python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" 117 | python builder.pyz build -p ${{ env.PACKAGE_NAME }} 118 | 119 | windows-vc14: 120 | runs-on: windows-2019 # windows-2019 is last env with Visual Studio 2015 (v14.0) 121 | strategy: 122 | matrix: 123 | arch: [x86, x64] 124 | steps: 125 | - uses: aws-actions/configure-aws-credentials@v4 126 | with: 127 | role-to-assume: ${{ env.CRT_CI_ROLE }} 128 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 129 | - name: Build ${{ env.PACKAGE_NAME }} + consumers 130 | run: | 131 | python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" 132 | python builder.pyz build -p ${{ env.PACKAGE_NAME }} --target windows-${{ matrix.arch }} --compiler msvc-14 133 | 134 | windows-shared-libs: 135 | runs-on: windows-2022 # latest 136 | steps: 137 | - uses: aws-actions/configure-aws-credentials@v4 138 | with: 139 | role-to-assume: ${{ env.CRT_CI_ROLE }} 140 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 141 | - name: Build ${{ env.PACKAGE_NAME }} + consumers 142 | run: | 143 | python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" 144 | python builder.pyz build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-DBUILD_SHARED_LIBS=ON 145 | 146 | windows-app-verifier: 147 | runs-on: windows-2022 # latest 148 | steps: 149 | - uses: aws-actions/configure-aws-credentials@v4 150 | with: 151 | role-to-assume: ${{ env.CRT_CI_ROLE }} 152 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 153 | - name: Build ${{ env.PACKAGE_NAME }} + consumers 154 | run: | 155 | python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" 156 | python builder.pyz build -p ${{ env.PACKAGE_NAME }} run_tests=false --cmake-extra=-DBUILD_TESTING=ON 157 | - name: Run and check AppVerifier 158 | run: | 159 | python .\aws-c-event-stream\build\deps\aws-c-common\scripts\appverifier_ctest.py --build_directory .\aws-c-event-stream\build\aws-c-event-stream 160 | 161 | macos: 162 | runs-on: macos-14 # latest 163 | steps: 164 | - uses: aws-actions/configure-aws-credentials@v4 165 | with: 166 | role-to-assume: ${{ env.CRT_CI_ROLE }} 167 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 168 | - name: Build ${{ env.PACKAGE_NAME }} + consumers 169 | run: | 170 | python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" 171 | chmod a+x builder 172 | ./builder build -p ${{ env.PACKAGE_NAME }} 173 | 174 | macos-x64: 175 | runs-on: macos-14-large # latest 176 | steps: 177 | - uses: aws-actions/configure-aws-credentials@v4 178 | with: 179 | role-to-assume: ${{ env.CRT_CI_ROLE }} 180 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 181 | - name: Build ${{ env.PACKAGE_NAME }} + consumers 182 | run: | 183 | python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" 184 | chmod a+x builder 185 | ./builder build -p ${{ env.PACKAGE_NAME }} 186 | 187 | # Test downstream repos. 188 | # This should not be required because we can run into a chicken and egg problem if there is a change that needs some fix in a downstream repo. 189 | downstream: 190 | runs-on: ubuntu-24.04 # latest 191 | steps: 192 | - uses: aws-actions/configure-aws-credentials@v4 193 | with: 194 | role-to-assume: ${{ env.CRT_CI_ROLE }} 195 | aws-region: ${{ env.AWS_DEFAULT_REGION }} 196 | # We can't use the `uses: docker://image` version yet, GitHub lacks authentication for actions -> packages 197 | - name: Build ${{ env.PACKAGE_NAME }} 198 | run: | 199 | aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh 200 | ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ env.LINUX_BASE_IMAGE }} build downstream -p ${{ env.PACKAGE_NAME }} 201 | -------------------------------------------------------------------------------- /.github/workflows/clang-format.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push] 4 | 5 | jobs: 6 | clang-format: 7 | 8 | runs-on: ubuntu-24.04 # latest 9 | 10 | steps: 11 | - name: Checkout Sources 12 | uses: actions/checkout@v4 13 | 14 | - name: clang-format lint 15 | run: | 16 | ./format-check.py 17 | -------------------------------------------------------------------------------- /.github/workflows/closed-issue-message.yml: -------------------------------------------------------------------------------- 1 | name: Closed Issue Message 2 | on: 3 | issues: 4 | types: [closed] 5 | jobs: 6 | auto_comment: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: aws-actions/closed-issue-message@v1 10 | with: 11 | # These inputs are both required 12 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 13 | message: | 14 | ### ⚠️COMMENT VISIBILITY WARNING⚠️ 15 | Comments on closed issues are hard for our team to see. 16 | If you need more assistance, please either tag a team member or open a new issue that references this one. 17 | If you wish to keep having a conversation with other community members under this issue feel free to do so. 18 | -------------------------------------------------------------------------------- /.github/workflows/handle-stale-discussions.yml: -------------------------------------------------------------------------------- 1 | name: HandleStaleDiscussions 2 | on: 3 | schedule: 4 | - cron: '0 */4 * * *' 5 | discussion_comment: 6 | types: [created] 7 | 8 | jobs: 9 | handle-stale-discussions: 10 | name: Handle stale discussions 11 | runs-on: ubuntu-latest 12 | permissions: 13 | discussions: write 14 | steps: 15 | - name: Stale discussions action 16 | uses: aws-github-ops/handle-stale-discussions@v1 17 | env: 18 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} -------------------------------------------------------------------------------- /.github/workflows/issue-regression-labeler.yml: -------------------------------------------------------------------------------- 1 | # Apply potential regression label on issues 2 | name: issue-regression-label 3 | on: 4 | issues: 5 | types: [opened, edited] 6 | jobs: 7 | add-regression-label: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | steps: 12 | - name: Fetch template body 13 | id: check_regression 14 | uses: actions/github-script@v7 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | TEMPLATE_BODY: ${{ github.event.issue.body }} 18 | with: 19 | script: | 20 | const regressionPattern = /\[x\] Select this option if this issue appears to be a regression\./i; 21 | const template = `${process.env.TEMPLATE_BODY}` 22 | const match = regressionPattern.test(template); 23 | core.setOutput('is_regression', match); 24 | - name: Manage regression label 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | run: | 28 | if [ "${{ steps.check_regression.outputs.is_regression }}" == "true" ]; then 29 | gh issue edit ${{ github.event.issue.number }} --add-label "potential-regression" -R ${{ github.repository }} 30 | else 31 | gh issue edit ${{ github.event.issue.number }} --remove-label "potential-regression" -R ${{ github.repository }} 32 | fi 33 | -------------------------------------------------------------------------------- /.github/workflows/stale_issue.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | 3 | # Controls when the action will run. 4 | on: 5 | schedule: 6 | - cron: "*/60 * * * *" 7 | 8 | jobs: 9 | cleanup: 10 | runs-on: ubuntu-latest 11 | name: Stale issue job 12 | permissions: 13 | issues: write 14 | pull-requests: write 15 | steps: 16 | - uses: aws-actions/stale-issue-cleanup@v3 17 | with: 18 | # Setting messages to an empty string will cause the automation to skip 19 | # that category 20 | ancient-issue-message: Greetings! Sorry to say but this is a very old issue that is probably not getting as much attention as it deserves. We encourage you to check if this is still an issue in the latest release and if you find that this is still a problem, please feel free to open a new one. 21 | stale-issue-message: Greetings! It looks like this issue hasn’t been active in longer than a week. We encourage you to check if this is still an issue in the latest release. Because it has been longer than a week since the last update on this, and in the absence of more information, we will be closing this issue soon. If you find that this is still a problem, please feel free to provide a comment or add an upvote to prevent automatic closure, or if the issue is already closed, please feel free to open a new one. 22 | stale-pr-message: Greetings! It looks like this PR hasn’t been active in longer than a week, add a comment or an upvote to prevent automatic closure, or if the issue is already closed, please feel free to open a new one. 23 | 24 | # These labels are required 25 | stale-issue-label: closing-soon 26 | exempt-issue-label: automation-exempt 27 | stale-pr-label: closing-soon 28 | exempt-pr-label: pr/needs-review 29 | response-requested-label: response-requested 30 | 31 | # Don't set closed-for-staleness label to skip closing very old issues 32 | # regardless of label 33 | closed-for-staleness-label: closed-for-staleness 34 | 35 | # Issue timing 36 | days-before-stale: 10 37 | days-before-close: 4 38 | days-before-ancient: 36500 39 | 40 | # If you don't want to mark a issue as being ancient based on a 41 | # threshold of "upvotes", you can set this here. An "upvote" is 42 | # the total number of +1, heart, hooray, and rocket reactions 43 | # on an issue. 44 | minimum-upvotes-to-exempt: 1 45 | 46 | repo-token: ${{ secrets.GITHUB_TOKEN }} 47 | loglevel: DEBUG 48 | # Set dry-run to true to not perform label or close actions. 49 | dry-run: false 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE Artifacts 2 | .metadata 3 | .build 4 | .idea 5 | *.d 6 | Debug 7 | Release 8 | *~ 9 | *# 10 | *.iml 11 | tags 12 | .vscode 13 | 14 | #vim swap file 15 | *.swp 16 | 17 | #compiled python files 18 | *.pyc 19 | 20 | #Vagrant stuff 21 | Vagrantfile 22 | .vagrant 23 | 24 | #Mac stuff 25 | .DS_Store 26 | 27 | #doxygen 28 | doxygen/html/ 29 | doxygen/latex/ 30 | 31 | 32 | #cmake artifacts 33 | dependencies 34 | _build 35 | build 36 | _build_* 37 | cmake-build* 38 | *-build 39 | 40 | # Compiled Object files 41 | *.slo 42 | *.lo 43 | *.o 44 | *.obj 45 | 46 | # Precompiled Headers 47 | *.gch 48 | *.pch 49 | 50 | # Compiled Dynamic libraries 51 | *.so 52 | *.dylib 53 | *.dll 54 | 55 | # Fortran module files 56 | *.mod 57 | 58 | # Compiled Static libraries 59 | *.lai 60 | *.la 61 | *.a 62 | *.lib 63 | 64 | # Executables 65 | *.exe 66 | *.out 67 | *.app 68 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | cmake_minimum_required(VERSION 3.9...3.31) 4 | project (aws-c-event-stream C) 5 | 6 | if (NOT IN_SOURCE_BUILD) 7 | # this is required so we can use aws-c-common's CMake modules 8 | find_package(aws-c-common REQUIRED) 9 | endif() 10 | 11 | include(AwsCFlags) 12 | include(AwsSharedLibSetup) 13 | include(AwsSanitizers) 14 | include(CheckCCompilerFlag) 15 | include(AwsFindPackage) 16 | include(AwsCheckHeaders) 17 | include(GNUInstallDirs) 18 | 19 | file(GLOB AWS_EVENT_STREAM_HEADERS 20 | "include/aws/event-stream/*.h" 21 | ) 22 | 23 | file(GLOB AWS_EVENT_STREAM_SRC 24 | "source/*.c" 25 | ) 26 | 27 | if(WIN32) 28 | if(MSVC) 29 | source_group("Header Files\\aws\\event-stream" FILES ${AWS_EVENT_STREAM_HEADERS}) 30 | source_group("Source Files" FILES ${AWS_EVENT_STREAM_SRC}) 31 | endif() 32 | endif() 33 | 34 | file(GLOB EVENT_STREAM_HEADERS 35 | ${AWS_EVENT_STREAM_HEADERS} 36 | ) 37 | 38 | file(GLOB EVENT_STREAM_SRC 39 | ${AWS_EVENT_STREAM_SRC} 40 | ) 41 | 42 | add_library(${PROJECT_NAME} ${EVENT_STREAM_SRC}) 43 | aws_set_common_properties(${PROJECT_NAME}) 44 | aws_add_sanitizers(${PROJECT_NAME}) 45 | aws_prepare_symbol_visibility_args(${PROJECT_NAME} "AWS_EVENT_STREAM") 46 | 47 | aws_check_headers(${PROJECT_NAME} ${AWS_EVENT_STREAM_HEADERS}) 48 | 49 | target_include_directories(${PROJECT_NAME} PUBLIC 50 | $ 51 | $) 52 | 53 | 54 | set_target_properties(${PROJECT_NAME} PROPERTIES VERSION 1.0.0) 55 | 56 | aws_use_package(aws-c-io) 57 | aws_use_package(aws-c-common) 58 | aws_use_package(aws-checksums) 59 | 60 | target_link_libraries(${PROJECT_NAME} PUBLIC ${DEP_AWS_LIBS}) 61 | 62 | aws_prepare_shared_lib_exports(${PROJECT_NAME}) 63 | 64 | install(FILES ${AWS_EVENT_STREAM_HEADERS} 65 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/aws/event-stream" 66 | COMPONENT Development) 67 | 68 | if (BUILD_SHARED_LIBS) 69 | set (TARGET_DIR "shared") 70 | else() 71 | set (TARGET_DIR "static") 72 | endif() 73 | 74 | install(EXPORT "${PROJECT_NAME}-targets" 75 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/${TARGET_DIR}/" 76 | NAMESPACE AWS:: 77 | COMPONENT Development) 78 | 79 | configure_file("cmake/${PROJECT_NAME}-config.cmake" 80 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 81 | @ONLY) 82 | 83 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 84 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/" 85 | COMPONENT Development) 86 | 87 | 88 | include(CTest) 89 | enable_testing() 90 | if (BUILD_TESTING) 91 | add_subdirectory(tests) 92 | add_subdirectory(bin) 93 | endif() 94 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/awslabs/aws-c-event-stream/issues), or [recently closed](https://github.com/awslabs/aws-c-event-stream/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/aws-c-event-stream/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/awslabs/aws-c-event-stream/blob/main/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | AWS C Event Stream 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## AWS C Event Stream 2 | 3 | C99 implementation of the vnd.amazon.event-stream content-type. 4 | 5 | ## License 6 | 7 | This library is licensed under the Apache 2.0 License. 8 | 9 | ## Usage 10 | 11 | ### Building 12 | 13 | CMake 3.9+ is required to build. 14 | 15 | `` must be an absolute path in the following instructions. 16 | 17 | #### Linux-Only Dependencies 18 | 19 | If you are building on Linux, you will need to build aws-lc and s2n-tls first. 20 | 21 | ``` 22 | git clone git@github.com:awslabs/aws-lc.git 23 | cmake -S aws-lc -B aws-lc/build -DCMAKE_INSTALL_PREFIX= 24 | cmake --build aws-lc/build --target install 25 | 26 | git clone git@github.com:aws/s2n-tls.git 27 | cmake -S s2n-tls -B s2n-tls/build -DCMAKE_INSTALL_PREFIX= -DCMAKE_PREFIX_PATH= 28 | cmake --build s2n-tls/build --target install 29 | ``` 30 | 31 | #### Building aws-c-event-stream and Remaining Dependencies 32 | 33 | ``` 34 | git clone git@github.com:awslabs/aws-c-common.git 35 | cmake -S aws-c-common -B aws-c-common/build -DCMAKE_INSTALL_PREFIX= 36 | cmake --build aws-c-common/build --target install 37 | 38 | git clone git@github.com:awslabs/aws-checksums.git 39 | cmake -S aws-checksums -B aws-checksums/build -DCMAKE_INSTALL_PREFIX= -DCMAKE_PREFIX_PATH= 40 | cmake --build aws-checksums/build --target install 41 | 42 | git clone git@github.com:awslabs/aws-c-cal.git 43 | cmake -S aws-c-cal -B aws-c-cal/build -DCMAKE_INSTALL_PREFIX= -DCMAKE_PREFIX_PATH= 44 | cmake --build aws-c-cal/build --target install 45 | 46 | git clone git@github.com:awslabs/aws-c-io.git 47 | cmake -S aws-c-io -B aws-c-io/build -DCMAKE_INSTALL_PREFIX= -DCMAKE_PREFIX_PATH= 48 | cmake --build aws-c-io/build --target install 49 | 50 | git clone git@github.com:awslabs/aws-c-event-stream.git 51 | cmake -S aws-c-event-stream -B aws-c-event-stream/build -DCMAKE_INSTALL_PREFIX= -DCMAKE_PREFIX_PATH= 52 | cmake --build aws-c-event-stream/build --target install 53 | ``` 54 | 55 | ## Encoding 56 | 57 | Event stream encoding provides bidirectional communication between a client and a server. 58 | 59 | Each message consists of two sections: the prelude and the data. The prelude consists of: 60 | 1. The total byte length of the message 61 | 2. The combined byte length of all headers 62 | 63 | The data section consists of: 64 | 1. Headers 65 | 2. Payload 66 | 67 | Each section ends with a 4-byte big-endian CRC32 checksum. The message checksum is for both the prelude section and the data section. 68 | 69 | Total message overhead, including the prelude and both checksums, is 16 bytes. 70 | 71 | The following diagram shows the components that make up a message and a header. There are multiple headers per message. 72 | 73 | ![Encoding Diagram](docs/images/encoding.png) 74 | -------------------------------------------------------------------------------- /bin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | add_executable(aws-c-event-stream-pipe "event_stream_pipe.c") 5 | aws_set_common_properties(aws-c-event-stream-pipe) 6 | target_link_libraries(aws-c-event-stream-pipe PRIVATE ${PROJECT_NAME}) 7 | set_target_properties(aws-c-event-stream-pipe PROPERTIES LINKER_LANGUAGE C) 8 | set_property(TARGET aws-c-event-stream-pipe PROPERTY C_STANDARD 99) 9 | 10 | add_executable(aws-c-event-stream-write-test-case "event_stream_write_test_case.c") 11 | aws_set_common_properties(aws-c-event-stream-write-test-case) 12 | target_link_libraries(aws-c-event-stream-write-test-case PRIVATE ${PROJECT_NAME}) 13 | set_target_properties(aws-c-event-stream-write-test-case PROPERTIES LINKER_LANGUAGE C) 14 | set_property(TARGET aws-c-event-stream-write-test-case PROPERTY C_STANDARD 99) 15 | -------------------------------------------------------------------------------- /bin/event_stream_pipe.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0. 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /** 12 | * 4996 is to disable unsafe function fopen vs fopen_s 13 | * 4706 is to disable assignment expression inside condition expression at line 133. 14 | */ 15 | #ifdef _MSC_VER 16 | #pragma warning (disable: 4996 4706) 17 | #endif 18 | 19 | static void s_on_payload_segment( 20 | struct aws_event_stream_streaming_decoder *decoder, 21 | struct aws_byte_buf *data, 22 | int8_t final_segment, 23 | void *user_data) { 24 | (void)decoder; 25 | (void)final_segment; 26 | (void)user_data; 27 | if (data->len) { 28 | fwrite(data->buffer, sizeof(uint8_t), data->len, stdout); 29 | } 30 | } 31 | 32 | static void s_on_prelude_received( 33 | struct aws_event_stream_streaming_decoder *decoder, 34 | struct aws_event_stream_message_prelude *prelude, 35 | void *user_data) { 36 | (void)decoder; 37 | (void)user_data; 38 | 39 | fprintf(stdout, "\n--------------------------------------------------------------------------------\n"); 40 | fprintf( 41 | stdout, 42 | "total_length = 0x%08" PRIx32 "\nheaders_len = 0x%08" PRIx32 "\nprelude_crc = 0x%08" PRIx32 "\n\n", 43 | prelude->total_len, 44 | prelude->headers_len, 45 | prelude->prelude_crc); 46 | } 47 | 48 | static void s_on_header_received( 49 | struct aws_event_stream_streaming_decoder *decoder, 50 | struct aws_event_stream_message_prelude *prelude, 51 | struct aws_event_stream_header_value_pair *header, 52 | void *user_data) { 53 | (void)decoder; 54 | (void)prelude; 55 | (void)user_data; 56 | fwrite(header->header_name, sizeof(uint8_t), (size_t)header->header_name_len, stdout); 57 | 58 | fprintf(stdout, ": "); 59 | 60 | if (header->header_value_type == AWS_EVENT_STREAM_HEADER_BOOL_FALSE) { 61 | fprintf(stdout, "false"); 62 | } else if (header->header_value_type == AWS_EVENT_STREAM_HEADER_BOOL_TRUE) { 63 | fprintf(stdout, "true"); 64 | } else if (header->header_value_type == AWS_EVENT_STREAM_HEADER_BYTE) { 65 | int8_t int_value = aws_event_stream_header_value_as_byte(header); 66 | fprintf(stdout, "%d", (int)int_value); 67 | } else if (header->header_value_type == AWS_EVENT_STREAM_HEADER_INT16) { 68 | int16_t int_value = aws_event_stream_header_value_as_int16(header); 69 | fprintf(stdout, "%d", (int)int_value); 70 | } else if (header->header_value_type == AWS_EVENT_STREAM_HEADER_INT32) { 71 | int32_t int_value = aws_event_stream_header_value_as_int32(header); 72 | fprintf(stdout, "%d", (int)int_value); 73 | } else if ( 74 | header->header_value_type == AWS_EVENT_STREAM_HEADER_INT64 || 75 | header->header_value_type == AWS_EVENT_STREAM_HEADER_TIMESTAMP) { 76 | int64_t int_value = aws_event_stream_header_value_as_int64(header); 77 | fprintf(stdout, "%lld", (long long)int_value); 78 | } else { 79 | if (header->header_value_type == AWS_EVENT_STREAM_HEADER_UUID) { 80 | struct aws_byte_buf uuid = aws_event_stream_header_value_as_uuid(header); 81 | fwrite(uuid.buffer, sizeof(uint8_t), uuid.len, stdout); 82 | } else { 83 | struct aws_byte_buf byte_buf = aws_event_stream_header_value_as_bytebuf(header); 84 | 85 | fwrite(byte_buf.buffer, sizeof(uint8_t), byte_buf.len, stdout); 86 | } 87 | } 88 | fprintf(stdout, "\n"); 89 | } 90 | 91 | static void s_on_error( 92 | struct aws_event_stream_streaming_decoder *decoder, 93 | struct aws_event_stream_message_prelude *prelude, 94 | int error_code, 95 | const char *message, 96 | void *user_data) { 97 | (void)decoder; 98 | (void)prelude; 99 | (void)user_data; 100 | fprintf( 101 | stderr, 102 | "Error encountered: Code: %d, Error Str: %s, Message: %s\n", 103 | error_code, 104 | aws_error_debug_str(error_code), 105 | message); 106 | exit(-1); 107 | } 108 | 109 | int main(void) { 110 | 111 | struct aws_allocator *alloc = aws_default_allocator(); 112 | aws_event_stream_library_init(alloc); 113 | 114 | struct aws_event_stream_streaming_decoder decoder; 115 | aws_event_stream_streaming_decoder_init( 116 | &decoder, alloc, s_on_payload_segment, s_on_prelude_received, s_on_header_received, s_on_error, NULL); 117 | 118 | setvbuf(stdin, NULL, _IONBF, 0); 119 | 120 | uint8_t data_buffer[1024]; 121 | size_t read_val = 0; 122 | while ((read_val = fread(data_buffer, sizeof(uint8_t), sizeof(data_buffer), stdin))) { 123 | if (read_val > 0) { 124 | struct aws_byte_buf decode_data = aws_byte_buf_from_array(data_buffer, read_val); 125 | int err_code = aws_event_stream_streaming_decoder_pump(&decoder, &decode_data); 126 | if (err_code) { 127 | fprintf(stderr, "Error occurred during parsing. Error code: %d\n", err_code); 128 | aws_event_stream_streaming_decoder_clean_up(&decoder); 129 | return -1; 130 | } 131 | continue; 132 | } 133 | if (feof(stdin)) { 134 | fprintf(stdout, "\n"); 135 | return 0; 136 | } 137 | 138 | if (ferror(stdin)) { 139 | perror("Error reading from stdin\n"); 140 | return ferror(stdin); 141 | } 142 | } 143 | 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /bin/event_stream_write_test_case.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0. 4 | */ 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef _WIN32 15 | # define DELIM "\\" 16 | #else 17 | # define DELIM "/" 18 | #endif 19 | 20 | /** 21 | * 4996 is to disable unsafe function fopen 22 | * 4310 is to disable type casting to smaller type at line 215, which is needed to avoid gcc overflow warning when casting int to int8_t 23 | */ 24 | #ifdef _MSC_VER 25 | #pragma warning (disable: 4996 4310) 26 | #endif 27 | 28 | static void write_negative_test_case( 29 | const char *root_dir, 30 | const char *test_name, 31 | const uint8_t *buffer, 32 | size_t buffer_size, 33 | const char *err_msg) { 34 | size_t dir_len = strlen(root_dir); 35 | size_t encoded_len = strlen("encoded") + strlen("negative") + strlen(test_name) + 2; 36 | size_t decoded_len = strlen("decoded") + strlen("negative") + strlen(test_name) + 2; 37 | 38 | size_t enc_output_file_size = dir_len + 1 + encoded_len + 1; 39 | char *enc_output_file = (char *)malloc(enc_output_file_size); 40 | snprintf(enc_output_file, enc_output_file_size, "%s%s%s%s%s%s%s", root_dir, DELIM, "encoded", DELIM, "negative", DELIM, test_name); 41 | 42 | size_t dec_output_file_size = dir_len + 1 + decoded_len + 1; 43 | char *dec_output_file = (char *)malloc(dec_output_file_size); 44 | snprintf(dec_output_file, dec_output_file_size, "%s%s%s%s%s%s%s", root_dir, DELIM, "decoded", DELIM, "negative", DELIM, test_name); 45 | 46 | FILE *enc = fopen(enc_output_file, "w"); 47 | if (!enc) { 48 | fprintf(stderr, "couldn't write to %s\n", enc_output_file); 49 | exit(-1); 50 | } 51 | 52 | fwrite(buffer, sizeof(uint8_t), buffer_size, enc); 53 | 54 | fflush(enc); 55 | fclose(enc); 56 | 57 | FILE *dec = fopen(dec_output_file, "w"); 58 | if (!dec) { 59 | fprintf(stderr, "couldn't write to %s\n", dec_output_file); 60 | exit(-1); 61 | } 62 | 63 | fwrite(err_msg, sizeof(char), strlen(err_msg), dec); 64 | 65 | fflush(dec); 66 | fclose(dec); 67 | 68 | free(enc_output_file); 69 | free(dec_output_file); 70 | } 71 | 72 | static void write_positive_test_case( 73 | const char *root_dir, 74 | const char *test_name, 75 | struct aws_event_stream_message *message) { 76 | size_t dir_len = strlen(root_dir); 77 | size_t encoded_len = strlen("encoded") + strlen("positive") + strlen(test_name) + 2; 78 | size_t decoded_len = strlen("decoded") + strlen("positive") + strlen(test_name) + 2; 79 | 80 | size_t enc_output_file_size = dir_len + 1 + encoded_len + 1; 81 | char *enc_output_file = (char *)malloc(enc_output_file_size); 82 | snprintf(enc_output_file, enc_output_file_size, "%s%s%s%s%s%s%s", root_dir, DELIM, "encoded", DELIM, "positive", DELIM, test_name); 83 | 84 | size_t dec_output_file_size = dir_len + 1 + decoded_len + 1; 85 | char *dec_output_file = (char *)malloc(dec_output_file_size); 86 | snprintf(dec_output_file, dec_output_file_size, "%s%s%s%s%s%s%s", root_dir, DELIM, "decoded", DELIM, "positive", DELIM, test_name); 87 | 88 | FILE *enc = fopen(enc_output_file, "w"); 89 | if (!enc) { 90 | fprintf(stderr, "couldn't write to %s\n", enc_output_file); 91 | exit(-1); 92 | } 93 | 94 | fwrite( 95 | aws_event_stream_message_buffer(message), sizeof(uint8_t), aws_event_stream_message_total_length(message), enc); 96 | 97 | fflush(enc); 98 | fclose(enc); 99 | 100 | FILE *dec = fopen(dec_output_file, "w"); 101 | if (!dec) { 102 | fprintf(stderr, "couldn't write to %s\n", dec_output_file); 103 | exit(-1); 104 | } 105 | 106 | aws_event_stream_message_to_debug_str(dec, message); 107 | 108 | fflush(dec); 109 | fclose(dec); 110 | free(enc_output_file); 111 | free(dec_output_file); 112 | } 113 | 114 | int main(void) { 115 | struct aws_array_list headers; 116 | struct aws_allocator *alloc = aws_default_allocator(); 117 | aws_event_stream_headers_list_init(&headers, alloc); 118 | 119 | struct aws_event_stream_message msg; 120 | aws_event_stream_message_init(&msg, alloc, &headers, NULL); 121 | 122 | write_positive_test_case(".", "empty_message", &msg); 123 | 124 | struct aws_byte_buf payload = aws_byte_buf_from_c_str("{'foo':'bar'}"); 125 | 126 | aws_event_stream_message_clean_up(&msg); 127 | aws_event_stream_message_init(&msg, alloc, &headers, &payload); 128 | 129 | write_positive_test_case(".", "payload_no_headers", &msg); 130 | aws_event_stream_message_clean_up(&msg); 131 | 132 | static const char content_type[] = "content-type"; 133 | static const char json[] = "application/json"; 134 | 135 | aws_event_stream_add_string_header(&headers, content_type, sizeof(content_type) - 1, json, sizeof(json) - 1, 0); 136 | aws_event_stream_message_init(&msg, alloc, &headers, &payload); 137 | 138 | write_positive_test_case(".", "payload_one_str_header", &msg); 139 | 140 | /* corrupt length */ 141 | uint32_t original_length = aws_event_stream_message_total_length(&msg); 142 | uint8_t *buffer_cpy = aws_mem_acquire(alloc, original_length); 143 | memcpy(buffer_cpy, aws_event_stream_message_buffer(&msg), original_length); 144 | aws_write_u32(original_length + 1, buffer_cpy); 145 | 146 | write_negative_test_case(".", "corrupted_length", buffer_cpy, original_length, "Prelude checksum mismatch"); 147 | aws_mem_release(alloc, buffer_cpy); 148 | 149 | /* corrupt header length */ 150 | buffer_cpy = aws_mem_acquire(alloc, original_length); 151 | memcpy(buffer_cpy, aws_event_stream_message_buffer(&msg), original_length); 152 | 153 | uint32_t original_hdr_len = aws_event_stream_message_headers_len(&msg); 154 | 155 | aws_write_u32(original_hdr_len + 1, buffer_cpy + 4); 156 | 157 | write_negative_test_case(".", "corrupted_header_len", buffer_cpy, original_length, "Prelude checksum mismatch"); 158 | aws_mem_release(alloc, buffer_cpy); 159 | 160 | /* corrupt headers */ 161 | buffer_cpy = aws_mem_acquire(alloc, original_length); 162 | memcpy(buffer_cpy, aws_event_stream_message_buffer(&msg), original_length); 163 | 164 | uint32_t hdr_len = aws_event_stream_message_headers_len(&msg); 165 | buffer_cpy[hdr_len + AWS_EVENT_STREAM_PRELUDE_LENGTH + 1] = 'a'; 166 | 167 | write_negative_test_case(".", "corrupted_headers", buffer_cpy, original_length, "Message checksum mismatch"); 168 | aws_mem_release(alloc, buffer_cpy); 169 | 170 | buffer_cpy = aws_mem_acquire(alloc, original_length); 171 | memcpy(buffer_cpy, aws_event_stream_message_buffer(&msg), original_length); 172 | aws_event_stream_message_clean_up(&msg); 173 | 174 | /* corrupt payload */ 175 | aws_event_stream_message_init(&msg, alloc, NULL, &payload); 176 | ((uint8_t *)aws_event_stream_message_payload(&msg))[0] = '['; 177 | write_negative_test_case( 178 | ".", 179 | "corrupted_payload", 180 | aws_event_stream_message_buffer(&msg), 181 | aws_event_stream_message_total_length(&msg), 182 | "Message checksum mismatch"); 183 | 184 | aws_event_stream_message_clean_up(&msg); 185 | 186 | /* int header */ 187 | static const char event_type[] = "event-type"; 188 | 189 | aws_array_list_clear(&headers); 190 | aws_event_stream_add_int32_header(&headers, event_type, sizeof(event_type) - 1, 0x0000A00C); 191 | aws_event_stream_message_init(&msg, alloc, &headers, &payload); 192 | 193 | write_positive_test_case(".", "int32_header", &msg); 194 | aws_event_stream_message_clean_up(&msg); 195 | 196 | aws_array_list_clear(&headers); 197 | 198 | /* one of every header type */ 199 | aws_event_stream_add_int32_header(&headers, event_type, sizeof(event_type) - 1, 0x0000A00C); 200 | aws_event_stream_add_string_header(&headers, content_type, sizeof(content_type) - 1, json, sizeof(json) - 1, 0); 201 | 202 | static const char bool_false[] = "bool false"; 203 | aws_event_stream_add_bool_header(&headers, bool_false, sizeof(bool_false) - 1, 0); 204 | 205 | static const char bool_true[] = "bool true"; 206 | aws_event_stream_add_bool_header(&headers, bool_true, sizeof(bool_true) - 1, 1); 207 | 208 | static const char byte_hdr[] = "byte"; 209 | aws_event_stream_add_byte_header(&headers, byte_hdr, sizeof(byte_hdr) - 1, (int8_t)0xcf); 210 | 211 | static const char byte_buf_hdr[] = "byte buf"; 212 | static const char byte_buf[] = "I'm a little teapot!"; 213 | 214 | aws_event_stream_add_bytebuf_header( 215 | &headers, byte_buf_hdr, sizeof(byte_buf_hdr) - 1, (uint8_t *)byte_buf, sizeof(byte_buf) - 1, 0); 216 | 217 | static const char timestamp_hdr[] = "timestamp"; 218 | aws_event_stream_add_timestamp_header(&headers, timestamp_hdr, sizeof(timestamp_hdr) - 1, 8675309); 219 | 220 | static const char int16_hdr[] = "int16"; 221 | aws_event_stream_add_int16_header(&headers, int16_hdr, sizeof(int16_hdr) - 1, 42); 222 | 223 | static const char int64_hdr[] = "int64"; 224 | aws_event_stream_add_int64_header(&headers, int64_hdr, sizeof(int64_hdr) - 1, 42424242); 225 | 226 | static const char uuid_hdr[] = "uuid"; 227 | static const uint8_t uuid[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; 228 | 229 | aws_event_stream_add_uuid_header(&headers, uuid_hdr, sizeof(uuid_hdr) - 1, (uint8_t *)uuid); 230 | aws_event_stream_message_init(&msg, alloc, &headers, &payload); 231 | 232 | struct aws_event_stream_message sanity_check_message; 233 | struct aws_byte_buf message_buffer = 234 | aws_byte_buf_from_array(aws_event_stream_message_buffer(&msg), aws_event_stream_message_total_length(&msg)); 235 | 236 | int err = aws_event_stream_message_from_buffer(&sanity_check_message, alloc, &message_buffer); 237 | 238 | if (err) { 239 | fprintf(stderr, "failed to parse what should have been a valid message\n"); 240 | exit(-1); 241 | } 242 | 243 | write_positive_test_case(".", "all_headers", &msg); 244 | aws_event_stream_message_clean_up(&msg); 245 | aws_event_stream_headers_list_cleanup(&headers); 246 | 247 | return 0; 248 | } 249 | -------------------------------------------------------------------------------- /builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-c-event-stream", 3 | "upstream": [ 4 | { "name": "aws-c-common" }, 5 | { "name": "aws-c-io" }, 6 | { "name": "aws-checksums" } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /clang-tidy/run-clang-tidy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SOURCE_FILES=`find source include tests -type f -name '*.h' -o -name '*.c'` 4 | for i in $SOURCE_FILES 5 | do 6 | clang-tidy -config="`cat .clang-tidy`" $i -- -Iinclude 7 | if [ $? -ne 1 ] 8 | then 9 | echo "$i failed clang-tidy check." 10 | FAIL=1 11 | fi 12 | done 13 | -------------------------------------------------------------------------------- /cmake/aws-c-event-stream-config.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeFindDependencyMacro) 2 | find_dependency(aws-c-io) 3 | find_dependency(aws-checksums) 4 | 5 | macro(aws_load_targets type) 6 | include(${CMAKE_CURRENT_LIST_DIR}/${type}/@PROJECT_NAME@-targets.cmake) 7 | endmacro() 8 | 9 | # try to load the lib follow BUILD_SHARED_LIBS. Fall back if not exist. 10 | if (BUILD_SHARED_LIBS) 11 | if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/shared") 12 | aws_load_targets(shared) 13 | else() 14 | aws_load_targets(static) 15 | endif() 16 | else() 17 | if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/static") 18 | aws_load_targets(static) 19 | else() 20 | aws_load_targets(shared) 21 | endif() 22 | endif() 23 | -------------------------------------------------------------------------------- /docs/images/encoding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-c-event-stream/bebb7c3461af8c8e547625b845f4e4892cba2515/docs/images/encoding.png -------------------------------------------------------------------------------- /format-check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import os 4 | from pathlib import Path 5 | import re 6 | from subprocess import list2cmdline, run 7 | from tempfile import NamedTemporaryFile 8 | 9 | CLANG_FORMAT_VERSION = '18.1.6' 10 | 11 | INCLUDE_REGEX = re.compile( 12 | r'^(include|source|tests|verification)/.*\.(c|h|inl)$') 13 | EXCLUDE_REGEX = re.compile(r'^$') 14 | 15 | arg_parser = argparse.ArgumentParser(description="Check with clang-format") 16 | arg_parser.add_argument('-i', '--inplace-edit', action='store_true', 17 | help="Edit files inplace") 18 | args = arg_parser.parse_args() 19 | 20 | os.chdir(Path(__file__).parent) 21 | 22 | # create file containing list of all files to format 23 | filepaths_file = NamedTemporaryFile(delete=False) 24 | for dirpath, dirnames, filenames in os.walk('.'): 25 | for filename in filenames: 26 | # our regexes expect filepath to use forward slash 27 | filepath = Path(dirpath, filename).as_posix() 28 | if not INCLUDE_REGEX.match(filepath): 29 | continue 30 | if EXCLUDE_REGEX.match(filepath): 31 | continue 32 | 33 | filepaths_file.write(f"{filepath}\n".encode()) 34 | filepaths_file.close() 35 | 36 | # use pipx to run clang-format from PyPI 37 | # this is a simple way to run the same clang-format version regardless of OS 38 | cmd = ['pipx', 'run', f'clang-format=={CLANG_FORMAT_VERSION}', 39 | f'--files={filepaths_file.name}'] 40 | if args.inplace_edit: 41 | cmd += ['-i'] 42 | else: 43 | cmd += ['--Werror', '--dry-run'] 44 | 45 | print(f"{Path.cwd()}$ {list2cmdline(cmd)}") 46 | if run(cmd).returncode: 47 | exit(1) 48 | -------------------------------------------------------------------------------- /include/aws/event-stream/event_stream.h: -------------------------------------------------------------------------------- 1 | #ifndef AWS_EVENT_STREAM_H_ 2 | #define AWS_EVENT_STREAM_H_ 3 | 4 | /** 5 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 6 | * SPDX-License-Identifier: Apache-2.0. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | AWS_PUSH_SANE_WARNING_LEVEL 17 | 18 | #define AWS_C_EVENT_STREAM_PACKAGE_ID 4 19 | /* Current service-side max is 24 MB, likely to increase in future. 20 | * This is encoded on the wire in 4 bytes, and could technically be larger (up to INT32_MAX). */ 21 | #define AWS_EVENT_STREAM_MAX_MESSAGE_SIZE (24 * 1024 * 1024) 22 | 23 | /* Max total size for headers. 24 | * This is encoded on the wire in 4 bytes, and could technically be larger (up to INT32_MAX). */ 25 | #define AWS_EVENT_STREAM_MAX_HEADERS_SIZE (AWS_EVENT_STREAM_MAX_MESSAGE_SIZE) 26 | 27 | /* Max header name length is 127 bytes. 28 | * This is encoded on the wire in 1 byte, it cannot change. */ 29 | #define AWS_EVENT_STREAM_HEADER_NAME_LEN_MAX (INT8_MAX) 30 | 31 | /* Max header value length is 32767 bytes. 32 | * This is encoded on the wire in 2 bytes, it cannot change. */ 33 | #define AWS_EVENT_STREAM_HEADER_VALUE_LEN_MAX (INT16_MAX) 34 | 35 | enum aws_event_stream_errors { 36 | AWS_ERROR_EVENT_STREAM_BUFFER_LENGTH_MISMATCH = AWS_ERROR_ENUM_BEGIN_RANGE(AWS_C_EVENT_STREAM_PACKAGE_ID), 37 | AWS_ERROR_EVENT_STREAM_INSUFFICIENT_BUFFER_LEN, 38 | AWS_ERROR_EVENT_STREAM_MESSAGE_FIELD_SIZE_EXCEEDED, 39 | AWS_ERROR_EVENT_STREAM_PRELUDE_CHECKSUM_FAILURE, 40 | AWS_ERROR_EVENT_STREAM_MESSAGE_CHECKSUM_FAILURE, 41 | AWS_ERROR_EVENT_STREAM_MESSAGE_INVALID_HEADERS_LEN, 42 | AWS_ERROR_EVENT_STREAM_MESSAGE_UNKNOWN_HEADER_TYPE, 43 | AWS_ERROR_EVENT_STREAM_MESSAGE_PARSER_ILLEGAL_STATE, 44 | AWS_ERROR_EVENT_STREAM_RPC_CONNECTION_CLOSED, 45 | AWS_ERROR_EVENT_STREAM_RPC_PROTOCOL_ERROR, 46 | AWS_ERROR_EVENT_STREAM_RPC_STREAM_CLOSED, 47 | AWS_ERROR_EVENT_STREAM_RPC_STREAM_NOT_ACTIVATED, 48 | 49 | AWS_ERROR_EVENT_STREAM_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_EVENT_STREAM_PACKAGE_ID), 50 | }; 51 | 52 | enum aws_event_stream_log_subject { 53 | AWS_LS_EVENT_STREAM_GENERAL = AWS_LOG_SUBJECT_BEGIN_RANGE(AWS_C_EVENT_STREAM_PACKAGE_ID), 54 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 55 | AWS_LS_EVENT_STREAM_RPC_SERVER, 56 | AWS_LS_EVENT_STREAM_RPC_CLIENT, 57 | 58 | AWS_LS_EVENT_STREAM_LAST = AWS_LOG_SUBJECT_END_RANGE(AWS_C_EVENT_STREAM_PACKAGE_ID), 59 | }; 60 | 61 | struct aws_event_stream_message_prelude { 62 | uint32_t total_len; 63 | uint32_t headers_len; 64 | uint32_t prelude_crc; 65 | }; 66 | 67 | struct aws_event_stream_message { 68 | struct aws_allocator *alloc; 69 | struct aws_byte_buf message_buffer; 70 | }; 71 | 72 | /* Prelude (12-bytes) = Total Byte Length (4-bytes) + Headers Byte Length (4-bytes) + Prelude CRC (4-bytes) */ 73 | #define AWS_EVENT_STREAM_PRELUDE_LENGTH (uint32_t)(12) 74 | 75 | /* Trailer (4-bytes) = Message CRC (4-bytes) */ 76 | #define AWS_EVENT_STREAM_TRAILER_LENGTH (uint32_t)(4) 77 | 78 | enum aws_event_stream_header_value_type { 79 | AWS_EVENT_STREAM_HEADER_BOOL_TRUE = 0, 80 | AWS_EVENT_STREAM_HEADER_BOOL_FALSE, 81 | AWS_EVENT_STREAM_HEADER_BYTE, 82 | AWS_EVENT_STREAM_HEADER_INT16, 83 | AWS_EVENT_STREAM_HEADER_INT32, 84 | AWS_EVENT_STREAM_HEADER_INT64, 85 | AWS_EVENT_STREAM_HEADER_BYTE_BUF, 86 | AWS_EVENT_STREAM_HEADER_STRING, 87 | /* 64 bit integer (millis since epoch) */ 88 | AWS_EVENT_STREAM_HEADER_TIMESTAMP, 89 | AWS_EVENT_STREAM_HEADER_UUID 90 | }; 91 | 92 | struct aws_event_stream_header_value_pair { 93 | uint8_t header_name_len; 94 | char header_name[AWS_EVENT_STREAM_HEADER_NAME_LEN_MAX]; 95 | enum aws_event_stream_header_value_type header_value_type; 96 | union { 97 | uint8_t *variable_len_val; /* for variable-size types (STRING, BYTE_BUF)*/ 98 | uint8_t static_val[16]; /* largest fixed-size types are 16 bytes (INT64, UUID, TIMESTAMP) */ 99 | } header_value; 100 | 101 | uint16_t header_value_len; 102 | int8_t value_owned; 103 | }; 104 | 105 | struct aws_event_stream_streaming_decoder; 106 | typedef int(aws_event_stream_process_state_fn)( 107 | struct aws_event_stream_streaming_decoder *decoder, 108 | const uint8_t *data, 109 | size_t len, 110 | size_t *processed); 111 | 112 | /** 113 | * Called by aws_aws_event_stream_streaming_decoder when payload data has been received. 114 | * 'data' doesn't belong to you, so copy the data if it is needed beyond the scope of your callback. 115 | * final_segment == 1 indicates the current data is the last payload buffer for that message. 116 | */ 117 | typedef void(aws_event_stream_process_on_payload_segment_fn)( 118 | struct aws_event_stream_streaming_decoder *decoder, 119 | struct aws_byte_buf *payload, 120 | int8_t final_segment, 121 | void *user_data); 122 | 123 | /** 124 | * Called by aws_aws_event_stream_streaming_decoder when a new message has arrived. The prelude will contain metadata 125 | * about the message. At this point no headers or payload have been received. prelude is copyable. 126 | */ 127 | typedef void(aws_event_stream_prelude_received_fn)( 128 | struct aws_event_stream_streaming_decoder *decoder, 129 | struct aws_event_stream_message_prelude *prelude, 130 | void *user_data); 131 | 132 | /** 133 | * Called by aws_aws_event_stream_streaming_decoder when a header is encountered. 'header' is not yours. Copy the data 134 | * you want from it if your scope extends beyond your callback. 135 | */ 136 | typedef void(aws_event_stream_header_received_fn)( 137 | struct aws_event_stream_streaming_decoder *decoder, 138 | struct aws_event_stream_message_prelude *prelude, 139 | struct aws_event_stream_header_value_pair *header, 140 | void *user_data); 141 | 142 | /** 143 | * Called by aws_aws_event_stream_streaming_decoder when a message decoding is complete 144 | * and crc is verified. 145 | */ 146 | typedef void(aws_event_stream_on_complete_fn)( 147 | struct aws_event_stream_streaming_decoder *decoder, 148 | uint32_t message_crc, 149 | void *user_data); 150 | 151 | /** 152 | * Called by aws_aws_event_stream_streaming_decoder when an error is encountered. The decoder is not in a good state for 153 | * usage after this callback. 154 | */ 155 | typedef void(aws_event_stream_on_error_fn)( 156 | struct aws_event_stream_streaming_decoder *decoder, 157 | struct aws_event_stream_message_prelude *prelude, 158 | int error_code, 159 | const char *message, 160 | void *user_data); 161 | 162 | struct aws_event_stream_streaming_decoder { 163 | struct aws_allocator *alloc; 164 | uint8_t working_buffer[AWS_EVENT_STREAM_PRELUDE_LENGTH]; 165 | size_t message_pos; 166 | uint32_t running_crc; 167 | size_t current_header_name_offset; 168 | size_t current_header_value_offset; 169 | struct aws_event_stream_header_value_pair current_header; 170 | struct aws_event_stream_message_prelude prelude; 171 | aws_event_stream_process_state_fn *state; 172 | aws_event_stream_process_on_payload_segment_fn *on_payload; 173 | aws_event_stream_prelude_received_fn *on_prelude; 174 | aws_event_stream_header_received_fn *on_header; 175 | aws_event_stream_on_complete_fn *on_complete; 176 | aws_event_stream_on_error_fn *on_error; 177 | void *user_context; 178 | }; 179 | 180 | struct aws_event_stream_streaming_decoder_options { 181 | /** 182 | * (Required) 183 | * Invoked repeatedly as payload segment are received. 184 | * See `aws_event_stream_process_on_payload_segment_fn`. 185 | */ 186 | aws_event_stream_process_on_payload_segment_fn *on_payload_segment; 187 | /** 188 | * (Required) 189 | * Invoked when when a new message has arrived. The prelude will contain metadata about the message. 190 | * See `aws_event_stream_prelude_received_fn`. 191 | */ 192 | aws_event_stream_prelude_received_fn *on_prelude; 193 | /** 194 | * (Required) 195 | * Invoked repeatedly as headers are received. 196 | * See `aws_event_stream_header_received_fn`. 197 | */ 198 | aws_event_stream_header_received_fn *on_header; 199 | /** 200 | * (Optional) 201 | * Invoked if a message is decoded successfully. 202 | * See `aws_event_stream_on_complete_fn`. 203 | */ 204 | aws_event_stream_on_complete_fn *on_complete; 205 | /** 206 | * (Required) 207 | * Invoked when an error is encountered. The decoder is not in a good state for usage after this callback. 208 | * See `aws_event_stream_on_error_fn`. 209 | */ 210 | aws_event_stream_on_error_fn *on_error; 211 | /** 212 | * (Optional) 213 | * user_data passed to callbacks. 214 | */ 215 | void *user_data; 216 | }; 217 | AWS_EXTERN_C_BEGIN 218 | 219 | /** 220 | * Initializes with a list of headers, the payload, and a payload length. CRCs will be computed for you. 221 | * If headers or payload is NULL, then the fields will be appropriately set to indicate no headers and/or no payload. 222 | * Both payload and headers will result in an allocation. 223 | */ 224 | AWS_EVENT_STREAM_API int aws_event_stream_message_init( 225 | struct aws_event_stream_message *message, 226 | struct aws_allocator *alloc, 227 | const struct aws_array_list *headers, 228 | const struct aws_byte_buf *payload); 229 | 230 | /** 231 | * Zero allocation, Zero copy. The message will simply wrap the buffer. The message functions are only useful as long as 232 | * buffer is referencable memory. 233 | */ 234 | AWS_EVENT_STREAM_API int aws_event_stream_message_from_buffer( 235 | struct aws_event_stream_message *message, 236 | struct aws_allocator *alloc, 237 | struct aws_byte_buf *buffer); 238 | 239 | /** 240 | * Allocates memory and copies buffer. Otherwise the same as aws_aws_event_stream_message_from_buffer. This is slower, 241 | * but possibly safer. 242 | */ 243 | AWS_EVENT_STREAM_API int aws_event_stream_message_from_buffer_copy( 244 | struct aws_event_stream_message *message, 245 | struct aws_allocator *alloc, 246 | const struct aws_byte_buf *buffer); 247 | 248 | /** 249 | * Cleans up any internally allocated memory. Always call this for API compatibility reasons, even if you only used the 250 | * aws_aws_event_stream_message_from_buffer function. 251 | */ 252 | AWS_EVENT_STREAM_API void aws_event_stream_message_clean_up(struct aws_event_stream_message *message); 253 | 254 | /** 255 | * Returns the total length of the message (including the length field). 256 | */ 257 | AWS_EVENT_STREAM_API uint32_t aws_event_stream_message_total_length(const struct aws_event_stream_message *message); 258 | 259 | /** 260 | * Returns the length of the headers portion of the message. 261 | */ 262 | AWS_EVENT_STREAM_API uint32_t aws_event_stream_message_headers_len(const struct aws_event_stream_message *message); 263 | 264 | /** 265 | * Returns the prelude crc (crc32) 266 | */ 267 | AWS_EVENT_STREAM_API uint32_t aws_event_stream_message_prelude_crc(const struct aws_event_stream_message *message); 268 | 269 | /** 270 | * Writes the message to fd in json format. All strings and binary fields are base64 encoded. 271 | */ 272 | AWS_EVENT_STREAM_API int aws_event_stream_message_to_debug_str( 273 | FILE *fd, 274 | const struct aws_event_stream_message *message); 275 | 276 | /** 277 | * Adds the headers for the message to list. The memory in each header is owned as part of the message, do not free it 278 | * or pass its address around. 279 | */ 280 | AWS_EVENT_STREAM_API int aws_event_stream_message_headers( 281 | const struct aws_event_stream_message *message, 282 | struct aws_array_list *headers); 283 | 284 | /** 285 | * Returns a pointer to the beginning of the message payload. 286 | */ 287 | AWS_EVENT_STREAM_API const uint8_t *aws_event_stream_message_payload(const struct aws_event_stream_message *message); 288 | 289 | /** 290 | * Returns the length of the message payload. 291 | */ 292 | AWS_EVENT_STREAM_API uint32_t aws_event_stream_message_payload_len(const struct aws_event_stream_message *message); 293 | 294 | /** 295 | * Returns the checksum of the entire message (crc32) 296 | */ 297 | AWS_EVENT_STREAM_API uint32_t aws_event_stream_message_message_crc(const struct aws_event_stream_message *message); 298 | 299 | /** 300 | * Returns the message as a buffer ready for transport. 301 | */ 302 | AWS_EVENT_STREAM_API const uint8_t *aws_event_stream_message_buffer(const struct aws_event_stream_message *message); 303 | 304 | AWS_EVENT_STREAM_API uint32_t 305 | aws_event_stream_compute_headers_required_buffer_len(const struct aws_array_list *headers); 306 | 307 | /** 308 | * Writes headers to buf assuming buf is large enough to hold the data. Prefer this function over the unsafe variant 309 | * 'aws_event_stream_write_headers_to_buffer'. 310 | * 311 | * Returns AWS_OP_SUCCESS if the headers were successfully and completely written and AWS_OP_ERR otherwise. 312 | */ 313 | AWS_EVENT_STREAM_API int aws_event_stream_write_headers_to_buffer_safe( 314 | const struct aws_array_list *headers, 315 | struct aws_byte_buf *buf); 316 | 317 | /** 318 | * Deprecated in favor of 'aws_event_stream_write_headers_to_buffer_safe' as this API is unsafe. 319 | * 320 | * Writes headers to buffer and returns the length of bytes written to buffer. Assumes buffer is large enough to 321 | * store the headers. 322 | */ 323 | AWS_EVENT_STREAM_API size_t 324 | aws_event_stream_write_headers_to_buffer(const struct aws_array_list *headers, uint8_t *buffer); 325 | 326 | /** Get the headers from the buffer, store them in the headers list. 327 | * the user's responsibility to cleanup the list when they are finished with it. 328 | * no buffer copies happen here, the lifetime of the buffer, must outlive the usage of the headers. 329 | * returns error codes defined in the public interface. 330 | */ 331 | AWS_EVENT_STREAM_API int aws_event_stream_read_headers_from_buffer( 332 | struct aws_array_list *headers, 333 | const uint8_t *buffer, 334 | size_t headers_len); 335 | 336 | /** 337 | * Initialize a streaming decoder for messages with callbacks for usage 338 | * and an optional user context pointer. 339 | */ 340 | AWS_EVENT_STREAM_API 341 | void aws_event_stream_streaming_decoder_init_from_options( 342 | struct aws_event_stream_streaming_decoder *decoder, 343 | struct aws_allocator *allocator, 344 | const struct aws_event_stream_streaming_decoder_options *options); 345 | 346 | /** 347 | * Deprecated. Use aws_event_stream_streaming_decoder_init_from_options instead. 348 | * Initialize a streaming decoder for messages with callbacks for usage and an optional user context pointer. 349 | */ 350 | AWS_EVENT_STREAM_API void aws_event_stream_streaming_decoder_init( 351 | struct aws_event_stream_streaming_decoder *decoder, 352 | struct aws_allocator *alloc, 353 | aws_event_stream_process_on_payload_segment_fn *on_payload_segment, 354 | aws_event_stream_prelude_received_fn *on_prelude, 355 | aws_event_stream_header_received_fn *on_header, 356 | aws_event_stream_on_error_fn *on_error, 357 | void *user_data); 358 | 359 | /** 360 | * Currently, no memory is allocated inside aws_aws_event_stream_streaming_decoder, but for future API compatibility, 361 | * you should call this when finished with it. 362 | */ 363 | AWS_EVENT_STREAM_API void aws_event_stream_streaming_decoder_clean_up( 364 | struct aws_event_stream_streaming_decoder *decoder); 365 | 366 | /** 367 | * initializes a headers list for you. It will default to a capacity of 4 in dynamic mode. 368 | */ 369 | AWS_EVENT_STREAM_API int aws_event_stream_headers_list_init( 370 | struct aws_array_list *headers, 371 | struct aws_allocator *allocator); 372 | 373 | /** 374 | * Cleans up the headers list. Also deallocates any headers that were the result of a copy flag for strings or buffer. 375 | */ 376 | AWS_EVENT_STREAM_API void aws_event_stream_headers_list_cleanup(struct aws_array_list *headers); 377 | 378 | /** 379 | * Adds a string header to the list of headers. If copy is set to true, this will result in an allocation for the header 380 | * value. Otherwise, the value will be set to the memory address of 'value'. 381 | */ 382 | AWS_EVENT_STREAM_API int aws_event_stream_add_string_header( 383 | struct aws_array_list *headers, 384 | const char *name, 385 | uint8_t name_len, 386 | const char *value, 387 | uint16_t value_len, 388 | int8_t copy); 389 | 390 | AWS_EVENT_STREAM_API struct aws_event_stream_header_value_pair aws_event_stream_create_string_header( 391 | struct aws_byte_cursor name, 392 | struct aws_byte_cursor value); 393 | 394 | AWS_EVENT_STREAM_API struct aws_event_stream_header_value_pair aws_event_stream_create_int32_header( 395 | struct aws_byte_cursor name, 396 | int32_t value); 397 | 398 | /** 399 | * Adds a byte header to the list of headers. 400 | */ 401 | AWS_EVENT_STREAM_API int aws_event_stream_add_byte_header( 402 | struct aws_array_list *headers, 403 | const char *name, 404 | uint8_t name_len, 405 | int8_t value); 406 | 407 | /** 408 | * Adds a bool header to the list of headers. 409 | */ 410 | AWS_EVENT_STREAM_API int aws_event_stream_add_bool_header( 411 | struct aws_array_list *headers, 412 | const char *name, 413 | uint8_t name_len, 414 | int8_t value); 415 | 416 | /** 417 | * adds a 16 bit integer to the list of headers. 418 | */ 419 | AWS_EVENT_STREAM_API int aws_event_stream_add_int16_header( 420 | struct aws_array_list *headers, 421 | const char *name, 422 | uint8_t name_len, 423 | int16_t value); 424 | 425 | /** 426 | * adds a 32 bit integer to the list of headers. 427 | */ 428 | AWS_EVENT_STREAM_API int aws_event_stream_add_int32_header( 429 | struct aws_array_list *headers, 430 | const char *name, 431 | uint8_t name_len, 432 | int32_t value); 433 | 434 | /** 435 | * adds a 64 bit integer to the list of headers. 436 | */ 437 | AWS_EVENT_STREAM_API int aws_event_stream_add_int64_header( 438 | struct aws_array_list *headers, 439 | const char *name, 440 | uint8_t name_len, 441 | int64_t value); 442 | 443 | /** 444 | * Adds a byte-buffer header to the list of headers. If copy is set to true, this will result in an allocation for the 445 | * header value. Otherwise, the value will be set to the memory address of 'value'. 446 | */ 447 | AWS_EVENT_STREAM_API int aws_event_stream_add_bytebuf_header( 448 | struct aws_array_list *headers, 449 | const char *name, 450 | uint8_t name_len, 451 | uint8_t *value, 452 | uint16_t value_len, 453 | int8_t copy); 454 | 455 | /** 456 | * adds a 64 bit integer representing milliseconds since unix epoch to the list of headers. 457 | */ 458 | AWS_EVENT_STREAM_API int aws_event_stream_add_timestamp_header( 459 | struct aws_array_list *headers, 460 | const char *name, 461 | uint8_t name_len, 462 | int64_t value); 463 | 464 | /** 465 | * adds a uuid buffer to the list of headers. Value must always be 16 bytes long. 466 | */ 467 | AWS_EVENT_STREAM_API int aws_event_stream_add_uuid_header( 468 | struct aws_array_list *headers, 469 | const char *name, 470 | uint8_t name_len, 471 | const uint8_t *value); 472 | 473 | /** 474 | * Adds a generic header to the list of headers. 475 | * Makes a copy of the underlaying data. 476 | */ 477 | AWS_EVENT_STREAM_API int aws_event_stream_add_header( 478 | struct aws_array_list *headers, 479 | const struct aws_event_stream_header_value_pair *header); 480 | 481 | /* Cursor-based header APIs */ 482 | 483 | /** 484 | * Adds a boolean-valued header to a header list 485 | * 486 | * @param headers header list to add to 487 | * @param name name of the header to add 488 | * @param value value of the header to add 489 | * @return AWS_OP_SUCCESS on success, AWS_OP_ERR on failure 490 | */ 491 | AWS_EVENT_STREAM_API int aws_event_stream_add_bool_header_by_cursor( 492 | struct aws_array_list *headers, 493 | struct aws_byte_cursor name, 494 | bool value); 495 | 496 | /** 497 | * Adds a byte-valued header to a header list 498 | * 499 | * @param headers header list to add to 500 | * @param name name of the header to add 501 | * @param value value of the header to add 502 | * @return AWS_OP_SUCCESS on success, AWS_OP_ERR on failure 503 | */ 504 | AWS_EVENT_STREAM_API int aws_event_stream_add_byte_header_by_cursor( 505 | struct aws_array_list *headers, 506 | struct aws_byte_cursor name, 507 | int8_t value); 508 | 509 | /** 510 | * Adds a int16-valued header to a header list 511 | * 512 | * @param headers header list to add to 513 | * @param name name of the header to add 514 | * @param value value of the header to add 515 | * @return AWS_OP_SUCCESS on success, AWS_OP_ERR on failure 516 | */ 517 | AWS_EVENT_STREAM_API int aws_event_stream_add_int16_header_by_cursor( 518 | struct aws_array_list *headers, 519 | struct aws_byte_cursor name, 520 | int16_t value); 521 | 522 | /** 523 | * Adds a int32-valued header to a header list 524 | * 525 | * @param headers header list to add to 526 | * @param name name of the header to add 527 | * @param value value of the header to add 528 | * @return AWS_OP_SUCCESS on success, AWS_OP_ERR on failure 529 | */ 530 | AWS_EVENT_STREAM_API int aws_event_stream_add_int32_header_by_cursor( 531 | struct aws_array_list *headers, 532 | struct aws_byte_cursor name, 533 | int32_t value); 534 | 535 | /** 536 | * Adds a int64-valued header to a header list 537 | * 538 | * @param headers header list to add to 539 | * @param name name of the header to add 540 | * @param value value of the header to add 541 | * @return AWS_OP_SUCCESS on success, AWS_OP_ERR on failure 542 | */ 543 | AWS_EVENT_STREAM_API int aws_event_stream_add_int64_header_by_cursor( 544 | struct aws_array_list *headers, 545 | struct aws_byte_cursor name, 546 | int64_t value); 547 | 548 | /** 549 | * Adds a string-valued header to a header list 550 | * 551 | * @param headers header list to add to 552 | * @param name name of the header to add 553 | * @param value value of the header to add 554 | * @return AWS_OP_SUCCESS on success, AWS_OP_ERR on failure 555 | */ 556 | AWS_EVENT_STREAM_API int aws_event_stream_add_string_header_by_cursor( 557 | struct aws_array_list *headers, 558 | struct aws_byte_cursor name, 559 | struct aws_byte_cursor value); 560 | 561 | /** 562 | * Adds a byte_buf-valued header to a header list 563 | * 564 | * @param headers header list to add to 565 | * @param name name of the header to add 566 | * @param value value of the header to add 567 | * @return AWS_OP_SUCCESS on success, AWS_OP_ERR on failure 568 | */ 569 | AWS_EVENT_STREAM_API int aws_event_stream_add_byte_buf_header_by_cursor( 570 | struct aws_array_list *headers, 571 | struct aws_byte_cursor name, 572 | struct aws_byte_cursor value); 573 | 574 | /** 575 | * Adds a timestamp-valued header to a header list 576 | * 577 | * @param headers header list to add to 578 | * @param name name of the header to add 579 | * @param value value of the header to add 580 | * @return AWS_OP_SUCCESS on success, AWS_OP_ERR on failure 581 | */ 582 | AWS_EVENT_STREAM_API int aws_event_stream_add_timestamp_header_by_cursor( 583 | struct aws_array_list *headers, 584 | struct aws_byte_cursor name, 585 | int64_t value); 586 | 587 | /** 588 | * Adds a uuid-valued header to a header list 589 | * 590 | * @param headers header list to add to 591 | * @param name name of the header to add 592 | * @param value value of the header to add 593 | * @return AWS_OP_SUCCESS on success, AWS_OP_ERR on failure 594 | */ 595 | AWS_EVENT_STREAM_API int aws_event_stream_add_uuid_header_by_cursor( 596 | struct aws_array_list *headers, 597 | struct aws_byte_cursor name, 598 | struct aws_byte_cursor value); 599 | 600 | /** 601 | * Returns the header name. Note: this value is not null terminated 602 | */ 603 | AWS_EVENT_STREAM_API struct aws_byte_buf aws_event_stream_header_name( 604 | struct aws_event_stream_header_value_pair *header); 605 | 606 | /** 607 | * Returns the header value as a string. Note: this value is not null terminated. 608 | */ 609 | AWS_EVENT_STREAM_API struct aws_byte_buf aws_event_stream_header_value_as_string( 610 | struct aws_event_stream_header_value_pair *header); 611 | 612 | /** 613 | * Returns the header value as a byte 614 | */ 615 | AWS_EVENT_STREAM_API int8_t aws_event_stream_header_value_as_byte(struct aws_event_stream_header_value_pair *header); 616 | 617 | /** 618 | * Returns the header value as a boolean value. 619 | */ 620 | AWS_EVENT_STREAM_API int8_t aws_event_stream_header_value_as_bool(struct aws_event_stream_header_value_pair *header); 621 | 622 | /** 623 | * Returns the header value as a 16 bit integer. 624 | */ 625 | AWS_EVENT_STREAM_API int16_t aws_event_stream_header_value_as_int16(struct aws_event_stream_header_value_pair *header); 626 | 627 | /** 628 | * Returns the header value as a 32 bit integer. 629 | */ 630 | AWS_EVENT_STREAM_API int32_t aws_event_stream_header_value_as_int32(struct aws_event_stream_header_value_pair *header); 631 | 632 | /** 633 | * Returns the header value as a 64 bit integer. 634 | */ 635 | AWS_EVENT_STREAM_API int64_t aws_event_stream_header_value_as_int64(struct aws_event_stream_header_value_pair *header); 636 | 637 | /** 638 | * Returns the header value as a pointer to a byte buffer, call aws_event_stream_header_value_length to determine 639 | * the length of the buffer. 640 | */ 641 | AWS_EVENT_STREAM_API struct aws_byte_buf aws_event_stream_header_value_as_bytebuf( 642 | struct aws_event_stream_header_value_pair *header); 643 | 644 | /** 645 | * Returns the header value as a 64 bit integer representing milliseconds since unix epoch. 646 | */ 647 | AWS_EVENT_STREAM_API int64_t 648 | aws_event_stream_header_value_as_timestamp(struct aws_event_stream_header_value_pair *header); 649 | 650 | /** 651 | * Returns the header value a byte buffer which is 16 bytes long. Represents a UUID. 652 | */ 653 | AWS_EVENT_STREAM_API struct aws_byte_buf aws_event_stream_header_value_as_uuid( 654 | struct aws_event_stream_header_value_pair *header); 655 | 656 | /** 657 | * Returns the length of the header value buffer. This is mostly intended for string and byte buffer types. 658 | */ 659 | AWS_EVENT_STREAM_API uint16_t aws_event_stream_header_value_length(struct aws_event_stream_header_value_pair *header); 660 | 661 | /** 662 | * The main driver of the decoder. Pass data that should be decoded with its length. A likely use-case here is in 663 | * response to a read event from an io-device 664 | */ 665 | AWS_EVENT_STREAM_API int aws_event_stream_streaming_decoder_pump( 666 | struct aws_event_stream_streaming_decoder *decoder, 667 | const struct aws_byte_buf *data); 668 | 669 | /** 670 | * Initializes internal datastructures used by aws-c-event-stream. 671 | * Must be called before using any functionality in aws-c-event-stream. 672 | */ 673 | AWS_EVENT_STREAM_API void aws_event_stream_library_init(struct aws_allocator *allocator); 674 | 675 | /** 676 | * Clean up internal datastructures used by aws-c-event-stream. 677 | * Must not be called until application is done using functionality in aws-c-event-stream. 678 | */ 679 | AWS_EVENT_STREAM_API void aws_event_stream_library_clean_up(void); 680 | 681 | AWS_EXTERN_C_END 682 | AWS_POP_SANE_WARNING_LEVEL 683 | 684 | #endif /* AWS_EVENT_STREAM_H_ */ 685 | -------------------------------------------------------------------------------- /include/aws/event-stream/event_stream_channel_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef AWS_EVENT_STREAM_CHANNEL_HANDLER_H 2 | #define AWS_EVENT_STREAM_CHANNEL_HANDLER_H 3 | /** 4 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0. 6 | */ 7 | 8 | #include 9 | 10 | AWS_PUSH_SANE_WARNING_LEVEL 11 | 12 | struct aws_event_stream_channel_handler; 13 | struct aws_channel_handler; 14 | 15 | /** 16 | * Invoked when an aws_event_stream_message is encountered. If the message 17 | * parsed successfully, message will be non-null and error_code will be AWS_ERROR_SUCCESS. 18 | * Otherwise message will be null and error_code will represent the error that was encountered. 19 | * Note that any case that error_code was not AWS_OP_SUCCESS, the channel also shuts down. 20 | */ 21 | typedef void(aws_event_stream_channel_handler_on_message_received_fn)( 22 | struct aws_event_stream_message *message, 23 | int error_code, 24 | void *user_data); 25 | 26 | /** 27 | * Invoked when an aws_event_stream_message is flushed to the IO interface. When error_code is AWS_ERROR_SUCCESS the 28 | * write happened successfuly. Regardless, message is held from the aws_event_stream_channel_handler_write_message() 29 | * call and should likely be freed in this callback. If error_code is non-zero, the channel will be shutdown immediately 30 | * after this callback returns. 31 | */ 32 | typedef void(aws_event_stream_channel_handler_on_message_written_fn)( 33 | struct aws_event_stream_message *message, 34 | int error_code, 35 | void *user_data); 36 | 37 | struct aws_event_stream_channel_handler_options { 38 | /** Callback for when messages are received. Can not be null. */ 39 | aws_event_stream_channel_handler_on_message_received_fn *on_message_received; 40 | /** user data passed to message callback. Optional */ 41 | void *user_data; 42 | /** initial window size to use for the channel. If automatic window management is set to true, this value is 43 | * ignored. */ 44 | size_t initial_window_size; 45 | /** 46 | * if set to false (the default), windowing will be managed automatically for the user. 47 | * Otherwise, after any on_message_received, the user must invoke 48 | * aws_event_stream_channel_handler_increment_read_window() 49 | */ 50 | bool manual_window_management; 51 | }; 52 | 53 | AWS_EXTERN_C_BEGIN 54 | /** 55 | * Allocates and initializes a new channel handler for processing aws_event_stream_message() events. Handler options 56 | * must not be null. 57 | */ 58 | AWS_EVENT_STREAM_API struct aws_channel_handler *aws_event_stream_channel_handler_new( 59 | struct aws_allocator *allocator, 60 | const struct aws_event_stream_channel_handler_options *handler_options); 61 | 62 | /** 63 | * Writes an aws_event_stream_message() to the channel. Once the channel flushes or an error occurs, on_message_written 64 | * will be invoked. message should stay valid until the callback is invoked. If an error an occurs, the channel will 65 | * automatically be shutdown. 66 | */ 67 | AWS_EVENT_STREAM_API int aws_event_stream_channel_handler_write_message( 68 | struct aws_channel_handler *handler, 69 | struct aws_event_stream_message *message, 70 | aws_event_stream_channel_handler_on_message_written_fn *on_message_written, 71 | void *user_data); 72 | 73 | /** 74 | * Updates the read window for the channel if automatic_window_managemanet was set to false. 75 | */ 76 | AWS_EVENT_STREAM_API void aws_event_stream_channel_handler_increment_read_window( 77 | struct aws_channel_handler *handler, 78 | size_t window_update_size); 79 | 80 | AWS_EVENT_STREAM_API void *aws_event_stream_channel_handler_get_user_data(struct aws_channel_handler *handler); 81 | 82 | AWS_EXTERN_C_END 83 | AWS_POP_SANE_WARNING_LEVEL 84 | 85 | #endif /* AWS_EVENT_STREAM_CHANNEL_HANDLER_H */ 86 | -------------------------------------------------------------------------------- /include/aws/event-stream/event_stream_exports.h: -------------------------------------------------------------------------------- 1 | #ifndef AWS_EVENT_STREAM_EXPORTS_H_ 2 | #define AWS_EVENT_STREAM_EXPORTS_H_ 3 | /** 4 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0. 6 | */ 7 | #if defined(AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined(_WIN32) 8 | # ifdef AWS_EVENT_STREAM_USE_IMPORT_EXPORT 9 | # ifdef AWS_EVENT_STREAM_EXPORTS 10 | # define AWS_EVENT_STREAM_API __declspec(dllexport) 11 | # else 12 | # define AWS_EVENT_STREAM_API __declspec(dllimport) 13 | # endif /* AWS_EVENT_STREAM_EXPORTS */ 14 | # else 15 | # define AWS_EVENT_STREAM_API 16 | # endif /* AWS_EVENT_STREAM_USE_IMPORT_EXPORT */ 17 | 18 | #else /* defined (AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */ 19 | 20 | # if defined(AWS_EVENT_STREAM_USE_IMPORT_EXPORT) && defined(AWS_EVENT_STREAM_EXPORTS) 21 | # define AWS_EVENT_STREAM_API __attribute__((visibility("default"))) 22 | # else 23 | # define AWS_EVENT_STREAM_API 24 | # endif 25 | 26 | #endif /* defined (AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */ 27 | 28 | #endif /* AWS_EVENT_STREAM_EXPORTS_H */ 29 | -------------------------------------------------------------------------------- /include/aws/event-stream/event_stream_rpc.h: -------------------------------------------------------------------------------- 1 | #ifndef AWS_EVENT_STREAM_RPC_H 2 | #define AWS_EVENT_STREAM_RPC_H 3 | /** 4 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0. 6 | */ 7 | #include 8 | 9 | AWS_PUSH_SANE_WARNING_LEVEL 10 | 11 | /** 12 | * :message-type header name 13 | */ 14 | extern AWS_EVENT_STREAM_API const struct aws_byte_cursor aws_event_stream_rpc_message_type_name; 15 | /** 16 | * :message-flags header name 17 | */ 18 | extern AWS_EVENT_STREAM_API const struct aws_byte_cursor aws_event_stream_rpc_message_flags_name; 19 | /** 20 | * :stream-id header name 21 | */ 22 | extern AWS_EVENT_STREAM_API const struct aws_byte_cursor aws_event_stream_rpc_stream_id_name; 23 | /** 24 | * operation header name. 25 | */ 26 | extern AWS_EVENT_STREAM_API const struct aws_byte_cursor aws_event_stream_rpc_operation_name; 27 | 28 | enum aws_event_stream_rpc_message_type { 29 | AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_MESSAGE, 30 | AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_APPLICATION_ERROR, 31 | AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_PING, 32 | AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_PING_RESPONSE, 33 | AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_CONNECT, 34 | AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_CONNECT_ACK, 35 | AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_PROTOCOL_ERROR, 36 | AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_INTERNAL_ERROR, 37 | 38 | AWS_EVENT_STREAM_RPC_MESSAGE_TYPE_COUNT, 39 | }; 40 | 41 | enum aws_event_stream_rpc_message_flag { 42 | AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_CONNECTION_ACCEPTED = 1, 43 | AWS_EVENT_STREAM_RPC_MESSAGE_FLAG_TERMINATE_STREAM = 2, 44 | }; 45 | 46 | struct aws_event_stream_rpc_message_args { 47 | /** array of headers for an event-stream message. */ 48 | struct aws_event_stream_header_value_pair *headers; 49 | /** number of headers in the headers array. 50 | * headers are copied in aws_event_stream_rpc_*_send_message() 51 | * so you can free the memory immediately after calling it if you need to.*/ 52 | size_t headers_count; 53 | /** payload buffer for the message, payload is copied in aws_event_stream_rpc_*_send_message() 54 | * so you can free the memory immediately after calling it if you need to. */ 55 | struct aws_byte_buf *payload; 56 | /** message type for the message. This will be added to the headers array 57 | * and the ":message-type" header should not be included in headers */ 58 | enum aws_event_stream_rpc_message_type message_type; 59 | /** message flags for the message. This will be added to the headers array 60 | * and the ":message-flags" header should not be included in headers */ 61 | uint32_t message_flags; 62 | }; 63 | AWS_POP_SANE_WARNING_LEVEL 64 | 65 | #endif /* AWS_EVENT_STREAM_RPC_SERVER_H */ 66 | -------------------------------------------------------------------------------- /include/aws/event-stream/event_stream_rpc_client.h: -------------------------------------------------------------------------------- 1 | #ifndef AWS_EVENT_STREAM_RPC_CLIENT_H 2 | #define AWS_EVENT_STREAM_RPC_CLIENT_H 3 | /** 4 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0. 6 | */ 7 | 8 | #include 9 | 10 | AWS_PUSH_SANE_WARNING_LEVEL 11 | 12 | struct aws_channel; 13 | struct aws_event_stream_rpc_client_connection; 14 | struct aws_event_stream_rpc_client_continuation_token; 15 | 16 | /** 17 | * Invoked when a connection receives a message on an existing stream. message_args contains the 18 | * message data. 19 | */ 20 | typedef void(aws_event_stream_rpc_client_stream_continuation_fn)( 21 | struct aws_event_stream_rpc_client_continuation_token *token, 22 | const struct aws_event_stream_rpc_message_args *message_args, 23 | void *user_data); 24 | 25 | /** 26 | * Invoked when a continuation has either been closed with the TERMINATE_STREAM flag, or when the connection 27 | * shuts down and deletes the continuation. 28 | */ 29 | typedef void(aws_event_stream_rpc_client_stream_continuation_closed_fn)( 30 | struct aws_event_stream_rpc_client_continuation_token *token, 31 | void *user_data); 32 | 33 | struct aws_event_stream_rpc_client_stream_continuation_options { 34 | aws_event_stream_rpc_client_stream_continuation_fn *on_continuation; 35 | aws_event_stream_rpc_client_stream_continuation_closed_fn *on_continuation_closed; 36 | void *user_data; 37 | }; 38 | 39 | /** 40 | * Invoked when a non-stream level message is received on a connection. 41 | */ 42 | typedef void(aws_event_stream_rpc_client_connection_protocol_message_fn)( 43 | struct aws_event_stream_rpc_client_connection *connection, 44 | const struct aws_event_stream_rpc_message_args *message_args, 45 | void *user_data); 46 | 47 | /** 48 | * Invoked when a successfully created connection is shutdown. error_code will indicate the reason for the shutdown. 49 | */ 50 | typedef void(aws_event_stream_rpc_client_on_connection_shutdown_fn)( 51 | struct aws_event_stream_rpc_client_connection *connection, 52 | int error_code, 53 | void *user_data); 54 | 55 | /** 56 | * Invoked when a connection attempt completes. 57 | * 58 | * If the attempt was unsuccessful, the error_code will be non-zero and the connection pointer will be NULL, 59 | * and aws_event_stream_rpc_client_on_connection_shutdown_fn will not be invoked. 60 | * 61 | * If the attempt was successful, error_code will be 0 and the connection pointer will be valid. 62 | * You must call aws_event_stream_rpc_client_connection_acquire() 63 | * to prevent the pointer's memory from being destroyed before you are ready. 64 | * When you are completely done with the connection pointer you must call 65 | * aws_event_stream_rpc_client_connection_release() or its memory will leak. 66 | * aws_event_stream_rpc_client_on_connection_shutdown_fn will be invoked 67 | * when the network connection has closed. If you are done with the connection, 68 | * but it is still open, you must call aws_aws_event_stream_rpc_client_close() 69 | * or network connection will remain open, even if you call release(). 70 | */ 71 | typedef void(aws_event_stream_rpc_client_on_connection_setup_fn)( 72 | struct aws_event_stream_rpc_client_connection *connection, 73 | int error_code, 74 | void *user_data); 75 | 76 | /** 77 | * Invoked whenever a message has been flushed to the channel. 78 | */ 79 | typedef void(aws_event_stream_rpc_client_message_flush_fn)(int error_code, void *user_data); 80 | 81 | struct aws_client_bootstrap; 82 | 83 | struct aws_event_stream_rpc_client_connection_options { 84 | /** host name to use for the connection. This depends on your socket type. */ 85 | const char *host_name; 86 | /** port to use for your connection, assuming for the appropriate socket type. */ 87 | uint32_t port; 88 | /** socket options for establishing the connection to the RPC server. */ 89 | const struct aws_socket_options *socket_options; 90 | /** optional: tls options for using when establishing your connection. */ 91 | const struct aws_tls_connection_options *tls_options; 92 | struct aws_client_bootstrap *bootstrap; 93 | aws_event_stream_rpc_client_on_connection_setup_fn *on_connection_setup; 94 | aws_event_stream_rpc_client_connection_protocol_message_fn *on_connection_protocol_message; 95 | aws_event_stream_rpc_client_on_connection_shutdown_fn *on_connection_shutdown; 96 | void *user_data; 97 | }; 98 | 99 | AWS_EXTERN_C_BEGIN 100 | 101 | /** 102 | * Initiate a new connection. If this function returns AWS_OP_SUCESSS, the 103 | * aws_event_stream_rpc_client_connection_options::on_connection_setup is guaranteed to be called exactly once. If that 104 | * callback successfully creates a connection, aws_event_stream_rpc_client_connection_options::on_connection_shutdown 105 | * will be invoked upon connection closure. However if the connection was never successfully setup, 106 | * aws_event_stream_rpc_client_connection_options::on_connection_shutdown will not be invoked later. 107 | */ 108 | AWS_EVENT_STREAM_API int aws_event_stream_rpc_client_connection_connect( 109 | struct aws_allocator *allocator, 110 | const struct aws_event_stream_rpc_client_connection_options *conn_options); 111 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_client_connection_acquire( 112 | const struct aws_event_stream_rpc_client_connection *connection); 113 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_client_connection_release( 114 | const struct aws_event_stream_rpc_client_connection *connection); 115 | 116 | /** 117 | * Closes the connection if it is open and aws_event_stream_rpc_client_connection_options::on_connection_shutdown will 118 | * be invoked upon shutdown. shutdown_error_code will indicate the reason for shutdown. For a graceful shutdown pass 0 119 | * or AWS_ERROR_SUCCESS. 120 | */ 121 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_client_connection_close( 122 | struct aws_event_stream_rpc_client_connection *connection, 123 | int shutdown_error_code); 124 | 125 | /** 126 | * Returns true if the connection is open, false otherwise. 127 | */ 128 | AWS_EVENT_STREAM_API bool aws_event_stream_rpc_client_connection_is_open( 129 | const struct aws_event_stream_rpc_client_connection *connection); 130 | 131 | /** 132 | * Sends a message on the connection. These must be connection level messages (not application messages). 133 | * 134 | * flush_fn will be invoked when the message has been successfully writen to the wire or when it fails. 135 | * 136 | * returns AWS_OP_SUCCESS if the message was successfully created and queued, and in that case flush_fn will always be 137 | * invoked. Otherwise, flush_fn will not be invoked. 138 | */ 139 | AWS_EVENT_STREAM_API int aws_event_stream_rpc_client_connection_send_protocol_message( 140 | struct aws_event_stream_rpc_client_connection *connection, 141 | const struct aws_event_stream_rpc_message_args *message_args, 142 | aws_event_stream_rpc_client_message_flush_fn *flush_fn, 143 | void *user_data); 144 | 145 | /** 146 | * Create a new stream. continuation_option's callbacks will not be invoked, and nothing will be sent across the wire 147 | * until aws_event_stream_rpc_client_continuation_activate() is invoked. 148 | * 149 | * returns an instance of a aws_event_stream_rpc_client_continuation_token on success with a reference count of 1. You 150 | * must call aws_event_stream_rpc_client_continuation_release() when you're finished with it. Returns NULL on failure. 151 | */ 152 | AWS_EVENT_STREAM_API struct aws_event_stream_rpc_client_continuation_token * 153 | aws_event_stream_rpc_client_connection_new_stream( 154 | struct aws_event_stream_rpc_client_connection *connection, 155 | const struct aws_event_stream_rpc_client_stream_continuation_options *continuation_options); 156 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_client_continuation_acquire( 157 | const struct aws_event_stream_rpc_client_continuation_token *continuation); 158 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_client_continuation_release( 159 | const struct aws_event_stream_rpc_client_continuation_token *continuation); 160 | 161 | /** 162 | * returns true if the continuation has been closed. 163 | */ 164 | AWS_EVENT_STREAM_API bool aws_event_stream_rpc_client_continuation_is_closed( 165 | const struct aws_event_stream_rpc_client_continuation_token *continuation); 166 | 167 | /** 168 | * Actually sends the initial stream to the peer. Callbacks from aws_event_stream_rpc_client_connection_new_stream() 169 | * will actually be invoked if this function returns AWS_OP_SUCCESS, otherwise, the stream has not been queued and no 170 | * callbacks will be invoked. 171 | * 172 | * operation_name is the name to identify which logical rpc call you want to kick off with the peer. It must be 173 | * non-empty. flush_fn will be invoked once the message has either been written to the wire or it fails. 174 | */ 175 | AWS_EVENT_STREAM_API int aws_event_stream_rpc_client_continuation_activate( 176 | struct aws_event_stream_rpc_client_continuation_token *continuation, 177 | struct aws_byte_cursor operation_name, 178 | const struct aws_event_stream_rpc_message_args *message_args, 179 | aws_event_stream_rpc_client_message_flush_fn *flush_fn, 180 | void *user_data); 181 | 182 | AWS_EVENT_STREAM_API void *aws_event_stream_rpc_client_continuation_get_user_data( 183 | struct aws_event_stream_rpc_client_continuation_token *continuation); 184 | 185 | /** 186 | * Sends a message on the continuation. aws_event_stream_rpc_client_continuation_activate() must be successfully invoked 187 | * prior to calling this function. 188 | * 189 | * If this function returns AWS_OP_SUCCESS, flush_fn will be invoked once the message has either been written to the 190 | * wire or it fails. 191 | */ 192 | AWS_EVENT_STREAM_API int aws_event_stream_rpc_client_continuation_send_message( 193 | struct aws_event_stream_rpc_client_continuation_token *continuation, 194 | const struct aws_event_stream_rpc_message_args *message_args, 195 | aws_event_stream_rpc_client_message_flush_fn *flush_fn, 196 | void *user_data); 197 | 198 | AWS_EXTERN_C_END 199 | AWS_POP_SANE_WARNING_LEVEL 200 | 201 | #endif /* AWS_EVENT_STREAM_RPC_CLIENT_H */ 202 | -------------------------------------------------------------------------------- /include/aws/event-stream/event_stream_rpc_server.h: -------------------------------------------------------------------------------- 1 | #ifndef AWS_EVENT_STREAM_RPC_SERVER_H 2 | #define AWS_EVENT_STREAM_RPC_SERVER_H 3 | /** 4 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0. 6 | */ 7 | 8 | #include 9 | 10 | AWS_PUSH_SANE_WARNING_LEVEL 11 | 12 | struct aws_channel; 13 | struct aws_event_stream_rpc_server_connection; 14 | 15 | struct aws_event_stream_rpc_server_continuation_token; 16 | 17 | /** 18 | * Invoked when a connection receives a message on an existing stream. message_args contains the 19 | * message data. 20 | */ 21 | typedef void(aws_event_stream_rpc_server_stream_continuation_fn)( 22 | struct aws_event_stream_rpc_server_continuation_token *token, 23 | const struct aws_event_stream_rpc_message_args *message_args, 24 | void *user_data); 25 | 26 | /** 27 | * Invoked when a continuation has either been closed with the TERMINATE_STREAM flag, or when the connection 28 | * shutsdown and deletes the continuation. 29 | */ 30 | typedef void(aws_event_stream_rpc_server_stream_continuation_closed_fn)( 31 | struct aws_event_stream_rpc_server_continuation_token *token, 32 | void *user_data); 33 | 34 | struct aws_event_stream_rpc_server_stream_continuation_options { 35 | aws_event_stream_rpc_server_stream_continuation_fn *on_continuation; 36 | aws_event_stream_rpc_server_stream_continuation_closed_fn *on_continuation_closed; 37 | void *user_data; 38 | }; 39 | 40 | /** 41 | * Invoked when a non-stream level message is received on a connection. 42 | */ 43 | typedef void(aws_event_stream_rpc_server_connection_protocol_message_fn)( 44 | struct aws_event_stream_rpc_server_connection *connection, 45 | const struct aws_event_stream_rpc_message_args *message_args, 46 | void *user_data); 47 | 48 | /** 49 | * Invoked when a new stream has been received on the connection. If you return AWS_OP_SUCCESS (0), 50 | * You must fill in the fields for continuation options or the program will assert and exit. 51 | * 52 | * A failure path MUST leave the ref count of the continuation alone. 53 | * 54 | * A success path should probably take a ref which will leave the continuation (assuming no other interference) 55 | * at two AFTER creation is complete: 1 for the connection's continuation table, and one for the callback 56 | * recipient which is presumably tracking it as well. 57 | */ 58 | typedef int(aws_event_stream_rpc_server_on_incoming_stream_fn)( 59 | struct aws_event_stream_rpc_server_connection *connection, 60 | struct aws_event_stream_rpc_server_continuation_token *token, 61 | struct aws_byte_cursor operation_name, 62 | struct aws_event_stream_rpc_server_stream_continuation_options *continuation_options, 63 | void *user_data); 64 | 65 | struct aws_event_stream_rpc_connection_options { 66 | aws_event_stream_rpc_server_on_incoming_stream_fn *on_incoming_stream; 67 | aws_event_stream_rpc_server_connection_protocol_message_fn *on_connection_protocol_message; 68 | void *user_data; 69 | }; 70 | 71 | /** 72 | * Invoked when a new connection is received on a server listener. If you return AWS_OP_SUCCESS, 73 | * You must fill in the fields for connection_options or the program will assert and exit. 74 | * 75 | * If error_code is non-zero, an error occurred upon setting up the channel and connection will be NULL. Otherwise, 76 | * connection is non-null. If you intend to seat a pointer to connection, you MUST call 77 | * aws_event_stream_rpc_server_connection_acquire() and when you're finished with the connection you MUST call 78 | * aws_event_stream_server_connection_release(). 79 | */ 80 | typedef int(aws_event_stream_rpc_server_on_new_connection_fn)( 81 | struct aws_event_stream_rpc_server_connection *connection, 82 | int error_code, 83 | struct aws_event_stream_rpc_connection_options *connection_options, 84 | void *user_data); 85 | 86 | /** 87 | * Invoked when a successfully created connection is shutdown. error_code will indicate the reason for the shutdown. 88 | */ 89 | typedef void(aws_event_stream_rpc_server_on_connection_shutdown_fn)( 90 | struct aws_event_stream_rpc_server_connection *connection, 91 | int error_code, 92 | void *user_data); 93 | 94 | /** 95 | * Invoked whenever a message has been flushed to the channel. 96 | */ 97 | typedef void(aws_event_stream_rpc_server_message_flush_fn)(int error_code, void *user_data); 98 | 99 | struct aws_server_bootstrap; 100 | struct aws_event_stream_rpc_server_listener; 101 | 102 | /** 103 | * (Optional). Invoked when the listener has been successfully shutdown (after the last ref count release). 104 | */ 105 | typedef void(aws_event_stream_rpc_server_on_listener_destroy_fn)( 106 | struct aws_event_stream_rpc_server_listener *server, 107 | void *user_data); 108 | 109 | struct aws_event_stream_rpc_server_listener_options { 110 | /** host name to use for the listener. This depends on your socket type. */ 111 | const char *host_name; 112 | /** port to use for your listener, assuming for the appropriate socket type. */ 113 | uint32_t port; 114 | const struct aws_socket_options *socket_options; 115 | /** optional: tls options for using when setting up your server. */ 116 | const struct aws_tls_connection_options *tls_options; 117 | struct aws_server_bootstrap *bootstrap; 118 | aws_event_stream_rpc_server_on_new_connection_fn *on_new_connection; 119 | aws_event_stream_rpc_server_on_connection_shutdown_fn *on_connection_shutdown; 120 | aws_event_stream_rpc_server_on_listener_destroy_fn *on_destroy_callback; 121 | void *user_data; 122 | }; 123 | 124 | AWS_EXTERN_C_BEGIN 125 | 126 | /** 127 | * Creates a listener with a ref count of 1. You are responsible for calling 128 | * aws_event_stream_rpc_server_listener_release() when you're finished with the listener. Returns NULL if an error 129 | * occurs. 130 | */ 131 | AWS_EVENT_STREAM_API struct aws_event_stream_rpc_server_listener *aws_event_stream_rpc_server_new_listener( 132 | struct aws_allocator *allocator, 133 | struct aws_event_stream_rpc_server_listener_options *options); 134 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_server_listener_acquire( 135 | struct aws_event_stream_rpc_server_listener *listener); 136 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_server_listener_release( 137 | struct aws_event_stream_rpc_server_listener *listener); 138 | 139 | /** 140 | * Get the local port which the listener's socket is bound to. 141 | */ 142 | AWS_EVENT_STREAM_API 143 | uint32_t aws_event_stream_rpc_server_listener_get_bound_port( 144 | const struct aws_event_stream_rpc_server_listener *listener); 145 | 146 | /** 147 | * Bypasses server, and creates a connection on an already existing channel. No connection lifetime callbacks will be 148 | * invoked on the returned connection. Returns NULL if an error occurs. If and only if, you use this API, the returned 149 | * connection is already ref counted and you must call aws_event_stream_rpc_server_connection_release() even if you did 150 | * not explictly call aws_event_stream_rpc_server_connection_acquire() 151 | */ 152 | AWS_EVENT_STREAM_API struct aws_event_stream_rpc_server_connection * 153 | aws_event_stream_rpc_server_connection_from_existing_channel( 154 | struct aws_event_stream_rpc_server_listener *server, 155 | struct aws_channel *channel, 156 | const struct aws_event_stream_rpc_connection_options *connection_options); 157 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_server_connection_acquire( 158 | struct aws_event_stream_rpc_server_connection *connection); 159 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_server_connection_release( 160 | struct aws_event_stream_rpc_server_connection *connection); 161 | 162 | AWS_EVENT_STREAM_API void *aws_event_stream_rpc_server_connection_get_user_data( 163 | struct aws_event_stream_rpc_server_connection *connection); 164 | /** 165 | * returns true if the connection is open. False otherwise. 166 | */ 167 | AWS_EVENT_STREAM_API bool aws_event_stream_rpc_server_connection_is_open( 168 | struct aws_event_stream_rpc_server_connection *connection); 169 | 170 | /** 171 | * Closes the connection (including all continuations on the connection), and releases the connection ref count. 172 | * shutdown_error_code is the error code to use when shutting down the channel. Use AWS_ERROR_SUCCESS for non-error 173 | * cases. 174 | */ 175 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_server_connection_close( 176 | struct aws_event_stream_rpc_server_connection *connection, 177 | int shutdown_error_code); 178 | 179 | /** 180 | * Sends a protocol message on the connection (not application data). If the message is valid and successfully written 181 | * to the channel, flush_fn will be invoked. 182 | */ 183 | AWS_EVENT_STREAM_API int aws_event_stream_rpc_server_connection_send_protocol_message( 184 | struct aws_event_stream_rpc_server_connection *connection, 185 | const struct aws_event_stream_rpc_message_args *message_args, 186 | aws_event_stream_rpc_server_message_flush_fn *flush_fn, 187 | void *user_data); 188 | 189 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_server_continuation_acquire( 190 | struct aws_event_stream_rpc_server_continuation_token *continuation); 191 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_server_continuation_release( 192 | struct aws_event_stream_rpc_server_continuation_token *continuation); 193 | 194 | /** 195 | * returns true if the continuation is still in an open state. 196 | */ 197 | AWS_EVENT_STREAM_API bool aws_event_stream_rpc_server_continuation_is_closed( 198 | struct aws_event_stream_rpc_server_continuation_token *continuation); 199 | 200 | /** 201 | * Sends an application message on the continuation. If the message is valid and successfully written 202 | * to the channel, flush_fn will be invoked. 203 | */ 204 | AWS_EVENT_STREAM_API int aws_event_stream_rpc_server_continuation_send_message( 205 | struct aws_event_stream_rpc_server_continuation_token *continuation, 206 | const struct aws_event_stream_rpc_message_args *message_args, 207 | aws_event_stream_rpc_server_message_flush_fn *flush_fn, 208 | void *user_data); 209 | 210 | AWS_EXTERN_C_END 211 | AWS_POP_SANE_WARNING_LEVEL 212 | 213 | #endif /* AWS_EVENT_STREAM_RPC_SERVER_H */ 214 | -------------------------------------------------------------------------------- /include/aws/event-stream/private/event_stream_rpc_priv.h: -------------------------------------------------------------------------------- 1 | #ifndef AWS_EVENT_STREAM_RPC_PRIV_H 2 | #define AWS_EVENT_STREAM_RPC_PRIV_H 3 | /** 4 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0. 6 | */ 7 | 8 | #include 9 | 10 | enum aws_event_stream_connection_handshake_state { 11 | CONNECTION_HANDSHAKE_STATE_INITIALIZED = 0u, 12 | CONNECTION_HANDSHAKE_STATE_CONNECT_PROCESSED = 1u, 13 | CONNECTION_HANDSHAKE_STATE_CONNECT_ACK_PROCESSED = 2u, 14 | }; 15 | 16 | int aws_event_stream_rpc_extract_message_metadata( 17 | const struct aws_array_list *message_headers, 18 | int32_t *stream_id, 19 | int32_t *message_type, 20 | int32_t *message_flags, 21 | struct aws_byte_buf *operation_name); 22 | 23 | uint64_t aws_event_stream_rpc_hash_streamid(const void *to_hash); 24 | bool aws_event_stream_rpc_streamid_eq(const void *a, const void *b); 25 | 26 | static const struct aws_byte_cursor s_json_content_type_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(":content-type"); 27 | static const struct aws_byte_cursor s_json_content_type_value = 28 | AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("application/json"); 29 | 30 | static const struct aws_byte_cursor s_invalid_stream_id_error = 31 | AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("{ \"message\": \"non-zero stream-id field is only allowed for messages of " 32 | "type APPLICATION_MESSAGE. The stream id max value is INT32_MAX.\" }"); 33 | 34 | static const struct aws_byte_cursor s_invalid_client_stream_id_error = 35 | AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("{ \"message\": \"stream-id values must be monotonically incrementing. A " 36 | "stream-id arrived that was lower than the last seen stream-id.\" }"); 37 | 38 | static const struct aws_byte_cursor s_invalid_new_client_stream_id_error = 39 | AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("{ \"message\": \"stream-id values must be monotonically incrementing. A new " 40 | "stream-id arrived that was incremented by more than 1.\" }"); 41 | 42 | static const struct aws_byte_cursor s_invalid_message_type_error = 43 | AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("{ \"message\": \"an invalid value for message-type field was received.\" }"); 44 | 45 | static const struct aws_byte_cursor s_invalid_message_error = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( 46 | "{ \"message\": \"A message was received with missing required fields. Check that your client is sending at least, " 47 | ":message-type, :message-flags, and :stream-id\" }"); 48 | 49 | static const struct aws_byte_cursor s_internal_error = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( 50 | "{ \"message\": \"An error occurred on the peer endpoint. This is not likely caused by your endpoint.\" }"); 51 | 52 | static const struct aws_byte_cursor s_connect_not_completed_error = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( 53 | "{ \"message\": \"A CONNECT message must be received, and the CONNECT_ACK must be sent in response, before any " 54 | "other message-types can be sent on this connection. In addition, only one CONNECT message is allowed on a " 55 | "connection.\" }"); 56 | 57 | #endif /* #define AWS_EVENT_STREAM_RPC_PRIV_H */ 58 | -------------------------------------------------------------------------------- /include/aws/event-stream/private/event_stream_rpc_test_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef AWS_EVENT_STREAM_RPC_TEST_HELPER_H 2 | #define AWS_EVENT_STREAM_RPC_TEST_HELPER_H 3 | /** 4 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0. 6 | */ 7 | 8 | #include 9 | 10 | #ifndef AWS_UNSTABLE_TESTING_API 11 | # error The functions in this header file are for testing purposes only! 12 | #endif 13 | 14 | AWS_EXTERN_C_BEGIN 15 | 16 | /** This is for testing edge cases around stream id exhaustion. Don't ever include this file outside of a unit test. */ 17 | AWS_EVENT_STREAM_API void aws_event_stream_rpc_server_override_last_stream_id( 18 | struct aws_event_stream_rpc_server_connection *connection, 19 | int32_t value); 20 | 21 | AWS_EXTERN_C_END 22 | 23 | #endif /* AWS_EVENT_STREAM_RPC_TEST_HELPER_H */ 24 | -------------------------------------------------------------------------------- /source/event_stream_channel_handler.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | 25 | static const size_t s_default_payload_size = 1024; 26 | 27 | /* an event stream message has overhead of 28 | * [msg len (uint32_t)] 29 | * [headers len (uint32_t)] 30 | * [prelude crc (uint32_t)] 31 | * ... headers and payload .... 32 | * [message crc (uint32_t)] 33 | */ 34 | static const size_t s_message_overhead_size = AWS_EVENT_STREAM_PRELUDE_LENGTH + AWS_EVENT_STREAM_TRAILER_LENGTH; 35 | 36 | struct aws_event_stream_channel_handler { 37 | struct aws_channel_handler handler; 38 | struct aws_byte_buf message_buf; 39 | uint32_t running_crc; 40 | uint32_t current_message_len; 41 | aws_event_stream_channel_handler_on_message_received_fn *on_message_received; 42 | void *user_data; 43 | size_t initial_window_size; 44 | bool manual_window_management; 45 | }; 46 | 47 | static int s_process_read_message( 48 | struct aws_channel_handler *handler, 49 | struct aws_channel_slot *slot, 50 | struct aws_io_message *message) { 51 | 52 | AWS_LOGF_TRACE( 53 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 54 | "id=%p: received message of size %zu", 55 | (void *)handler, 56 | message->message_data.len); 57 | struct aws_event_stream_channel_handler *event_stream_handler = handler->impl; 58 | 59 | struct aws_byte_cursor message_cursor = aws_byte_cursor_from_buf(&message->message_data); 60 | 61 | int error_code = AWS_ERROR_SUCCESS; 62 | while (message_cursor.len) { 63 | AWS_LOGF_TRACE( 64 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 65 | "id=%p: processing chunk of size %zu", 66 | (void *)handler, 67 | message_cursor.len); 68 | 69 | /* first read only the prelude so we can do checks before reading the entire buffer. */ 70 | if (event_stream_handler->message_buf.len < AWS_EVENT_STREAM_PRELUDE_LENGTH) { 71 | size_t remaining_prelude = AWS_EVENT_STREAM_PRELUDE_LENGTH - event_stream_handler->message_buf.len; 72 | size_t to_copy = aws_min_size(message_cursor.len, remaining_prelude); 73 | AWS_LOGF_TRACE( 74 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 75 | "id=%p: processing prelude, %zu bytes of an expected 12.", 76 | (void *)handler, 77 | to_copy); 78 | 79 | if (!aws_byte_buf_write(&event_stream_handler->message_buf, message_cursor.ptr, to_copy)) { 80 | error_code = aws_last_error(); 81 | AWS_LOGF_ERROR( 82 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 83 | "id=%p: writing to prelude buffer failed with error %s", 84 | (void *)handler, 85 | aws_error_debug_str(error_code)); 86 | goto finished; 87 | } 88 | 89 | aws_byte_cursor_advance(&message_cursor, to_copy); 90 | } 91 | 92 | /* we need to get the prelude so we can get the message length to know how much to read and also 93 | * to check the prelude CRC to protect against bit-flips causing us to read to much memory */ 94 | if (event_stream_handler->message_buf.len == AWS_EVENT_STREAM_PRELUDE_LENGTH) { 95 | AWS_LOGF_TRACE(AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, "id=%p: processing prelude buffer", (void *)handler); 96 | 97 | struct aws_byte_cursor prelude_cursor = aws_byte_cursor_from_buf(&event_stream_handler->message_buf); 98 | 99 | event_stream_handler->running_crc = 100 | aws_checksums_crc32(prelude_cursor.ptr, sizeof(uint32_t) + sizeof(uint32_t), 0); 101 | AWS_LOGF_DEBUG( 102 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 103 | "id=%p: calculated prelude CRC of %" PRIu32, 104 | (void *)handler, 105 | event_stream_handler->running_crc); 106 | 107 | aws_byte_cursor_read_be32(&prelude_cursor, &event_stream_handler->current_message_len); 108 | 109 | AWS_LOGF_DEBUG( 110 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 111 | "id=%p: read total message length of %" PRIu32, 112 | (void *)handler, 113 | event_stream_handler->current_message_len); 114 | if (event_stream_handler->current_message_len > AWS_EVENT_STREAM_MAX_MESSAGE_SIZE) { 115 | AWS_LOGF_ERROR( 116 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 117 | "id=%p: message length of %" PRIu32 " exceeds the max size of %zu", 118 | (void *)handler, 119 | event_stream_handler->current_message_len, 120 | (size_t)AWS_EVENT_STREAM_MAX_MESSAGE_SIZE); 121 | aws_raise_error(AWS_ERROR_EVENT_STREAM_MESSAGE_FIELD_SIZE_EXCEEDED); 122 | error_code = aws_last_error(); 123 | goto finished; 124 | } 125 | 126 | /* advance past the headers field since we don't really care about it at this point */ 127 | aws_byte_cursor_advance(&prelude_cursor, sizeof(uint32_t)); 128 | 129 | uint32_t prelude_crc = 0; 130 | aws_byte_cursor_read_be32(&prelude_cursor, &prelude_crc); 131 | AWS_LOGF_DEBUG( 132 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 133 | "id=%p: read prelude CRC of %" PRIu32, 134 | (void *)handler, 135 | prelude_crc); 136 | 137 | /* make sure the checksum matches before processing any further */ 138 | if (event_stream_handler->running_crc != prelude_crc) { 139 | AWS_LOGF_ERROR( 140 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 141 | "id=%p: prelude CRC mismatch. calculated %" PRIu32 " but the crc for the message was %" PRIu32, 142 | (void *)handler, 143 | event_stream_handler->running_crc, 144 | prelude_crc); 145 | aws_raise_error(AWS_ERROR_EVENT_STREAM_PRELUDE_CHECKSUM_FAILURE); 146 | error_code = aws_last_error(); 147 | goto finished; 148 | } 149 | } 150 | 151 | /* read whatever is remaining from the message */ 152 | if (event_stream_handler->message_buf.len < event_stream_handler->current_message_len) { 153 | AWS_LOGF_TRACE( 154 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, "id=%p: processing remaining message buffer", (void *)handler); 155 | size_t remaining = event_stream_handler->current_message_len - event_stream_handler->message_buf.len; 156 | size_t to_copy = aws_min_size(message_cursor.len, remaining); 157 | AWS_LOGF_TRACE( 158 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 159 | "id=%p: of the remaining %zu, processing %zu from the " 160 | "current message.", 161 | (void *)handler, 162 | remaining, 163 | to_copy); 164 | 165 | struct aws_byte_cursor to_append = aws_byte_cursor_advance(&message_cursor, to_copy); 166 | if (aws_byte_buf_append_dynamic(&event_stream_handler->message_buf, &to_append)) { 167 | error_code = aws_last_error(); 168 | AWS_LOGF_ERROR( 169 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 170 | "id=%p: Appending to the message buffer failed with error %s.", 171 | (void *)handler, 172 | aws_error_debug_str(error_code)); 173 | 174 | goto finished; 175 | } 176 | } 177 | 178 | /* If we read the entire message, parse it and give it back to the subscriber. Keep in mind, once we're to this 179 | * point the aws_event_stream API handles the rest of the message parsing and validation. */ 180 | if (event_stream_handler->message_buf.len == event_stream_handler->current_message_len) { 181 | AWS_LOGF_TRACE( 182 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 183 | "id=%p: An entire message has been read. Parsing the message now.", 184 | (void *)handler); 185 | struct aws_event_stream_message received_message; 186 | AWS_ZERO_STRUCT(received_message); 187 | 188 | if (aws_event_stream_message_from_buffer( 189 | &received_message, event_stream_handler->handler.alloc, &event_stream_handler->message_buf)) { 190 | error_code = aws_last_error(); 191 | AWS_LOGF_ERROR( 192 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 193 | "id=%p: Parsing the message failed with error %s.", 194 | (void *)handler, 195 | aws_error_debug_str(error_code)); 196 | goto finished; 197 | } 198 | 199 | size_t message_size = event_stream_handler->message_buf.len; 200 | AWS_LOGF_TRACE( 201 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, "id=%p: Invoking on_message_received callback.", (void *)handler); 202 | event_stream_handler->on_message_received( 203 | &received_message, AWS_ERROR_SUCCESS, event_stream_handler->user_data); 204 | aws_event_stream_message_clean_up(&received_message); 205 | event_stream_handler->current_message_len = 0; 206 | event_stream_handler->running_crc = 0; 207 | aws_byte_buf_reset(&event_stream_handler->message_buf, true); 208 | 209 | if (!event_stream_handler->manual_window_management) { 210 | aws_channel_slot_increment_read_window(slot, message_size); 211 | } 212 | } 213 | } 214 | 215 | finished: 216 | if (error_code) { 217 | event_stream_handler->on_message_received(NULL, error_code, event_stream_handler->user_data); 218 | aws_channel_shutdown(slot->channel, error_code); 219 | } 220 | aws_mem_release(message->allocator, message); 221 | return AWS_OP_SUCCESS; 222 | } 223 | 224 | struct message_write_data { 225 | struct aws_allocator *allocator; 226 | struct aws_channel_task task; 227 | struct aws_event_stream_channel_handler *handler; 228 | struct aws_event_stream_message *message; 229 | aws_event_stream_channel_handler_on_message_written_fn *on_message_written; 230 | void *user_data; 231 | }; 232 | 233 | static void s_on_message_write_completed_fn( 234 | struct aws_channel *channel, 235 | struct aws_io_message *message, 236 | int err_code, 237 | void *user_data) { 238 | (void)channel; 239 | (void)message; 240 | 241 | struct message_write_data *message_data = user_data; 242 | AWS_LOGF_TRACE( 243 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 244 | "channel=%p: Message write completed. Invoking " 245 | "on_message_written callback.", 246 | (void *)channel); 247 | message_data->on_message_written(message_data->message, err_code, message_data->user_data); 248 | aws_mem_release(message_data->allocator, message_data); 249 | } 250 | 251 | static void s_write_handler_message(struct aws_channel_task *task, void *arg, enum aws_task_status status) { 252 | (void)task; 253 | 254 | struct message_write_data *message_data = arg; 255 | 256 | AWS_LOGF_TRACE(AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, "static: Write message task invoked."); 257 | if (status == AWS_TASK_STATUS_RUN_READY) { 258 | struct aws_event_stream_message *message = message_data->message; 259 | struct aws_event_stream_channel_handler *handler = message_data->handler; 260 | 261 | struct aws_byte_cursor message_cur = aws_byte_cursor_from_array( 262 | aws_event_stream_message_buffer(message), aws_event_stream_message_total_length(message)); 263 | 264 | while (message_cur.len) { 265 | AWS_LOGF_TRACE( 266 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 267 | "id=%p: writing message chunk of size %zu.", 268 | (void *)&handler->handler, 269 | message_cur.len); 270 | 271 | /* io messages from the pool are allowed to be smaller than the requested size. */ 272 | struct aws_io_message *io_message = aws_channel_acquire_message_from_pool( 273 | handler->handler.slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, message_cur.len); 274 | 275 | if (!io_message) { 276 | int error_code = aws_last_error(); 277 | AWS_LOGF_ERROR( 278 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 279 | "id=%p: Error occurred while acquiring io message %s.", 280 | (void *)&handler->handler, 281 | aws_error_debug_str(error_code)); 282 | 283 | message_data->on_message_written(message, error_code, message_data->user_data); 284 | aws_mem_release(message_data->allocator, message_data); 285 | aws_channel_shutdown(handler->handler.slot->channel, error_code); 286 | break; 287 | } 288 | 289 | aws_byte_buf_write_to_capacity(&io_message->message_data, &message_cur); 290 | 291 | /* if that was the end of the buffer we want to write, attach the completion callback to that io message */ 292 | if (message_cur.len == 0) { 293 | AWS_LOGF_TRACE( 294 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 295 | "id=%p: Message completely written to all io buffers.", 296 | (void *)&handler->handler); 297 | io_message->on_completion = s_on_message_write_completed_fn; 298 | io_message->user_data = message_data; 299 | } 300 | 301 | /* note if this fails the io message will not be queued and as a result will not have it's completion 302 | * callback invoked. */ 303 | if (aws_channel_slot_send_message(handler->handler.slot, io_message, AWS_CHANNEL_DIR_WRITE)) { 304 | aws_mem_release(io_message->allocator, io_message); 305 | int error_code = aws_last_error(); 306 | AWS_LOGF_ERROR( 307 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 308 | "id=%p: Error occurred while sending message to channel %s.", 309 | (void *)&handler->handler, 310 | aws_error_debug_str(error_code)); 311 | message_data->on_message_written(message, error_code, message_data->user_data); 312 | aws_mem_release(message_data->allocator, message_data); 313 | aws_channel_shutdown(handler->handler.slot->channel, error_code); 314 | break; 315 | } 316 | AWS_LOGF_TRACE( 317 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, "id=%p: Message sent to channel", (void *)&handler->handler); 318 | } 319 | } else { 320 | AWS_LOGF_WARN(AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, "static: Channel was shutdown. Message not sent"); 321 | message_data->on_message_written( 322 | message_data->message, AWS_ERROR_IO_OPERATION_CANCELLED, message_data->user_data); 323 | aws_mem_release(message_data->allocator, message_data); 324 | } 325 | } 326 | 327 | int aws_event_stream_channel_handler_write_message( 328 | struct aws_channel_handler *channel_handler, 329 | struct aws_event_stream_message *message, 330 | aws_event_stream_channel_handler_on_message_written_fn *on_message_written, 331 | void *user_data) { 332 | AWS_PRECONDITION(channel_handler); 333 | AWS_PRECONDITION(message); 334 | AWS_PRECONDITION(on_message_written); 335 | 336 | struct aws_event_stream_channel_handler *handler = channel_handler->impl; 337 | 338 | struct message_write_data *write_data = 339 | aws_mem_calloc(handler->handler.alloc, 1, sizeof(struct message_write_data)); 340 | 341 | if (!write_data) { 342 | AWS_LOGF_ERROR( 343 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 344 | "id=%p: Error occurred while allocating callback data %s.", 345 | (void *)channel_handler, 346 | aws_error_debug_str(aws_last_error())); 347 | aws_channel_shutdown(channel_handler->slot->channel, aws_last_error()); 348 | return AWS_OP_ERR; 349 | } 350 | 351 | write_data->handler = handler; 352 | write_data->user_data = user_data; 353 | write_data->message = message; 354 | write_data->on_message_written = on_message_written; 355 | write_data->allocator = handler->handler.alloc; 356 | 357 | AWS_LOGF_TRACE( 358 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, "id=%p: Scheduling message write task", (void *)channel_handler); 359 | aws_channel_task_init( 360 | &write_data->task, s_write_handler_message, write_data, "aws_event_stream_channel_handler_write_message"); 361 | aws_channel_schedule_task_now_serialized(handler->handler.slot->channel, &write_data->task); 362 | 363 | return AWS_OP_SUCCESS; 364 | } 365 | 366 | void *aws_event_stream_channel_handler_get_user_data(struct aws_channel_handler *channel_handler) { 367 | struct aws_event_stream_channel_handler *handler = channel_handler->impl; 368 | return handler->user_data; 369 | } 370 | 371 | struct window_update_data { 372 | struct aws_allocator *allocator; 373 | struct aws_channel_task task; 374 | struct aws_event_stream_channel_handler *handler; 375 | size_t window_update_size; 376 | }; 377 | 378 | static void s_update_window_task(struct aws_channel_task *task, void *arg, enum aws_task_status status) { 379 | (void)task; 380 | struct window_update_data *update_data = arg; 381 | 382 | if (status == AWS_TASK_STATUS_RUN_READY) { 383 | AWS_LOGF_DEBUG( 384 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 385 | "static: updating window. increment of %zu", 386 | update_data->window_update_size); 387 | aws_channel_slot_increment_read_window(update_data->handler->handler.slot, update_data->window_update_size); 388 | } 389 | 390 | aws_mem_release(update_data->allocator, update_data); 391 | } 392 | 393 | void aws_event_stream_channel_handler_increment_read_window( 394 | struct aws_channel_handler *channel_handler, 395 | size_t window_update_size) { 396 | AWS_PRECONDITION(channel_handler); 397 | 398 | struct aws_event_stream_channel_handler *handler = channel_handler->impl; 399 | 400 | if (!handler->manual_window_management) { 401 | return; 402 | } 403 | 404 | AWS_LOGF_DEBUG( 405 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 406 | "id=%p: A user requested window update and manual window management is specified. Updating size of %zu", 407 | (void *)channel_handler, 408 | window_update_size); 409 | 410 | if (aws_channel_thread_is_callers_thread(handler->handler.slot->channel)) { 411 | if (aws_channel_slot_increment_read_window(handler->handler.slot, window_update_size)) { 412 | aws_channel_shutdown(handler->handler.slot->channel, aws_last_error()); 413 | return; 414 | } 415 | } 416 | 417 | struct window_update_data *update_data = 418 | aws_mem_calloc(handler->handler.alloc, 1, sizeof(struct window_update_data)); 419 | 420 | if (!update_data) { 421 | AWS_LOGF_ERROR( 422 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 423 | "id=%p: Error occurred while allocating update window data %s.", 424 | (void *)channel_handler, 425 | aws_error_debug_str(aws_last_error())); 426 | aws_channel_shutdown(handler->handler.slot->channel, aws_last_error()); 427 | return; 428 | } 429 | 430 | update_data->allocator = handler->handler.alloc; 431 | update_data->handler = handler; 432 | update_data->window_update_size = window_update_size; 433 | 434 | aws_channel_task_init( 435 | &update_data->task, 436 | s_update_window_task, 437 | update_data, 438 | "aws_event_stream_channel_handler_increment_read_window"); 439 | aws_channel_schedule_task_now(handler->handler.slot->channel, &update_data->task); 440 | } 441 | 442 | static int s_process_write_message( 443 | struct aws_channel_handler *handler, 444 | struct aws_channel_slot *slot, 445 | struct aws_io_message *message) { 446 | (void)handler; 447 | (void)slot; 448 | (void)message; 449 | AWS_FATAL_ASSERT(!"The event-stream-channel-handler is not designed to be a mid-channel handler."); 450 | return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); 451 | } 452 | 453 | static int s_increment_read_window(struct aws_channel_handler *handler, struct aws_channel_slot *slot, size_t size) { 454 | (void)handler; 455 | return aws_channel_slot_increment_read_window(slot, size); 456 | } 457 | 458 | static size_t s_initial_window_size(struct aws_channel_handler *handler) { 459 | struct aws_event_stream_channel_handler *message_handler = handler->impl; 460 | return message_handler->initial_window_size; 461 | } 462 | 463 | static size_t s_message_overhead(struct aws_channel_handler *handler) { 464 | (void)handler; 465 | return s_message_overhead_size; 466 | } 467 | 468 | static void s_destroy(struct aws_channel_handler *handler) { 469 | AWS_LOGF_DEBUG( 470 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 471 | "id=%p: destroying event-stream message channel handler.", 472 | (void *)handler); 473 | struct aws_event_stream_channel_handler *event_stream_handler = handler->impl; 474 | aws_byte_buf_clean_up(&event_stream_handler->message_buf); 475 | aws_mem_release(handler->alloc, event_stream_handler); 476 | } 477 | 478 | static int s_shutdown( 479 | struct aws_channel_handler *handler, 480 | struct aws_channel_slot *slot, 481 | enum aws_channel_direction dir, 482 | int error_code, 483 | bool free_scarce_resources_immediately) { 484 | (void)handler; 485 | AWS_LOGF_DEBUG( 486 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 487 | "id=%p: shutdown called on event-stream channel handler with error %s.", 488 | (void *)handler, 489 | aws_error_debug_str(error_code)); 490 | 491 | return aws_channel_slot_on_handler_shutdown_complete(slot, dir, error_code, free_scarce_resources_immediately); 492 | } 493 | 494 | static struct aws_channel_handler_vtable vtable = { 495 | .destroy = s_destroy, 496 | .increment_read_window = s_increment_read_window, 497 | .initial_window_size = s_initial_window_size, 498 | .process_read_message = s_process_read_message, 499 | .process_write_message = s_process_write_message, 500 | .message_overhead = s_message_overhead, 501 | .shutdown = s_shutdown, 502 | }; 503 | 504 | struct aws_channel_handler *aws_event_stream_channel_handler_new( 505 | struct aws_allocator *allocator, 506 | const struct aws_event_stream_channel_handler_options *handler_options) { 507 | AWS_PRECONDITION(allocator); 508 | AWS_PRECONDITION(handler_options); 509 | AWS_PRECONDITION(handler_options->on_message_received); 510 | 511 | AWS_LOGF_INFO(AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, "static: creating new event-stream message channel handler."); 512 | 513 | struct aws_event_stream_channel_handler *event_stream_handler = 514 | aws_mem_calloc(allocator, 1, sizeof(struct aws_event_stream_channel_handler)); 515 | 516 | if (!event_stream_handler) { 517 | AWS_LOGF_ERROR( 518 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 519 | "static: Error occurred while allocating handler %s.", 520 | aws_error_debug_str(aws_last_error())); 521 | return NULL; 522 | } 523 | 524 | AWS_LOGF_DEBUG(AWS_LS_EVENT_STREAM_RPC_CLIENT, "static: new handler is %p", (void *)&event_stream_handler->handler); 525 | 526 | if (aws_byte_buf_init( 527 | &event_stream_handler->message_buf, allocator, s_default_payload_size + s_message_overhead_size)) { 528 | AWS_LOGF_ERROR( 529 | AWS_LS_EVENT_STREAM_CHANNEL_HANDLER, 530 | "id=%p: Error occurred while allocating scratch buffer %s.", 531 | (void *)&event_stream_handler->handler, 532 | aws_error_debug_str(aws_last_error())); 533 | aws_mem_release(allocator, event_stream_handler); 534 | return NULL; 535 | } 536 | 537 | event_stream_handler->on_message_received = handler_options->on_message_received; 538 | event_stream_handler->user_data = handler_options->user_data; 539 | event_stream_handler->initial_window_size = 540 | handler_options->initial_window_size > 0 ? handler_options->initial_window_size : SIZE_MAX; 541 | event_stream_handler->manual_window_management = handler_options->manual_window_management; 542 | event_stream_handler->handler.vtable = &vtable; 543 | event_stream_handler->handler.alloc = allocator; 544 | event_stream_handler->handler.impl = event_stream_handler; 545 | 546 | return &event_stream_handler->handler; 547 | } 548 | -------------------------------------------------------------------------------- /source/event_stream_rpc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0. 4 | */ 5 | #include 6 | 7 | #include 8 | 9 | const struct aws_byte_cursor aws_event_stream_rpc_message_type_name = 10 | AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(":message-type"); 11 | const struct aws_byte_cursor aws_event_stream_rpc_message_flags_name = 12 | AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(":message-flags"); 13 | const struct aws_byte_cursor aws_event_stream_rpc_stream_id_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(":stream-id"); 14 | const struct aws_byte_cursor aws_event_stream_rpc_operation_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("operation"); 15 | 16 | /* just a convenience function for fetching message metadata from the event stream headers on a single iteration. */ 17 | int aws_event_stream_rpc_extract_message_metadata( 18 | const struct aws_array_list *message_headers, 19 | int32_t *stream_id, 20 | int32_t *message_type, 21 | int32_t *message_flags, 22 | struct aws_byte_buf *operation_name) { 23 | size_t length = aws_array_list_length(message_headers); 24 | bool message_type_found = 0; 25 | bool message_flags_found = 0; 26 | bool stream_id_found = 0; 27 | bool operation_name_found = 0; 28 | 29 | AWS_LOGF_TRACE( 30 | AWS_LS_EVENT_STREAM_GENERAL, "processing message headers for rpc protocol. %zu headers to process.", length); 31 | 32 | for (size_t i = 0; i < length; ++i) { 33 | struct aws_event_stream_header_value_pair *header = NULL; 34 | aws_array_list_get_at_ptr(message_headers, (void **)&header, i); 35 | struct aws_byte_buf name_buf = aws_event_stream_header_name(header); 36 | AWS_LOGF_DEBUG(AWS_LS_EVENT_STREAM_GENERAL, "processing header name " PRInSTR, AWS_BYTE_BUF_PRI(name_buf)); 37 | 38 | /* check type first since that's cheaper than a string compare */ 39 | if (header->header_value_type == AWS_EVENT_STREAM_HEADER_INT32) { 40 | 41 | struct aws_byte_buf stream_id_field = aws_byte_buf_from_array( 42 | aws_event_stream_rpc_stream_id_name.ptr, aws_event_stream_rpc_stream_id_name.len); 43 | if (aws_byte_buf_eq_ignore_case(&name_buf, &stream_id_field)) { 44 | *stream_id = aws_event_stream_header_value_as_int32(header); 45 | AWS_LOGF_DEBUG(AWS_LS_EVENT_STREAM_GENERAL, "stream id header value %" PRId32, *stream_id); 46 | stream_id_found += 1; 47 | goto found; 48 | } 49 | 50 | struct aws_byte_buf message_type_field = aws_byte_buf_from_array( 51 | aws_event_stream_rpc_message_type_name.ptr, aws_event_stream_rpc_message_type_name.len); 52 | if (aws_byte_buf_eq_ignore_case(&name_buf, &message_type_field)) { 53 | *message_type = aws_event_stream_header_value_as_int32(header); 54 | AWS_LOGF_DEBUG(AWS_LS_EVENT_STREAM_GENERAL, "message type header value %" PRId32, *message_type); 55 | message_type_found += 1; 56 | goto found; 57 | } 58 | 59 | struct aws_byte_buf message_flags_field = aws_byte_buf_from_array( 60 | aws_event_stream_rpc_message_flags_name.ptr, aws_event_stream_rpc_message_flags_name.len); 61 | if (aws_byte_buf_eq_ignore_case(&name_buf, &message_flags_field)) { 62 | *message_flags = aws_event_stream_header_value_as_int32(header); 63 | AWS_LOGF_DEBUG(AWS_LS_EVENT_STREAM_GENERAL, "message flags header value %" PRId32, *message_flags); 64 | message_flags_found += 1; 65 | goto found; 66 | } 67 | } 68 | 69 | if (header->header_value_type == AWS_EVENT_STREAM_HEADER_STRING) { 70 | struct aws_byte_buf operation_field = aws_byte_buf_from_array( 71 | aws_event_stream_rpc_operation_name.ptr, aws_event_stream_rpc_operation_name.len); 72 | 73 | if (aws_byte_buf_eq_ignore_case(&name_buf, &operation_field)) { 74 | *operation_name = aws_event_stream_header_value_as_string(header); 75 | AWS_LOGF_DEBUG( 76 | AWS_LS_EVENT_STREAM_GENERAL, 77 | "operation name header value" PRInSTR, 78 | AWS_BYTE_BUF_PRI(*operation_name)); 79 | operation_name_found += 1; 80 | goto found; 81 | } 82 | } 83 | 84 | continue; 85 | 86 | found: 87 | if (message_flags_found && message_type_found && stream_id_found && operation_name_found) { 88 | return AWS_OP_SUCCESS; 89 | } 90 | } 91 | 92 | return message_flags_found && message_type_found && stream_id_found 93 | ? AWS_OP_SUCCESS 94 | : aws_raise_error(AWS_ERROR_EVENT_STREAM_RPC_PROTOCOL_ERROR); 95 | } 96 | 97 | static const uint32_t s_bit_scrambling_magic = 0x45d9f3bU; 98 | static const uint32_t s_bit_shift_magic = 16U; 99 | 100 | /* this is a repurposed hash function based on the technique in splitmix64. The magic number was a result of numerical 101 | * analysis on maximum bit entropy. */ 102 | uint64_t aws_event_stream_rpc_hash_streamid(const void *to_hash) { 103 | uint32_t int_to_hash = *(const uint32_t *)to_hash; 104 | uint32_t hash = ((int_to_hash >> s_bit_shift_magic) ^ int_to_hash) * s_bit_scrambling_magic; 105 | hash = ((hash >> s_bit_shift_magic) ^ hash) * s_bit_scrambling_magic; 106 | hash = (hash >> s_bit_shift_magic) ^ hash; 107 | return (uint64_t)hash; 108 | } 109 | 110 | bool aws_event_stream_rpc_streamid_eq(const void *a, const void *b) { 111 | return *(const uint32_t *)a == *(const uint32_t *)b; 112 | } 113 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | include(AwsTestHarness) 4 | enable_testing() 5 | 6 | file(GLOB TEST_SRC "*.c") 7 | file(GLOB TESTS ${TEST_SRC}) 8 | 9 | set(TEST_BINARY_NAME ${PROJECT_NAME}-tests) 10 | 11 | add_test_case(test_incoming_no_op_valid) 12 | add_test_case(test_incoming_application_data_no_headers_valid) 13 | add_test_case(test_incoming_application_one_compressed_header_pair_valid) 14 | add_test_case(test_incoming_application_int32_header_valid) 15 | 16 | add_test_case(test_outgoing_no_op_valid) 17 | add_test_case(test_outgoing_application_data_no_headers_valid) 18 | add_test_case(test_outgoing_application_one_compressed_header_pair_valid) 19 | 20 | add_test_case(test_streaming_decoder_incoming_no_op_valid_single_message) 21 | add_test_case(test_streaming_decoder_incoming_application_no_headers) 22 | add_test_case(test_streaming_decoder_incoming_application_one_compressed_header_pair_valid) 23 | add_test_case(test_streaming_decoder_incoming_application_one_int32_header_pair_valid) 24 | add_test_case(test_streaming_decoder_incoming_application_variable_headers_with_empty_length_pair_valid) 25 | add_test_case(test_streaming_decoder_incoming_application_one_bool_header_pair_valid) 26 | add_test_case(test_streaming_decoder_incoming_multiple_messages) 27 | 28 | add_test_case(test_channel_handler_single_valid_messages_parse) 29 | add_test_case(test_channel_handler_multiple_valid_messages_parse) 30 | add_test_case(test_channel_handler_corrupted_crc_fails) 31 | add_test_case(test_channel_handler_large_msg_success) 32 | add_test_case(test_channel_handler_write_message) 33 | 34 | add_net_test_case(test_event_stream_rpc_server_connection_setup_and_teardown) 35 | add_net_test_case(test_event_stream_rpc_server_connection_setup_and_teardown_with_bind_to_zero_port) 36 | add_net_test_case(test_event_stream_rpc_server_connection_connect_flow) 37 | add_net_test_case(test_event_stream_rpc_server_connection_connect_reject_flow) 38 | add_net_test_case(test_event_stream_rpc_server_connection_messages_before_connect_received) 39 | add_net_test_case(test_event_stream_rpc_server_connection_messages_before_connect_ack_sent) 40 | add_net_test_case(test_event_stream_rpc_server_connection_unknown_message_type) 41 | add_net_test_case(test_event_stream_rpc_server_connection_missing_message_type) 42 | add_net_test_case(test_event_stream_rpc_server_connection_missing_message_flags) 43 | add_net_test_case(test_event_stream_rpc_server_connection_continuation_missing_operation) 44 | add_net_test_case(test_event_stream_rpc_server_connection_missing_stream_id) 45 | add_net_test_case(test_event_stream_rpc_server_connection_continuation_messages_flow) 46 | add_net_test_case(test_event_stream_rpc_server_connection_continuation_failure) 47 | add_net_test_case(test_event_stream_rpc_server_connection_stream_id_ahead) 48 | add_net_test_case(test_event_stream_rpc_server_connection_continuation_reused_stream_id_fails) 49 | add_net_test_case(test_event_stream_rpc_server_connection_continuation_max_stream_id_reached) 50 | 51 | add_net_test_case(test_event_stream_rpc_client_connection_setup_and_teardown) 52 | add_net_test_case(test_event_stream_rpc_client_connection_connect) 53 | add_net_test_case(test_event_stream_rpc_client_connection_message_before_connect) 54 | add_net_test_case(test_event_stream_rpc_client_connection_protocol_message) 55 | add_net_test_case(test_event_stream_rpc_client_connection_continuation_flow) 56 | add_net_test_case(test_event_stream_rpc_client_connection_unactivated_continuation_fails) 57 | add_net_test_case(test_event_stream_rpc_client_connection_continuation_send_message_on_closed_fails) 58 | add_net_test_case(test_event_stream_rpc_client_connection_continuation_duplicated_activate_fails) 59 | 60 | generate_test_driver(${TEST_BINARY_NAME}) 61 | -------------------------------------------------------------------------------- /tests/channel_handler_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | struct test_data { 11 | struct aws_allocator *allocator; 12 | struct testing_channel testing_channel; 13 | struct aws_channel_handler *handler; 14 | aws_event_stream_channel_handler_on_message_received_fn *received_fn; 15 | void *user_data; 16 | }; 17 | 18 | static struct test_data s_test_data; 19 | 20 | static void s_fixture_on_message(struct aws_event_stream_message *message, int error_code, void *user_data) { 21 | struct test_data *test_data = user_data; 22 | test_data->received_fn(message, error_code, test_data->user_data); 23 | } 24 | 25 | static int s_fixture_setup(struct aws_allocator *allocator, void *ctx) { 26 | aws_event_stream_library_init(allocator); 27 | struct test_data *test_data = ctx; 28 | AWS_ZERO_STRUCT(*test_data); 29 | 30 | test_data->allocator = allocator; 31 | 32 | struct aws_testing_channel_options testing_channel_options = { 33 | .clock_fn = aws_high_res_clock_get_ticks, 34 | }; 35 | ASSERT_SUCCESS(testing_channel_init(&test_data->testing_channel, allocator, &testing_channel_options)); 36 | 37 | struct aws_channel_slot *slot = aws_channel_slot_new(test_data->testing_channel.channel); 38 | ASSERT_NOT_NULL(slot); 39 | ASSERT_SUCCESS(aws_channel_slot_insert_end(test_data->testing_channel.channel, slot)); 40 | 41 | struct aws_event_stream_channel_handler_options options = { 42 | .initial_window_size = SIZE_MAX, 43 | .user_data = ctx, 44 | .on_message_received = s_fixture_on_message, 45 | }; 46 | test_data->handler = aws_event_stream_channel_handler_new(allocator, &options); 47 | ASSERT_NOT_NULL(test_data->handler); 48 | ASSERT_SUCCESS(aws_channel_slot_set_handler(slot, test_data->handler)); 49 | testing_channel_run_currently_queued_tasks(&test_data->testing_channel); 50 | 51 | return AWS_OP_SUCCESS; 52 | } 53 | 54 | static int s_fixture_shutdown(struct aws_allocator *allocator, int setup_result, void *ctx) { 55 | (void)allocator; 56 | struct test_data *test_data = ctx; 57 | 58 | if (!setup_result) { 59 | testing_channel_clean_up(&test_data->testing_channel); 60 | } 61 | 62 | aws_event_stream_library_clean_up(); 63 | 64 | return AWS_OP_SUCCESS; 65 | } 66 | 67 | struct single_message_test_data { 68 | int last_error_code; 69 | struct aws_event_stream_message received_msg_cpy; 70 | }; 71 | 72 | static void s_test_on_single_message(struct aws_event_stream_message *message, int error_code, void *user_data) { 73 | 74 | struct single_message_test_data *msg_test_data = user_data; 75 | msg_test_data->last_error_code = error_code; 76 | 77 | if (!error_code) { 78 | struct aws_byte_buf message_buf = aws_byte_buf_from_array( 79 | aws_event_stream_message_buffer(message), aws_event_stream_message_total_length(message)); 80 | aws_event_stream_message_from_buffer_copy( 81 | &msg_test_data->received_msg_cpy, s_test_data.allocator, &message_buf); 82 | } 83 | } 84 | 85 | /* send various valid messages in serial to make sure the happy path of message parsing is correct. */ 86 | static int s_test_channel_handler_single_valid_messages_parse(struct aws_allocator *allocator, void *ctx) { 87 | struct test_data *test_data = ctx; 88 | 89 | struct single_message_test_data message_test_data; 90 | AWS_ZERO_STRUCT(message_test_data); 91 | 92 | test_data->received_fn = s_test_on_single_message; 93 | test_data->user_data = &message_test_data; 94 | 95 | uint8_t empty_message[] = { 96 | 0x00, 97 | 0x00, 98 | 0x00, 99 | 0x10, 100 | 0x00, 101 | 0x00, 102 | 0x00, 103 | 0x00, 104 | 0x05, 105 | 0xc2, 106 | 0x48, 107 | 0xeb, 108 | 0x7d, 109 | 0x98, 110 | 0xc8, 111 | 0xff, 112 | }; 113 | 114 | struct aws_byte_cursor empty_message_cursor = aws_byte_cursor_from_array(empty_message, sizeof(empty_message)); 115 | ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, empty_message_cursor)); 116 | ASSERT_UINT_EQUALS(AWS_OP_SUCCESS, message_test_data.last_error_code); 117 | ASSERT_UINT_EQUALS(0x00000010, aws_event_stream_message_total_length(&message_test_data.received_msg_cpy)); 118 | ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&message_test_data.received_msg_cpy)); 119 | ASSERT_UINT_EQUALS(0x05c248eb, aws_event_stream_message_prelude_crc(&message_test_data.received_msg_cpy)); 120 | ASSERT_UINT_EQUALS(0x7d98c8ff, aws_event_stream_message_message_crc(&message_test_data.received_msg_cpy)); 121 | aws_event_stream_message_clean_up(&message_test_data.received_msg_cpy); 122 | AWS_ZERO_STRUCT(message_test_data); 123 | 124 | uint8_t no_headers_data[] = { 125 | 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x52, 0x8c, 0x5a, 0x7b, 0x27, 0x66, 126 | 0x6f, 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0xc3, 0x65, 0x39, 0x36, 127 | }; 128 | 129 | struct aws_byte_cursor no_headers_cur = aws_byte_cursor_from_array(no_headers_data, sizeof(no_headers_data)); 130 | ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, no_headers_cur)); 131 | ASSERT_UINT_EQUALS(AWS_OP_SUCCESS, message_test_data.last_error_code); 132 | ASSERT_UINT_EQUALS(0x0000001D, aws_event_stream_message_total_length(&message_test_data.received_msg_cpy)); 133 | ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&message_test_data.received_msg_cpy)); 134 | ASSERT_UINT_EQUALS(0xfd528c5a, aws_event_stream_message_prelude_crc(&message_test_data.received_msg_cpy)); 135 | 136 | const char *expected_str = "{'foo':'bar'}"; 137 | ASSERT_UINT_EQUALS(strlen(expected_str), aws_event_stream_message_payload_len(&message_test_data.received_msg_cpy)); 138 | 139 | ASSERT_BIN_ARRAYS_EQUALS( 140 | expected_str, 141 | strlen(expected_str), 142 | aws_event_stream_message_payload(&message_test_data.received_msg_cpy), 143 | aws_event_stream_message_payload_len(&message_test_data.received_msg_cpy)); 144 | ASSERT_UINT_EQUALS(0xc3653936, aws_event_stream_message_message_crc(&message_test_data.received_msg_cpy)); 145 | 146 | aws_event_stream_message_clean_up(&message_test_data.received_msg_cpy); 147 | AWS_ZERO_STRUCT(message_test_data); 148 | 149 | uint8_t headers_test_data[] = { 150 | 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x20, 0x07, 0xFD, 0x83, 0x96, 0x0C, 'c', 'o', 'n', 151 | 't', 'e', 'n', 't', '-', 't', 'y', 'p', 'e', 0x07, 0x00, 0x10, 'a', 'p', 'p', 'l', 152 | 'i', 'c', 'a', 't', 'i', 'o', 'n', '/', 'j', 's', 'o', 'n', 0x7b, 0x27, 0x66, 0x6f, 153 | 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0x8D, 0x9C, 0x08, 0xB1, 154 | }; 155 | 156 | struct aws_byte_cursor headers_test_cur = aws_byte_cursor_from_array(headers_test_data, sizeof(headers_test_data)); 157 | 158 | ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, headers_test_cur)); 159 | ASSERT_UINT_EQUALS(AWS_OP_SUCCESS, message_test_data.last_error_code); 160 | 161 | ASSERT_UINT_EQUALS(0x0000003D, aws_event_stream_message_total_length(&message_test_data.received_msg_cpy)); 162 | ASSERT_UINT_EQUALS(0x00000020, aws_event_stream_message_headers_len(&message_test_data.received_msg_cpy)); 163 | ASSERT_UINT_EQUALS(0x07FD8396, aws_event_stream_message_prelude_crc(&message_test_data.received_msg_cpy)); 164 | 165 | ASSERT_UINT_EQUALS(strlen(expected_str), aws_event_stream_message_payload_len(&message_test_data.received_msg_cpy)); 166 | 167 | ASSERT_BIN_ARRAYS_EQUALS( 168 | expected_str, 169 | strlen(expected_str), 170 | aws_event_stream_message_payload(&message_test_data.received_msg_cpy), 171 | aws_event_stream_message_payload_len(&message_test_data.received_msg_cpy)); 172 | 173 | struct aws_array_list headers; 174 | ASSERT_SUCCESS(aws_event_stream_headers_list_init(&headers, allocator)); 175 | 176 | ASSERT_SUCCESS(aws_event_stream_message_headers(&message_test_data.received_msg_cpy, &headers)); 177 | ASSERT_UINT_EQUALS(1, headers.length, ); 178 | struct aws_event_stream_header_value_pair header; 179 | ASSERT_SUCCESS(aws_array_list_front(&headers, &header)); 180 | 181 | const char *content_type = "content-type"; 182 | const char *content_type_value = "application/json"; 183 | 184 | struct aws_byte_buf header_name_buf = aws_event_stream_header_name(&header); 185 | ASSERT_BIN_ARRAYS_EQUALS(content_type, strlen(content_type), header_name_buf.buffer, header_name_buf.len); 186 | 187 | struct aws_byte_buf header_value_buf = aws_event_stream_header_value_as_string(&header); 188 | 189 | ASSERT_BIN_ARRAYS_EQUALS( 190 | content_type_value, strlen(content_type_value), header_value_buf.buffer, header_value_buf.len); 191 | 192 | ASSERT_UINT_EQUALS(0x8D9C08B1, aws_event_stream_message_message_crc(&message_test_data.received_msg_cpy)); 193 | 194 | aws_event_stream_headers_list_cleanup(&headers); 195 | aws_event_stream_message_clean_up(&message_test_data.received_msg_cpy); 196 | 197 | return AWS_OP_SUCCESS; 198 | } 199 | 200 | AWS_TEST_CASE_FIXTURE( 201 | test_channel_handler_single_valid_messages_parse, 202 | s_fixture_setup, 203 | s_test_channel_handler_single_valid_messages_parse, 204 | s_fixture_shutdown, 205 | &s_test_data) 206 | 207 | struct multiple_message_test_data { 208 | int last_error_code; 209 | struct aws_event_stream_message *received_msgs_cpy; 210 | size_t msg_count; 211 | }; 212 | 213 | static void s_test_on_multiple_message(struct aws_event_stream_message *message, int error_code, void *user_data) { 214 | 215 | struct multiple_message_test_data *msg_test_data = user_data; 216 | msg_test_data->last_error_code = error_code; 217 | 218 | if (!error_code) { 219 | struct aws_byte_buf message_buf = aws_byte_buf_from_array( 220 | aws_event_stream_message_buffer(message), aws_event_stream_message_total_length(message)); 221 | struct aws_event_stream_message msg_cpy; 222 | AWS_ZERO_STRUCT(msg_cpy); 223 | aws_event_stream_message_from_buffer_copy(&msg_cpy, s_test_data.allocator, &message_buf); 224 | msg_test_data->received_msgs_cpy[msg_test_data->msg_count++] = msg_cpy; 225 | } 226 | } 227 | 228 | /* send various valid messages as a batch to make sure the happy path of message parsing is correct. */ 229 | static int s_test_channel_handler_multiple_valid_messages_parse(struct aws_allocator *allocator, void *ctx) { 230 | struct test_data *test_data = ctx; 231 | 232 | uint8_t multi_message[] = { 233 | 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc2, 0x48, 0xeb, 0x7d, 0x98, 0xc8, 0xff, 234 | 235 | 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x52, 0x8c, 0x5a, 0x7b, 0x27, 0x66, 0x6f, 236 | 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0xc3, 0x65, 0x39, 0x36, 237 | 238 | 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x20, 0x07, 0xFD, 0x83, 0x96, 0x0C, 'c', 'o', 'n', 239 | 't', 'e', 'n', 't', '-', 't', 'y', 'p', 'e', 0x07, 0x00, 0x10, 'a', 'p', 'p', 'l', 240 | 'i', 'c', 'a', 't', 'i', 'o', 'n', '/', 'j', 's', 'o', 'n', 0x7b, 0x27, 0x66, 0x6f, 241 | 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0x8D, 0x9C, 0x08, 0xB1, 242 | }; 243 | 244 | /* fuzz the parser's boundary conditions */ 245 | for (size_t fragment_size = 1; fragment_size <= sizeof(multi_message); ++fragment_size) { 246 | 247 | struct multiple_message_test_data message_test_data; 248 | AWS_ZERO_STRUCT(message_test_data); 249 | 250 | struct aws_event_stream_message messages[100]; 251 | AWS_ZERO_ARRAY(messages); 252 | message_test_data.received_msgs_cpy = messages; 253 | 254 | test_data->received_fn = s_test_on_multiple_message; 255 | test_data->user_data = &message_test_data; 256 | 257 | size_t processed = 0; 258 | while (processed < sizeof(multi_message)) { 259 | size_t remaining = sizeof(multi_message) - processed; 260 | size_t to_copy = fragment_size < remaining ? fragment_size : remaining; 261 | struct aws_byte_cursor multi_message_cursor = 262 | aws_byte_cursor_from_array(multi_message + processed, to_copy); 263 | processed += to_copy; 264 | ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, multi_message_cursor)); 265 | } 266 | 267 | ASSERT_UINT_EQUALS(AWS_OP_SUCCESS, message_test_data.last_error_code); 268 | ASSERT_UINT_EQUALS(3, message_test_data.msg_count); 269 | 270 | ASSERT_UINT_EQUALS(0x00000010, aws_event_stream_message_total_length(&message_test_data.received_msgs_cpy[0])); 271 | ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&message_test_data.received_msgs_cpy[0])); 272 | ASSERT_UINT_EQUALS(0x05c248eb, aws_event_stream_message_prelude_crc(&message_test_data.received_msgs_cpy[0])); 273 | ASSERT_UINT_EQUALS(0x7d98c8ff, aws_event_stream_message_message_crc(&message_test_data.received_msgs_cpy[0])); 274 | aws_event_stream_message_clean_up(&message_test_data.received_msgs_cpy[0]); 275 | 276 | ASSERT_UINT_EQUALS(0x0000001D, aws_event_stream_message_total_length(&message_test_data.received_msgs_cpy[1])); 277 | ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&message_test_data.received_msgs_cpy[1])); 278 | ASSERT_UINT_EQUALS(0xfd528c5a, aws_event_stream_message_prelude_crc(&message_test_data.received_msgs_cpy[1])); 279 | 280 | const char *expected_str = "{'foo':'bar'}"; 281 | ASSERT_UINT_EQUALS( 282 | strlen(expected_str), aws_event_stream_message_payload_len(&message_test_data.received_msgs_cpy[1])); 283 | 284 | ASSERT_BIN_ARRAYS_EQUALS( 285 | expected_str, 286 | strlen(expected_str), 287 | aws_event_stream_message_payload(&message_test_data.received_msgs_cpy[1]), 288 | aws_event_stream_message_payload_len(&message_test_data.received_msgs_cpy[1])); 289 | ASSERT_UINT_EQUALS(0xc3653936, aws_event_stream_message_message_crc(&message_test_data.received_msgs_cpy[1])); 290 | 291 | aws_event_stream_message_clean_up(&message_test_data.received_msgs_cpy[1]); 292 | 293 | ASSERT_UINT_EQUALS(0x0000003D, aws_event_stream_message_total_length(&message_test_data.received_msgs_cpy[2])); 294 | ASSERT_UINT_EQUALS(0x00000020, aws_event_stream_message_headers_len(&message_test_data.received_msgs_cpy[2])); 295 | ASSERT_UINT_EQUALS(0x07FD8396, aws_event_stream_message_prelude_crc(&message_test_data.received_msgs_cpy[2])); 296 | 297 | ASSERT_UINT_EQUALS( 298 | strlen(expected_str), aws_event_stream_message_payload_len(&message_test_data.received_msgs_cpy[2])); 299 | 300 | ASSERT_BIN_ARRAYS_EQUALS( 301 | expected_str, 302 | strlen(expected_str), 303 | aws_event_stream_message_payload(&message_test_data.received_msgs_cpy[2]), 304 | aws_event_stream_message_payload_len(&message_test_data.received_msgs_cpy[2])); 305 | 306 | struct aws_array_list headers; 307 | ASSERT_SUCCESS(aws_event_stream_headers_list_init(&headers, allocator)); 308 | 309 | ASSERT_SUCCESS(aws_event_stream_message_headers(&message_test_data.received_msgs_cpy[2], &headers)); 310 | ASSERT_UINT_EQUALS(1, headers.length, ); 311 | struct aws_event_stream_header_value_pair header; 312 | ASSERT_SUCCESS(aws_array_list_front(&headers, &header)); 313 | 314 | const char *content_type = "content-type"; 315 | const char *content_type_value = "application/json"; 316 | 317 | struct aws_byte_buf header_name_buf = aws_event_stream_header_name(&header); 318 | ASSERT_BIN_ARRAYS_EQUALS(content_type, strlen(content_type), header_name_buf.buffer, header_name_buf.len); 319 | 320 | struct aws_byte_buf header_value_buf = aws_event_stream_header_value_as_string(&header); 321 | 322 | ASSERT_BIN_ARRAYS_EQUALS( 323 | content_type_value, strlen(content_type_value), header_value_buf.buffer, header_value_buf.len); 324 | 325 | ASSERT_UINT_EQUALS(0x8D9C08B1, aws_event_stream_message_message_crc(&message_test_data.received_msgs_cpy[2])); 326 | 327 | aws_event_stream_headers_list_cleanup(&headers); 328 | aws_event_stream_message_clean_up(&message_test_data.received_msgs_cpy[2]); 329 | } 330 | 331 | return AWS_OP_SUCCESS; 332 | } 333 | 334 | AWS_TEST_CASE_FIXTURE( 335 | test_channel_handler_multiple_valid_messages_parse, 336 | s_fixture_setup, 337 | s_test_channel_handler_multiple_valid_messages_parse, 338 | s_fixture_shutdown, 339 | &s_test_data) 340 | 341 | /* send various valid messages in serial to make sure the happy path of message parsing is correct. */ 342 | static int s_test_channel_handler_corrupted_crc_fails(struct aws_allocator *allocator, void *ctx) { 343 | (void)allocator; 344 | struct test_data *test_data = ctx; 345 | 346 | struct single_message_test_data message_test_data; 347 | AWS_ZERO_STRUCT(message_test_data); 348 | 349 | test_data->received_fn = s_test_on_single_message; 350 | test_data->user_data = &message_test_data; 351 | 352 | /* altered the 9th byte to a single bit flip */ 353 | uint8_t empty_message[] = { 354 | 0x00, 355 | 0x00, 356 | 0x00, 357 | 0x10, 358 | 0x00, 359 | 0x00, 360 | 0x00, 361 | 0x00, 362 | 0x05, 363 | 0xc3, 364 | 0x48, 365 | 0xeb, 366 | 0x7d, 367 | 0x98, 368 | 0xc8, 369 | 0xff, 370 | }; 371 | 372 | struct aws_byte_cursor empty_message_cursor = aws_byte_cursor_from_array(empty_message, sizeof(empty_message)); 373 | ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, empty_message_cursor)); 374 | ASSERT_UINT_EQUALS(AWS_ERROR_EVENT_STREAM_PRELUDE_CHECKSUM_FAILURE, message_test_data.last_error_code); 375 | 376 | return AWS_OP_SUCCESS; 377 | } 378 | 379 | AWS_TEST_CASE_FIXTURE( 380 | test_channel_handler_corrupted_crc_fails, 381 | s_fixture_setup, 382 | s_test_channel_handler_corrupted_crc_fails, 383 | s_fixture_shutdown, 384 | &s_test_data) 385 | 386 | /* send various valid messages in serial to make sure the happy path of message parsing is correct. */ 387 | static int s_test_channel_handler_large_msg_success(struct aws_allocator *allocator, void *ctx) { 388 | (void)allocator; 389 | 390 | struct test_data *test_data = ctx; 391 | 392 | struct single_message_test_data message_test_data; 393 | AWS_ZERO_STRUCT(message_test_data); 394 | 395 | test_data->received_fn = s_test_on_single_message; 396 | test_data->user_data = &message_test_data; 397 | 398 | /* message length is 16MB + 1 byte. We used to have our stream message limit set at 16MB. Now this test validates 399 | * that we can send messages > 16MB */ 400 | uint8_t empty_message[] = { 401 | 0x01, 402 | 0x00, 403 | 0x00, 404 | 0x01, 405 | 0x00, 406 | 0x00, 407 | 0x00, 408 | 0x00, 409 | 0x94, 410 | 0xE8, 411 | 0xF6, 412 | 0x47, 413 | 0xC8, 414 | 0x86, 415 | 0xFB, 416 | 0xA0, 417 | }; 418 | 419 | struct aws_byte_cursor empty_message_cursor = aws_byte_cursor_from_array(empty_message, sizeof(empty_message)); 420 | ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, empty_message_cursor)); 421 | ASSERT_UINT_EQUALS(AWS_OP_SUCCESS, message_test_data.last_error_code); 422 | return AWS_OP_SUCCESS; 423 | } 424 | 425 | AWS_TEST_CASE_FIXTURE( 426 | test_channel_handler_large_msg_success, 427 | s_fixture_setup, 428 | s_test_channel_handler_large_msg_success, 429 | s_fixture_shutdown, 430 | &s_test_data) 431 | 432 | static void s_on_message_written(struct aws_event_stream_message *message, int error_code, void *user_data) { 433 | struct single_message_test_data *msg_test_data = user_data; 434 | msg_test_data->last_error_code = error_code; 435 | 436 | if (!error_code) { 437 | struct aws_byte_buf message_buf = aws_byte_buf_from_array( 438 | aws_event_stream_message_buffer(message), aws_event_stream_message_total_length(message)); 439 | aws_event_stream_message_from_buffer_copy( 440 | &msg_test_data->received_msg_cpy, s_test_data.allocator, &message_buf); 441 | } 442 | } 443 | 444 | static int s_test_channel_handler_write_message(struct aws_allocator *allocator, void *ctx) { 445 | struct test_data *test_data = ctx; 446 | 447 | struct single_message_test_data message_test_data; 448 | AWS_ZERO_STRUCT(message_test_data); 449 | 450 | uint8_t empty_message[] = { 451 | 0x00, 452 | 0x00, 453 | 0x00, 454 | 0x10, 455 | 0x00, 456 | 0x00, 457 | 0x00, 458 | 0x00, 459 | 0x05, 460 | 0xc2, 461 | 0x48, 462 | 0xeb, 463 | 0x7d, 464 | 0x98, 465 | 0xc8, 466 | 0xff, 467 | }; 468 | 469 | struct aws_byte_buf empty_message_buf = aws_byte_buf_from_array(empty_message, sizeof(empty_message)); 470 | struct aws_event_stream_message msg_to_send; 471 | AWS_ZERO_STRUCT(msg_to_send); 472 | ASSERT_SUCCESS(aws_event_stream_message_from_buffer(&msg_to_send, allocator, &empty_message_buf)); 473 | ASSERT_SUCCESS(aws_event_stream_channel_handler_write_message( 474 | test_data->handler, &msg_to_send, s_on_message_written, &message_test_data)); 475 | testing_channel_drain_queued_tasks(&s_test_data.testing_channel); 476 | ASSERT_UINT_EQUALS(0x00000010, aws_event_stream_message_total_length(&message_test_data.received_msg_cpy)); 477 | ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&message_test_data.received_msg_cpy)); 478 | ASSERT_UINT_EQUALS(0x05c248eb, aws_event_stream_message_prelude_crc(&message_test_data.received_msg_cpy)); 479 | ASSERT_UINT_EQUALS(0x7d98c8ff, aws_event_stream_message_message_crc(&message_test_data.received_msg_cpy)); 480 | aws_event_stream_message_clean_up(&message_test_data.received_msg_cpy); 481 | aws_event_stream_message_clean_up(&msg_to_send); 482 | 483 | struct aws_linked_list *list = testing_channel_get_written_message_queue(&test_data->testing_channel); 484 | ASSERT_FALSE(aws_linked_list_empty(list)); 485 | struct aws_linked_list_node *node = aws_linked_list_front(list); 486 | ASSERT_NOT_NULL(node); 487 | struct aws_io_message *message = AWS_CONTAINER_OF(node, struct aws_io_message, queueing_handle); 488 | struct aws_event_stream_message sent_msg; 489 | AWS_ZERO_STRUCT(sent_msg); 490 | 491 | ASSERT_SUCCESS(aws_event_stream_message_from_buffer(&sent_msg, allocator, &message->message_data)); 492 | ASSERT_UINT_EQUALS(0x00000010, aws_event_stream_message_total_length(&sent_msg)); 493 | ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&sent_msg)); 494 | ASSERT_UINT_EQUALS(0x05c248eb, aws_event_stream_message_prelude_crc(&sent_msg)); 495 | ASSERT_UINT_EQUALS(0x7d98c8ff, aws_event_stream_message_message_crc(&sent_msg)); 496 | aws_event_stream_message_clean_up(&sent_msg); 497 | return AWS_OP_SUCCESS; 498 | } 499 | 500 | AWS_TEST_CASE_FIXTURE( 501 | test_channel_handler_write_message, 502 | s_fixture_setup, 503 | s_test_channel_handler_write_message, 504 | s_fixture_shutdown, 505 | &s_test_data) 506 | -------------------------------------------------------------------------------- /tests/message_deserializer_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | static int s_test_outgoing_no_op_valid_fn(struct aws_allocator *allocator, void *ctx) { 10 | (void)allocator; 11 | (void)ctx; 12 | uint8_t test_data[] = { 13 | 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc2, 0x48, 0xeb, 0x7d, 0x98, 0xc8, 0xff}; 14 | 15 | struct aws_event_stream_message message; 16 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); 17 | ASSERT_SUCCESS( 18 | aws_event_stream_message_from_buffer(&message, allocator, &test_buf), 19 | "Message validation should have succeeded"); 20 | 21 | ASSERT_INT_EQUALS( 22 | 0x00000010, aws_event_stream_message_total_length(&message), "Message length should have been 0x10"); 23 | ASSERT_INT_EQUALS( 24 | 0x00000000, aws_event_stream_message_headers_len(&message), "Headers Length should have been 0x00"); 25 | ASSERT_INT_EQUALS( 26 | 0x05c248eb, aws_event_stream_message_prelude_crc(&message), "Prelude CRC should have been 0x05c248eb"); 27 | ASSERT_INT_EQUALS( 28 | 0x7d98c8ff, aws_event_stream_message_message_crc(&message), "Message CRC should have been 0x7d98c8ff"); 29 | 30 | aws_event_stream_message_clean_up(&message); 31 | 32 | return 0; 33 | } 34 | 35 | AWS_TEST_CASE(test_outgoing_no_op_valid, s_test_outgoing_no_op_valid_fn) 36 | 37 | static int s_test_outgoing_application_data_no_headers_valid_fn(struct aws_allocator *allocator, void *ctx) { 38 | (void)allocator; 39 | (void)ctx; 40 | uint8_t test_data[] = {0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x52, 0x8c, 0x5a, 0x7b, 0x27, 0x66, 41 | 0x6f, 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0xc3, 0x65, 0x39, 0x36}; 42 | 43 | struct aws_event_stream_message message; 44 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); 45 | 46 | ASSERT_SUCCESS( 47 | aws_event_stream_message_from_buffer(&message, allocator, &test_buf), 48 | "Message validation should have succeeded"); 49 | 50 | ASSERT_INT_EQUALS( 51 | 0x0000001D, aws_event_stream_message_total_length(&message), "Message length should have been 0x0000001D"); 52 | ASSERT_INT_EQUALS( 53 | 0x00000000, aws_event_stream_message_headers_len(&message), "Headers Length should have been 0x00"); 54 | ASSERT_INT_EQUALS( 55 | 0xfd528c5a, aws_event_stream_message_prelude_crc(&message), "Prelude CRC should have been 0xfd528c5a"); 56 | 57 | const char *expected_str = "{'foo':'bar'}"; 58 | ASSERT_INT_EQUALS( 59 | strlen(expected_str), 60 | aws_event_stream_message_payload_len(&message), 61 | "payload length should have been %d", 62 | (int)(strlen(expected_str))); 63 | 64 | ASSERT_BIN_ARRAYS_EQUALS( 65 | expected_str, 66 | strlen(expected_str), 67 | aws_event_stream_message_payload(&message), 68 | aws_event_stream_message_payload_len(&message), 69 | "payload should have been %s", 70 | expected_str); 71 | ASSERT_INT_EQUALS( 72 | 0xc3653936, aws_event_stream_message_message_crc(&message), "Message CRC should have been 0xc3653936"); 73 | 74 | aws_event_stream_message_clean_up(&message); 75 | 76 | return 0; 77 | } 78 | 79 | AWS_TEST_CASE(test_outgoing_application_data_no_headers_valid, s_test_outgoing_application_data_no_headers_valid_fn) 80 | 81 | static int s_test_outgoing_application_one_compressed_header_pair_valid_fn(struct aws_allocator *allocator, void *ctx) { 82 | (void)allocator; 83 | (void)ctx; 84 | uint8_t test_data[] = {0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x20, 0x07, 0xFD, 0x83, 0x96, 0x0C, 85 | 'c', 'o', 'n', 't', 'e', 'n', 't', '-', 't', 'y', 'p', 'e', 0x07, 86 | 0x00, 0x10, 'a', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o', 'n', 87 | '/', 'j', 's', 'o', 'n', 0x7b, 0x27, 0x66, 0x6f, 0x6f, 0x27, 0x3a, 0x27, 88 | 0x62, 0x61, 0x72, 0x27, 0x7d, 0x8D, 0x9C, 0x08, 0xB1}; 89 | 90 | struct aws_event_stream_message message; 91 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); 92 | 93 | ASSERT_SUCCESS( 94 | aws_event_stream_message_from_buffer(&message, allocator, &test_buf), 95 | "Message validation should have succeeded"); 96 | 97 | ASSERT_INT_EQUALS( 98 | 0x0000003D, aws_event_stream_message_total_length(&message), "Message length should have been 0x0000003D"); 99 | ASSERT_INT_EQUALS( 100 | 0x00000020, aws_event_stream_message_headers_len(&message), "Headers Length should have been 0x00000020"); 101 | ASSERT_INT_EQUALS( 102 | 0x07FD8396, aws_event_stream_message_prelude_crc(&message), "Prelude CRC should have been 0x07FD8396"); 103 | 104 | const char *expected_str = "{'foo':'bar'}"; 105 | ASSERT_INT_EQUALS( 106 | strlen(expected_str), 107 | aws_event_stream_message_payload_len(&message), 108 | "payload length should have been %d", 109 | (int)(strlen(expected_str))); 110 | 111 | ASSERT_BIN_ARRAYS_EQUALS( 112 | expected_str, 113 | strlen(expected_str), 114 | aws_event_stream_message_payload(&message), 115 | aws_event_stream_message_payload_len(&message), 116 | "payload should have been %s", 117 | expected_str); 118 | 119 | struct aws_array_list headers; 120 | ASSERT_SUCCESS(aws_event_stream_headers_list_init(&headers, allocator), "Header initialization failed"); 121 | 122 | ASSERT_SUCCESS(aws_event_stream_message_headers(&message, &headers), "Header parsing should have succeeded"); 123 | ASSERT_INT_EQUALS(1, headers.length, "There should be exactly one header found"); 124 | struct aws_event_stream_header_value_pair header; 125 | ASSERT_SUCCESS( 126 | aws_array_list_front(&headers, &header), 127 | "accessing the first element of an array of size 1 should have succeeded"); 128 | 129 | const char *content_type = "content-type"; 130 | const char *content_type_value = "application/json"; 131 | 132 | struct aws_byte_buf header_name_buf = aws_event_stream_header_name(&header); 133 | ASSERT_BIN_ARRAYS_EQUALS( 134 | content_type, 135 | strlen(content_type), 136 | header_name_buf.buffer, 137 | header_name_buf.len, 138 | "header name should have been %s", 139 | content_type); 140 | 141 | struct aws_byte_buf header_value_buf = aws_event_stream_header_value_as_string(&header); 142 | 143 | ASSERT_BIN_ARRAYS_EQUALS( 144 | content_type_value, 145 | strlen(content_type_value), 146 | header_value_buf.buffer, 147 | header_value_buf.len, 148 | "header value should have been %s", 149 | content_type_value); 150 | 151 | ASSERT_INT_EQUALS( 152 | 0x8D9C08B1, aws_event_stream_message_message_crc(&message), "Message CRC should have been 0x8D9C08B1"); 153 | 154 | aws_event_stream_headers_list_cleanup(&headers); 155 | aws_event_stream_message_clean_up(&message); 156 | 157 | return 0; 158 | } 159 | 160 | AWS_TEST_CASE( 161 | test_outgoing_application_one_compressed_header_pair_valid, 162 | s_test_outgoing_application_one_compressed_header_pair_valid_fn) 163 | -------------------------------------------------------------------------------- /tests/message_serializer_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0. 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | static int s_test_incoming_no_op_valid_fn(struct aws_allocator *allocator, void *ctx) { 10 | (void)ctx; 11 | uint8_t expected_data[] = { 12 | 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc2, 0x48, 0xeb, 0x7d, 0x98, 0xc8, 0xff}; 13 | 14 | struct aws_event_stream_message message; 15 | ASSERT_SUCCESS( 16 | aws_event_stream_message_init(&message, allocator, NULL, NULL), "Message validation should have succeeded"); 17 | 18 | ASSERT_BIN_ARRAYS_EQUALS( 19 | expected_data, 20 | sizeof(expected_data), 21 | aws_event_stream_message_buffer(&message), 22 | aws_event_stream_message_total_length(&message), 23 | "buffers didn't match"); 24 | 25 | aws_event_stream_message_clean_up(&message); 26 | 27 | return 0; 28 | } 29 | 30 | AWS_TEST_CASE(test_incoming_no_op_valid, s_test_incoming_no_op_valid_fn) 31 | 32 | static int s_test_incoming_application_data_no_headers_valid_fn(struct aws_allocator *allocator, void *ctx) { 33 | (void)ctx; 34 | uint8_t expected_data[] = {0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x52, 0x8c, 0x5a, 0x7b, 0x27, 0x66, 35 | 0x6f, 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0xc3, 0x65, 0x39, 0x36}; 36 | 37 | const char *test_str = "{'foo':'bar'}"; 38 | struct aws_event_stream_message message; 39 | struct aws_byte_buf test_buf = aws_byte_buf_from_c_str(test_str); 40 | ASSERT_SUCCESS( 41 | aws_event_stream_message_init(&message, allocator, NULL, &test_buf), 42 | "Message validation should have succeeded"); 43 | 44 | ASSERT_BIN_ARRAYS_EQUALS( 45 | expected_data, 46 | sizeof(expected_data), 47 | aws_event_stream_message_buffer(&message), 48 | aws_event_stream_message_total_length(&message), 49 | "buffers didn't match"); 50 | 51 | aws_event_stream_message_clean_up(&message); 52 | 53 | return 0; 54 | } 55 | 56 | AWS_TEST_CASE(test_incoming_application_data_no_headers_valid, s_test_incoming_application_data_no_headers_valid_fn) 57 | 58 | static int s_test_incoming_application_one_compressed_header_pair_valid_fn(struct aws_allocator *allocator, void *ctx) { 59 | (void)ctx; 60 | uint8_t expected_data[] = {0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x20, 0x07, 0xFD, 0x83, 0x96, 0x0C, 61 | 'c', 'o', 'n', 't', 'e', 'n', 't', '-', 't', 'y', 'p', 'e', 0x07, 62 | 0x00, 0x10, 'a', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o', 'n', 63 | '/', 'j', 's', 'o', 'n', 0x7b, 0x27, 0x66, 0x6f, 0x6f, 0x27, 0x3a, 0x27, 64 | 0x62, 0x61, 0x72, 0x27, 0x7d, 0x8D, 0x9C, 0x08, 0xB1}; 65 | 66 | const char *test_str = "{'foo':'bar'}"; 67 | struct aws_event_stream_message message; 68 | 69 | struct aws_array_list headers; 70 | ASSERT_SUCCESS(aws_event_stream_headers_list_init(&headers, allocator), "Header initialization failed"); 71 | 72 | const char *header_name = "content-type"; 73 | const char *header_value = "application/json"; 74 | 75 | ASSERT_SUCCESS( 76 | aws_event_stream_add_string_header( 77 | &headers, header_name, (int8_t)strlen(header_name), header_value, (uint16_t)strlen(header_value), 0), 78 | "Adding a header should have succeeded."); 79 | 80 | struct aws_byte_buf test_buf = aws_byte_buf_from_c_str(test_str); 81 | 82 | ASSERT_SUCCESS( 83 | aws_event_stream_message_init(&message, allocator, &headers, &test_buf), 84 | "Message validation should have succeeded"); 85 | 86 | ASSERT_BIN_ARRAYS_EQUALS( 87 | expected_data, 88 | sizeof(expected_data), 89 | aws_event_stream_message_buffer(&message), 90 | aws_event_stream_message_total_length(&message), 91 | "buffers didn't match"); 92 | 93 | aws_event_stream_headers_list_cleanup(&headers); 94 | aws_event_stream_message_clean_up(&message); 95 | 96 | return 0; 97 | } 98 | 99 | AWS_TEST_CASE( 100 | test_incoming_application_one_compressed_header_pair_valid, 101 | s_test_incoming_application_one_compressed_header_pair_valid_fn) 102 | 103 | static int s_test_incoming_application_int32_header_valid_fn(struct aws_allocator *allocator, void *ctx) { 104 | (void)ctx; 105 | uint8_t expected_data[] = {0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x0E, 0x34, 0x8B, 0xEC, 0x7B, 0x08, 'e', 'v', 106 | 'e', 'n', 't', '-', 'i', 'd', 0x04, 0x00, 0x00, 0xA0, 0x0C, 0x7b, 0x27, 0x66, 0x6f, 107 | 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0xD3, 0x89, 0x02, 0x85}; 108 | 109 | const char *test_str = "{'foo':'bar'}"; 110 | struct aws_event_stream_message message; 111 | 112 | struct aws_array_list headers; 113 | ASSERT_SUCCESS(aws_event_stream_headers_list_init(&headers, allocator), "Header initialization failed"); 114 | 115 | const char *header_name = "event-id"; 116 | 117 | ASSERT_SUCCESS( 118 | aws_event_stream_add_int32_header(&headers, header_name, (int8_t)strlen(header_name), 0x0000A00c), 119 | "Adding a header should have succeeded."); 120 | 121 | struct aws_byte_buf test_buf = aws_byte_buf_from_c_str(test_str); 122 | 123 | ASSERT_SUCCESS(aws_event_stream_message_init(&message, allocator, &headers, &test_buf), "buffers didn't match"); 124 | 125 | ASSERT_BIN_ARRAYS_EQUALS( 126 | expected_data, 127 | sizeof(expected_data), 128 | aws_event_stream_message_buffer(&message), 129 | aws_event_stream_message_total_length(&message), 130 | "buffers didn't match"); 131 | 132 | aws_array_list_clean_up(&headers); 133 | aws_event_stream_message_clean_up(&message); 134 | 135 | return 0; 136 | } 137 | 138 | AWS_TEST_CASE(test_incoming_application_int32_header_valid, s_test_incoming_application_int32_header_valid_fn) 139 | -------------------------------------------------------------------------------- /tests/message_streaming_decoder_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | struct test_decoder_data { 11 | struct aws_event_stream_message_prelude latest_prelude; 12 | struct aws_array_list headers_list; 13 | uint8_t *latest_payload; 14 | size_t written; 15 | struct aws_allocator *alloc; 16 | int latest_error; 17 | uint32_t message_crc; 18 | }; 19 | 20 | static void s_decoder_test_on_payload_segment( 21 | struct aws_event_stream_streaming_decoder *decoder, 22 | struct aws_byte_buf *data, 23 | int8_t final_segment, 24 | void *user_data) { 25 | (void)final_segment; 26 | (void)decoder; 27 | struct test_decoder_data *decoder_data = (struct test_decoder_data *)user_data; 28 | memcpy(decoder_data->latest_payload + decoder_data->written, data->buffer, data->len); 29 | decoder_data->written += data->len; 30 | } 31 | 32 | static void s_decoder_test_on_prelude_received( 33 | struct aws_event_stream_streaming_decoder *decoder, 34 | struct aws_event_stream_message_prelude *prelude, 35 | void *user_data) { 36 | 37 | (void)decoder; 38 | struct test_decoder_data *decoder_data = (struct test_decoder_data *)user_data; 39 | decoder_data->latest_prelude = *prelude; 40 | 41 | if (decoder_data->latest_payload) { 42 | aws_mem_release(decoder_data->alloc, decoder_data->latest_payload); 43 | } 44 | 45 | const size_t payload_size = decoder_data->latest_prelude.total_len - AWS_EVENT_STREAM_PRELUDE_LENGTH - 46 | AWS_EVENT_STREAM_TRAILER_LENGTH - decoder_data->latest_prelude.headers_len; 47 | 48 | if (payload_size) { 49 | decoder_data->latest_payload = aws_mem_acquire(decoder_data->alloc, payload_size); 50 | } else { 51 | decoder_data->latest_payload = NULL; 52 | } 53 | decoder_data->written = 0; 54 | } 55 | 56 | static void s_decoder_test_header_received( 57 | struct aws_event_stream_streaming_decoder *decoder, 58 | struct aws_event_stream_message_prelude *prelude, 59 | struct aws_event_stream_header_value_pair *header, 60 | void *user_data) { 61 | (void)decoder; 62 | (void)prelude; 63 | struct test_decoder_data *decoder_data = (struct test_decoder_data *)user_data; 64 | aws_event_stream_add_header(&decoder_data->headers_list, header); 65 | } 66 | 67 | static void s_decoder_test_on_complete( 68 | struct aws_event_stream_streaming_decoder *decoder, 69 | uint32_t message_crc, 70 | void *user_data) { 71 | (void)decoder; 72 | struct test_decoder_data *decoder_data = (struct test_decoder_data *)user_data; 73 | decoder_data->message_crc = message_crc; 74 | } 75 | 76 | static void s_decoder_test_on_error( 77 | struct aws_event_stream_streaming_decoder *decoder, 78 | struct aws_event_stream_message_prelude *prelude, 79 | int error_code, 80 | const char *message, 81 | void *user_data) { 82 | 83 | (void)decoder; 84 | (void)prelude; 85 | (void)message; 86 | struct test_decoder_data *decoder_data = (struct test_decoder_data *)user_data; 87 | decoder_data->latest_error = error_code; 88 | } 89 | 90 | static int s_test_streaming_decoder_incoming_no_op_valid_single_message_fn(struct aws_allocator *allocator, void *ctx) { 91 | uint8_t test_data[] = { 92 | 0x00, 93 | 0x00, 94 | 0x00, 95 | 0x10, 96 | 0x00, 97 | 0x00, 98 | 0x00, 99 | 0x00, 100 | 0x05, 101 | 0xc2, 102 | 0x48, 103 | 0xeb, 104 | 0x7d, 105 | 0x98, 106 | 0xc8, 107 | 0xff, 108 | }; 109 | 110 | (void)ctx; 111 | struct test_decoder_data decoder_data = {.latest_payload = 0, .written = 0, .alloc = allocator, .latest_error = 0}; 112 | 113 | struct aws_event_stream_streaming_decoder_options decoder_options = { 114 | .on_payload_segment = s_decoder_test_on_payload_segment, 115 | .on_prelude = s_decoder_test_on_prelude_received, 116 | .on_header = s_decoder_test_header_received, 117 | .on_complete = s_decoder_test_on_complete, 118 | .on_error = s_decoder_test_on_error, 119 | .user_data = &decoder_data}; 120 | 121 | struct aws_event_stream_streaming_decoder decoder; 122 | aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); 123 | 124 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); 125 | ASSERT_SUCCESS( 126 | aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); 127 | ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); 128 | 129 | ASSERT_INT_EQUALS(0x00000010, decoder_data.latest_prelude.total_len, "Message length should have been 0x10"); 130 | ASSERT_INT_EQUALS(0x00000000, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x00"); 131 | ASSERT_INT_EQUALS(0x05c248eb, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0x8c335472"); 132 | ASSERT_INT_EQUALS(0, decoder_data.written, "No payload data should have been written"); 133 | 134 | if (decoder_data.latest_payload) { 135 | aws_mem_release(allocator, decoder_data.latest_payload); 136 | } 137 | ASSERT_UINT_EQUALS(0x7D98C8FF, decoder_data.message_crc); 138 | 139 | aws_event_stream_streaming_decoder_clean_up(&decoder); 140 | 141 | return 0; 142 | } 143 | 144 | AWS_TEST_CASE( 145 | test_streaming_decoder_incoming_no_op_valid_single_message, 146 | s_test_streaming_decoder_incoming_no_op_valid_single_message_fn) 147 | 148 | static int s_test_streaming_decoder_incoming_application_no_headers_fn(struct aws_allocator *allocator, void *ctx) { 149 | uint8_t test_data[] = { 150 | 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x52, 0x8c, 0x5a, 0x7b, 0x27, 0x66, 151 | 0x6f, 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0xc3, 0x65, 0x39, 0x36, 152 | }; 153 | 154 | (void)ctx; 155 | struct test_decoder_data decoder_data = {.latest_payload = 0, .written = 0, .alloc = allocator, .latest_error = 0}; 156 | 157 | struct aws_event_stream_streaming_decoder_options decoder_options = { 158 | .on_payload_segment = s_decoder_test_on_payload_segment, 159 | .on_prelude = s_decoder_test_on_prelude_received, 160 | .on_header = s_decoder_test_header_received, 161 | .on_complete = s_decoder_test_on_complete, 162 | .on_error = s_decoder_test_on_error, 163 | .user_data = &decoder_data}; 164 | 165 | struct aws_event_stream_streaming_decoder decoder; 166 | aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); 167 | 168 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); 169 | 170 | ASSERT_SUCCESS( 171 | aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); 172 | ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); 173 | 174 | ASSERT_INT_EQUALS(0x0000001D, decoder_data.latest_prelude.total_len, "Message length should have been 0x1D"); 175 | ASSERT_INT_EQUALS(0x00000000, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x00"); 176 | ASSERT_INT_EQUALS(0xfd528c5a, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0xfd528c5a"); 177 | 178 | const char *expected_str = "{'foo':'bar'}"; 179 | size_t payload_len = decoder_data.latest_prelude.total_len - AWS_EVENT_STREAM_PRELUDE_LENGTH - 180 | AWS_EVENT_STREAM_TRAILER_LENGTH - decoder_data.latest_prelude.headers_len; 181 | ASSERT_INT_EQUALS( 182 | strlen(expected_str), payload_len, "payload length should have been %d", (int)(strlen(expected_str))); 183 | 184 | ASSERT_BIN_ARRAYS_EQUALS( 185 | expected_str, 186 | strlen(expected_str), 187 | decoder_data.latest_payload, 188 | payload_len, 189 | "payload should have been %s", 190 | expected_str); 191 | 192 | if (decoder_data.latest_payload) { 193 | aws_mem_release(allocator, decoder_data.latest_payload); 194 | } 195 | ASSERT_UINT_EQUALS(0xC3653936, decoder_data.message_crc); 196 | 197 | aws_event_stream_streaming_decoder_clean_up(&decoder); 198 | 199 | return 0; 200 | } 201 | 202 | AWS_TEST_CASE( 203 | test_streaming_decoder_incoming_application_no_headers, 204 | s_test_streaming_decoder_incoming_application_no_headers_fn) 205 | 206 | static int s_test_streaming_decoder_incoming_application_one_compressed_header_pair_valid_fn( 207 | struct aws_allocator *allocator, 208 | void *ctx) { 209 | (void)ctx; 210 | uint8_t test_data[] = { 211 | 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x20, 0x07, 0xFD, 0x83, 0x96, 0x0C, 'c', 'o', 'n', 212 | 't', 'e', 'n', 't', '-', 't', 'y', 'p', 'e', 0x07, 0x00, 0x10, 'a', 'p', 'p', 'l', 213 | 'i', 'c', 'a', 't', 'i', 'o', 'n', '/', 'j', 's', 'o', 'n', 0x7b, 0x27, 0x66, 0x6f, 214 | 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0x8D, 0x9C, 0x08, 0xB1, 215 | }; 216 | 217 | struct test_decoder_data decoder_data = { 218 | .latest_payload = 0, 219 | .written = 0, 220 | .alloc = allocator, 221 | .latest_error = 0, 222 | }; 223 | aws_event_stream_headers_list_init(&decoder_data.headers_list, allocator); 224 | 225 | struct aws_event_stream_streaming_decoder_options decoder_options = { 226 | .on_payload_segment = s_decoder_test_on_payload_segment, 227 | .on_prelude = s_decoder_test_on_prelude_received, 228 | .on_header = s_decoder_test_header_received, 229 | .on_complete = s_decoder_test_on_complete, 230 | .on_error = s_decoder_test_on_error, 231 | .user_data = &decoder_data}; 232 | 233 | struct aws_event_stream_streaming_decoder decoder; 234 | aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); 235 | 236 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); 237 | 238 | ASSERT_SUCCESS( 239 | aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); 240 | ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); 241 | 242 | ASSERT_INT_EQUALS(0x0000003D, decoder_data.latest_prelude.total_len, "Message length should have been 0x3D"); 243 | ASSERT_INT_EQUALS(0x00000020, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x20"); 244 | ASSERT_INT_EQUALS(0x07FD8396, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0x07FD8396"); 245 | 246 | const char *content_type = "content-type"; 247 | const char *content_type_value = "application/json"; 248 | 249 | struct aws_event_stream_header_value_pair latest_header; 250 | aws_array_list_get_at(&decoder_data.headers_list, &latest_header, 0); 251 | struct aws_byte_buf latest_header_value = aws_event_stream_header_value_as_string(&latest_header); 252 | 253 | ASSERT_BIN_ARRAYS_EQUALS( 254 | content_type, 255 | strlen(content_type), 256 | latest_header.header_name, 257 | latest_header.header_name_len, 258 | "header name should have been %s", 259 | content_type); 260 | ASSERT_BIN_ARRAYS_EQUALS( 261 | content_type_value, 262 | strlen(content_type_value), 263 | latest_header_value.buffer, 264 | latest_header_value.len, 265 | "header value should have been %s", 266 | content_type_value); 267 | 268 | const char *expected_str = "{'foo':'bar'}"; 269 | size_t payload_len = decoder_data.latest_prelude.total_len - AWS_EVENT_STREAM_PRELUDE_LENGTH - 270 | AWS_EVENT_STREAM_TRAILER_LENGTH - decoder_data.latest_prelude.headers_len; 271 | ASSERT_INT_EQUALS( 272 | strlen(expected_str), payload_len, "payload length should have been %d", (int)(strlen(expected_str))); 273 | 274 | ASSERT_BIN_ARRAYS_EQUALS( 275 | expected_str, 276 | strlen(expected_str), 277 | decoder_data.latest_payload, 278 | payload_len, 279 | "payload should have been %s", 280 | expected_str); 281 | 282 | if (decoder_data.latest_payload) { 283 | aws_mem_release(allocator, decoder_data.latest_payload); 284 | } 285 | ASSERT_UINT_EQUALS(0x8D9C08B1, decoder_data.message_crc); 286 | 287 | aws_event_stream_headers_list_cleanup(&decoder_data.headers_list); 288 | return 0; 289 | } 290 | 291 | AWS_TEST_CASE( 292 | test_streaming_decoder_incoming_application_one_compressed_header_pair_valid, 293 | s_test_streaming_decoder_incoming_application_one_compressed_header_pair_valid_fn) 294 | 295 | static int s_test_streaming_decoder_incoming_application_one_int32_header_pair_valid_fn( 296 | struct aws_allocator *allocator, 297 | void *ctx) { 298 | (void)ctx; 299 | /* clang-format off */ 300 | uint8_t test_data[] = { 301 | 0x00, 0x00, 0x00, 0x1b, /* total length */ 302 | 0x00, 0x00, 0x00, 0x0b, /* headers length */ 303 | 0xe5, 0xc0, 0xa0, 0x72, /* prelude crc */ 304 | 0x05, /* header name length */ 305 | 'e', 'v', 'e', 'n', 't', /* header name */ 306 | 0x04, /* header value type */ 307 | 0x00, 0x00, /* header value length */ 308 | 0x00, 0x20, /* header value */ 309 | 0x04, 0xa1, 0xd4, 0x7c /* message crc */ 310 | }; 311 | /* clang-format on */ 312 | 313 | struct test_decoder_data decoder_data = { 314 | .latest_payload = 0, 315 | .written = 0, 316 | .alloc = allocator, 317 | .latest_error = 0, 318 | }; 319 | aws_event_stream_headers_list_init(&decoder_data.headers_list, allocator); 320 | 321 | struct aws_event_stream_streaming_decoder_options decoder_options = { 322 | .on_payload_segment = s_decoder_test_on_payload_segment, 323 | .on_prelude = s_decoder_test_on_prelude_received, 324 | .on_header = s_decoder_test_header_received, 325 | .on_complete = s_decoder_test_on_complete, 326 | .on_error = s_decoder_test_on_error, 327 | .user_data = &decoder_data}; 328 | 329 | struct aws_event_stream_streaming_decoder decoder; 330 | aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); 331 | 332 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); 333 | 334 | ASSERT_SUCCESS( 335 | aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); 336 | ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); 337 | 338 | ASSERT_INT_EQUALS(0x0000001B, decoder_data.latest_prelude.total_len, "Message length should have been 0x1B"); 339 | ASSERT_INT_EQUALS(0x0000000B, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0xB"); 340 | ASSERT_INT_EQUALS(0xE5C0A072, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0xE5C0A072"); 341 | 342 | const char *expected_header_name = "event"; 343 | struct aws_event_stream_header_value_pair latest_header; 344 | aws_array_list_get_at(&decoder_data.headers_list, &latest_header, 0); 345 | 346 | ASSERT_BIN_ARRAYS_EQUALS( 347 | expected_header_name, 348 | strlen(expected_header_name), 349 | latest_header.header_name, 350 | latest_header.header_name_len, 351 | "header name should have been %s", 352 | expected_header_name); 353 | 354 | int32_t latest_header_value = aws_event_stream_header_value_as_int32(&latest_header); 355 | ASSERT_INT_EQUALS(0x00000020, latest_header_value, "Header value should have been 0x00000020"); 356 | ASSERT_UINT_EQUALS(0x04A1D47C, decoder_data.message_crc); 357 | 358 | aws_event_stream_headers_list_cleanup(&decoder_data.headers_list); 359 | return 0; 360 | } 361 | 362 | AWS_TEST_CASE( 363 | test_streaming_decoder_incoming_application_one_int32_header_pair_valid, 364 | s_test_streaming_decoder_incoming_application_one_int32_header_pair_valid_fn) 365 | 366 | static int s_test_streaming_decoder_incoming_application_variable_headers_with_empty_length_pair_valid_fn( 367 | struct aws_allocator *allocator, 368 | void *ctx) { 369 | (void)ctx; 370 | /* clang-format off */ 371 | uint8_t test_data[] = { 372 | 0x00, 0x00, 0x00, 0x22, /* total length */ 373 | 0x00, 0x00, 0x00, 0x12, /* headers length */ 374 | 0x2D, 0x9A, 0xD2, 0x45, /* prelude crc */ 375 | 0x04, /* header name length */ 376 | 'b', 'u', 'f', 'f', /* header name */ 377 | 0x06, /* header value type (BYTE ARRAY)*/ 378 | 0x00, 0x00, /* header value length */ 379 | 0x06, /* header name length */ 380 | 's', 't', 'r', 'i','n','g', /* header name */ 381 | 0x07, /* header value type (String)*/ 382 | 0x00, 0x00, /* header value length */ 383 | 0xC8, 0x4C, 0xF8, 0x53 /* message crc */ 384 | }; 385 | /* clang-format on */ 386 | struct test_decoder_data decoder_data = { 387 | .latest_payload = 0, 388 | .written = 0, 389 | .alloc = allocator, 390 | .latest_error = 0, 391 | }; 392 | aws_event_stream_headers_list_init(&decoder_data.headers_list, allocator); 393 | struct aws_event_stream_streaming_decoder_options decoder_options = { 394 | .on_payload_segment = s_decoder_test_on_payload_segment, 395 | .on_prelude = s_decoder_test_on_prelude_received, 396 | .on_header = s_decoder_test_header_received, 397 | .on_complete = s_decoder_test_on_complete, 398 | .on_error = s_decoder_test_on_error, 399 | .user_data = &decoder_data}; 400 | 401 | struct aws_event_stream_streaming_decoder decoder; 402 | aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); 403 | 404 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); 405 | ASSERT_SUCCESS( 406 | aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); 407 | ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); 408 | 409 | ASSERT_INT_EQUALS(0x00000022, decoder_data.latest_prelude.total_len); 410 | ASSERT_INT_EQUALS(0x00000012, decoder_data.latest_prelude.headers_len); 411 | ASSERT_INT_EQUALS(0x2D9AD245, decoder_data.latest_prelude.prelude_crc); 412 | ASSERT_UINT_EQUALS(0xC84CF853, decoder_data.message_crc); 413 | 414 | const char *expected_header_name = "buff"; 415 | struct aws_event_stream_header_value_pair latest_header; 416 | aws_array_list_get_at(&decoder_data.headers_list, &latest_header, 0); 417 | 418 | ASSERT_BIN_ARRAYS_EQUALS( 419 | expected_header_name, 420 | strlen(expected_header_name), 421 | latest_header.header_name, 422 | latest_header.header_name_len, 423 | "header name should have been %s", 424 | expected_header_name); 425 | 426 | struct aws_byte_buf latest_header_value = aws_event_stream_header_value_as_bytebuf(&latest_header); 427 | ASSERT_INT_EQUALS(0, latest_header_value.len); 428 | ASSERT_NULL(latest_header_value.buffer); 429 | 430 | const char *expected_string_header_name = "string"; 431 | aws_array_list_get_at(&decoder_data.headers_list, &latest_header, 1); 432 | 433 | ASSERT_BIN_ARRAYS_EQUALS( 434 | expected_string_header_name, 435 | strlen(expected_string_header_name), 436 | latest_header.header_name, 437 | latest_header.header_name_len, 438 | "header name should have been %s", 439 | expected_header_name); 440 | 441 | latest_header_value = aws_event_stream_header_value_as_bytebuf(&latest_header); 442 | ASSERT_INT_EQUALS(0, latest_header_value.len); 443 | ASSERT_NULL(latest_header_value.buffer); 444 | 445 | aws_event_stream_headers_list_cleanup(&decoder_data.headers_list); 446 | return 0; 447 | } 448 | 449 | AWS_TEST_CASE( 450 | test_streaming_decoder_incoming_application_variable_headers_with_empty_length_pair_valid, 451 | s_test_streaming_decoder_incoming_application_variable_headers_with_empty_length_pair_valid_fn) 452 | 453 | static int s_test_streaming_decoder_incoming_application_one_bool_header_pair_valid_fn( 454 | struct aws_allocator *allocator, 455 | void *ctx) { 456 | (void)ctx; 457 | /* clang-format off */ 458 | uint8_t test_data[] = { 459 | 0x00, 0x00, 0x00, 0x17, /* total length */ 460 | 0x00, 0x00, 0x00, 0x07, /* headers length */ 461 | 0x29, 0x86, 0x01, 0x58, /* prelude crc */ 462 | 0x05, /* header name length */ 463 | 'e', 'v', 'e', 'n', 't', /* header name */ 464 | 0x00, /* header value type */ 465 | 0x4b, 0x4d, 0x2b, 0xe7 /* message crc */ 466 | }; 467 | /* clang-format on */ 468 | 469 | struct test_decoder_data decoder_data = { 470 | .latest_payload = 0, 471 | .written = 0, 472 | .alloc = allocator, 473 | .latest_error = 0, 474 | }; 475 | aws_event_stream_headers_list_init(&decoder_data.headers_list, allocator); 476 | 477 | struct aws_event_stream_streaming_decoder_options decoder_options = { 478 | .on_payload_segment = s_decoder_test_on_payload_segment, 479 | .on_prelude = s_decoder_test_on_prelude_received, 480 | .on_header = s_decoder_test_header_received, 481 | .on_complete = s_decoder_test_on_complete, 482 | .on_error = s_decoder_test_on_error, 483 | .user_data = &decoder_data}; 484 | 485 | struct aws_event_stream_streaming_decoder decoder; 486 | aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); 487 | 488 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); 489 | 490 | ASSERT_SUCCESS( 491 | aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); 492 | ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); 493 | 494 | ASSERT_INT_EQUALS(0x00000017, decoder_data.latest_prelude.total_len, "Message length should have been 0x17"); 495 | ASSERT_INT_EQUALS(0x00000007, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x7"); 496 | ASSERT_INT_EQUALS(0x29860158, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0x29860158"); 497 | 498 | const char *expected_header_name = "event"; 499 | struct aws_event_stream_header_value_pair latest_header; 500 | aws_array_list_get_at(&decoder_data.headers_list, &latest_header, 0); 501 | 502 | ASSERT_BIN_ARRAYS_EQUALS( 503 | expected_header_name, 504 | strlen(expected_header_name), 505 | latest_header.header_name, 506 | latest_header.header_name_len, 507 | "header name should have been %s", 508 | expected_header_name); 509 | 510 | int8_t latest_header_value = aws_event_stream_header_value_as_bool(&latest_header); 511 | ASSERT_INT_EQUALS(1, latest_header_value, "Header value should have been true"); 512 | ASSERT_UINT_EQUALS(0x4B4D2BE7, decoder_data.message_crc); 513 | 514 | aws_event_stream_headers_list_cleanup(&decoder_data.headers_list); 515 | return 0; 516 | } 517 | 518 | AWS_TEST_CASE( 519 | test_streaming_decoder_incoming_application_one_bool_header_pair_valid, 520 | s_test_streaming_decoder_incoming_application_one_bool_header_pair_valid_fn) 521 | 522 | static int s_test_streaming_decoder_incoming_multiple_messages_fn(struct aws_allocator *allocator, void *ctx) { 523 | (void)ctx; 524 | uint8_t test_data[] = { 525 | /* message 1 */ 526 | 0x00, 527 | 0x00, 528 | 0x00, 529 | 0x10, 530 | 0x00, 531 | 0x00, 532 | 0x00, 533 | 0x00, 534 | 0x05, 535 | 0xc2, 536 | 0x48, 537 | 0xeb, 538 | 0x7d, 539 | 0x98, 540 | 0xc8, 541 | 0xff, 542 | /* message 2 */ 543 | 0x00, 544 | 0x00, 545 | 0x00, 546 | 0x3D, 547 | 0x00, 548 | 0x00, 549 | 0x00, 550 | 0x20, 551 | 0x07, 552 | 0xFD, 553 | 0x83, 554 | 0x96, 555 | 0x0C, 556 | 'c', 557 | 'o', 558 | 'n', 559 | 't', 560 | 'e', 561 | 'n', 562 | 't', 563 | '-', 564 | 't', 565 | 'y', 566 | 'p', 567 | 'e', 568 | 0x07, 569 | 0x00, 570 | 0x10, 571 | 'a', 572 | 'p', 573 | 'p', 574 | 'l', 575 | 'i', 576 | 'c', 577 | 'a', 578 | 't', 579 | 'i', 580 | 'o', 581 | 'n', 582 | '/', 583 | 'j', 584 | 's', 585 | 'o', 586 | 'n', 587 | 0x7b, 588 | 0x27, 589 | 0x66, 590 | 0x6f, 591 | 0x6f, 592 | 0x27, 593 | 0x3a, 594 | 0x27, 595 | 0x62, 596 | 0x61, 597 | 0x72, 598 | 0x27, 599 | 0x7d, 600 | 0x8D, 601 | 0x9C, 602 | 0x08, 603 | 0xB1, 604 | }; 605 | 606 | size_t first_message_size = 0x10; 607 | size_t read_size = 7; /* make this a weird number to force edge case coverage in the parser. 608 | This will fall into the middle of message boundaries and preludes. */ 609 | 610 | struct test_decoder_data decoder_data = {.latest_payload = 0, .written = 0, .alloc = allocator, .latest_error = 0}; 611 | aws_event_stream_headers_list_init(&decoder_data.headers_list, allocator); 612 | 613 | struct aws_event_stream_streaming_decoder_options decoder_options = { 614 | .on_payload_segment = s_decoder_test_on_payload_segment, 615 | .on_prelude = s_decoder_test_on_prelude_received, 616 | .on_header = s_decoder_test_header_received, 617 | .on_complete = s_decoder_test_on_complete, 618 | .on_error = s_decoder_test_on_error, 619 | .user_data = &decoder_data}; 620 | 621 | struct aws_event_stream_streaming_decoder decoder; 622 | aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); 623 | 624 | size_t current_written = 0; 625 | int err_code = 0; 626 | while (current_written < first_message_size && !err_code) { 627 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data + current_written, read_size); 628 | err_code = aws_event_stream_streaming_decoder_pump(&decoder, &test_buf); 629 | current_written += read_size; 630 | } 631 | 632 | /* we should have written into the second message, but prior to the new prelude being found. 633 | check first message was parsed correctly */ 634 | ASSERT_SUCCESS(err_code, "Message validation should have succeeded"); 635 | ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); 636 | 637 | ASSERT_INT_EQUALS(0x00000010, decoder_data.latest_prelude.total_len, "Message length should have been 0x10"); 638 | ASSERT_INT_EQUALS(0x00000000, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x00"); 639 | ASSERT_INT_EQUALS(0x05c248eb, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0x8c335472"); 640 | ASSERT_INT_EQUALS(0, decoder_data.written, "No payload data should have been written"); 641 | ASSERT_UINT_EQUALS(0x7D98C8FF, decoder_data.message_crc); 642 | 643 | while (current_written < sizeof(test_data) && !err_code) { 644 | size_t to_write = 645 | current_written + read_size < sizeof(test_data) ? read_size : sizeof(test_data) - current_written; 646 | struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data + current_written, to_write); 647 | err_code = aws_event_stream_streaming_decoder_pump(&decoder, &test_buf); 648 | current_written += to_write; 649 | } 650 | 651 | /* Second message should have been found and fully parsed at this point. */ 652 | ASSERT_SUCCESS(err_code, "Message validation should have succeeded"); 653 | ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); 654 | 655 | ASSERT_INT_EQUALS(0x0000003D, decoder_data.latest_prelude.total_len, "Message length should have been 0x3D"); 656 | ASSERT_INT_EQUALS(0x00000020, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x20"); 657 | ASSERT_INT_EQUALS(0x07FD8396, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0x07FD8396"); 658 | 659 | const char *content_type = "content-type"; 660 | const char *content_type_value = "application/json"; 661 | 662 | struct aws_event_stream_header_value_pair latest_header; 663 | aws_array_list_get_at(&decoder_data.headers_list, &latest_header, 0); 664 | struct aws_byte_buf latest_header_value = aws_event_stream_header_value_as_string(&latest_header); 665 | 666 | ASSERT_BIN_ARRAYS_EQUALS( 667 | content_type, 668 | strlen(content_type), 669 | latest_header.header_name, 670 | latest_header.header_name_len, 671 | "header name should have been %s", 672 | content_type); 673 | ASSERT_BIN_ARRAYS_EQUALS( 674 | content_type_value, 675 | strlen(content_type_value), 676 | latest_header_value.buffer, 677 | latest_header_value.len, 678 | "header value should have been %s", 679 | content_type_value); 680 | 681 | const char *expected_str = "{'foo':'bar'}"; 682 | size_t payload_len = decoder_data.latest_prelude.total_len - AWS_EVENT_STREAM_PRELUDE_LENGTH - 683 | AWS_EVENT_STREAM_TRAILER_LENGTH - decoder_data.latest_prelude.headers_len; 684 | ASSERT_INT_EQUALS( 685 | strlen(expected_str), payload_len, "payload length should have been %d", (int)(strlen(expected_str))); 686 | 687 | ASSERT_BIN_ARRAYS_EQUALS( 688 | expected_str, 689 | strlen(expected_str), 690 | decoder_data.latest_payload, 691 | payload_len, 692 | "payload should have been %s", 693 | expected_str); 694 | 695 | if (decoder_data.latest_payload) { 696 | aws_mem_release(allocator, decoder_data.latest_payload); 697 | } 698 | ASSERT_UINT_EQUALS(0x8D9C08B1, decoder_data.message_crc); 699 | 700 | aws_event_stream_streaming_decoder_clean_up(&decoder); 701 | aws_event_stream_headers_list_cleanup(&decoder_data.headers_list); 702 | 703 | return 0; 704 | } 705 | 706 | AWS_TEST_CASE(test_streaming_decoder_incoming_multiple_messages, s_test_streaming_decoder_incoming_multiple_messages_fn) 707 | --------------------------------------------------------------------------------